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    Csrs, ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands,
9    ExecutionError, ExecutionResult, FetchInstructionResult, InstructionFetcher,
10    OpaqueThreadedExecutionResult, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands,
11    ThreadedExecutableInstruction, ThreadedExecutionResult,
12};
13use ab_riscv_macros::instruction_execution;
14use ab_riscv_primitives::prelude::*;
15
16#[instruction_execution]
17const impl<Reg> ExecutableInstructionOperands for ZicsrInstruction<Reg> where Reg: Register {}
18
19#[instruction_execution]
20const impl<Reg, Env> ExecutableInstructionCsr<Env> for ZicsrInstruction<Reg> where Reg: Register {}
21
22#[instruction_execution]
23const impl<Reg, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
24    for ZicsrInstruction<Reg>
25where
26    Reg: [const] Register,
27    Regs: [const] RegisterFile<Reg>,
28    Env: [const] Csrs<Reg>,
29{
30    #[inline(always)]
31    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
32    fn execute(
33        self,
34        Rs1Rs2OperandValues {
35            rs1_value,
36            rs2_value: _,
37        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
38        _regs: &mut Regs,
39        env: &mut Env,
40        _memory: &mut Memory,
41        _program_counter: &mut PC,
42    ) -> ExecutionResult<Self::Reg> {
43        match self {
44            // Atomic read/write CSR.
45            //
46            // Reads old CSR value into rd (unless `rd == x0`, in which case no read side effects
47            // occur per spec), then writes `rs1` unconditionally.
48            Self::Csrrw {
49                rd,
50                rs1: _,
51                csr_index,
52            } => {
53                let csr_is_read_only = (csr_index >> 10) == 0b11;
54                if csr_is_read_only {
55                    ::core::hint::cold_path();
56                    return ExecutionResult::Err(ExecutionError::CsrReadOnly { csr_index });
57                }
58                zicsr_helpers::check_csr_privilege_level(env, csr_index)?;
59
60                let write_value = rs1_value;
61
62                // Per spec: if `rd == x0`, the CSR read (and its side effects) must not occur
63                let read_output = if rd == Reg::ZERO {
64                    ::core::hint::cold_path();
65                    Reg::Type::from(0u8)
66                } else {
67                    let read_value = match env.read_csr(csr_index) {
68                        Ok(read_value) => read_value,
69                        Err(err) => {
70                            ::core::hint::cold_path();
71                            return ExecutionResult::Err(ExecutionError::from(err));
72                        }
73                    };
74                    zicsr_helpers::process_csr_read::<Reg, Env, Self>(
75                        env, csr_index, true, read_value,
76                    )?
77                };
78
79                let write_output = zicsr_helpers::process_csr_write::<Reg, Env, Self>(
80                    env,
81                    csr_index,
82                    write_value,
83                )?;
84                match env.write_csr(csr_index, write_output) {
85                    Ok(()) => ExecutionResult::Continue {
86                        rd,
87                        value: read_output,
88                    },
89                    Err(err) => {
90                        ::core::hint::cold_path();
91                        ExecutionResult::Err(ExecutionError::from(err))
92                    }
93                }
94            }
95
96            // Atomic read and set bits in CSR.
97            //
98            // Always reads old value into `rd`. Writes `(old | rs1)` only if `rs1 != x0`.
99            // Accessing a read-only CSR with `rs1 == x0` is legal (pure read).
100            Self::Csrrs { rd, rs1, csr_index } => {
101                let csr_is_read_only = (csr_index >> 10) == 0b11;
102                if rs1 != Reg::ZERO && csr_is_read_only {
103                    ::core::hint::cold_path();
104                    return ExecutionResult::Err(ExecutionError::CsrReadOnly { csr_index });
105                }
106                zicsr_helpers::check_csr_privilege_level(env, csr_index)?;
107
108                let read_value = match env.read_csr(csr_index) {
109                    Ok(read_value) => read_value,
110                    Err(error) => {
111                        ::core::hint::cold_path();
112                        return ExecutionResult::Err(ExecutionError::from(error));
113                    }
114                };
115                let read_output = zicsr_helpers::process_csr_read::<Reg, Env, Self>(
116                    env,
117                    csr_index,
118                    rs1 != Reg::ZERO,
119                    read_value,
120                )?;
121
122                if rs1 == Reg::ZERO {
123                    ::core::hint::cold_path();
124                } else {
125                    let write_value = read_value | rs1_value;
126                    let write_output = zicsr_helpers::process_csr_write::<Reg, Env, Self>(
127                        env,
128                        csr_index,
129                        write_value,
130                    )?;
131                    if let Err(error) = env.write_csr(csr_index, write_output) {
132                        ::core::hint::cold_path();
133                        return ExecutionResult::Err(ExecutionError::from(error));
134                    }
135                }
136                ExecutionResult::Continue {
137                    rd,
138                    value: read_output,
139                }
140            }
141
142            // Atomic read and clear bits in CSR.
143            //
144            // Always reads old value into `rd`. Writes `(old & !rs1)` only if `rs1 != x0`.
145            // Accessing a read-only CSR with `rs1 == x0` is legal (pure read).
146            Self::Csrrc { rd, rs1, csr_index } => {
147                let csr_is_read_only = (csr_index >> 10) == 0b11;
148                if rs1 != Reg::ZERO && csr_is_read_only {
149                    ::core::hint::cold_path();
150                    return ExecutionResult::Err(ExecutionError::CsrReadOnly { csr_index });
151                }
152                zicsr_helpers::check_csr_privilege_level(env, csr_index)?;
153
154                let read_value = match env.read_csr(csr_index) {
155                    Ok(read_value) => read_value,
156                    Err(error) => {
157                        ::core::hint::cold_path();
158                        return ExecutionResult::Err(ExecutionError::from(error));
159                    }
160                };
161                let read_output = zicsr_helpers::process_csr_read::<Reg, Env, Self>(
162                    env,
163                    csr_index,
164                    rs1 != Reg::ZERO,
165                    read_value,
166                )?;
167
168                if rs1 == Reg::ZERO {
169                    ::core::hint::cold_path();
170                } else {
171                    let write_value = read_value & !rs1_value;
172                    let write_output = zicsr_helpers::process_csr_write::<Reg, Env, Self>(
173                        env,
174                        csr_index,
175                        write_value,
176                    )?;
177                    if let Err(error) = env.write_csr(csr_index, write_output) {
178                        ::core::hint::cold_path();
179                        return ExecutionResult::Err(ExecutionError::from(error));
180                    }
181                }
182
183                ExecutionResult::Continue {
184                    rd,
185                    value: read_output,
186                }
187            }
188
189            // Atomic read/write CSR immediate.
190            //
191            // Same `rd == x0` optimization as Csrrw. Writes zero-extended `zimm` unconditionally.
192            Self::Csrrwi {
193                rd,
194                zimm,
195                csr_index,
196            } => {
197                let csr_is_read_only = (csr_index >> 10) == 0b11;
198                if csr_is_read_only {
199                    ::core::hint::cold_path();
200                    return ExecutionResult::Err(ExecutionError::CsrReadOnly { csr_index });
201                }
202                zicsr_helpers::check_csr_privilege_level(env, csr_index)?;
203
204                let read_output = if rd == Reg::ZERO {
205                    ::core::hint::cold_path();
206                    Reg::Type::from(0u8)
207                } else {
208                    let read_value = match env.read_csr(csr_index) {
209                        Ok(read_value) => read_value,
210                        Err(error) => {
211                            ::core::hint::cold_path();
212                            return ExecutionResult::Err(ExecutionError::from(error));
213                        }
214                    };
215                    zicsr_helpers::process_csr_read::<Reg, Env, Self>(
216                        env, csr_index, true, read_value,
217                    )?
218                };
219
220                let write_output = zicsr_helpers::process_csr_write::<Reg, Env, Self>(
221                    env,
222                    csr_index,
223                    zimm.into(),
224                )?;
225                match env.write_csr(csr_index, write_output) {
226                    Ok(()) => ExecutionResult::Continue {
227                        rd,
228                        value: read_output,
229                    },
230                    Err(error) => {
231                        ::core::hint::cold_path();
232                        ExecutionResult::Err(ExecutionError::from(error))
233                    }
234                }
235            }
236
237            // Atomic read and set bits in CSR immediate.
238            //
239            // Always reads old value into `rd`. Writes `(old | zimm)` only if `zimm != 0`.
240            // Accessing a read-only CSR with `zimm == 0` is legal (pure read).
241            Self::Csrrsi {
242                rd,
243                zimm,
244                csr_index,
245            } => {
246                let csr_is_read_only = (csr_index >> 10) == 0b11;
247                if zimm != 0 && csr_is_read_only {
248                    ::core::hint::cold_path();
249                    return ExecutionResult::Err(ExecutionError::CsrReadOnly { csr_index });
250                }
251                zicsr_helpers::check_csr_privilege_level(env, csr_index)?;
252
253                let read_value = match env.read_csr(csr_index) {
254                    Ok(read_value) => read_value,
255                    Err(error) => {
256                        ::core::hint::cold_path();
257                        return ExecutionResult::Err(ExecutionError::from(error));
258                    }
259                };
260                let read_output = zicsr_helpers::process_csr_read::<Reg, Env, Self>(
261                    env,
262                    csr_index,
263                    zimm != 0,
264                    read_value,
265                )?;
266
267                if zimm == 0 {
268                    ::core::hint::cold_path();
269                } else {
270                    let write_value = read_value | Reg::Type::from(zimm);
271                    let write_output = zicsr_helpers::process_csr_write::<Reg, Env, Self>(
272                        env,
273                        csr_index,
274                        write_value,
275                    )?;
276                    if let Err(error) = env.write_csr(csr_index, write_output) {
277                        ::core::hint::cold_path();
278                        return ExecutionResult::Err(ExecutionError::from(error));
279                    }
280                }
281
282                ExecutionResult::Continue {
283                    rd,
284                    value: read_output,
285                }
286            }
287
288            // Atomic read and clear bits in CSR immediate.
289            //
290            // Always reads old value into `rd`. Writes `(old & !zimm)` only if `zimm != 0`.
291            // Accessing a read-only CSR with `zimm == 0` is legal (pure read).
292            Self::Csrrci {
293                rd,
294                zimm,
295                csr_index,
296            } => {
297                let csr_is_read_only = (csr_index >> 10) == 0b11;
298                if zimm != 0 && csr_is_read_only {
299                    ::core::hint::cold_path();
300                    return ExecutionResult::Err(ExecutionError::CsrReadOnly { csr_index });
301                }
302                zicsr_helpers::check_csr_privilege_level(env, csr_index)?;
303
304                let read_value = match env.read_csr(csr_index) {
305                    Ok(read_value) => read_value,
306                    Err(error) => {
307                        ::core::hint::cold_path();
308                        return ExecutionResult::Err(ExecutionError::from(error));
309                    }
310                };
311                let read_output = zicsr_helpers::process_csr_read::<Reg, Env, Self>(
312                    env,
313                    csr_index,
314                    zimm != 0,
315                    read_value,
316                )?;
317
318                if zimm == 0 {
319                    ::core::hint::cold_path();
320                } else {
321                    let write_value = read_value & !Reg::Type::from(zimm);
322                    let write_output = zicsr_helpers::process_csr_write::<Reg, Env, Self>(
323                        env,
324                        csr_index,
325                        write_value,
326                    )?;
327                    if let Err(error) = env.write_csr(csr_index, write_output) {
328                        ::core::hint::cold_path();
329                        return ExecutionResult::Err(ExecutionError::from(error));
330                    }
331                }
332
333                ExecutionResult::Continue {
334                    rd,
335                    value: read_output,
336                }
337            }
338        }
339    }
340}