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