Skip to main content

ab_riscv_interpreter/
lib.rs

1//! Composable and generic RISC-V interpreter.
2//!
3//! This interpreter is designed to work with abstractions from [`ab-riscv-primitives`] crate and is
4//! similarly composable with a powerful macro system and trait abstractions over handling of
5//! memory, syscalls, etc.
6//!
7//! [`ab-riscv-primitives`]: ab_riscv_primitives
8//!
9//! The immediate needs dictate the current set of available instructions and extensions. Consider
10//! contributing if you need something not yet available.
11//!
12//! `ab-riscv-act4-runner` crate in the repository contains a complementary RISC-V Architectural
13//! Certification Tests runner for <https://github.com/riscv-non-isa/riscv-arch-test> that ensures
14//! correct implementation.
15//!
16//! Does not require a standard library (`no_std`) or an allocator.
17//!
18//! ## Supported ISA variants and extensions
19//!
20//! ISA variants:
21//! * RV32I (version 2.1)
22//! * RV32E (version 2.0)
23//! * RV64I (version 2.1)
24//! * RV64E (version 2.0)
25//!
26//! Extensions:
27//! * M (version 2.0)
28//! * B (version 1.0.0)
29//! * Zba (version 1.0.0)
30//! * Zbb (version 1.0.0)
31//! * Zbc (version 1.0.0)
32//! * Zbkb (version 1.0.1)
33//! * Zbkc (version 1.0.1)
34//! * Zbkx (version 1.0.1)
35//! * Zbs (version 1.0.0)
36//! * Zca (version 1.0.0)
37//! * Zcb (version 1.0.0)
38//! * (experimental) Zcmp (version 1.0.0)
39//! * Zkn (version 1.0.1)
40//! * Zknd (version 1.0.1)
41//! * Zkne (version 1.0.1)
42//! * Zknh (version 1.0.1)
43//! * Zicond (version 2.0)
44//! * Zicsr (version 2.0)
45//! * Zvbb (version 1.0.0)
46//! * Zvbc (version 1.0.0)
47//! * ZveXx (version 1.0.0), where `X` is anything allowed by the specification like Zve32x or
48//!   Zve64x
49//! * Zvkb (version 1.0.0)
50//! * Zvl*b (version 1.0.0), where `*` is anything allowed by the specification like Zvl128b or
51//!   Zvl512b
52//!
53//! All extensions except experimental pass all relevant RISC-V Architectural Certification Tests
54//! (ACTs) using the ACT4 framework.
55//!
56//! Any permutation of compatible extensions is supported.
57//!
58//! Experimental extensions are known to have bugs and need more work. They are not tested against
59//! ACTs yet.
60
61#![expect(incomplete_features, reason = "generic_const_exprs")]
62#![feature(
63    const_cmp,
64    const_convert,
65    const_default,
66    const_index,
67    const_trait_impl,
68    generic_const_exprs,
69    result_option_map_or_default,
70    signed_bigint_helpers
71)]
72#![cfg_attr(
73    not(any(
74        all(target_arch = "riscv32", target_feature = "zbkx"),
75        all(target_arch = "riscv64", target_feature = "zbkx")
76    )),
77    feature(portable_simd)
78)]
79#![cfg_attr(
80    any(
81        all(
82            target_arch = "riscv32",
83            any(
84                target_feature = "zbb",
85                target_feature = "zbc",
86                target_feature = "zbkb",
87                target_feature = "zbkx",
88                target_feature = "zknd",
89                target_feature = "zkne",
90                target_feature = "zknh"
91            )
92        ),
93        all(
94            target_arch = "riscv64",
95            any(
96                target_feature = "zbb",
97                target_feature = "zbc",
98                target_feature = "zbkx",
99                target_feature = "zknd",
100                target_feature = "zkne",
101                target_feature = "zknh"
102            )
103        )
104    ),
105    feature(riscv_ext_intrinsics)
106)]
107#![no_std]
108
109pub mod basic;
110pub mod prelude;
111mod private;
112pub mod rv32;
113pub mod rv64;
114pub mod v;
115pub mod zicond;
116pub mod zicsr;
117pub mod zvbb;
118pub mod zvbc;
119
120use crate::private::BasicIntSealed;
121use ab_riscv_primitives::prelude::*;
122use core::fmt;
123use core::hint::cold_path;
124use core::ops::{ControlFlow, Sub};
125
126type RegisterType<I> = <<I as Instruction>::Reg as Register>::Type;
127type Address<I> = RegisterType<I>;
128
129/// A GPR (General Purpose Register) file abstraction
130pub const trait RegisterFile<Reg>
131where
132    Reg: [const] Register,
133{
134    /// Read register value
135    fn read(&self, reg: Reg) -> Reg::Type;
136
137    /// Write register value
138    fn write(&mut self, reg: Reg, value: Reg::Type);
139}
140
141/// Errors for [`VirtualMemory`]
142#[derive(Debug, thiserror::Error)]
143pub enum VirtualMemoryError {
144    /// Out-of-bounds read
145    #[error("Out-of-bounds read at address {address}")]
146    OutOfBoundsRead {
147        /// Address of the out-of-bounds read
148        address: u64,
149    },
150    /// Out-of-bounds write
151    #[error("Out-of-bounds write at address {address}")]
152    OutOfBoundsWrite {
153        /// Address of the out-of-bounds write
154        address: u64,
155    },
156}
157
158/// Basic integer types that can be read and written to/from memory freely
159pub trait BasicInt: Sized + Copy + BasicIntSealed + 'static {}
160
161impl BasicIntSealed for u8 {}
162impl BasicIntSealed for u16 {}
163impl BasicIntSealed for u32 {}
164impl BasicIntSealed for u64 {}
165impl BasicIntSealed for i8 {}
166impl BasicIntSealed for i16 {}
167impl BasicIntSealed for i32 {}
168impl BasicIntSealed for i64 {}
169
170impl BasicInt for u8 {}
171impl BasicInt for u16 {}
172impl BasicInt for u32 {}
173impl BasicInt for u64 {}
174impl BasicInt for i8 {}
175impl BasicInt for i16 {}
176impl BasicInt for i32 {}
177impl BasicInt for i64 {}
178
179/// Virtual memory interface
180pub trait VirtualMemory {
181    /// Read a value from memory at the specified address
182    fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
183    where
184        T: BasicInt;
185
186    /// Unchecked read a value from memory at the specified address.
187    ///
188    /// # Safety
189    /// The address and value must be in-bounds.
190    unsafe fn read_unchecked<T>(&self, address: u64) -> T
191    where
192        T: BasicInt;
193
194    /// Read a contiguous byte slice from memory
195    fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError>;
196
197    /// Read as many contiguous bytes as possible starting at `address`, up to `len` bytes total.
198    ///
199    /// Can return an empty slice in cases like when the address is out of bounds.
200    fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8];
201
202    /// Write a value to memory at the specified address
203    fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
204    where
205        T: BasicInt;
206
207    /// Write a contiguous byte slice to memory
208    fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError>;
209}
210
211/// Placeholder for custom errors in [`ExecutionError`]
212#[derive(Debug, Copy, Clone)]
213pub struct CustomErrorPlaceholder;
214
215impl fmt::Display for CustomErrorPlaceholder {
216    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        Ok(())
218    }
219}
220
221/// Program counter errors
222#[derive(Debug, thiserror::Error)]
223pub enum ProgramCounterError<Address, CustomError = CustomErrorPlaceholder> {
224    /// Unaligned instruction
225    #[error("Unaligned instruction at address {address}")]
226    UnalignedInstruction {
227        /// Address of the unaligned instruction fetch
228        address: Address,
229    },
230    /// Memory access error
231    #[error("Memory access error: {0}")]
232    MemoryAccess(#[from] VirtualMemoryError),
233    /// Custom error
234    #[error("Custom error: {0}")]
235    Custom(CustomError),
236}
237
238/// Generic program counter
239pub trait ProgramCounter<Address, Memory, CustomError = CustomErrorPlaceholder> {
240    /// Get the current value of the program counter
241    fn get_pc(&self) -> Address;
242
243    /// Get the previous value of the program counter before executing an `instruction`.
244    ///
245    /// This is usually called from under instruction execution when the program counter is already
246    /// advanced during instruction fetching. As such, `pc - instruction_size` is expected to never
247    /// underflow.
248    #[inline(always)]
249    fn old_pc(&self, instruction_size: u8) -> Address
250    where
251        Address: From<u8> + Sub<Output = Address>,
252    {
253        // TODO: Wrapping subtraction would be nice, but causes a lot of additional generic bounds
254        //  that are bad for ergonomics
255        self.get_pc() - Address::from(instruction_size)
256    }
257
258    /// Set the current value of the program counter
259    fn set_pc(
260        &mut self,
261        memory: &Memory,
262        pc: Address,
263    ) -> Result<ControlFlow<()>, ProgramCounterError<Address, CustomError>>;
264}
265
266/// Execution errors
267#[derive(Debug, thiserror::Error)]
268pub enum ExecutionError<Address, CustomError = CustomErrorPlaceholder> {
269    /// Program counter error
270    #[error("Program counter error: {0}")]
271    ProgramCounter(#[from] ProgramCounterError<Address, CustomError>),
272    /// Memory access error
273    #[error("Memory access error: {0}")]
274    MemoryAccess(#[from] VirtualMemoryError),
275    /// Unsupported `ecall` instruction
276    #[error("Unsupported `ecall` instruction at address {address:#x}")]
277    EcallUnsupported {
278        /// Address of the unsupported instruction
279        address: Address,
280    },
281    /// Unimplemented/illegal instruction
282    #[error("Unimplemented/illegal instruction at address {address:#x}")]
283    IllegalInstruction {
284        /// Address of the `unimp` instruction
285        address: Address,
286    },
287    /// Invalid instruction
288    #[error("Invalid instruction at address {address:#x}: {instruction:#010x}")]
289    InvalidInstruction {
290        /// Address of the invalid instruction
291        address: Address,
292        /// Instruction that caused the error
293        instruction: u32,
294    },
295    /// CSR error
296    #[error("CSR error: {0}")]
297    CsrError(#[from] CsrError<CustomError>),
298    /// Custom error
299    #[error("Custom error: {0}")]
300    Custom(CustomError),
301}
302
303/// Result of [`InstructionFetcher::fetch_instruction()`] call
304#[derive(Debug, Copy, Clone)]
305pub enum FetchInstructionResult<Instruction> {
306    /// Instruction fetched successfully
307    Instruction(Instruction),
308    /// Control flow instruction encountered
309    ControlFlow(ControlFlow<()>),
310}
311
312/// Generic instruction fetcher
313pub trait InstructionFetcher<I, Memory, CustomError = CustomErrorPlaceholder>
314where
315    Self: ProgramCounter<Address<I>, Memory, CustomError>,
316    I: Instruction,
317{
318    /// Fetch a single instruction at a specified address and advance the program counter on
319    /// successful fetch
320    fn fetch_instruction(
321        &mut self,
322        memory: &Memory,
323    ) -> Result<FetchInstructionResult<I>, ExecutionError<Address<I>, CustomError>>;
324}
325
326/// CSR error
327#[derive(Debug, thiserror::Error)]
328pub enum CsrError<CustomError = CustomErrorPlaceholder> {
329    /// Read only CSR
330    #[error("Read only CSR {csr_index:#x}")]
331    ReadOnly {
332        /// Index of CSR where write was attempted
333        csr_index: u16,
334    },
335    /// Illegal read access
336    #[error("Illegal read access to CSR {csr_index:#x}")]
337    IllegalRead {
338        /// Index of the accessed CSR
339        csr_index: u16,
340    },
341    /// Illegal write access
342    #[error("Illegal write access to CSR {csr_index:#x}")]
343    IllegalWrite {
344        /// Index of the accessed CSR
345        csr_index: u16,
346    },
347    /// Unknown CSR
348    #[error("Unknown CSR {csr_index:#x}")]
349    Unknown {
350        /// Index of the accessed CSR
351        csr_index: u16,
352    },
353    /// Insufficient privilege level
354    #[error(
355        "Insufficient privilege level for CSR {csr_index:#x}: required {required:?}, \
356        current {current:?}"
357    )]
358    InsufficientPrivilege {
359        /// Index of the accessed CSR
360        csr_index: u16,
361        /// Required privilege level
362        required: PrivilegeLevel,
363        /// Current privilege level
364        current: PrivilegeLevel,
365    },
366    /// Custom error
367    #[error("Custom error: {0}")]
368    Custom(CustomError),
369}
370
371/// CSRs (Control and Status Registers)
372pub trait Csrs<Reg, CustomError = CustomErrorPlaceholder>
373where
374    Reg: Register,
375{
376    /// Current privilege level
377    #[inline(always)]
378    fn privilege_level(&self) -> PrivilegeLevel {
379        PrivilegeLevel::Machine
380    }
381
382    /// Reads register value
383    fn read_csr(&self, csr_index: u16) -> Result<Reg::Type, CsrError<CustomError>>;
384
385    /// Writes register value
386    fn write_csr(&mut self, csr_index: u16, value: Reg::Type) -> Result<(), CsrError<CustomError>>;
387
388    // TODO: Remove this method once tests do not need to customize it
389    /// Process CSR read.
390    ///
391    /// Must proxy calls to [`ExecutableInstructionCsr::prepare_csr_read()`] of the root instruction
392    /// and return the output value on success. The default implementation of the method should be
393    /// fine most of the time but can be overridden in special cases or for testing purposes.
394    #[doc(hidden)]
395    #[inline(always)]
396    fn process_csr_read<I>(
397        &self,
398        csr_index: u16,
399        raw_value: Reg::Type,
400    ) -> Result<Reg::Type, CsrError<CustomError>>
401    where
402        I: ExecutableInstructionCsr<Self, CustomError, Reg = Reg>,
403    {
404        let mut out = Reg::Type::default();
405        match I::prepare_csr_read(self, csr_index, raw_value, &mut out) {
406            Ok(true) => Ok(out),
407            Ok(false) => {
408                cold_path();
409                Err(CsrError::IllegalRead { csr_index })
410            }
411            Err(err) => {
412                cold_path();
413                Err(err)
414            }
415        }
416    }
417
418    // TODO: Remove this method once tests do not need to customize it
419    /// Process CSR write.
420    ///
421    /// Must proxy calls to [`ExecutableInstructionCsr::prepare_csr_write()`] of the root
422    /// instruction and return the output value on success. The default implementation of the method
423    /// should be fine most of the time but can be overridden in special cases or for testing
424    /// purposes.
425    #[doc(hidden)]
426    #[inline(always)]
427    fn process_csr_write<I>(
428        &mut self,
429        csr_index: u16,
430        write_value: Reg::Type,
431    ) -> Result<Reg::Type, CsrError<CustomError>>
432    where
433        I: ExecutableInstructionCsr<Self, CustomError, Reg = Reg>,
434    {
435        let mut out = Reg::Type::default();
436        match I::prepare_csr_write(self, csr_index, write_value, &mut out) {
437            Ok(true) => Ok(out),
438            Ok(false) => {
439                cold_path();
440                Err(CsrError::IllegalWrite { csr_index })
441            }
442            Err(err) => {
443                cold_path();
444                Err(err)
445            }
446        }
447    }
448}
449
450/// Custom handler for system instructions `ecall` and `ebreak`
451pub trait SystemInstructionHandler<Reg, Regs, Memory, PC, CustomError = CustomErrorPlaceholder>
452where
453    Reg: Register,
454    Regs: RegisterFile<Reg>,
455{
456    // TODO: Figure out the correct API for this method
457    /// Handle a `fence` instruction
458    #[inline(always)]
459    fn handle_fence(&mut self, pred: u8, succ: u8) {
460        let _: u8 = pred;
461        let _: u8 = succ;
462        // NOP by default
463    }
464
465    // TODO: Figure out the correct API for this method
466    /// Handle a `fence.tso` instruction
467    #[inline(always)]
468    fn handle_fence_tso(&mut self) {
469        // NOP by default
470    }
471
472    /// Handle an `ecall` instruction
473    fn handle_ecall(
474        &mut self,
475        regs: &mut Regs,
476        memory: &mut Memory,
477        program_counter: &mut PC,
478    ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type, CustomError>>;
479
480    /// Handle an `ebreak` instruction.
481    ///
482    /// NOTE: the program counter here is the current value, meaning it is already incremented past
483    /// the instruction itself.
484    #[inline(always)]
485    fn handle_ebreak(&mut self, regs: &mut Regs, memory: &mut Memory, pc: Reg::Type) {
486        // These are for cleaner trait API without leading `_` on arguments
487        let _: &Regs = regs;
488        let _: &mut Memory = memory;
489        let _: Reg::Type = pc;
490        // NOP by default
491    }
492}
493
494/// `rs1`/`rs2` instruction operands
495#[derive(Debug, Default, Copy, Clone)]
496pub struct Rs1Rs2Operands<Reg> {
497    /// `rs1` operand.
498    ///
499    /// Zero register if `rs1` was missing in the original instruction definition.
500    pub rs1: Reg,
501    /// `rs2` operand.
502    ///
503    /// Zero register if `rs2` was missing in the original instruction definition.
504    pub rs2: Reg,
505}
506
507/// `rs1`/`rs2` instruction operands
508#[derive(Debug, Default, Copy, Clone)]
509pub struct Rs1Rs2OperandValues<RegType> {
510    /// `rs1` operand value.
511    ///
512    /// Zero if `rs1` was missing in the original instruction definition.
513    pub rs1_value: RegType,
514    /// `rs2` operand value.
515    ///
516    /// Zero if `rs2` was missing in the original instruction definition.
517    pub rs2_value: RegType,
518}
519
520/// `rs1`/`rs2` instruction operands
521pub trait ExecutableInstructionOperands
522where
523    Self: Instruction,
524{
525    /// `rs1`/`rs2` instruction operands.
526    ///
527    /// Returns zero register for `rs1`/`rs2` that were missing in the original instruction
528    /// definition.
529    fn get_rs1_rs2_operands(self) -> Rs1Rs2Operands<Self::Reg>;
530}
531
532pub trait ExecutableInstructionCsr<ExtState, CustomError = CustomErrorPlaceholder>
533where
534    Self: Instruction,
535    ExtState: ?Sized,
536{
537    /// Prepare CSR read.
538    ///
539    /// This method is called on each extension one by one with the `raw_value` (contents of the
540    /// corresponding CSR register) and initially zero-initialized `output_value`. In return value
541    /// every extension can accept (`Ok(true)`), ignore (`Ok(false)`) or reject (`Err(CsrError)`)
542    /// read request. For accepted reads the extension must update `output_value` accordingly, which
543    /// will be the value used by the `Zicsr` extension handler.
544    ///
545    /// Some extensions will just copy `raw_value` to output value, others will copy only some bits
546    /// or zero some bits of the `raw_value`, as required by the specification.
547    ///
548    /// If no extension returns `Ok(true)`, the read operation is implicitly rejected as illegal
549    /// access.
550    #[inline(always)]
551    fn prepare_csr_read(
552        ext_state: &ExtState,
553        csr_index: u16,
554        raw_value: RegisterType<Self>,
555        output_value: &mut RegisterType<Self>,
556    ) -> Result<bool, CsrError<CustomError>> {
557        // These are for cleaner trait API without leading `_` on arguments
558        let _: &ExtState = ext_state;
559        let _: u16 = csr_index;
560        let _: RegisterType<Self> = raw_value;
561        let _: &mut RegisterType<Self> = output_value;
562        // The default implementation is to not allow anything
563        Ok(false)
564    }
565
566    /// Prepare CSR write.
567    ///
568    /// This method is called on each extension one by one with `write_value` being prepared by the
569    /// `Zicsr` extension handler. In return value every extension can accept (`Ok(true)`), ignore
570    /// (`Ok(false)`) or reject (`Err(CsrError)`) write request. For accepted writes the extension
571    /// must update `output_value` accordingly, which will be written to the corresponding CSR
572    /// register.
573    ///
574    /// Some extensions will just copy `write_value` to output value, others will copy some bits or
575    /// zero some bits of the `write_value`, as required by the specification.
576    ///
577    /// If no extension returns `Ok(true)`, the write operation is implicitly rejected as illegal
578    /// access.
579    #[inline(always)]
580    fn prepare_csr_write(
581        ext_state: &mut ExtState,
582        csr_index: u16,
583        write_value: RegisterType<Self>,
584        output_value: &mut RegisterType<Self>,
585    ) -> Result<bool, CsrError<CustomError>> {
586        // These are for cleaner trait API without leading `_` on arguments
587        let _: &mut ExtState = ext_state;
588        let _: u16 = csr_index;
589        let _: RegisterType<Self> = write_value;
590        let _: &mut RegisterType<Self> = output_value;
591        // The default implementation is to not allow anything
592        Ok(false)
593    }
594}
595
596/// Trait for executable instructions
597pub trait ExecutableInstruction<
598    Regs,
599    ExtState,
600    Memory,
601    PC,
602    InstructionHandler,
603    CustomError = CustomErrorPlaceholder,
604> where
605    Self: ExecutableInstructionOperands + ExecutableInstructionCsr<ExtState, CustomError>,
606{
607    /// Execute instruction.
608    ///
609    /// Instructions might place additional constraints on `ExtState` to require additional
610    /// registers or other resources. If no such constraint is used, `()` can be used as a
611    /// placeholder.
612    ///
613    /// On success `Ok(ControlFlow::Continue((rd, rd_value)))` is returned, which will be written
614    /// into the register file. In most cases this is the only register that needs to be written. If
615    /// no value needs to be written, `Ok(ControlFlow::Continue(Default::default()))` should be
616    /// returned, which corresponds to `Ok(ControlFlow::Continue(Reg::ZERO, 0))` and is no-op.
617    #[expect(clippy::type_complexity, reason = "Generic return type")]
618    fn execute(
619        self,
620        rs1rs2_values: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
621        regs: &mut Regs,
622        ext_state: &mut ExtState,
623        memory: &mut Memory,
624        program_counter: &mut PC,
625        system_instruction_handler: &mut InstructionHandler,
626    ) -> Result<
627        ControlFlow<(), (Self::Reg, <Self::Reg as Register>::Type)>,
628        ExecutionError<Address<Self>, CustomError>,
629    >;
630}