From 6c75aa034dc6f8e9108cc3e29b592eb02795d31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Thu, 30 May 2024 11:04:22 +0200 Subject: [PATCH] Generics for interrupts and exceptions --- riscv-pac/macros/src/lib.rs | 8 +- riscv-pac/src/lib.rs | 16 +- riscv-pac/tests/ui/fail_wrong_repr.stderr | 2 +- riscv-pac/tests/ui/pass_test.rs | 4 +- riscv/src/interrupt.rs | 1 + riscv/src/register/mcause.rs | 169 ++++++++++------------ riscv/src/register/scause.rs | 164 +++++++++------------ 7 files changed, 159 insertions(+), 205 deletions(-) diff --git a/riscv-pac/macros/src/lib.rs b/riscv-pac/macros/src/lib.rs index a19714e8..b4211781 100644 --- a/riscv-pac/macros/src/lib.rs +++ b/riscv-pac/macros/src/lib.rs @@ -107,7 +107,7 @@ impl PacNumberEnum { fn from_number(number: #num_type) -> Result { if #valid_condition { // SAFETY: The number is valid for this enum - Ok(unsafe { core::mem::transmute(number) }) + Ok(unsafe { core::mem::transmute::<#num_type, Self>(number) }) } else { Err(number) } @@ -148,7 +148,7 @@ impl PacNumberEnum { /// ```rust /// use riscv_pac::*; /// -/// #[repr(u16)] +/// #[repr(usize)] /// #[pac_enum(unsafe ExceptionNumber)] /// #[derive(Clone, Copy, Debug, Eq, PartialEq)] /// enum Exception { @@ -190,8 +190,8 @@ pub fn pac_enum(attr: TokenStream, item: TokenStream) -> TokenStream { } let trait_impl = match attrs[1] { - "ExceptionNumber" => pac_enum.quote("ExceptionNumber", "u16", "MAX_EXCEPTION_NUMBER"), - "InterruptNumber" => pac_enum.quote("InterruptNumber", "u16", "MAX_INTERRUPT_NUMBER"), + "ExceptionNumber" => pac_enum.quote("ExceptionNumber", "usize", "MAX_EXCEPTION_NUMBER"), + "InterruptNumber" => pac_enum.quote("InterruptNumber", "usize", "MAX_INTERRUPT_NUMBER"), "PriorityNumber" => pac_enum.quote("PriorityNumber", "u8", "MAX_PRIORITY_NUMBER"), "HartIdNumber" => pac_enum.quote("HartIdNumber", "u16", "MAX_HART_ID_NUMBER"), _ => panic!("Unknown trait '{}'. Expected: 'ExceptionNumber', 'InterruptNumber', 'PriorityNumber', or 'HartIdNumber'", attrs[1]), diff --git a/riscv-pac/src/lib.rs b/riscv-pac/src/lib.rs index d1951c34..93c73d80 100644 --- a/riscv-pac/src/lib.rs +++ b/riscv-pac/src/lib.rs @@ -7,7 +7,7 @@ pub use riscv_pac_macros::*; /// /// This trait should be implemented by a peripheral access crate (PAC) on its enum of available /// exceptions for a specific device. Alternatively, the `riscv` crate provides a default -/// implementation for the RISC-V ISA. Each variant must convert to a `u16` of its exception number. +/// implementation for the RISC-V ISA. Each variant must convert to a `usize` of its exception number. /// /// # Safety /// @@ -19,21 +19,21 @@ pub use riscv_pac_macros::*; /// * `MAX_EXCEPTION_NUMBER` must coincide with the highest allowed exception number. pub unsafe trait ExceptionNumber: Copy { /// Highest number assigned to an exception. - const MAX_EXCEPTION_NUMBER: u16; + const MAX_EXCEPTION_NUMBER: usize; /// Converts an exception to its corresponding number. - fn number(self) -> u16; + fn number(self) -> usize; /// Tries to convert a number to a valid exception. /// If the conversion fails, it returns an error with the number back. - fn from_number(value: u16) -> Result; + fn from_number(value: usize) -> Result; } /// Trait for enums of target-specific interrupt numbers. /// /// This trait should be implemented by a peripheral access crate (PAC) on its enum of available /// interrupts for a specific device. Alternatively, the `riscv` crate provides a default -/// implementation for the RISC-V ISA. Each variant must convert to a `u16` of its interrupt number. +/// implementation for the RISC-V ISA. Each variant must convert to a `usize` of its interrupt number. /// /// # Safety /// @@ -45,14 +45,14 @@ pub unsafe trait ExceptionNumber: Copy { /// * `MAX_INTERRUPT_NUMBER` must coincide with the highest allowed interrupt number. pub unsafe trait InterruptNumber: Copy { /// Highest number assigned to an interrupt source. - const MAX_INTERRUPT_NUMBER: u16; + const MAX_INTERRUPT_NUMBER: usize; /// Converts an interrupt source to its corresponding number. - fn number(self) -> u16; + fn number(self) -> usize; /// Tries to convert a number to a valid interrupt source. /// If the conversion fails, it returns an error with the number back. - fn from_number(value: u16) -> Result; + fn from_number(value: usize) -> Result; } /// Marker trait for enums of target-specific core interrupt numbers. diff --git a/riscv-pac/tests/ui/fail_wrong_repr.stderr b/riscv-pac/tests/ui/fail_wrong_repr.stderr index 167a47ef..6a545f0e 100644 --- a/riscv-pac/tests/ui/fail_wrong_repr.stderr +++ b/riscv-pac/tests/ui/fail_wrong_repr.stderr @@ -4,6 +4,6 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- 1 | #[riscv_pac::pac_enum(unsafe InterruptNumber)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: source type: `u16` (16 bits) + = note: source type: `usize` (64 bits) = note: target type: `Interrupt` (8 bits) = note: this error originates in the attribute macro `riscv_pac::pac_enum` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/riscv-pac/tests/ui/pass_test.rs b/riscv-pac/tests/ui/pass_test.rs index 2001a986..2be913eb 100644 --- a/riscv-pac/tests/ui/pass_test.rs +++ b/riscv-pac/tests/ui/pass_test.rs @@ -1,6 +1,6 @@ use riscv_pac::*; -#[repr(u16)] +#[repr(usize)] #[pac_enum(unsafe ExceptionNumber)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum Exception { @@ -8,7 +8,7 @@ enum Exception { E3 = 3, } -#[repr(u16)] +#[repr(usize)] #[pac_enum(unsafe InterruptNumber)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum Interrupt { diff --git a/riscv/src/interrupt.rs b/riscv/src/interrupt.rs index fefdb14b..2db7976c 100644 --- a/riscv/src/interrupt.rs +++ b/riscv/src/interrupt.rs @@ -92,6 +92,7 @@ pub mod machine { r } } + pub mod supervisor { use crate::register::{sepc, sstatus}; diff --git a/riscv/src/register/mcause.rs b/riscv/src/register/mcause.rs index cdc09473..6d89c89f 100644 --- a/riscv/src/register/mcause.rs +++ b/riscv/src/register/mcause.rs @@ -3,27 +3,7 @@ use riscv_pac::CoreInterruptNumber; pub use riscv_pac::{ExceptionNumber, InterruptNumber}; // re-export useful riscv-pac traits -/// mcause register -#[derive(Clone, Copy, Debug)] -pub struct Mcause { - bits: usize, -} - -impl From for Mcause { - #[inline] - fn from(bits: usize) -> Self { - Self { bits } - } -} - -/// Trap Cause -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Trap { - Interrupt(Interrupt), - Exception(Exception), -} - -/// Interrupt +/// Standard M-mode RISC-V interrupts #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(usize)] pub enum Interrupt { @@ -33,10 +13,32 @@ pub enum Interrupt { MachineTimer = 7, SupervisorExternal = 9, MachineExternal = 11, - Unknown, } -/// Exception +/// SAFETY: `Interrupt` represents the standard RISC-V interrupts +unsafe impl InterruptNumber for Interrupt { + const MAX_INTERRUPT_NUMBER: usize = Self::MachineExternal as usize; + + #[inline] + fn number(self) -> usize { + self as usize + } + + #[inline] + fn from_number(value: usize) -> Result { + if value > 11 || value % 2 == 0 { + Err(value) + } else { + // SAFETY: valid interrupt number + unsafe { Ok(core::mem::transmute::(value)) } + } + } +} + +/// SAFETY: `Interrupt` represents the standard RISC-V core interrupts +unsafe impl CoreInterruptNumber for Interrupt {} + +/// Standard M-mode RISC-V exceptions #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(usize)] pub enum Exception { @@ -54,93 +56,52 @@ pub enum Exception { InstructionPageFault = 12, LoadPageFault = 13, StorePageFault = 15, - Unknown, -} - -impl From for Interrupt { - #[inline] - fn from(nr: usize) -> Self { - if nr > 11 || nr % 2 == 0 { - Self::Unknown - } else { - // SAFETY: valid interrupt number - unsafe { core::mem::transmute::(nr) } - } - } -} - -impl TryFrom for usize { - type Error = Interrupt; - - #[inline] - fn try_from(value: Interrupt) -> Result { - match value { - Interrupt::Unknown => Err(Self::Error::Unknown), - _ => Ok(value as Self), - } - } } -/// SAFETY: `Interrupt` represents the standard RISC-V interrupts -unsafe impl InterruptNumber for Interrupt { - const MAX_INTERRUPT_NUMBER: u16 = Self::MachineExternal as u16; +/// SAFETY: `Exception` represents the standard RISC-V exceptions +unsafe impl ExceptionNumber for Exception { + const MAX_EXCEPTION_NUMBER: usize = Self::StorePageFault as usize; #[inline] - fn number(self) -> u16 { - self as u16 + fn number(self) -> usize { + self as usize } #[inline] - fn from_number(value: u16) -> Result { - match (value as usize).into() { - Self::Unknown => Err(value), - value => Ok(value), - } - } -} - -/// SAFETY: `Interrupt` represents the standard RISC-V core interrupts -unsafe impl CoreInterruptNumber for Interrupt {} - -impl From for Exception { - #[inline] - fn from(nr: usize) -> Self { - if nr == 10 || nr == 14 || nr > 15 { - Self::Unknown + fn from_number(value: usize) -> Result { + if value == 10 || value == 14 || value > 15 { + Err(value) } else { // SAFETY: valid exception number - unsafe { core::mem::transmute::(nr) } + unsafe { Ok(core::mem::transmute::(value)) } } } } -impl TryFrom for usize { - type Error = Exception; - - #[inline] - fn try_from(value: Exception) -> Result { - match value { - Exception::Unknown => Err(Self::Error::Unknown), - _ => Ok(value as Self), - } - } +/// Trap Cause +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Trap { + Interrupt(I), + Exception(E), } -/// SAFETY: `Exception` represents the standard RISC-V exceptions -unsafe impl ExceptionNumber for Exception { - const MAX_EXCEPTION_NUMBER: u16 = Self::StorePageFault as u16; +/// Trap Error +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum TrapError { + InvalidInterrupt(usize), + InvalidException(usize), +} - #[inline] - fn number(self) -> u16 { - self as u16 - } +/// mcause register +#[derive(Clone, Copy, Debug)] +pub struct Mcause { + bits: usize, +} +impl From for Mcause { #[inline] - fn from_number(value: u16) -> Result { - match (value as usize).into() { - Self::Unknown => Err(value), - value => Ok(value), - } + fn from(bits: usize) -> Self { + Self { bits } } } @@ -157,16 +118,32 @@ impl Mcause { self.bits & !(1 << (usize::BITS as usize - 1)) } - /// Trap Cause + /// Try to get the trap cause #[inline] - pub fn cause(&self) -> Trap { + pub fn try_cause(&self) -> Result, TrapError> + where + I: CoreInterruptNumber, + E: ExceptionNumber, + { if self.is_interrupt() { - Trap::Interrupt(Interrupt::from(self.code())) + match I::from_number(self.code()) { + Ok(interrupt) => Ok(Trap::Interrupt(interrupt)), + Err(code) => Err(TrapError::InvalidInterrupt(code)), + } } else { - Trap::Exception(Exception::from(self.code())) + match E::from_number(self.code()) { + Ok(exception) => Ok(Trap::Exception(exception)), + Err(code) => Err(TrapError::InvalidException(code)), + } } } + /// Trap Cause + #[inline] + pub fn cause(&self) -> Trap { + self.try_cause().unwrap() + } + /// Is trap cause an interrupt. #[inline] pub fn is_interrupt(&self) -> bool { diff --git a/riscv/src/register/scause.rs b/riscv/src/register/scause.rs index d03a89d1..6379ddf9 100644 --- a/riscv/src/register/scause.rs +++ b/riscv/src/register/scause.rs @@ -3,19 +3,6 @@ use riscv_pac::CoreInterruptNumber; pub use riscv_pac::{ExceptionNumber, InterruptNumber}; // re-export useful riscv-pac traits -/// scause register -#[derive(Clone, Copy)] -pub struct Scause { - bits: usize, -} - -/// Trap Cause -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Trap { - Interrupt(Interrupt), - Exception(Exception), -} - /// Interrupt #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(usize)] @@ -23,9 +10,31 @@ pub enum Interrupt { SupervisorSoft = 1, SupervisorTimer = 5, SupervisorExternal = 9, - Unknown, } +/// SAFETY: `Interrupt` represents the standard RISC-V interrupts +unsafe impl InterruptNumber for Interrupt { + const MAX_INTERRUPT_NUMBER: usize = Self::SupervisorExternal as usize; + + #[inline] + fn number(self) -> usize { + self as usize + } + + #[inline] + fn from_number(value: usize) -> Result { + if value == 1 || value == 5 || value == 9 { + // SAFETY: valid interrupt number + Ok(unsafe { core::mem::transmute::(value) }) + } else { + Err(value) + } + } +} + +/// SAFETY: `Interrupt` represents the standard RISC-V core interrupts +unsafe impl CoreInterruptNumber for Interrupt {} + /// Exception #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(usize)] @@ -43,94 +52,46 @@ pub enum Exception { InstructionPageFault = 12, LoadPageFault = 13, StorePageFault = 15, - Unknown, -} - -impl From for Interrupt { - #[inline] - fn from(nr: usize) -> Self { - match nr { - 1 => Self::SupervisorSoft, - 5 => Self::SupervisorTimer, - 9 => Self::SupervisorExternal, - _ => Self::Unknown, - } - } -} - -impl TryFrom for usize { - type Error = Interrupt; - - #[inline] - fn try_from(value: Interrupt) -> Result { - match value { - Interrupt::Unknown => Err(Self::Error::Unknown), - _ => Ok(value as Self), - } - } } -/// SAFETY: `Interrupt` represents the standard RISC-V interrupts -unsafe impl InterruptNumber for Interrupt { - const MAX_INTERRUPT_NUMBER: u16 = Self::SupervisorExternal as u16; - - #[inline] - fn number(self) -> u16 { - self as u16 - } +/// SAFETY: `Exception` represents the standard RISC-V exceptions +unsafe impl ExceptionNumber for Exception { + const MAX_EXCEPTION_NUMBER: usize = Self::StorePageFault as usize; #[inline] - fn from_number(value: u16) -> Result { - match (value as usize).into() { - Self::Unknown => Err(value), - value => Ok(value), - } + fn number(self) -> usize { + self as usize } -} -/// SAFETY: `Interrupt` represents the standard RISC-V core interrupts -unsafe impl CoreInterruptNumber for Interrupt {} - -impl From for Exception { #[inline] - fn from(nr: usize) -> Self { - if nr == 10 || nr == 11 || nr == 14 || nr > 15 { - Self::Unknown + fn from_number(value: usize) -> Result { + if value == 10 || value == 11 || value == 14 || value > 15 { + Err(value) } else { // SAFETY: valid exception number - unsafe { core::mem::transmute::(nr) } + unsafe { Ok(core::mem::transmute::(value)) } } } } -impl TryFrom for usize { - type Error = Exception; - - #[inline] - fn try_from(value: Exception) -> Result { - match value { - Exception::Unknown => Err(Self::Error::Unknown), - _ => Ok(value as Self), - } - } +/// Trap Cause +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Trap { + Interrupt(I), + Exception(E), } -/// SAFETY: `Exception` represents the standard RISC-V exceptions -unsafe impl ExceptionNumber for Exception { - const MAX_EXCEPTION_NUMBER: u16 = Self::StorePageFault as u16; - - #[inline] - fn number(self) -> u16 { - self as u16 - } +/// Trap Error +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum TrapError { + InvalidInterrupt(usize), + InvalidException(usize), +} - #[inline] - fn from_number(value: u16) -> Result { - match (value as usize).into() { - Self::Unknown => Err(value), - value => Ok(value), - } - } +/// scause register +#[derive(Clone, Copy)] +pub struct Scause { + bits: usize, } impl Scause { @@ -146,16 +107,32 @@ impl Scause { self.bits & !(1 << (usize::BITS as usize - 1)) } - /// Trap Cause + /// Try to get the trap cause #[inline] - pub fn cause(&self) -> Trap { + pub fn try_cause(&self) -> Result, TrapError> + where + I: CoreInterruptNumber, + E: ExceptionNumber, + { if self.is_interrupt() { - Trap::Interrupt(Interrupt::from(self.code())) + match I::from_number(self.code()) { + Ok(interrupt) => Ok(Trap::Interrupt(interrupt)), + Err(code) => Err(TrapError::InvalidInterrupt(code)), + } } else { - Trap::Exception(Exception::from(self.code())) + match E::from_number(self.code()) { + Ok(exception) => Ok(Trap::Exception(exception)), + Err(code) => Err(TrapError::InvalidException(code)), + } } } + /// Trap Cause + #[inline] + pub fn cause(&self) -> Trap { + self.try_cause().unwrap() + } + /// Is trap cause an interrupt. #[inline] pub fn is_interrupt(&self) -> bool { @@ -180,13 +157,12 @@ pub unsafe fn write(bits: usize) { /// Set supervisor cause register to corresponding cause. #[inline] -pub unsafe fn set(cause: Trap) { +pub unsafe fn set(cause: Trap) { let bits = match cause { Trap::Interrupt(i) => { - let i = usize::try_from(i).expect("unknown interrupt"); - i | (1 << (usize::BITS as usize - 1)) // interrupt bit is 1 + i.number() | (1 << (usize::BITS as usize - 1)) // interrupt bit is 1 } - Trap::Exception(e) => usize::try_from(e).expect("unknown exception"), + Trap::Exception(e) => e.number(), }; _write(bits); }