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