1#![feature(bigint_helper_methods, const_convert, const_trait_impl)]
2#![expect(incomplete_features, reason = "generic_const_exprs")]
3#![feature(generic_const_exprs)]
6#![no_std]
7
8pub mod b_64_ext;
9pub mod m_64_ext;
10pub mod rv64;
11
12use crate::rv64::Rv64SystemInstructionHandler;
13use ab_riscv_primitives::instruction::rv64::Rv64Instruction;
14use ab_riscv_primitives::instruction::{GenericBaseInstruction, GenericInstruction};
15use ab_riscv_primitives::registers::{GenericRegister, Registers};
16use core::fmt;
17use core::ops::ControlFlow;
18
19#[derive(Debug, thiserror::Error)]
21pub enum VirtualMemoryError {
22 #[error("Out-of-bounds read at address {address}")]
24 OutOfBoundsRead {
25 address: u64,
27 },
28 #[error("Out-of-bounds write at address {address}")]
30 OutOfBoundsWrite {
31 address: u64,
33 },
34}
35
36mod private {
37 pub trait Sealed {}
38
39 impl Sealed for u8 {}
40 impl Sealed for u16 {}
41 impl Sealed for u32 {}
42 impl Sealed for u64 {}
43 impl Sealed for i8 {}
44 impl Sealed for i16 {}
45 impl Sealed for i32 {}
46 impl Sealed for i64 {}
47}
48
49pub trait BasicInt: Sized + Copy + private::Sealed {}
51
52impl BasicInt for u8 {}
53impl BasicInt for u16 {}
54impl BasicInt for u32 {}
55impl BasicInt for u64 {}
56impl BasicInt for i8 {}
57impl BasicInt for i16 {}
58impl BasicInt for i32 {}
59impl BasicInt for i64 {}
60
61pub trait VirtualMemory {
63 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
65 where
66 T: BasicInt;
67
68 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
70 where
71 T: BasicInt;
72}
73
74#[derive(Debug, thiserror::Error)]
76pub enum ExecuteError<Instruction, Custom>
77where
78 Instruction: fmt::Display,
79 Custom: fmt::Display,
80{
81 #[error("Unaligned instruction fetch at address {address}")]
83 UnalignedInstructionFetch {
84 address: u64,
86 },
87 #[error("Memory access error: {0}")]
89 MemoryAccess(#[from] VirtualMemoryError),
90 #[error("Unsupported instruction at address {address:#x}: {instruction}")]
92 UnsupportedInstruction {
93 address: u64,
95 instruction: Instruction,
97 },
98 #[error("Unimplemented/illegal instruction at address {address:#x}")]
100 UnimpInstruction {
101 address: u64,
103 },
104 #[error("Invalid instruction at address {address:#x}: {instruction:#010x}")]
106 InvalidInstruction {
107 address: u64,
109 instruction: u32,
111 },
112 #[error("Custom error: {0}")]
114 Custom(Custom),
115}
116
117impl<BaseInstruction, Custom> ExecuteError<BaseInstruction, Custom>
118where
119 BaseInstruction: GenericBaseInstruction,
120 Custom: fmt::Display,
121{
122 #[inline]
124 pub fn map_from_base<Instruction>(self) -> ExecuteError<Instruction, Custom>
125 where
126 Instruction: GenericBaseInstruction<Base = BaseInstruction>,
127 {
128 match self {
129 Self::UnalignedInstructionFetch { address } => {
130 ExecuteError::UnalignedInstructionFetch { address }
131 }
132 Self::MemoryAccess(error) => ExecuteError::MemoryAccess(error),
133 Self::UnsupportedInstruction {
134 address,
135 instruction,
136 } => ExecuteError::UnsupportedInstruction {
137 address,
138 instruction: Instruction::from_base(instruction),
139 },
140 Self::UnimpInstruction { address } => ExecuteError::UnimpInstruction { address },
141 Self::InvalidInstruction {
142 address,
143 instruction,
144 } => ExecuteError::InvalidInstruction {
145 address,
146 instruction,
147 },
148 Self::Custom(error) => ExecuteError::Custom(error),
149 }
150 }
151}
152
153#[derive(Debug, Copy, Clone)]
155pub enum FetchInstructionResult<Instruction> {
156 Instruction(Instruction),
158 ControlFlow(ControlFlow<()>),
160}
161
162pub trait GenericInstructionHandler<Instruction, Memory, CustomError>
164where
165 Instruction: GenericBaseInstruction,
166 [(); Instruction::Reg::N]:,
167 CustomError: fmt::Display,
168{
169 fn fetch_instruction(
171 &mut self,
172 _regs: &mut Registers<Instruction::Reg>,
173 memory: &mut Memory,
174 pc: &mut u64,
175 ) -> Result<FetchInstructionResult<Instruction>, ExecuteError<Instruction, CustomError>>;
176}
177
178#[derive(Debug, Default, Copy, Clone)]
182pub struct BasicInstructionHandler<const RETURN_TRAP_ADDRESS: u64>;
183
184impl<const RETURN_TRAP_ADDRESS: u64, Instruction, Memory>
185 GenericInstructionHandler<Instruction, Memory, &'static str>
186 for BasicInstructionHandler<RETURN_TRAP_ADDRESS>
187where
188 Instruction: GenericBaseInstruction,
189 [(); Instruction::Reg::N]:,
190 Memory: VirtualMemory,
191{
192 #[inline(always)]
193 fn fetch_instruction(
194 &mut self,
195 _regs: &mut Registers<Instruction::Reg>,
196 memory: &mut Memory,
197 pc: &mut u64,
198 ) -> Result<FetchInstructionResult<Instruction>, ExecuteError<Instruction, &'static str>> {
199 let address = *pc;
200
201 if address == RETURN_TRAP_ADDRESS {
202 return Ok(FetchInstructionResult::ControlFlow(ControlFlow::Break(())));
203 }
204
205 if !address.is_multiple_of(size_of::<u32>() as u64) {
206 return Err(ExecuteError::UnalignedInstructionFetch { address });
207 }
208
209 let instruction = memory.read(address)?;
210 let instruction = Instruction::decode(instruction);
211 *pc += instruction.size() as u64;
212
213 Ok(FetchInstructionResult::Instruction(instruction))
214 }
215}
216
217impl<const RETURN_TRAP_ADDRESS: u64, Reg, Memory>
218 Rv64SystemInstructionHandler<Reg, Memory, &'static str>
219 for BasicInstructionHandler<RETURN_TRAP_ADDRESS>
220where
221 Reg: GenericRegister<Type = u64>,
222 [(); Reg::N]:,
223 Memory: VirtualMemory,
224{
225 #[inline(always)]
226 fn handle_ecall(
227 &mut self,
228 _regs: &mut Registers<Reg>,
229 _memory: &mut Memory,
230 pc: &mut u64,
231 instruction: Rv64Instruction<Reg>,
232 ) -> Result<(), ExecuteError<Rv64Instruction<Reg>, &'static str>> {
233 Err(ExecuteError::UnsupportedInstruction {
234 address: *pc - instruction.size() as u64,
235 instruction,
236 })
237 }
238}