Skip to main content

ab_riscv_primitives/registers/
machine.rs

1//! Machine-mode registers
2
3use crate::registers::general_purpose::{RegType, Register};
4
5// TODO: CSR composition?
6/// Machine CSR addresses (core mandatory registers from the Privileged Spec)
7#[derive(Debug, Clone, Copy)]
8#[derive_const(PartialEq, Eq)]
9#[repr(u16)]
10pub enum MCsr {
11    /// Machine vendor ID register (MRO)
12    Mvendorid = 0xF11,
13    /// Machine architecture ID register (MRO)
14    Marchid = 0xF12,
15    /// Machine implementation ID register (MRO)
16    Mimpid = 0xF13,
17    /// Hart ID register (MRO)
18    Mhartid = 0xF14,
19
20    /// Machine status register (MRW)
21    Mstatus = 0x300,
22    /// Machine ISA and extensions register (MRW)
23    Misa = 0x301,
24    /// Machine interrupt-enable register (MRW)
25    Mie = 0x304,
26    /// Machine trap-vector base address register (MRW)
27    Mtvec = 0x305,
28    /// Additional machine status register, RV32 only (MRW)
29    Mstatush = 0x310,
30    /// Machine count-inhibit register (MRW)
31    Mcountinhibit = 0x320,
32
33    /// Machine scratch register (MRW)
34    Mscratch = 0x340,
35    /// Machine exception program counter (MRW)
36    Mepc = 0x341,
37    /// Machine trap cause (MRW)
38    Mcause = 0x342,
39    /// Machine trap value (MRW)
40    Mtval = 0x343,
41    /// Machine interrupt pending (MRW)
42    Mip = 0x344,
43}
44
45impl MCsr {
46    /// Try to match a CSR index to a machine CSR
47    #[inline(always)]
48    pub const fn from_index(index: u16) -> Option<Self> {
49        match index {
50            0xF11 => Some(Self::Mvendorid),
51            0xF12 => Some(Self::Marchid),
52            0xF13 => Some(Self::Mimpid),
53            0xF14 => Some(Self::Mhartid),
54            0x300 => Some(Self::Mstatus),
55            0x301 => Some(Self::Misa),
56            0x304 => Some(Self::Mie),
57            0x305 => Some(Self::Mtvec),
58            0x310 => Some(Self::Mstatush),
59            0x320 => Some(Self::Mcountinhibit),
60            0x340 => Some(Self::Mscratch),
61            0x341 => Some(Self::Mepc),
62            0x342 => Some(Self::Mcause),
63            0x343 => Some(Self::Mtval),
64            0x344 => Some(Self::Mip),
65            _ => None,
66        }
67    }
68}
69
70/// Machine exception causes (`mcause[XLEN‑1] = 0`)
71#[derive(Debug, Clone, Copy)]
72#[derive_const(PartialEq, Eq)]
73#[repr(u32)]
74pub enum MCauseException {
75    /// Instruction address misaligned
76    InstructionAddressMisaligned = 0,
77    /// Instruction access fault
78    InstructionAccessFault = 1,
79    /// Illegal instruction
80    IllegalInstruction = 2,
81    /// Breakpoint
82    Breakpoint = 3,
83    /// Load address misaligned
84    LoadAddressMisaligned = 4,
85    /// Load access fault
86    LoadAccessFault = 5,
87    /// Store/AMO address misaligned
88    StoreAddressMisaligned = 6,
89    /// Store/AMO access fault
90    StoreAccessFault = 7,
91    /// Environment call from U-mode
92    UserEnvironmentCall = 8,
93    /// Environment call from S-mode
94    SupervisorEnvironmentCall = 9,
95    /// Environment call from M-mode
96    MachineEnvironmentCall = 11,
97    /// Instruction page fault
98    InstructionPageFault = 12,
99    /// Load page fault
100    LoadPageFault = 13,
101    /// Store/AMO page fault
102    StorePageFault = 15,
103}
104
105impl MCauseException {
106    /// Try to match an exception code to an [`MCauseException`] (returns `None` for
107    /// reserved/unknown codes)
108    #[inline(always)]
109    pub const fn from_code(code: u64) -> Option<Self> {
110        match code {
111            0 => Some(Self::InstructionAddressMisaligned),
112            1 => Some(Self::InstructionAccessFault),
113            2 => Some(Self::IllegalInstruction),
114            3 => Some(Self::Breakpoint),
115            4 => Some(Self::LoadAddressMisaligned),
116            5 => Some(Self::LoadAccessFault),
117            6 => Some(Self::StoreAddressMisaligned),
118            7 => Some(Self::StoreAccessFault),
119            8 => Some(Self::UserEnvironmentCall),
120            9 => Some(Self::SupervisorEnvironmentCall),
121            11 => Some(Self::MachineEnvironmentCall),
122            12 => Some(Self::InstructionPageFault),
123            13 => Some(Self::LoadPageFault),
124            15 => Some(Self::StorePageFault),
125            _ => None,
126        }
127    }
128
129    /// Convert this exception to its full raw `mcause` CSR value
130    #[inline(always)]
131    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
132    pub const fn to_raw<Reg>(self) -> Reg::Type
133    where
134        Reg: [const] Register,
135    {
136        Reg::Type::from(self as u32)
137    }
138}
139
140/// Machine interrupt causes (`mcause[XLEN‑1] = 1`)
141#[derive(Debug, Clone, Copy)]
142#[derive_const(PartialEq, Eq)]
143#[repr(u32)]
144pub enum MCauseInterrupt {
145    /// User software interrupt
146    UserSoftware = 0,
147    /// Supervisor software interrupt
148    SupervisorSoftware = 1,
149    /// Machine software interrupt
150    MachineSoftware = 3,
151    /// User timer interrupt
152    UserTimer = 4,
153    /// Supervisor timer interrupt
154    SupervisorTimer = 5,
155    /// Machine timer interrupt
156    MachineTimer = 7,
157    /// User external interrupt
158    UserExternal = 8,
159    /// Supervisor external interrupt
160    SupervisorExternal = 9,
161    /// Machine external interrupt
162    MachineExternal = 11,
163}
164
165impl MCauseInterrupt {
166    /// Try to match an interrupt code to an `MInterrupt` (returns `None` for reserved/unknown
167    /// codes)
168    #[inline(always)]
169    pub const fn from_code(code: u64) -> Option<Self> {
170        match code {
171            0 => Some(Self::UserSoftware),
172            1 => Some(Self::SupervisorSoftware),
173            3 => Some(Self::MachineSoftware),
174            4 => Some(Self::UserTimer),
175            5 => Some(Self::SupervisorTimer),
176            7 => Some(Self::MachineTimer),
177            8 => Some(Self::UserExternal),
178            9 => Some(Self::SupervisorExternal),
179            11 => Some(Self::MachineExternal),
180            _ => None,
181        }
182    }
183
184    /// Convert this interrupt to its full raw `mcause` CSR value
185    #[inline(always)]
186    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
187    pub const fn to_raw<Reg>(self) -> Reg::Type
188    where
189        Reg: [const] Register,
190    {
191        Reg::Type::from(self as u32) | (Reg::Type::from(1u8) << (Reg::XLEN - 1))
192    }
193}
194
195/// Combined `mcause` CSR value
196#[derive(Debug, Clone, Copy)]
197#[derive_const(PartialEq, Eq)]
198pub enum MCause {
199    Exception(MCauseException),
200    Interrupt(MCauseInterrupt),
201}
202
203impl From<MCauseException> for MCause {
204    #[inline(always)]
205    fn from(cause: MCauseException) -> Self {
206        Self::Exception(cause)
207    }
208}
209
210impl From<MCauseInterrupt> for MCause {
211    #[inline(always)]
212    fn from(cause: MCauseInterrupt) -> Self {
213        Self::Interrupt(cause)
214    }
215}
216
217impl MCause {
218    /// Try to create `MCause` from a raw `mcause` CSR value
219    #[inline(always)]
220    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
221    pub const fn from_raw<Reg>(raw: Reg::Type) -> Option<Self>
222    where
223        Reg: [const] Register,
224    {
225        let raw = raw.as_u64();
226        let is_interrupt = (raw & (1u64 << (Reg::XLEN - 1))) != 0;
227        let code = raw & !(1u64 << (Reg::XLEN - 1));
228
229        if is_interrupt {
230            MCauseInterrupt::from_code(code).map(Self::Interrupt)
231        } else {
232            MCauseException::from_code(code).map(Self::Exception)
233        }
234    }
235
236    /// Convert this `MCause` back to the full raw `mcause` CSR value
237    #[inline(always)]
238    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
239    pub const fn to_raw<Reg>(self) -> Reg::Type
240    where
241        Reg: [const] Register,
242    {
243        match self {
244            MCause::Exception(exception) => exception.to_raw::<Reg>(),
245            MCause::Interrupt(interrupt) => interrupt.to_raw::<Reg>(),
246        }
247    }
248}