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