Skip to main content

ab_riscv_interpreter/
zkr.rs

1//! Zkr extension
2
3#[cfg(test)]
4mod tests;
5pub mod zkr_helpers;
6
7use crate::zicsr::zicsr_helpers;
8use crate::{
9    CsrError, Csrs, ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands,
10    ExecutionError, ExecutionResult, FetchInstructionResult, InstructionFetcher,
11    OpaqueThreadedExecutionResult, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands,
12    ThreadedExecutableInstruction, ThreadedExecutionResult,
13};
14use ab_riscv_macros::instruction_execution;
15use ab_riscv_primitives::prelude::*;
16
17/// Result of polling the entropy source behind the `Zkr` extension's `seed` CSR, corresponding to
18/// one of the specification's `OPST` states
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum ZkrSeedPoll {
21    /// Built-in self-test is being performed (`OPST=BIST`).
22    ///
23    /// If this is returned after some other state was previously observed, it signals a
24    /// non-fatal, but noteworthy, self-test alarm.
25    Bist,
26    /// A sufficient amount of entropy is not yet available (`OPST=WAIT`).
27    ///
28    /// This is not an error condition and may be observed more often than [`Self::Es16`], since
29    /// physical entropy sources often have low bandwidth.
30    Wait,
31    /// 16 bits of randomness are available (`OPST=ES16`), guaranteed to meet the specification's
32    /// minimum entropy requirements regardless of implementation.
33    Es16(u16),
34    /// An unrecoverable self-test error has occurred (`OPST=DEAD`).
35    ///
36    /// Once returned, all future polls are expected to also return [`Self::Dead`].
37    Dead,
38}
39
40/// Entropy source behind the `Zkr` extension's `seed` CSR.
41///
42/// Every access to `seed` polls the entropy source once.
43pub const trait ZkrSeedSource {
44    /// Poll the entropy source once, advancing its internal state as necessary
45    fn poll_seed(&mut self) -> ZkrSeedPoll;
46}
47
48// Convenience for threaded execution
49const impl<T> ZkrSeedSource for &mut T
50where
51    T: [const] ZkrSeedSource,
52{
53    #[inline(always)]
54    fn poll_seed(&mut self) -> ZkrSeedPoll {
55        T::poll_seed(self)
56    }
57}
58
59#[instruction_execution]
60const impl<Reg> ExecutableInstructionOperands for ZkrInstruction<Reg> where Reg: [const] Register {}
61
62#[instruction_execution]
63const impl<Reg, Env> ExecutableInstructionCsr<Env> for ZkrInstruction<Reg>
64where
65    Reg: [const] Register,
66    Env: [const] ZkrSeedSource,
67{
68    /// Reads of `seed` are pass-through: the raw stored value already reflects the outcome of the
69    /// most recent poll (see [`Self::prepare_csr_write()`]).
70    ///
71    /// Per specification, only genuine read-write accesses (not e.g. `csrrs`/`csrrc` with
72    /// `rs1 = x0`) are legal against `seed`; a pure read is rejected as an illegal access.
73    #[inline(always)]
74    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
75    fn prepare_csr_read(
76        _env: &Env,
77        csr_index: u16,
78        will_write: bool,
79        raw_value: Reg::Type,
80        output_value: &mut Reg::Type,
81    ) -> Result<bool, CsrError> {
82        if csr_index == SEED_CSR_INDEX {
83            if will_write {
84                *output_value = raw_value;
85                Ok(true)
86            } else {
87                Err(CsrError::IllegalRead { csr_index })
88            }
89        } else {
90            Ok(false)
91        }
92    }
93
94    /// Writes to `seed` ignore the write value entirely (per specification) and instead poll the
95    /// entropy source once, storing the newly encoded [`ZkrSeedPoll`] to be observed by the next
96    /// read
97    #[inline(always)]
98    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
99    fn prepare_csr_write(
100        env: &mut Env,
101        csr_index: u16,
102        write_value: Reg::Type,
103        output_value: &mut Reg::Type,
104    ) -> Result<bool, CsrError> {
105        // Necessary to avoid unused variable depending on instructions composition
106        let _: Reg::Type = write_value;
107
108        if csr_index == SEED_CSR_INDEX {
109            *output_value = zkr_helpers::encode_seed_poll::<Reg>(env.poll_seed());
110            Ok(true)
111        } else {
112            Ok(false)
113        }
114    }
115}
116
117#[instruction_execution]
118const impl<Reg, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
119    for ZkrInstruction<Reg>
120where
121    Reg: Register,
122    Env: [const] ZkrSeedSource,
123{
124    #[inline(always)]
125    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
126    fn execute(
127        self,
128        Rs1Rs2OperandValues {
129            rs1_value,
130            rs2_value: _,
131        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
132        _regs: &mut Regs,
133        env: &mut Env,
134        _memory: &mut Memory,
135        _program_counter: &mut PC,
136    ) -> ExecutionResult<Self::Reg> {
137        ExecutionResult::ContinueNoWrite
138    }
139}