diff --git a/riscv/src/interrupt.rs b/riscv/src/interrupt.rs index e77c7915..90d9a1da 100644 --- a/riscv/src/interrupt.rs +++ b/riscv/src/interrupt.rs @@ -2,188 +2,70 @@ // NOTE: Adapted from cortex-m/src/interrupt.rs -pub mod machine { - use crate::register::{mepc, mstatus}; +pub use riscv_pac::{CoreInterruptNumber, ExceptionNumber, InterruptNumber}; // re-export useful riscv-pac traits - /// Disables all interrupts in the current hart (machine mode). - #[inline] - pub fn disable() { - // SAFETY: It is safe to disable interrupts - unsafe { mstatus::clear_mie() } - } +pub mod machine; +pub mod supervisor; - /// Enables all the interrupts in the current hart (machine mode). - /// - /// # Safety - /// - /// Do not call this function inside a critical section. - #[inline] - pub unsafe fn enable() { - mstatus::set_mie() - } - - /// Execute closure `f` with interrupts disabled in the current hart (machine mode). - /// - /// This method does not synchronise multiple harts, so it is not suitable for - /// using as a critical section. See the `critical-section` crate for a cross-platform - /// way to enter a critical section which provides a `CriticalSection` token. - /// - /// This crate provides an implementation for `critical-section` suitable for single-hart systems, - /// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature. - #[inline] - pub fn free(f: F) -> R - where - F: FnOnce() -> R, - { - let mstatus = mstatus::read(); +#[cfg(not(feature = "s-mode"))] +pub use machine::*; +#[cfg(feature = "s-mode")] +pub use supervisor::*; - // disable interrupts - disable(); +/// Trap Cause +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Trap { + Interrupt(I), + Exception(E), +} - let r = f(); +/// Trap Error +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum TrapError { + InvalidInterrupt(usize), + InvalidException(usize), +} - // If the interrupts were active before our `disable` call, then re-enable - // them. Otherwise, keep them disabled - if mstatus.mie() { - unsafe { enable() }; +impl Trap { + /// Converts a target-specific trap cause to a generic trap cause + #[inline] + pub fn from(trap: Trap) -> Self { + match trap { + Trap::Interrupt(interrupt) => Trap::Interrupt(interrupt.number()), + Trap::Exception(exception) => Trap::Exception(exception.number()), } - - r } - /// Execute closure `f` with interrupts enabled in the current hart (machine mode). - /// - /// This method is assumed to be called within an interrupt handler, and allows - /// nested interrupts to occur. After the closure `f` is executed, the [`mstatus`] - /// and [`mepc`] registers are properly restored to their previous values. - /// - /// # Safety - /// - /// - Do not call this function inside a critical section. - /// - This method is assumed to be called within an interrupt handler. - /// - Make sure to clear the interrupt flag that caused the interrupt before calling - /// this method. Otherwise, the interrupt will be re-triggered before executing `f`. + /// Tries to convert the generic trap cause to a target-specific trap cause #[inline] - pub unsafe fn nested(f: F) -> R + pub fn try_into(self) -> Result, TrapError> where - F: FnOnce() -> R, + I: CoreInterruptNumber, + E: ExceptionNumber, { - let mstatus = mstatus::read(); - let mepc = mepc::read(); - - // enable interrupts to allow nested interrupts - enable(); - - let r = f(); - - // If the interrupts were inactive before our `enable` call, then re-disable - // them. Otherwise, keep them enabled - if !mstatus.mie() { - disable(); + match self { + Trap::Interrupt(code) => match I::from_number(code) { + Ok(interrupt) => Ok(Trap::Interrupt(interrupt)), + Err(code) => Err(TrapError::InvalidInterrupt(code)), + }, + Trap::Exception(code) => match E::from_number(code) { + Ok(exception) => Ok(Trap::Exception(exception)), + Err(code) => Err(TrapError::InvalidException(code)), + }, } - - // Restore MSTATUS.PIE, MSTATUS.MPP, and SEPC - if mstatus.mpie() { - mstatus::set_mpie(); - } - mstatus::set_mpp(mstatus.mpp()); - mepc::write(mepc); - - r } } -pub mod supervisor { - use crate::register::{sepc, sstatus}; - - /// Disables all interrupts in the current hart (supervisor mode). - #[inline] - pub fn disable() { - // SAFETY: It is safe to disable interrupts - unsafe { sstatus::clear_sie() } - } - - /// Enables all the interrupts in the current hart (supervisor mode). - /// - /// # Safety - /// - /// Do not call this function inside a critical section. +impl Trap { + /// Converts a target-specific trap cause to a generic trap cause #[inline] - pub unsafe fn enable() { - sstatus::set_sie() + pub fn into(self) -> Trap { + Trap::from(self) } - /// Execute closure `f` with interrupts disabled in the current hart (supervisor mode). - /// - /// This method does not synchronise multiple harts, so it is not suitable for - /// using as a critical section. See the `critical-section` crate for a cross-platform - /// way to enter a critical section which provides a `CriticalSection` token. - /// - /// This crate provides an implementation for `critical-section` suitable for single-hart systems, - /// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature. + /// Tries to convert the generic trap cause to a target-specific trap cause #[inline] - pub fn free(f: F) -> R - where - F: FnOnce() -> R, - { - let sstatus = sstatus::read(); - - // disable interrupts - disable(); - - let r = f(); - - // If the interrupts were active before our `disable` call, then re-enable - // them. Otherwise, keep them disabled - if sstatus.sie() { - unsafe { enable() }; - } - - r - } - - /// Execute closure `f` with interrupts enabled in the current hart (supervisor mode). - /// This method is assumed to be called within an interrupt handler, and allows - /// nested interrupts to occur. After the closure `f` is executed, the [`sstatus`] - /// and [`sepc`] registers are properly restored to their previous values. - /// - /// # Safety - /// - /// - Do not call this function inside a critical section. - /// - This method is assumed to be called within an interrupt handler. - /// - Make sure to clear the interrupt flag that caused the interrupt before calling - /// this method. Otherwise, the interrupt will be re-triggered before executing `f`. - #[inline] - pub unsafe fn nested(f: F) -> R - where - F: FnOnce() -> R, - { - let sstatus = sstatus::read(); - let sepc = sepc::read(); - - // enable interrupts to allow nested interrupts - enable(); - - let r = f(); - - // If the interrupts were inactive before our `enable` call, then re-disable - // them. Otherwise, keep them enabled - if !sstatus.sie() { - disable(); - } - - // Restore SSTATUS.SPIE, SSTATUS.SPP, and SEPC - if sstatus.spie() { - sstatus::set_spie(); - } - sstatus::set_spp(sstatus.spp()); - sepc::write(sepc); - - r + pub fn try_from(trap: Trap) -> Result { + trap.try_into() } } - -#[cfg(not(feature = "s-mode"))] -pub use machine::*; -#[cfg(feature = "s-mode")] -pub use supervisor::*; diff --git a/riscv/src/interrupt/machine.rs b/riscv/src/interrupt/machine.rs new file mode 100644 index 00000000..40233f56 --- /dev/null +++ b/riscv/src/interrupt/machine.rs @@ -0,0 +1,269 @@ +use crate::{ + interrupt::{Trap, TrapError}, + register::{mcause, mepc, mstatus}, +}; +use riscv_pac::{CoreInterruptNumber, ExceptionNumber, InterruptNumber}; + +/// Standard M-mode RISC-V interrupts +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(usize)] +pub enum Interrupt { + SupervisorSoft = 1, + MachineSoft = 3, + SupervisorTimer = 5, + MachineTimer = 7, + SupervisorExternal = 9, + MachineExternal = 11, +} + +/// 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 { + match value { + 1 => Ok(Self::SupervisorSoft), + 3 => Ok(Self::MachineSoft), + 5 => Ok(Self::SupervisorTimer), + 7 => Ok(Self::MachineTimer), + 9 => Ok(Self::SupervisorExternal), + 11 => Ok(Self::MachineExternal), + _ => Err(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 { + InstructionMisaligned = 0, + InstructionFault = 1, + IllegalInstruction = 2, + Breakpoint = 3, + LoadMisaligned = 4, + LoadFault = 5, + StoreMisaligned = 6, + StoreFault = 7, + UserEnvCall = 8, + SupervisorEnvCall = 9, + MachineEnvCall = 11, + InstructionPageFault = 12, + LoadPageFault = 13, + StorePageFault = 15, +} + +/// 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) -> usize { + self as usize + } + + #[inline] + fn from_number(value: usize) -> Result { + match value { + 0 => Ok(Self::InstructionMisaligned), + 1 => Ok(Self::InstructionFault), + 2 => Ok(Self::IllegalInstruction), + 3 => Ok(Self::Breakpoint), + 4 => Ok(Self::LoadMisaligned), + 5 => Ok(Self::LoadFault), + 6 => Ok(Self::StoreMisaligned), + 7 => Ok(Self::StoreFault), + 8 => Ok(Self::UserEnvCall), + 9 => Ok(Self::SupervisorEnvCall), + 11 => Ok(Self::MachineEnvCall), + 12 => Ok(Self::InstructionPageFault), + 13 => Ok(Self::LoadPageFault), + 15 => Ok(Self::StorePageFault), + _ => Err(value), + } + } +} + +/// Disables all interrupts in the current hart (machine mode). +#[inline] +pub fn disable() { + // SAFETY: It is safe to disable interrupts + unsafe { mstatus::clear_mie() } +} + +/// Enables all the interrupts in the current hart (machine mode). +/// +/// # Safety +/// +/// Do not call this function inside a critical section. +#[inline] +pub unsafe fn enable() { + mstatus::set_mie() +} + +/// Retrieves the cause of a trap in the current hart (machine mode). +/// +/// This function expects the target-specific interrupt and exception types. +/// If the raw cause is not a valid interrupt or exception for the target, it returns an error. +#[inline] +pub fn try_cause() -> Result, TrapError> { + mcause::read().cause().try_into() +} + +/// Retrieves the cause of a trap in the current hart (machine mode). +/// +/// This function expects the target-specific interrupt and exception types. +/// If the raw cause is not a valid interrupt or exception for the target, it panics. +#[inline] +pub fn cause() -> Trap { + try_cause().unwrap() +} + +/// Execute closure `f` with interrupts disabled in the current hart (machine mode). +/// +/// This method does not synchronise multiple harts, so it is not suitable for +/// using as a critical section. See the `critical-section` crate for a cross-platform +/// way to enter a critical section which provides a `CriticalSection` token. +/// +/// This crate provides an implementation for `critical-section` suitable for single-hart systems, +/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature. +#[inline] +pub fn free(f: F) -> R +where + F: FnOnce() -> R, +{ + let mstatus = mstatus::read(); + + // disable interrupts + disable(); + + let r = f(); + + // If the interrupts were active before our `disable` call, then re-enable + // them. Otherwise, keep them disabled + if mstatus.mie() { + unsafe { enable() }; + } + + r +} + +/// Execute closure `f` with interrupts enabled in the current hart (machine mode). +/// +/// This method is assumed to be called within an interrupt handler, and allows +/// nested interrupts to occur. After the closure `f` is executed, the [`mstatus`] +/// and [`mepc`] registers are properly restored to their previous values. +/// +/// # Safety +/// +/// - Do not call this function inside a critical section. +/// - This method is assumed to be called within an interrupt handler. +/// - Make sure to clear the interrupt flag that caused the interrupt before calling +/// this method. Otherwise, the interrupt will be re-triggered before executing `f`. +#[inline] +pub unsafe fn nested(f: F) -> R +where + F: FnOnce() -> R, +{ + let mstatus = mstatus::read(); + let mepc = mepc::read(); + + // enable interrupts to allow nested interrupts + enable(); + + let r = f(); + + // If the interrupts were inactive before our `enable` call, then re-disable + // them. Otherwise, keep them enabled + if !mstatus.mie() { + disable(); + } + + // Restore MSTATUS.PIE, MSTATUS.MPP, and SEPC + if mstatus.mpie() { + mstatus::set_mpie(); + } + mstatus::set_mpp(mstatus.mpp()); + mepc::write(mepc); + + r +} + +#[cfg(test)] +mod test { + use super::*; + use Exception::*; + use Interrupt::*; + + #[test] + fn test_interrupt() { + assert_eq!(Interrupt::from_number(1), Ok(SupervisorSoft)); + assert_eq!(Interrupt::from_number(2), Err(2)); + assert_eq!(Interrupt::from_number(3), Ok(MachineSoft)); + assert_eq!(Interrupt::from_number(4), Err(4)); + assert_eq!(Interrupt::from_number(5), Ok(SupervisorTimer)); + assert_eq!(Interrupt::from_number(6), Err(6)); + assert_eq!(Interrupt::from_number(7), Ok(MachineTimer)); + assert_eq!(Interrupt::from_number(8), Err(8)); + assert_eq!(Interrupt::from_number(9), Ok(SupervisorExternal)); + assert_eq!(Interrupt::from_number(10), Err(10)); + assert_eq!(Interrupt::from_number(11), Ok(MachineExternal)); + assert_eq!(Interrupt::from_number(12), Err(12)); + + assert_eq!(SupervisorSoft.number(), 1); + assert_eq!(MachineSoft.number(), 3); + assert_eq!(SupervisorTimer.number(), 5); + assert_eq!(MachineTimer.number(), 7); + assert_eq!(SupervisorExternal.number(), 9); + assert_eq!(MachineExternal.number(), 11); + + assert_eq!(MachineExternal.number(), Interrupt::MAX_INTERRUPT_NUMBER) + } + + #[test] + fn test_exception() { + assert_eq!(Exception::from_number(0), Ok(InstructionMisaligned)); + assert_eq!(Exception::from_number(1), Ok(InstructionFault)); + assert_eq!(Exception::from_number(2), Ok(IllegalInstruction)); + assert_eq!(Exception::from_number(3), Ok(Breakpoint)); + assert_eq!(Exception::from_number(4), Ok(LoadMisaligned)); + assert_eq!(Exception::from_number(5), Ok(LoadFault)); + assert_eq!(Exception::from_number(6), Ok(StoreMisaligned)); + assert_eq!(Exception::from_number(7), Ok(StoreFault)); + assert_eq!(Exception::from_number(8), Ok(UserEnvCall)); + assert_eq!(Exception::from_number(9), Ok(SupervisorEnvCall)); + assert_eq!(Exception::from_number(10), Err(10)); + assert_eq!(Exception::from_number(11), Ok(MachineEnvCall)); + assert_eq!(Exception::from_number(12), Ok(InstructionPageFault)); + assert_eq!(Exception::from_number(13), Ok(LoadPageFault)); + assert_eq!(Exception::from_number(14), Err(14)); + assert_eq!(Exception::from_number(15), Ok(StorePageFault)); + assert_eq!(Exception::from_number(16), Err(16)); + + assert_eq!(InstructionMisaligned.number(), 0); + assert_eq!(InstructionFault.number(), 1); + assert_eq!(IllegalInstruction.number(), 2); + assert_eq!(Breakpoint.number(), 3); + assert_eq!(LoadMisaligned.number(), 4); + assert_eq!(LoadFault.number(), 5); + assert_eq!(StoreMisaligned.number(), 6); + assert_eq!(StoreFault.number(), 7); + assert_eq!(UserEnvCall.number(), 8); + assert_eq!(SupervisorEnvCall.number(), 9); + assert_eq!(MachineEnvCall.number(), 11); + assert_eq!(InstructionPageFault.number(), 12); + assert_eq!(LoadPageFault.number(), 13); + assert_eq!(StorePageFault.number(), 15); + + assert_eq!(StorePageFault.number(), Exception::MAX_EXCEPTION_NUMBER) + } +} diff --git a/riscv/src/interrupt/supervisor.rs b/riscv/src/interrupt/supervisor.rs new file mode 100644 index 00000000..1d032e6f --- /dev/null +++ b/riscv/src/interrupt/supervisor.rs @@ -0,0 +1,256 @@ +use crate::{ + interrupt::{Trap, TrapError}, + register::{scause, sepc, sstatus}, +}; +use riscv_pac::{CoreInterruptNumber, ExceptionNumber, InterruptNumber}; + +/// Interrupt +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(usize)] +pub enum Interrupt { + SupervisorSoft = 1, + SupervisorTimer = 5, + SupervisorExternal = 9, +} + +/// 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 { + match value { + 1 => Ok(Self::SupervisorSoft), + 5 => Ok(Self::SupervisorTimer), + 9 => Ok(Self::SupervisorExternal), + _ => 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)] +pub enum Exception { + InstructionMisaligned = 0, + InstructionFault = 1, + IllegalInstruction = 2, + Breakpoint = 3, + LoadMisaligned = 4, + LoadFault = 5, + StoreMisaligned = 6, + StoreFault = 7, + UserEnvCall = 8, + SupervisorEnvCall = 9, + InstructionPageFault = 12, + LoadPageFault = 13, + StorePageFault = 15, +} + +/// 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) -> usize { + self as usize + } + + #[inline] + fn from_number(value: usize) -> Result { + match value { + 0 => Ok(Self::InstructionMisaligned), + 1 => Ok(Self::InstructionFault), + 2 => Ok(Self::IllegalInstruction), + 3 => Ok(Self::Breakpoint), + 4 => Ok(Self::LoadMisaligned), + 5 => Ok(Self::LoadFault), + 6 => Ok(Self::StoreMisaligned), + 7 => Ok(Self::StoreFault), + 8 => Ok(Self::UserEnvCall), + 9 => Ok(Self::SupervisorEnvCall), + 12 => Ok(Self::InstructionPageFault), + 13 => Ok(Self::LoadPageFault), + 15 => Ok(Self::StorePageFault), + _ => Err(value), + } + } +} + +/// Disables all interrupts in the current hart (supervisor mode). +#[inline] +pub fn disable() { + // SAFETY: It is safe to disable interrupts + unsafe { sstatus::clear_sie() } +} + +/// Enables all the interrupts in the current hart (supervisor mode). +/// +/// # Safety +/// +/// Do not call this function inside a critical section. +#[inline] +pub unsafe fn enable() { + sstatus::set_sie() +} + +/// Retrieves the cause of a trap in the current hart (supervisor mode). +/// +/// This function expects the target-specific interrupt and exception types. +/// If the raw cause is not a valid interrupt or exception for the target, it returns an error. +#[inline] +pub fn try_cause() -> Result, TrapError> { + scause::read().cause().try_into() +} + +/// Retrieves the cause of a trap in the current hart (supervisor mode). +/// +/// This function expects the target-specific interrupt and exception types. +/// If the raw cause is not a valid interrupt or exception for the target, it panics. +#[inline] +pub fn cause() -> Trap { + try_cause().unwrap() +} + +/// Execute closure `f` with interrupts disabled in the current hart (supervisor mode). +/// +/// This method does not synchronise multiple harts, so it is not suitable for +/// using as a critical section. See the `critical-section` crate for a cross-platform +/// way to enter a critical section which provides a `CriticalSection` token. +/// +/// This crate provides an implementation for `critical-section` suitable for single-hart systems, +/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature. +#[inline] +pub fn free(f: F) -> R +where + F: FnOnce() -> R, +{ + let sstatus = sstatus::read(); + + // disable interrupts + disable(); + + let r = f(); + + // If the interrupts were active before our `disable` call, then re-enable + // them. Otherwise, keep them disabled + if sstatus.sie() { + unsafe { enable() }; + } + + r +} + +/// Execute closure `f` with interrupts enabled in the current hart (supervisor mode). +/// This method is assumed to be called within an interrupt handler, and allows +/// nested interrupts to occur. After the closure `f` is executed, the [`sstatus`] +/// and [`sepc`] registers are properly restored to their previous values. +/// +/// # Safety +/// +/// - Do not call this function inside a critical section. +/// - This method is assumed to be called within an interrupt handler. +/// - Make sure to clear the interrupt flag that caused the interrupt before calling +/// this method. Otherwise, the interrupt will be re-triggered before executing `f`. +#[inline] +pub unsafe fn nested(f: F) -> R +where + F: FnOnce() -> R, +{ + let sstatus = sstatus::read(); + let sepc = sepc::read(); + + // enable interrupts to allow nested interrupts + enable(); + + let r = f(); + + // If the interrupts were inactive before our `enable` call, then re-disable + // them. Otherwise, keep them enabled + if !sstatus.sie() { + disable(); + } + + // Restore SSTATUS.SPIE, SSTATUS.SPP, and SEPC + if sstatus.spie() { + sstatus::set_spie(); + } + sstatus::set_spp(sstatus.spp()); + sepc::write(sepc); + + r +} + +#[cfg(test)] +mod test { + use super::*; + use Exception::*; + use Interrupt::*; + + #[test] + fn test_interrupt() { + assert_eq!(Interrupt::from_number(1), Ok(SupervisorSoft)); + assert_eq!(Interrupt::from_number(2), Err(2)); + assert_eq!(Interrupt::from_number(3), Err(3)); + assert_eq!(Interrupt::from_number(4), Err(4)); + assert_eq!(Interrupt::from_number(5), Ok(SupervisorTimer)); + assert_eq!(Interrupt::from_number(6), Err(6)); + assert_eq!(Interrupt::from_number(7), Err(7)); + assert_eq!(Interrupt::from_number(8), Err(8)); + assert_eq!(Interrupt::from_number(9), Ok(SupervisorExternal)); + assert_eq!(Interrupt::from_number(10), Err(10)); + assert_eq!(Interrupt::from_number(11), Err(11)); + assert_eq!(Interrupt::from_number(12), Err(12)); + + assert_eq!(SupervisorSoft.number(), 1); + assert_eq!(SupervisorTimer.number(), 5); + assert_eq!(SupervisorExternal.number(), 9); + + assert_eq!(SupervisorExternal.number(), Interrupt::MAX_INTERRUPT_NUMBER) + } + + #[test] + fn test_exception() { + assert_eq!(Exception::from_number(0), Ok(InstructionMisaligned)); + assert_eq!(Exception::from_number(1), Ok(InstructionFault)); + assert_eq!(Exception::from_number(2), Ok(IllegalInstruction)); + assert_eq!(Exception::from_number(3), Ok(Breakpoint)); + assert_eq!(Exception::from_number(4), Ok(LoadMisaligned)); + assert_eq!(Exception::from_number(5), Ok(LoadFault)); + assert_eq!(Exception::from_number(6), Ok(StoreMisaligned)); + assert_eq!(Exception::from_number(7), Ok(StoreFault)); + assert_eq!(Exception::from_number(8), Ok(UserEnvCall)); + assert_eq!(Exception::from_number(9), Ok(SupervisorEnvCall)); + assert_eq!(Exception::from_number(10), Err(10)); + assert_eq!(Exception::from_number(11), Err(11)); + assert_eq!(Exception::from_number(12), Ok(InstructionPageFault)); + assert_eq!(Exception::from_number(13), Ok(LoadPageFault)); + assert_eq!(Exception::from_number(14), Err(14)); + assert_eq!(Exception::from_number(15), Ok(StorePageFault)); + assert_eq!(Exception::from_number(16), Err(16)); + + assert_eq!(InstructionMisaligned.number(), 0); + assert_eq!(InstructionFault.number(), 1); + assert_eq!(IllegalInstruction.number(), 2); + assert_eq!(Breakpoint.number(), 3); + assert_eq!(LoadMisaligned.number(), 4); + assert_eq!(LoadFault.number(), 5); + assert_eq!(StoreMisaligned.number(), 6); + assert_eq!(StoreFault.number(), 7); + assert_eq!(UserEnvCall.number(), 8); + assert_eq!(SupervisorEnvCall.number(), 9); + assert_eq!(InstructionPageFault.number(), 12); + assert_eq!(LoadPageFault.number(), 13); + assert_eq!(StorePageFault.number(), 15); + + assert_eq!(StorePageFault.number(), Exception::MAX_EXCEPTION_NUMBER) + } +} diff --git a/riscv/src/register/mcause.rs b/riscv/src/register/mcause.rs index 69924b91..fde22af4 100644 --- a/riscv/src/register/mcause.rs +++ b/riscv/src/register/mcause.rs @@ -1,110 +1,6 @@ //! mcause register -use riscv_pac::CoreInterruptNumber; -pub use riscv_pac::{ExceptionNumber, InterruptNumber}; // re-export useful riscv-pac traits - -/// Standard M-mode RISC-V interrupts -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(usize)] -pub enum Interrupt { - SupervisorSoft = 1, - MachineSoft = 3, - SupervisorTimer = 5, - MachineTimer = 7, - SupervisorExternal = 9, - MachineExternal = 11, -} - -/// 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 { - match value { - 1 => Ok(Self::SupervisorSoft), - 3 => Ok(Self::MachineSoft), - 5 => Ok(Self::SupervisorTimer), - 7 => Ok(Self::MachineTimer), - 9 => Ok(Self::SupervisorExternal), - 11 => Ok(Self::MachineExternal), - _ => Err(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 { - InstructionMisaligned = 0, - InstructionFault = 1, - IllegalInstruction = 2, - Breakpoint = 3, - LoadMisaligned = 4, - LoadFault = 5, - StoreMisaligned = 6, - StoreFault = 7, - UserEnvCall = 8, - SupervisorEnvCall = 9, - MachineEnvCall = 11, - InstructionPageFault = 12, - LoadPageFault = 13, - StorePageFault = 15, -} - -/// 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) -> usize { - self as usize - } - - #[inline] - fn from_number(value: usize) -> Result { - match value { - 0 => Ok(Self::InstructionMisaligned), - 1 => Ok(Self::InstructionFault), - 2 => Ok(Self::IllegalInstruction), - 3 => Ok(Self::Breakpoint), - 4 => Ok(Self::LoadMisaligned), - 5 => Ok(Self::LoadFault), - 6 => Ok(Self::StoreMisaligned), - 7 => Ok(Self::StoreFault), - 8 => Ok(Self::UserEnvCall), - 9 => Ok(Self::SupervisorEnvCall), - 11 => Ok(Self::MachineEnvCall), - 12 => Ok(Self::InstructionPageFault), - 13 => Ok(Self::LoadPageFault), - 15 => Ok(Self::StorePageFault), - _ => Err(value), - } - } -} - -/// Trap Cause -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Trap { - Interrupt(I), - Exception(E), -} - -/// Trap Error -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum TrapError { - InvalidInterrupt(usize), - InvalidException(usize), -} +pub use crate::interrupt::Trap; /// mcause register #[derive(Clone, Copy, Debug)] @@ -132,32 +28,21 @@ impl Mcause { self.bits & !(1 << (usize::BITS as usize - 1)) } - /// Try to get the trap cause + /// Returns the trap cause represented by this register. + /// + /// # Note + /// + /// This method returns a **raw trap cause**, which means that values are represented as `usize`. + /// To get a target-specific trap cause, use [`Trap::try_into`] with your target-specific M-Mode trap cause types. #[inline] - pub fn try_cause(&self) -> Result, TrapError> - where - I: CoreInterruptNumber, - E: ExceptionNumber, - { + pub fn cause(&self) -> Trap { if self.is_interrupt() { - match I::from_number(self.code()) { - Ok(interrupt) => Ok(Trap::Interrupt(interrupt)), - Err(code) => Err(TrapError::InvalidInterrupt(code)), - } + Trap::Interrupt(self.code()) } else { - match E::from_number(self.code()) { - Ok(exception) => Ok(Trap::Exception(exception)), - Err(code) => Err(TrapError::InvalidException(code)), - } + Trap::Exception(self.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 { @@ -172,73 +57,3 @@ impl Mcause { } read_csr_as!(Mcause, 0x342); - -#[cfg(test)] -mod test { - use super::*; - use Exception::*; - use Interrupt::*; - - #[test] - fn test_interrupt() { - assert_eq!(Interrupt::from_number(1), Ok(SupervisorSoft)); - assert_eq!(Interrupt::from_number(2), Err(2)); - assert_eq!(Interrupt::from_number(3), Ok(MachineSoft)); - assert_eq!(Interrupt::from_number(4), Err(4)); - assert_eq!(Interrupt::from_number(5), Ok(SupervisorTimer)); - assert_eq!(Interrupt::from_number(6), Err(6)); - assert_eq!(Interrupt::from_number(7), Ok(MachineTimer)); - assert_eq!(Interrupt::from_number(8), Err(8)); - assert_eq!(Interrupt::from_number(9), Ok(SupervisorExternal)); - assert_eq!(Interrupt::from_number(10), Err(10)); - assert_eq!(Interrupt::from_number(11), Ok(MachineExternal)); - assert_eq!(Interrupt::from_number(12), Err(12)); - - assert_eq!(SupervisorSoft.number(), 1); - assert_eq!(MachineSoft.number(), 3); - assert_eq!(SupervisorTimer.number(), 5); - assert_eq!(MachineTimer.number(), 7); - assert_eq!(SupervisorExternal.number(), 9); - assert_eq!(MachineExternal.number(), 11); - - assert_eq!(MachineExternal.number(), Interrupt::MAX_INTERRUPT_NUMBER) - } - - #[test] - fn test_exception() { - assert_eq!(Exception::from_number(0), Ok(InstructionMisaligned)); - assert_eq!(Exception::from_number(1), Ok(InstructionFault)); - assert_eq!(Exception::from_number(2), Ok(IllegalInstruction)); - assert_eq!(Exception::from_number(3), Ok(Breakpoint)); - assert_eq!(Exception::from_number(4), Ok(LoadMisaligned)); - assert_eq!(Exception::from_number(5), Ok(LoadFault)); - assert_eq!(Exception::from_number(6), Ok(StoreMisaligned)); - assert_eq!(Exception::from_number(7), Ok(StoreFault)); - assert_eq!(Exception::from_number(8), Ok(UserEnvCall)); - assert_eq!(Exception::from_number(9), Ok(SupervisorEnvCall)); - assert_eq!(Exception::from_number(10), Err(10)); - assert_eq!(Exception::from_number(11), Ok(MachineEnvCall)); - assert_eq!(Exception::from_number(12), Ok(InstructionPageFault)); - assert_eq!(Exception::from_number(13), Ok(LoadPageFault)); - assert_eq!(Exception::from_number(14), Err(14)); - assert_eq!(Exception::from_number(15), Ok(StorePageFault)); - assert_eq!(Exception::from_number(16), Err(16)); - - assert_eq!(InstructionMisaligned.number(), 0); - assert_eq!(InstructionFault.number(), 1); - assert_eq!(IllegalInstruction.number(), 2); - assert_eq!(Breakpoint.number(), 3); - assert_eq!(LoadMisaligned.number(), 4); - assert_eq!(LoadFault.number(), 5); - assert_eq!(StoreMisaligned.number(), 6); - assert_eq!(StoreFault.number(), 7); - assert_eq!(UserEnvCall.number(), 8); - assert_eq!(SupervisorEnvCall.number(), 9); - assert_eq!(MachineEnvCall.number(), 11); - assert_eq!(InstructionPageFault.number(), 12); - assert_eq!(LoadPageFault.number(), 13); - assert_eq!(StorePageFault.number(), 15); - - assert_eq!(StorePageFault.number(), Exception::MAX_EXCEPTION_NUMBER) - } -} diff --git a/riscv/src/register/scause.rs b/riscv/src/register/scause.rs index e9d7f5e6..ed6ada60 100644 --- a/riscv/src/register/scause.rs +++ b/riscv/src/register/scause.rs @@ -1,102 +1,7 @@ //! scause register -use riscv_pac::CoreInterruptNumber; -pub use riscv_pac::{ExceptionNumber, InterruptNumber}; // re-export useful riscv-pac traits - -/// Interrupt -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -#[repr(usize)] -pub enum Interrupt { - SupervisorSoft = 1, - SupervisorTimer = 5, - SupervisorExternal = 9, -} - -/// 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 { - match value { - 1 => Ok(Self::SupervisorSoft), - 5 => Ok(Self::SupervisorTimer), - 9 => Ok(Self::SupervisorExternal), - _ => 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)] -pub enum Exception { - InstructionMisaligned = 0, - InstructionFault = 1, - IllegalInstruction = 2, - Breakpoint = 3, - LoadMisaligned = 4, - LoadFault = 5, - StoreMisaligned = 6, - StoreFault = 7, - UserEnvCall = 8, - SupervisorEnvCall = 9, - InstructionPageFault = 12, - LoadPageFault = 13, - StorePageFault = 15, -} - -/// 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) -> usize { - self as usize - } - - #[inline] - fn from_number(value: usize) -> Result { - match value { - 0 => Ok(Self::InstructionMisaligned), - 1 => Ok(Self::InstructionFault), - 2 => Ok(Self::IllegalInstruction), - 3 => Ok(Self::Breakpoint), - 4 => Ok(Self::LoadMisaligned), - 5 => Ok(Self::LoadFault), - 6 => Ok(Self::StoreMisaligned), - 7 => Ok(Self::StoreFault), - 8 => Ok(Self::UserEnvCall), - 9 => Ok(Self::SupervisorEnvCall), - 12 => Ok(Self::InstructionPageFault), - 13 => Ok(Self::LoadPageFault), - 15 => Ok(Self::StorePageFault), - _ => Err(value), - } - } -} - -/// Trap Cause -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Trap { - Interrupt(I), - Exception(E), -} - -/// Trap Error -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum TrapError { - InvalidInterrupt(usize), - InvalidException(usize), -} +pub use crate::interrupt::Trap; +pub use riscv_pac::{CoreInterruptNumber, ExceptionNumber, InterruptNumber}; // re-export useful riscv-pac traits /// scause register #[derive(Clone, Copy)] @@ -117,32 +22,21 @@ impl Scause { self.bits & !(1 << (usize::BITS as usize - 1)) } - /// Try to get the trap cause + /// Returns the trap cause represented by this register. + /// + /// # Note + /// + /// This method returns a **raw trap cause**, which means that values are represented as `usize`. + /// To get a target-specific trap cause, use [`Trap::try_into`] with your target-specific S-Mode trap cause types. #[inline] - pub fn try_cause(&self) -> Result, TrapError> - where - I: CoreInterruptNumber, - E: ExceptionNumber, - { + pub fn cause(&self) -> Trap { if self.is_interrupt() { - match I::from_number(self.code()) { - Ok(interrupt) => Ok(Trap::Interrupt(interrupt)), - Err(code) => Err(TrapError::InvalidInterrupt(code)), - } + Trap::Interrupt(self.code()) } else { - match E::from_number(self.code()) { - Ok(exception) => Ok(Trap::Exception(exception)), - Err(code) => Err(TrapError::InvalidException(code)), - } + Trap::Exception(self.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 { @@ -176,69 +70,3 @@ pub unsafe fn set(cause: Trap) }; _write(bits); } - -#[cfg(test)] -mod test { - use super::*; - use Exception::*; - use Interrupt::*; - - #[test] - fn test_interrupt() { - assert_eq!(Interrupt::from_number(1), Ok(SupervisorSoft)); - assert_eq!(Interrupt::from_number(2), Err(2)); - assert_eq!(Interrupt::from_number(3), Err(3)); - assert_eq!(Interrupt::from_number(4), Err(4)); - assert_eq!(Interrupt::from_number(5), Ok(SupervisorTimer)); - assert_eq!(Interrupt::from_number(6), Err(6)); - assert_eq!(Interrupt::from_number(7), Err(7)); - assert_eq!(Interrupt::from_number(8), Err(8)); - assert_eq!(Interrupt::from_number(9), Ok(SupervisorExternal)); - assert_eq!(Interrupt::from_number(10), Err(10)); - assert_eq!(Interrupt::from_number(11), Err(11)); - assert_eq!(Interrupt::from_number(12), Err(12)); - - assert_eq!(SupervisorSoft.number(), 1); - assert_eq!(SupervisorTimer.number(), 5); - assert_eq!(SupervisorExternal.number(), 9); - - assert_eq!(SupervisorExternal.number(), Interrupt::MAX_INTERRUPT_NUMBER) - } - - #[test] - fn test_exception() { - assert_eq!(Exception::from_number(0), Ok(InstructionMisaligned)); - assert_eq!(Exception::from_number(1), Ok(InstructionFault)); - assert_eq!(Exception::from_number(2), Ok(IllegalInstruction)); - assert_eq!(Exception::from_number(3), Ok(Breakpoint)); - assert_eq!(Exception::from_number(4), Ok(LoadMisaligned)); - assert_eq!(Exception::from_number(5), Ok(LoadFault)); - assert_eq!(Exception::from_number(6), Ok(StoreMisaligned)); - assert_eq!(Exception::from_number(7), Ok(StoreFault)); - assert_eq!(Exception::from_number(8), Ok(UserEnvCall)); - assert_eq!(Exception::from_number(9), Ok(SupervisorEnvCall)); - assert_eq!(Exception::from_number(10), Err(10)); - assert_eq!(Exception::from_number(11), Err(11)); - assert_eq!(Exception::from_number(12), Ok(InstructionPageFault)); - assert_eq!(Exception::from_number(13), Ok(LoadPageFault)); - assert_eq!(Exception::from_number(14), Err(14)); - assert_eq!(Exception::from_number(15), Ok(StorePageFault)); - assert_eq!(Exception::from_number(16), Err(16)); - - assert_eq!(InstructionMisaligned.number(), 0); - assert_eq!(InstructionFault.number(), 1); - assert_eq!(IllegalInstruction.number(), 2); - assert_eq!(Breakpoint.number(), 3); - assert_eq!(LoadMisaligned.number(), 4); - assert_eq!(LoadFault.number(), 5); - assert_eq!(StoreMisaligned.number(), 6); - assert_eq!(StoreFault.number(), 7); - assert_eq!(UserEnvCall.number(), 8); - assert_eq!(SupervisorEnvCall.number(), 9); - assert_eq!(InstructionPageFault.number(), 12); - assert_eq!(LoadPageFault.number(), 13); - assert_eq!(StorePageFault.number(), 15); - - assert_eq!(StorePageFault.number(), Exception::MAX_EXCEPTION_NUMBER) - } -}