Skip to main content

ab_riscv_interpreter/
zicsr.rs

1//! Zicsr extension
2
3#[cfg(test)]
4mod tests;
5pub mod zicsr_helpers;
6
7use crate::{CsrError, Csrs, ExecutableInstruction, ExecutionError, InterpreterState};
8use ab_riscv_macros::instruction_execution;
9use ab_riscv_primitives::instructions::zicsr::ZicsrInstruction;
10use ab_riscv_primitives::registers::general_purpose::Register;
11use core::ops::ControlFlow;
12
13#[instruction_execution]
14impl<Reg, ExtState, Memory, PC, InstructionHandler, CustomError>
15    ExecutableInstruction<
16        InterpreterState<Reg, ExtState, Memory, PC, InstructionHandler, CustomError>,
17        CustomError,
18    > for ZicsrInstruction<Reg>
19where
20    Reg: Register,
21    [(); Reg::N]:,
22    ExtState: Csrs<Reg, CustomError>,
23{
24    #[inline(always)]
25    fn execute(
26        self,
27        state: &mut InterpreterState<Reg, ExtState, Memory, PC, InstructionHandler, CustomError>,
28    ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type, CustomError>> {
29        match self {
30            // Atomic read/write CSR.
31            //
32            // Reads old CSR value into rd (unless `rd == x0`, in which case no read side effects
33            // occur per spec), then writes `rs1` unconditionally.
34            Self::Csrrw { rd, rs1, csr } => {
35                let csr_is_read_only = (csr >> 10) == 0b11;
36                if csr_is_read_only {
37                    return Err(ExecutionError::CsrError(CsrError::ReadOnly {
38                        csr_index: csr,
39                    }));
40                }
41                zicsr_helpers::check_csr_privilege_level(&state.ext_state, csr)?;
42
43                let write_value = state.regs.read(rs1);
44
45                // Per spec: if `rd == x0`, the CSR read (and its side effects) must not occur
46                if !rd.is_zero() {
47                    let raw_value = state.ext_state.read_csr(csr)?;
48                    let output_value = state.ext_state.process_csr_read(csr, raw_value)?;
49                    state.regs.write(rd, output_value);
50                }
51
52                let output_value = state.ext_state.process_csr_write(csr, write_value)?;
53                state.ext_state.write_csr(csr, output_value)?;
54            }
55
56            // Atomic read and set bits in CSR.
57            //
58            // Always reads old value into `rd`. Writes `(old | rs1)` only if `rs1 != x0`.
59            // Accessing a read-only CSR with `rs1 == x0` is legal (pure read).
60            Self::Csrrs { rd, rs1, csr } => {
61                let csr_is_read_only = (csr >> 10) == 0b11;
62                if !rs1.is_zero() && csr_is_read_only {
63                    return Err(ExecutionError::CsrError(CsrError::ReadOnly {
64                        csr_index: csr,
65                    }));
66                }
67                zicsr_helpers::check_csr_privilege_level(&state.ext_state, csr)?;
68
69                let rs1_value = state.regs.read(rs1);
70
71                let raw_value = state.ext_state.read_csr(csr)?;
72                let read_output = state.ext_state.process_csr_read(csr, raw_value)?;
73                state.regs.write(rd, read_output);
74
75                if !rs1.is_zero() {
76                    let write_value = raw_value | rs1_value;
77                    let write_output = state.ext_state.process_csr_write(csr, write_value)?;
78                    state.ext_state.write_csr(csr, write_output)?;
79                }
80            }
81
82            // Atomic read and clear bits in CSR.
83            //
84            // Always reads old value into `rd`. Writes `(old & !rs1)` only if `rs1 != x0`.
85            // Accessing a read-only CSR with `rs1 == x0` is legal (pure read).
86            Self::Csrrc { rd, rs1, csr } => {
87                let csr_is_read_only = (csr >> 10) == 0b11;
88                if !rs1.is_zero() && csr_is_read_only {
89                    return Err(ExecutionError::CsrError(CsrError::ReadOnly {
90                        csr_index: csr,
91                    }));
92                }
93                zicsr_helpers::check_csr_privilege_level(&state.ext_state, csr)?;
94
95                let rs1_value = state.regs.read(rs1);
96
97                let raw_value = state.ext_state.read_csr(csr)?;
98                let read_output = state.ext_state.process_csr_read(csr, raw_value)?;
99                state.regs.write(rd, read_output);
100
101                if !rs1.is_zero() {
102                    let write_value = raw_value & !rs1_value;
103                    let write_output = state.ext_state.process_csr_write(csr, write_value)?;
104                    state.ext_state.write_csr(csr, write_output)?;
105                }
106            }
107
108            // Atomic read/write CSR immediate.
109            //
110            // Same `rd == x0` optimization as Csrrw. Writes zero-extended `zimm` unconditionally.
111            Self::Csrrwi { rd, zimm, csr } => {
112                let csr_is_read_only = (csr >> 10) == 0b11;
113                if csr_is_read_only {
114                    return Err(ExecutionError::CsrError(CsrError::ReadOnly {
115                        csr_index: csr,
116                    }));
117                }
118                zicsr_helpers::check_csr_privilege_level(&state.ext_state, csr)?;
119
120                if !rd.is_zero() {
121                    let raw_value = state.ext_state.read_csr(csr)?;
122                    let output_value = state.ext_state.process_csr_read(csr, raw_value)?;
123                    state.regs.write(rd, output_value);
124                }
125
126                let output_value = state.ext_state.process_csr_write(csr, zimm.into())?;
127                state.ext_state.write_csr(csr, output_value)?;
128            }
129
130            // Atomic read and set bits in CSR immediate.
131            //
132            // Always reads old value into `rd`. Writes `(old | zimm)` only if `zimm != 0`.
133            // Accessing a read-only CSR with `zimm == 0` is legal (pure read).
134            Self::Csrrsi { rd, zimm, csr } => {
135                let csr_is_read_only = (csr >> 10) == 0b11;
136                if zimm != 0 && csr_is_read_only {
137                    return Err(ExecutionError::CsrError(CsrError::ReadOnly {
138                        csr_index: csr,
139                    }));
140                }
141                zicsr_helpers::check_csr_privilege_level(&state.ext_state, csr)?;
142
143                let raw_value = state.ext_state.read_csr(csr)?;
144                let read_output = state.ext_state.process_csr_read(csr, raw_value)?;
145                state.regs.write(rd, read_output);
146
147                if zimm != 0 {
148                    let write_value = raw_value | Reg::Type::from(zimm);
149                    let write_output = state.ext_state.process_csr_write(csr, write_value)?;
150                    state.ext_state.write_csr(csr, write_output)?;
151                }
152            }
153
154            // Atomic read and clear bits in CSR immediate.
155            //
156            // Always reads old value into `rd`. Writes `(old & !zimm)` only if `zimm != 0`.
157            // Accessing a read-only CSR with `zimm == 0` is legal (pure read).
158            Self::Csrrci { rd, zimm, csr } => {
159                let csr_is_read_only = (csr >> 10) == 0b11;
160                if zimm != 0 && csr_is_read_only {
161                    return Err(ExecutionError::CsrError(CsrError::ReadOnly {
162                        csr_index: csr,
163                    }));
164                }
165                zicsr_helpers::check_csr_privilege_level(&state.ext_state, csr)?;
166
167                let raw_value = state.ext_state.read_csr(csr)?;
168                let read_output = state.ext_state.process_csr_read(csr, raw_value)?;
169                state.regs.write(rd, read_output);
170
171                if zimm != 0 {
172                    let write_value = raw_value & !Reg::Type::from(zimm);
173                    let write_output = state.ext_state.process_csr_write(csr, write_value)?;
174                    state.ext_state.write_csr(csr, write_output)?;
175                }
176            }
177        }
178
179        Ok(ControlFlow::Continue(()))
180    }
181}