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}