use crate::{
interrupt::Trap,
register::{mcause, mepc, mstatus},
};
use riscv_pac::{
result::{Error, Result},
CoreInterruptNumber, ExceptionNumber, InterruptNumber,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(usize)]
pub enum Interrupt {
SupervisorSoft = 1,
MachineSoft = 3,
SupervisorTimer = 5,
MachineTimer = 7,
SupervisorExternal = 9,
MachineExternal = 11,
}
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<Self> {
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(Error::InvalidVariant(value)),
}
}
}
unsafe impl CoreInterruptNumber for Interrupt {}
#[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,
}
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<Self> {
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(Error::InvalidVariant(value)),
}
}
}
#[inline]
pub fn disable() {
unsafe { mstatus::clear_mie() }
}
#[inline]
pub unsafe fn enable() {
mstatus::set_mie()
}
#[inline]
pub fn try_cause<I: CoreInterruptNumber, E: ExceptionNumber>() -> Result<Trap<I, E>> {
mcause::read().cause().try_into()
}
#[inline]
pub fn cause<I: CoreInterruptNumber, E: ExceptionNumber>() -> Trap<I, E> {
try_cause().unwrap()
}
#[inline]
pub fn free<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let mstatus = mstatus::read();
disable();
let r = f();
if mstatus.mie() {
unsafe { enable() };
}
r
}
#[inline]
pub unsafe fn nested<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let mstatus = mstatus::read();
let mepc = mepc::read();
enable();
let r = f();
if !mstatus.mie() {
disable();
}
let mut after_mstatus = mstatus::read();
if mstatus.mpie() {
after_mstatus.set_mpie(mstatus.mpie());
}
after_mstatus.set_mpp(mstatus.mpp());
mstatus::write(after_mstatus);
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(Error::InvalidVariant(2)));
assert_eq!(Interrupt::from_number(3), Ok(MachineSoft));
assert_eq!(Interrupt::from_number(4), Err(Error::InvalidVariant(4)));
assert_eq!(Interrupt::from_number(5), Ok(SupervisorTimer));
assert_eq!(Interrupt::from_number(6), Err(Error::InvalidVariant(6)));
assert_eq!(Interrupt::from_number(7), Ok(MachineTimer));
assert_eq!(Interrupt::from_number(8), Err(Error::InvalidVariant(8)));
assert_eq!(Interrupt::from_number(9), Ok(SupervisorExternal));
assert_eq!(Interrupt::from_number(10), Err(Error::InvalidVariant(10)));
assert_eq!(Interrupt::from_number(11), Ok(MachineExternal));
assert_eq!(Interrupt::from_number(12), Err(Error::InvalidVariant(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(Error::InvalidVariant(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(Error::InvalidVariant(14)));
assert_eq!(Exception::from_number(15), Ok(StorePageFault));
assert_eq!(Exception::from_number(16), Err(Error::InvalidVariant(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)
}
}