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