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