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