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                    ::core::hint::cold_path();
64                    return Err(ExecutionError::CsrError(CsrError::ReadOnly { csr_index }));
65                }
66                zicsr_helpers::check_csr_privilege_level(ext_state, csr_index)?;
67
68                let write_value = rs1_value;
69
70                // Per spec: if `rd == x0`, the CSR read (and its side effects) must not occur
71                let read_output = if rd == Reg::ZERO {
72                    ::core::hint::cold_path();
73                    Reg::Type::from(0u8)
74                } else {
75                    let read_value = match ext_state.read_csr(csr_index) {
76                        Ok(read_value) => read_value,
77                        Err(err) => {
78                            ::core::hint::cold_path();
79                            return Err(ExecutionError::CsrError(err));
80                        }
81                    };
82                    ext_state.process_csr_read::<Self>(csr_index, read_value)?
83                };
84
85                let write_output = ext_state.process_csr_write::<Self>(csr_index, write_value)?;
86                match ext_state.write_csr(csr_index, write_output) {
87                    Ok(()) => Ok(ControlFlow::Continue((rd, read_output))),
88                    Err(err) => {
89                        ::core::hint::cold_path();
90                        Err(ExecutionError::CsrError(err))
91                    }
92                }
93            }
94
95            // Atomic read and set bits in CSR.
96            //
97            // Always reads old value into `rd`. Writes `(old | rs1)` only if `rs1 != x0`.
98            // Accessing a read-only CSR with `rs1 == x0` is legal (pure read).
99            Self::Csrrs { rd, rs1, csr_index } => {
100                let csr_is_read_only = (csr_index >> 10) == 0b11;
101                if rs1 != Reg::ZERO && csr_is_read_only {
102                    ::core::hint::cold_path();
103                    return Err(ExecutionError::CsrError(CsrError::ReadOnly { csr_index }));
104                }
105                zicsr_helpers::check_csr_privilege_level(ext_state, csr_index)?;
106
107                let read_value = match ext_state.read_csr(csr_index) {
108                    Ok(read_value) => read_value,
109                    Err(error) => {
110                        ::core::hint::cold_path();
111                        return Err(ExecutionError::CsrError(error));
112                    }
113                };
114                let read_output = ext_state.process_csr_read::<Self>(csr_index, read_value)?;
115
116                if rs1 == Reg::ZERO {
117                    ::core::hint::cold_path();
118                } else {
119                    let write_value = read_value | rs1_value;
120                    let write_output =
121                        ext_state.process_csr_write::<Self>(csr_index, write_value)?;
122                    if let Err(error) = ext_state.write_csr(csr_index, write_output) {
123                        ::core::hint::cold_path();
124                        return Err(ExecutionError::CsrError(error));
125                    }
126                }
127                Ok(ControlFlow::Continue((rd, read_output)))
128            }
129
130            // Atomic read and clear bits in CSR.
131            //
132            // Always reads old value into `rd`. Writes `(old & !rs1)` only if `rs1 != x0`.
133            // Accessing a read-only CSR with `rs1 == x0` is legal (pure read).
134            Self::Csrrc { rd, rs1, csr_index } => {
135                let csr_is_read_only = (csr_index >> 10) == 0b11;
136                if rs1 != Reg::ZERO && csr_is_read_only {
137                    ::core::hint::cold_path();
138                    return Err(ExecutionError::CsrError(CsrError::ReadOnly { csr_index }));
139                }
140                zicsr_helpers::check_csr_privilege_level(ext_state, csr_index)?;
141
142                let read_value = match ext_state.read_csr(csr_index) {
143                    Ok(read_value) => read_value,
144                    Err(error) => {
145                        ::core::hint::cold_path();
146                        return Err(ExecutionError::CsrError(error));
147                    }
148                };
149                let read_output = ext_state.process_csr_read::<Self>(csr_index, read_value)?;
150
151                if rs1 == Reg::ZERO {
152                    ::core::hint::cold_path();
153                } else {
154                    let write_value = read_value & !rs1_value;
155                    let write_output =
156                        ext_state.process_csr_write::<Self>(csr_index, write_value)?;
157                    if let Err(error) = ext_state.write_csr(csr_index, write_output) {
158                        ::core::hint::cold_path();
159                        return Err(ExecutionError::CsrError(error));
160                    }
161                }
162
163                Ok(ControlFlow::Continue((rd, read_output)))
164            }
165
166            // Atomic read/write CSR immediate.
167            //
168            // Same `rd == x0` optimization as Csrrw. Writes zero-extended `zimm` unconditionally.
169            Self::Csrrwi {
170                rd,
171                zimm,
172                csr_index,
173            } => {
174                let csr_is_read_only = (csr_index >> 10) == 0b11;
175                if csr_is_read_only {
176                    ::core::hint::cold_path();
177                    return Err(ExecutionError::CsrError(CsrError::ReadOnly { csr_index }));
178                }
179                zicsr_helpers::check_csr_privilege_level(ext_state, csr_index)?;
180
181                let read_output = if rd == Reg::ZERO {
182                    ::core::hint::cold_path();
183                    Reg::Type::from(0u8)
184                } else {
185                    let read_value = match ext_state.read_csr(csr_index) {
186                        Ok(read_value) => read_value,
187                        Err(error) => {
188                            ::core::hint::cold_path();
189                            return Err(ExecutionError::CsrError(error));
190                        }
191                    };
192                    ext_state.process_csr_read::<Self>(csr_index, read_value)?
193                };
194
195                let write_output = ext_state.process_csr_write::<Self>(csr_index, zimm.into())?;
196                match ext_state.write_csr(csr_index, write_output) {
197                    Ok(()) => Ok(ControlFlow::Continue((rd, read_output))),
198                    Err(error) => {
199                        ::core::hint::cold_path();
200                        Err(ExecutionError::CsrError(error))
201                    }
202                }
203            }
204
205            // Atomic read and set bits in CSR immediate.
206            //
207            // Always reads old value into `rd`. Writes `(old | zimm)` only if `zimm != 0`.
208            // Accessing a read-only CSR with `zimm == 0` is legal (pure read).
209            Self::Csrrsi {
210                rd,
211                zimm,
212                csr_index,
213            } => {
214                let csr_is_read_only = (csr_index >> 10) == 0b11;
215                if zimm != 0 && csr_is_read_only {
216                    ::core::hint::cold_path();
217                    return Err(ExecutionError::CsrError(CsrError::ReadOnly { csr_index }));
218                }
219                zicsr_helpers::check_csr_privilege_level(ext_state, csr_index)?;
220
221                let read_value = match ext_state.read_csr(csr_index) {
222                    Ok(read_value) => read_value,
223                    Err(error) => {
224                        ::core::hint::cold_path();
225                        return Err(ExecutionError::CsrError(error));
226                    }
227                };
228                let read_output = ext_state.process_csr_read::<Self>(csr_index, read_value)?;
229
230                if zimm == 0 {
231                    ::core::hint::cold_path();
232                } else {
233                    let write_value = read_value | Reg::Type::from(zimm);
234                    let write_output =
235                        ext_state.process_csr_write::<Self>(csr_index, write_value)?;
236                    if let Err(error) = ext_state.write_csr(csr_index, write_output) {
237                        ::core::hint::cold_path();
238                        return Err(ExecutionError::CsrError(error));
239                    }
240                }
241
242                Ok(ControlFlow::Continue((rd, read_output)))
243            }
244
245            // Atomic read and clear bits in CSR immediate.
246            //
247            // Always reads old value into `rd`. Writes `(old & !zimm)` only if `zimm != 0`.
248            // Accessing a read-only CSR with `zimm == 0` is legal (pure read).
249            Self::Csrrci {
250                rd,
251                zimm,
252                csr_index,
253            } => {
254                let csr_is_read_only = (csr_index >> 10) == 0b11;
255                if zimm != 0 && csr_is_read_only {
256                    ::core::hint::cold_path();
257                    return Err(ExecutionError::CsrError(CsrError::ReadOnly { csr_index }));
258                }
259                zicsr_helpers::check_csr_privilege_level(ext_state, csr_index)?;
260
261                let read_value = match ext_state.read_csr(csr_index) {
262                    Ok(read_value) => read_value,
263                    Err(error) => {
264                        ::core::hint::cold_path();
265                        return Err(ExecutionError::CsrError(error));
266                    }
267                };
268                let read_output = ext_state.process_csr_read::<Self>(csr_index, read_value)?;
269
270                if zimm == 0 {
271                    ::core::hint::cold_path();
272                } else {
273                    let write_value = read_value & !Reg::Type::from(zimm);
274                    let write_output =
275                        ext_state.process_csr_write::<Self>(csr_index, write_value)?;
276                    if let Err(error) = ext_state.write_csr(csr_index, write_output) {
277                        ::core::hint::cold_path();
278                        return Err(ExecutionError::CsrError(error));
279                    }
280                }
281
282                Ok(ControlFlow::Continue((rd, read_output)))
283            }
284        }
285    }
286}