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