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