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