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