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    pub const fn to_raw<Reg>(self) -> Reg::Type
124    where
125        Reg: [const] Register,
126    {
127        Reg::Type::from(self as u32)
128    }
129}
130
131/// Machine interrupt causes (`mcause[XLEN‑1] = 1`)
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133#[repr(u32)]
134pub enum MCauseInterrupt {
135    /// User software interrupt
136    UserSoftware = 0,
137    /// Supervisor software interrupt
138    SupervisorSoftware = 1,
139    /// Machine software interrupt
140    MachineSoftware = 3,
141    /// User timer interrupt
142    UserTimer = 4,
143    /// Supervisor timer interrupt
144    SupervisorTimer = 5,
145    /// Machine timer interrupt
146    MachineTimer = 7,
147    /// User external interrupt
148    UserExternal = 8,
149    /// Supervisor external interrupt
150    SupervisorExternal = 9,
151    /// Machine external interrupt
152    MachineExternal = 11,
153}
154
155impl MCauseInterrupt {
156    /// Try to match an interrupt code to an `MInterrupt` (returns `None` for reserved/unknown
157    /// codes)
158    #[inline(always)]
159    pub const fn from_code(code: u64) -> Option<Self> {
160        match code {
161            0 => Some(Self::UserSoftware),
162            1 => Some(Self::SupervisorSoftware),
163            3 => Some(Self::MachineSoftware),
164            4 => Some(Self::UserTimer),
165            5 => Some(Self::SupervisorTimer),
166            7 => Some(Self::MachineTimer),
167            8 => Some(Self::UserExternal),
168            9 => Some(Self::SupervisorExternal),
169            11 => Some(Self::MachineExternal),
170            _ => None,
171        }
172    }
173
174    /// Convert this interrupt to its full raw `mcause` CSR value
175    #[inline(always)]
176    pub const fn to_raw<Reg>(self) -> Reg::Type
177    where
178        Reg: [const] Register,
179    {
180        Reg::Type::from(self as u32) | (Reg::Type::from(1u8) << (Reg::XLEN - 1))
181    }
182}
183
184/// Combined `mcause` CSR value
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186pub enum MCause {
187    Exception(MCauseException),
188    Interrupt(MCauseInterrupt),
189}
190
191impl From<MCauseException> for MCause {
192    #[inline(always)]
193    fn from(cause: MCauseException) -> Self {
194        Self::Exception(cause)
195    }
196}
197
198impl From<MCauseInterrupt> for MCause {
199    #[inline(always)]
200    fn from(cause: MCauseInterrupt) -> Self {
201        Self::Interrupt(cause)
202    }
203}
204
205impl MCause {
206    /// Try to create `MCause` from a raw `mcause` CSR value
207    #[inline(always)]
208    pub const fn from_raw<Reg>(raw: Reg::Type) -> Option<Self>
209    where
210        Reg: [const] Register,
211    {
212        let raw = raw.as_u64();
213        let is_interrupt = (raw & (1u64 << (Reg::XLEN - 1))) != 0;
214        let code = raw & !(1u64 << (Reg::XLEN - 1));
215
216        if is_interrupt {
217            MCauseInterrupt::from_code(code).map(Self::Interrupt)
218        } else {
219            MCauseException::from_code(code).map(Self::Exception)
220        }
221    }
222
223    /// Convert this `MCause` back to the full raw `mcause` CSR value
224    #[inline(always)]
225    pub const fn to_raw<Reg>(self) -> Reg::Type
226    where
227        Reg: [const] Register,
228    {
229        match self {
230            MCause::Exception(exception) => exception.to_raw::<Reg>(),
231            MCause::Interrupt(interrupt) => interrupt.to_raw::<Reg>(),
232        }
233    }
234}