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