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