Skip to main content

ab_riscv_interpreter/zicsr/
zicsr_helpers.rs

1//! Opaque helpers for Zicsr extension
2
3use crate::{CsrError, Csrs, ExecutableInstructionCsr};
4use ab_riscv_primitives::prelude::*;
5use core::hint::cold_path;
6
7/// CSR privilege level check helper.
8///
9/// Returns `Err` if `current` is below the privilege level encoded in `csr_index` bits `[9:8]`.
10/// May return `Ok(())` for invalid CSRs for efficiency reasons since those will be rejected by
11/// extensions anyway.
12#[inline(always)]
13#[doc(hidden)]
14#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
15pub const fn check_csr_privilege_level<Reg, C>(csrs: &C, csr_index: u16) -> Result<(), CsrError>
16where
17    Reg: [const] Register,
18    C: [const] Csrs<Reg>,
19{
20    let current = csrs.privilege_level();
21    let required_bits = ((csr_index >> 8u8) & 0b11) as u8;
22    // Privilege level uses two bits. Using machine value as a placeholder (`0b11`) allows the
23    // compiler to optimize this whole function away if `csrs.privilege_level()` returns fixed
24    // `PrivilegeLevel::Machine` value, which is the most common case since `0b11` is larger or
25    // equal than any other 2-bit value. Invalid level will still be rejected at a later stage as
26    // unknown CSR.
27    let required = PrivilegeLevel::from_bits(required_bits).unwrap_or(PrivilegeLevel::Machine);
28
29    if current >= required {
30        Ok(())
31    } else {
32        cold_path();
33        Err(CsrError::InsufficientPrivilege {
34            csr_index,
35            required,
36            current,
37        })
38    }
39}
40
41/// Process a CSR read by calling [`ExecutableInstructionCsr::prepare_csr_read()`] of the root
42/// instruction `I` and returning the output value on success.
43///
44/// `will_write` indicates whether this same CSR instruction will also perform a write afterward,
45/// see [`ExecutableInstructionCsr::prepare_csr_read()`] for details.
46#[inline(always)]
47#[doc(hidden)]
48pub const fn process_csr_read<Reg, Env, I>(
49    env: &Env,
50    csr_index: u16,
51    will_write: bool,
52    raw_value: Reg::Type,
53) -> Result<Reg::Type, CsrError>
54where
55    Reg: [const] Register,
56    I: [const] ExecutableInstructionCsr<Env, Reg = Reg>,
57{
58    let mut out = Reg::Type::default();
59    match I::prepare_csr_read(env, csr_index, will_write, raw_value, &mut out) {
60        Ok(true) => Ok(out),
61        Ok(false) => {
62            cold_path();
63            Err(CsrError::IllegalRead { csr_index })
64        }
65        Err(err) => {
66            cold_path();
67            Err(err)
68        }
69    }
70}
71
72/// Process a CSR write by calling [`ExecutableInstructionCsr::prepare_csr_write()`] of the root
73/// instruction `I` and returning the output value on success.
74#[inline(always)]
75#[doc(hidden)]
76pub const fn process_csr_write<Reg, Env, I>(
77    env: &mut Env,
78    csr_index: u16,
79    write_value: Reg::Type,
80) -> Result<Reg::Type, CsrError>
81where
82    Reg: [const] Register,
83    I: [const] ExecutableInstructionCsr<Env, Reg = Reg>,
84{
85    let mut out = Reg::Type::default();
86    match I::prepare_csr_write(env, csr_index, write_value, &mut out) {
87        Ok(true) => Ok(out),
88        Ok(false) => {
89            cold_path();
90            Err(CsrError::IllegalWrite { csr_index })
91        }
92        Err(err) => {
93            cold_path();
94            Err(err)
95        }
96    }
97}