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