use crate::{ecall0, ecall1, ecall3, SbiError};
pub const EXTENSION_ID: usize = 0x504D55;
#[inline]
pub fn num_counters() -> usize {
unsafe { ecall0(EXTENSION_ID, 0).unwrap() }
}
#[inline]
#[doc(alias = "counter_get_info", alias = "sbi_pmu_counter_get_info")]
pub fn counter_info(counter_idx: CounterIndex) -> Result<CounterInfo, SbiError> {
let res = unsafe { ecall1(counter_idx.0, EXTENSION_ID, 1) }?;
Ok(match (res as isize).is_positive() {
true => CounterInfo::Hardware {
csr_number: res & 0xFFF,
width: (res >> 12) & 0b0011_1111,
},
false => CounterInfo::Firmware,
})
}
#[inline]
#[doc(
alias = "counter_config_matching",
alias = "sbi_pmu_counter_config_matching"
)]
pub fn configure_matching_counters(
counter_mask: CounterIndexMask,
config_flags: CounterConfigurationFlags,
event_idx: EventIndex,
event_data: u64,
) -> Result<CounterIndex, SbiError> {
#[cfg(target_arch = "riscv64")]
let res = unsafe {
crate::ecall5(
counter_mask.base,
counter_mask.mask,
config_flags.0,
event_idx.0,
event_data as usize,
EXTENSION_ID,
2,
)
}?;
#[cfg(target_arch = "riscv32")]
let res = unsafe {
crate::ecall6(
counter_mask.base,
counter_mask.mask,
config_flags.0,
event_idx.0,
event_data as usize,
(event_data >> 32) as usize,
EXTENSION_ID,
2,
)
}?;
Ok(CounterIndex(res))
}
#[inline]
#[doc(alias = "counter_start", alias = "sbi_pmu_counter_start")]
pub fn start_counters(
counter_mask: CounterIndexMask,
start_flags: CounterStartFlags,
initial_value: u64,
) -> Result<(), SbiError> {
#[cfg(target_arch = "riscv64")]
unsafe {
crate::ecall4(
counter_mask.base,
counter_mask.mask,
start_flags.0,
initial_value as usize,
EXTENSION_ID,
3,
)
}?;
#[cfg(target_arch = "riscv32")]
unsafe {
crate::ecall5(
counter_mask.base,
counter_mask.mask,
start_flags.0,
initial_value as usize,
(initial_value >> 32) as usize,
EXTENSION_ID,
3,
)
}?;
Ok(())
}
#[inline]
#[doc(alias = "counter_stop", alias = "sbi_pmu_counter_stop")]
pub fn stop_counters(
counter_mask: CounterIndexMask,
stop_flags: CounterStopFlags,
) -> Result<(), SbiError> {
unsafe {
crate::ecall3(
counter_mask.base,
counter_mask.mask,
stop_flags.0,
EXTENSION_ID,
4,
)
.map(drop)
}
}
#[inline]
#[doc(alias = "counter_fw_read", alias = "sbi_pmu_counter_fw_read")]
pub fn read_firmware_counter(counter_idx: CounterIndex) -> Result<usize, SbiError> {
unsafe { ecall1(counter_idx.0, EXTENSION_ID, 5) }
}
#[inline]
#[doc(alias = "counter_fw_read_hi", alias = "sbi_pmu_counter_fw_read_hi")]
pub fn read_firmware_counter_hi(counter_idx: CounterIndex) -> Result<usize, SbiError> {
unsafe { ecall1(counter_idx.0, EXTENSION_ID, 6) }
}
#[inline]
#[doc(alias = "snapshot_set_shmem", alias = "sbi_pmu_snapshot_set_shmem")]
pub unsafe fn set_snapshot_shared_memory_region(
shmem_phys_lo: usize,
shmem_phys_hi: usize,
flags: SnapshotFlags,
) -> Result<usize, SbiError> {
unsafe { ecall3(shmem_phys_lo, shmem_phys_hi, flags.0, EXTENSION_ID, 6) }
}
#[inline]
#[doc(alias = "snapshot_set_shmem", alias = "sbi_pmu_snapshot_set_shmem")]
pub unsafe fn set_snapshot_shared_memory_region_ptr(
shared_memory_ptr: *mut SnapshotSharedMemory,
flags: SnapshotFlags,
) -> Result<usize, SbiError> {
unsafe { set_snapshot_shared_memory_region(shared_memory_ptr as usize, 0, flags) }
}
#[derive(Debug, Default, Clone, Copy)]
pub struct SnapshotFlags(usize);
impl SnapshotFlags {
pub const NONE: Self = Self(0);
}
#[derive(Clone)]
#[repr(C)]
pub struct SnapshotSharedMemory {
pub counter_overflow_bitmap: u64,
pub counter_values: [u64; 64],
_resv: [u8; 3576],
}
impl SnapshotSharedMemory {
pub fn new() -> Self {
Self::default()
}
}
impl core::default::Default for SnapshotSharedMemory {
fn default() -> Self {
Self {
counter_overflow_bitmap: 0,
counter_values: [0; 64],
_resv: [0; 3576],
}
}
}
impl core::fmt::Debug for SnapshotSharedMemory {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SnapshotSharedMemory")
.field("counter_overflow_bitmap", &self.counter_overflow_bitmap)
.field("counter_values", &self.counter_values)
.finish_non_exhaustive()
}
}
#[derive(Debug, Clone, Copy)]
pub struct CounterConfigurationFlags(usize);
impl CounterConfigurationFlags {
pub const NONE: Self = Self(0);
pub const SKIP_MATCH: Self = Self(1 << 0);
pub const CLEAR_VALUE: Self = Self(1 << 1);
pub const AUTO_START: Self = Self(1 << 2);
pub const SET_VUINH: Self = Self(1 << 3);
pub const VU_MODE_INHIBIT: Self = Self::SET_VUINH;
pub const SET_VSINH: Self = Self(1 << 4);
pub const VS_MODE_INHIBIT: Self = Self::SET_VSINH;
pub const SET_UINH: Self = Self(1 << 5);
pub const U_MODE_INHIBIT: Self = Self::SET_UINH;
pub const SET_SINH: Self = Self(1 << 6);
pub const S_MODE_INHIBIT: Self = Self::SET_SINH;
pub const SET_MINH: Self = Self(1 << 6);
pub const M_MODE_INHIBIT: Self = Self::SET_MINH;
}
impl core::ops::BitOr for CounterConfigurationFlags {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl core::ops::BitOrAssign for CounterConfigurationFlags {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl Default for CounterConfigurationFlags {
#[inline]
fn default() -> Self {
Self::NONE
}
}
pub struct CounterStartFlags(usize);
impl CounterStartFlags {
pub const NONE: Self = Self(0);
pub const SET_INIT_VALUE: Self = Self(1);
}
impl core::ops::BitOr for CounterStartFlags {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl core::ops::BitOrAssign for CounterStartFlags {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl Default for CounterStartFlags {
#[inline]
fn default() -> Self {
Self::NONE
}
}
pub struct CounterStopFlags(usize);
impl CounterStopFlags {
pub const NONE: Self = Self(0);
pub const RESET: Self = Self(1);
}
impl core::ops::BitOr for CounterStopFlags {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl core::ops::BitOrAssign for CounterStopFlags {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl Default for CounterStopFlags {
#[inline]
fn default() -> Self {
Self::NONE
}
}
pub struct CounterIndexMask {
base: usize,
mask: usize,
}
impl CounterIndexMask {
#[inline]
pub const fn empty() -> Self {
Self { base: 0, mask: 0 }
}
#[inline]
pub const fn new(base: CounterIndex) -> Self {
Self {
base: base.0,
mask: 0,
}
}
#[inline]
pub const fn from(counter_idx: CounterIndex) -> Self {
Self {
base: counter_idx.0,
mask: 1,
}
}
#[inline]
#[must_use]
pub const fn with(mut self, counter_idx: CounterIndex) -> Self {
if counter_idx.0 >= self.base && counter_idx.0 < (self.base + usize::BITS as usize) {
self.mask |= 1 << (counter_idx.0 - self.base);
}
self
}
}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct CounterIndex(usize);
impl CounterIndex {
#[inline]
pub fn new(idx: usize) -> Self {
Self(idx)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CounterInfo {
Hardware {
csr_number: usize,
width: usize,
},
Firmware,
}
mod sealed {
pub trait Sealed {}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct EventIndex(usize);
impl EventIndex {
#[inline]
pub fn new<T: EventType>(
#[allow(unused_variables)] event_type: T,
event_code: <T as EventType>::EventCode,
) -> Self {
Self(((T::TYPE_VALUE & 0b1111) << 16) | (event_code.to_code() as usize))
}
#[inline]
pub fn from_raw(event_type: u8, event_code: u16) -> Self {
Self(((usize::from(event_type) & 0b1111) << 16) | usize::from(event_code))
}
}
#[allow(missing_docs)]
pub trait EventType: sealed::Sealed {
const TYPE_VALUE: usize;
type EventCode: EventCode;
}
#[allow(missing_docs)]
pub trait EventCode: Sized + sealed::Sealed {
fn to_code(self) -> u16;
}
#[derive(Debug, Clone, Copy)]
pub struct HardwareGeneralEvent;
impl sealed::Sealed for HardwareGeneralEvent {}
impl EventType for HardwareGeneralEvent {
const TYPE_VALUE: usize = 0;
type EventCode = HardwareGeneralEventCode;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(missing_docs)]
#[non_exhaustive]
#[repr(u16)]
pub enum HardwareGeneralEventCode {
CpuCycles = 1,
Instructions = 2,
CacheReferences = 3,
CacheMisses = 4,
BranchInstructions = 5,
BranchMisses = 6,
BusCycles = 7,
StalledCyclesFrontend = 8,
StalledCyclesBackend = 9,
ReferenceCpuCycles = 10,
}
impl sealed::Sealed for HardwareGeneralEventCode {}
impl EventCode for HardwareGeneralEventCode {
#[inline]
fn to_code(self) -> u16 {
self as u16
}
}
#[derive(Debug, Clone, Copy)]
pub struct HardwareCacheEvent;
impl sealed::Sealed for HardwareCacheEvent {}
impl EventType for HardwareCacheEvent {
const TYPE_VALUE: usize = 1;
type EventCode = HardwareCacheEventCode;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct HardwareCacheEventCode(u16);
#[rustfmt::skip]
#[allow(missing_docs)]
impl HardwareCacheEventCode {
pub const LEVEL_1_DATA_READ_ACCESS: Self = Self::new(HardwareCacheEventCodeId::Level1Data, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Access);
pub const LEVEL_1_DATA_READ_MISS: Self = Self::new(HardwareCacheEventCodeId::Level1Data, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Miss);
pub const LEVEL_1_DATA_WRITE_ACCESS: Self = Self::new(HardwareCacheEventCodeId::Level1Data, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Access);
pub const LEVEL_1_DATA_WRITE_MISS: Self = Self::new(HardwareCacheEventCodeId::Level1Data, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Miss);
pub const LEVEL_1_DATA_PREFETCH_ACCESS: Self = Self::new(HardwareCacheEventCodeId::Level1Data, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Access);
pub const LEVEL_1_DATA_PREFETCH_MISS: Self = Self::new(HardwareCacheEventCodeId::Level1Data, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Miss);
pub const LEVEL_1_INSTRUCTION_READ_ACCESS: Self = Self::new(HardwareCacheEventCodeId::Level1Instruction, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Access);
pub const LEVEL_1_INSTRUCTION_READ_MISS: Self = Self::new(HardwareCacheEventCodeId::Level1Instruction, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Miss);
pub const LEVEL_1_INSTRUCTION_WRITE_ACCESS: Self = Self::new(HardwareCacheEventCodeId::Level1Instruction, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Access);
pub const LEVEL_1_INSTRUCTION_WRITE_MISS: Self = Self::new(HardwareCacheEventCodeId::Level1Instruction, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Miss);
pub const LEVEL_1_INSTRUCTION_PREFETCH_ACCESS: Self = Self::new(HardwareCacheEventCodeId::Level1Instruction, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Access);
pub const LEVEL_1_INSTRUCTION_PREFETCH_MISS: Self = Self::new(HardwareCacheEventCodeId::Level1Instruction, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Miss);
pub const LAST_LEVEL_READ_ACCESS: Self = Self::new(HardwareCacheEventCodeId::LastLevel, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Access);
pub const LAST_LEVEL_READ_MISS: Self = Self::new(HardwareCacheEventCodeId::LastLevel, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Miss);
pub const LAST_LEVEL_WRITE_ACCESS: Self = Self::new(HardwareCacheEventCodeId::LastLevel, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Access);
pub const LAST_LEVEL_WRITE_MISS: Self = Self::new(HardwareCacheEventCodeId::LastLevel, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Miss);
pub const LAST_LEVEL_PREFETCH_ACCESS: Self = Self::new(HardwareCacheEventCodeId::LastLevel, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Access);
pub const LAST_LEVEL_PREFETCH_MISS: Self = Self::new(HardwareCacheEventCodeId::LastLevel, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Miss);
pub const DATA_TLB_READ_ACCESS: Self = Self::new(HardwareCacheEventCodeId::DataTlb, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Access);
pub const DATA_TLB_READ_MISS: Self = Self::new(HardwareCacheEventCodeId::DataTlb, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Miss);
pub const DATA_TLB_WRITE_ACCESS: Self = Self::new(HardwareCacheEventCodeId::DataTlb, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Access);
pub const DATA_TLB_WRITE_MISS: Self = Self::new(HardwareCacheEventCodeId::DataTlb, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Miss);
pub const DATA_TLB_PREFETCH_ACCESS: Self = Self::new(HardwareCacheEventCodeId::DataTlb, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Access);
pub const DATA_TLB_PREFETCH_MISS: Self = Self::new(HardwareCacheEventCodeId::DataTlb, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Miss);
pub const INSTRUCTION_TLB_READ_ACCESS: Self = Self::new(HardwareCacheEventCodeId::InstructionTlb, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Access);
pub const INSTRUCTION_TLB_READ_MISS: Self = Self::new(HardwareCacheEventCodeId::InstructionTlb, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Miss);
pub const INSTRUCTION_TLB_WRITE_ACCESS: Self = Self::new(HardwareCacheEventCodeId::InstructionTlb, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Access);
pub const INSTRUCTION_TLB_WRITE_MISS: Self = Self::new(HardwareCacheEventCodeId::InstructionTlb, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Miss);
pub const INSTRUCTION_TLB_PREFETCH_ACCESS: Self = Self::new(HardwareCacheEventCodeId::InstructionTlb, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Access);
pub const INSTRUCTION_TLB_PREFETCH_MISS: Self = Self::new(HardwareCacheEventCodeId::InstructionTlb, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Miss);
pub const BRANCH_PREDICTOR_UNIT_READ_ACCESS: Self = Self::new(HardwareCacheEventCodeId::BranchPredictorUnit, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Access);
pub const BRANCH_PREDICTOR_UNIT_READ_MISS: Self = Self::new(HardwareCacheEventCodeId::BranchPredictorUnit, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Miss);
pub const BRANCH_PREDICTOR_UNIT_WRITE_ACCESS: Self = Self::new(HardwareCacheEventCodeId::BranchPredictorUnit, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Access);
pub const BRANCH_PREDICTOR_UNIT_WRITE_MISS: Self = Self::new(HardwareCacheEventCodeId::BranchPredictorUnit, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Miss);
pub const BRANCH_PREDICTOR_UNIT_PREFETCH_ACCESS: Self = Self::new(HardwareCacheEventCodeId::BranchPredictorUnit, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Access);
pub const BRANCH_PREDICTOR_UNIT_PREFETCH_MISS: Self = Self::new(HardwareCacheEventCodeId::BranchPredictorUnit, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Miss);
pub const NUMA_NODE_READ_ACCESS: Self = Self::new(HardwareCacheEventCodeId::NumaNode, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Access);
pub const NUMA_NODE_READ_MISS: Self = Self::new(HardwareCacheEventCodeId::NumaNode, HardwareCacheEventCodeOperationId::Read, HardwareCacheEventCodeResultId::Miss);
pub const NUMA_NODE_WRITE_ACCESS: Self = Self::new(HardwareCacheEventCodeId::NumaNode, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Access);
pub const NUMA_NODE_WRITE_MISS: Self = Self::new(HardwareCacheEventCodeId::NumaNode, HardwareCacheEventCodeOperationId::Write, HardwareCacheEventCodeResultId::Miss);
pub const NUMA_NODE_PREFETCH_ACCESS: Self = Self::new(HardwareCacheEventCodeId::NumaNode, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Access);
pub const NUMA_NODE_PREFETCH_MISS: Self = Self::new(HardwareCacheEventCodeId::NumaNode, HardwareCacheEventCodeOperationId::Prefetch, HardwareCacheEventCodeResultId::Miss);
#[inline]
pub const fn new(
id: HardwareCacheEventCodeId,
op: HardwareCacheEventCodeOperationId,
result: HardwareCacheEventCodeResultId,
) -> Self {
Self(((id as u16) << 3) | ((op as u16) << 1) | (result as u16))
}
}
impl sealed::Sealed for HardwareCacheEventCode {}
impl EventCode for HardwareCacheEventCode {
#[inline]
fn to_code(self) -> u16 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
#[repr(u16)]
pub enum HardwareCacheEventCodeId {
Level1Data = 0,
Level1Instruction = 1,
LastLevel = 2,
DataTlb = 3,
InstructionTlb = 4,
#[allow(missing_docs)]
BranchPredictorUnit = 5,
NumaNode = 6,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(missing_docs)]
#[repr(u16)]
pub enum HardwareCacheEventCodeOperationId {
Read = 0,
Write = 1,
Prefetch = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(missing_docs)]
#[repr(u16)]
pub enum HardwareCacheEventCodeResultId {
Access = 0,
Miss = 1,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct HardwareRawEvent;
impl sealed::Sealed for HardwareRawEvent {}
impl EventType for HardwareRawEvent {
const TYPE_VALUE: usize = 2;
type EventCode = HardwareRawEventCode;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct HardwareRawEventCode;
impl sealed::Sealed for HardwareRawEventCode {}
impl EventCode for HardwareRawEventCode {
#[inline]
fn to_code(self) -> u16 {
0
}
}
#[derive(Debug, Clone, Copy)]
pub struct FirmwareEvent;
impl sealed::Sealed for FirmwareEvent {}
impl EventType for FirmwareEvent {
const TYPE_VALUE: usize = 0xF;
type EventCode = FirmwareEventCode;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[allow(missing_docs)]
#[non_exhaustive]
#[repr(u16)]
pub enum FirmwareEventCode {
MisalignedLoad = 0,
MisalignedStore = 1,
AccessLoad = 2,
AccessStore = 3,
IllegalInstruction = 4,
SetTimer = 5,
IpiSent = 6,
IpiReceived = 7,
FenceISent = 8,
FenceIReceived = 9,
SfenceVmaSent = 10,
SfenceVmaReceived = 11,
SfenceVmaAsidSent = 12,
SfenceVmaAsidReceived = 13,
HfenceGvmaSent = 14,
HfenceGvmaReceived = 15,
HfenceGvmaVmidSent = 16,
HfenceGvmaVmidReceived = 17,
HfenceVvmaSent = 18,
HfenceVvmaReceived = 19,
HfenceVvmaAsidSent = 20,
HfenceVvmaAsidReceived = 21,
Platform = 65535,
}
impl sealed::Sealed for FirmwareEventCode {}
impl EventCode for FirmwareEventCode {
#[inline]
fn to_code(self) -> u16 {
self as u16
}
}