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