1#[cfg(test)]
4mod tests;
5
6use crate::{
7 Address, BasicInt, CustomErrorPlaceholder, ExecutableInstruction, ExecutionError,
8 FetchInstructionResult, InstructionFetcher, ProgramCounter, ProgramCounterError, RegisterFile,
9 Rs1Rs2OperandValues, Rs1Rs2Operands, SystemInstructionHandler, VirtualMemory,
10 VirtualMemoryError,
11};
12use ab_riscv_primitives::prelude::*;
13use core::hint::cold_path;
14use core::marker::PhantomData;
15use core::ops::ControlFlow;
16use replace_with::replace_with_or_abort_and_return;
17
18pub const unsafe trait BasicRegister
24where
25 Self: [const] Register,
26{
27 const N: usize;
31
32 fn offset(self) -> u8;
34}
35
36const unsafe impl<Type> BasicRegister for EReg<Type>
38where
39 Self: [const] Register,
40{
41 const N: usize = 16;
42
43 #[inline(always)]
44 fn offset(self) -> u8 {
45 unsafe { core::mem::transmute::<Self, u8>(self) }
47 }
48}
49
50const unsafe impl<Type> BasicRegister for Reg<Type>
52where
53 Self: [const] Register,
54{
55 const N: usize = 32;
56
57 #[inline(always)]
58 fn offset(self) -> u8 {
59 unsafe { core::mem::transmute::<Self, u8>(self) }
61 }
62}
63
64#[derive(Debug, Clone, Copy)]
66#[repr(align(16))]
67pub struct BasicRegisters<Reg>
68where
69 Reg: BasicRegister,
70{
71 regs: [Reg::Type; Reg::N],
72}
73
74impl<Reg> Default for BasicRegisters<Reg>
75where
76 Reg: BasicRegister,
77{
78 #[inline(always)]
79 fn default() -> Self {
80 Self {
81 regs: [Reg::Type::default(); _],
82 }
83 }
84}
85
86const impl<Reg> RegisterFile<Reg> for BasicRegisters<Reg>
87where
88 Reg: [const] BasicRegister,
89{
90 #[inline(always)]
91 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
92 fn read(&self, reg: Reg) -> Reg::Type {
93 if reg == Reg::ZERO {
94 return Reg::Type::default();
96 }
97
98 *unsafe { self.regs.get_unchecked(usize::from(reg.offset())) }
100 }
101
102 #[inline(always)]
103 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
104 fn write(&mut self, reg: Reg, value: Reg::Type) {
105 *unsafe { self.regs.get_unchecked_mut(usize::from(reg.offset())) } = value;
107 }
108}
109
110#[derive(Debug)]
115pub struct BasicInterpreterState<Regs, ExtState, Memory, IF, InstructionHandler> {
116 pub regs: Regs,
118 pub ext_state: ExtState,
124 pub memory: Memory,
126 pub instruction_fetcher: IF,
128 pub system_instruction_handler: InstructionHandler,
130}
131
132impl<Regs, ExtState, Memory, IF, InstructionHandler>
133 BasicInterpreterState<Regs, ExtState, Memory, IF, InstructionHandler>
134{
135 pub fn execute<I>(&mut self) -> Result<(), ExecutionError<Address<I>>>
143 where
144 Regs: RegisterFile<<I as Instruction>::Reg>,
145 I: ExecutableInstruction<Regs, ExtState, Memory, IF, InstructionHandler>,
146 Memory: VirtualMemory,
147 IF: InstructionFetcher<I, Memory> + ProgramCounter<Address<I>, Memory>,
148 {
149 replace_with_or_abort_and_return(
150 &mut self.instruction_fetcher,
151 #[inline(always)]
152 |mut instruction_fetcher| {
153 loop {
154 let instruction = match instruction_fetcher.fetch_instruction(&self.memory) {
155 Ok(FetchInstructionResult::Instruction(instruction)) => instruction,
156 Ok(FetchInstructionResult::ControlFlow(ControlFlow::Continue(()))) => {
157 cold_path();
158 continue;
159 }
160 Ok(FetchInstructionResult::ControlFlow(ControlFlow::Break(()))) => {
161 cold_path();
162 break;
163 }
164 Err(error) => {
165 cold_path();
166 return (Err(error), instruction_fetcher);
167 }
168 };
169
170 let Rs1Rs2Operands { rs1, rs2 } = instruction.get_rs1_rs2_operands();
171 let rs1rs2_values = Rs1Rs2OperandValues {
172 rs1_value: self.regs.read(rs1),
173 rs2_value: self.regs.read(rs2),
174 };
175
176 match instruction.execute(
177 rs1rs2_values,
178 &mut self.regs,
179 &mut self.ext_state,
180 &mut self.memory,
181 &mut instruction_fetcher,
182 &mut self.system_instruction_handler,
183 ) {
184 Ok(ControlFlow::Continue((rd, rd_value))) => {
185 self.regs.write(rd, rd_value);
186 }
187 Ok(ControlFlow::Break(())) => {
188 cold_path();
189 break;
190 }
191 Err(error) => {
192 cold_path();
193 return (Err(error), instruction_fetcher);
194 }
195 }
196 }
197
198 (Ok(()), instruction_fetcher)
199 },
200 )
201 }
202}
203
204#[derive(Debug, Copy, Clone)]
218#[repr(align(16))]
219pub struct BasicMemory<const BASE_ADDR: u64, const SIZE: usize> {
220 data: [u8; SIZE],
221}
222
223impl<const BASE_ADDR: u64, const SIZE: usize> VirtualMemory for BasicMemory<BASE_ADDR, SIZE> {
224 #[inline(always)]
225 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
226 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
227 where
228 T: BasicInt,
229 {
230 let Some(offset) = address.checked_sub(BASE_ADDR) else {
231 cold_path();
232 return Err(VirtualMemoryError::OutOfBoundsRead { address });
233 };
234
235 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
236 cold_path();
237 return Err(VirtualMemoryError::OutOfBoundsRead { address });
238 }
239
240 unsafe {
242 Ok(self
243 .data
244 .as_ptr()
245 .cast::<T>()
246 .byte_add(offset as usize)
247 .read_unaligned())
248 }
249 }
250
251 #[inline(always)]
252 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
253 unsafe fn read_unchecked<T>(&self, address: u64) -> T
254 where
255 T: BasicInt,
256 {
257 unsafe {
259 let offset = address.unchecked_sub(BASE_ADDR) as usize;
260 self.data
261 .as_ptr()
262 .cast::<T>()
263 .byte_add(offset)
264 .read_unaligned()
265 }
266 }
267
268 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
269 fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError> {
270 let Some(offset) = address.checked_sub(BASE_ADDR) else {
271 cold_path();
272 return Err(VirtualMemoryError::OutOfBoundsRead { address });
273 };
274
275 if offset > self.data.len() as u64 {
276 cold_path();
277 return Err(VirtualMemoryError::OutOfBoundsRead { address });
278 }
279
280 self.data
281 .get(offset as usize..)
282 .and_then(|data| data.get(..len as usize))
283 .ok_or(VirtualMemoryError::OutOfBoundsRead { address })
284 }
285
286 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
287 fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8] {
288 let Some(offset) = address.checked_sub(BASE_ADDR) else {
289 cold_path();
290 return &[];
291 };
292
293 if offset > self.data.len() as u64 {
294 cold_path();
295 return &[];
296 }
297
298 let remaining = self.data.get(offset as usize..).unwrap_or_default();
299 remaining.get(..len as usize).unwrap_or(remaining)
300 }
301
302 #[inline(always)]
303 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
304 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
305 where
306 T: BasicInt,
307 {
308 let Some(offset) = address.checked_sub(BASE_ADDR) else {
309 cold_path();
310 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
311 };
312
313 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
314 cold_path();
315 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
316 }
317
318 unsafe {
320 self.data
321 .as_mut_ptr()
322 .cast::<T>()
323 .byte_add(offset as usize)
324 .write_unaligned(value);
325 }
326
327 Ok(())
328 }
329
330 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
331 fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError> {
332 let Some(offset) = address.checked_sub(BASE_ADDR) else {
333 cold_path();
334 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
335 };
336
337 if offset > self.data.len() as u64 {
338 cold_path();
339 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
340 }
341
342 let len = data.len();
343 let Some(target_data) = self
344 .data
345 .get_mut(offset as usize..)
346 .and_then(|data| data.get_mut(..len))
347 else {
348 cold_path();
349 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
350 };
351
352 target_data.copy_from_slice(data);
353
354 Ok(())
355 }
356}
357
358impl<const BASE_ADDR: u64, const SIZE: usize> Default for BasicMemory<BASE_ADDR, SIZE> {
359 #[inline(always)]
360 fn default() -> Self {
361 Self { data: [0; _] }
362 }
363}
364
365impl<const BASE_ADDR: u64, const SIZE: usize> BasicMemory<BASE_ADDR, SIZE> {
366 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
370 pub fn get_mut_bytes(
371 &mut self,
372 address: u64,
373 size: usize,
374 ) -> Result<&mut [u8], VirtualMemoryError> {
375 let Some(offset) = address.checked_sub(BASE_ADDR) else {
376 cold_path();
377 return Err(VirtualMemoryError::OutOfBoundsRead { address });
378 };
379 let offset = offset as usize;
380
381 let Some(slice) = self
382 .data
383 .get_mut(offset..)
384 .and_then(|data| data.get_mut(..size))
385 else {
386 cold_path();
387 return Err(VirtualMemoryError::OutOfBoundsRead { address });
388 };
389
390 Ok(slice)
391 }
392}
393
394#[derive(Debug, Copy, Clone)]
403pub struct BasicInstructionFetcher<I, CustomError = CustomErrorPlaceholder>
404where
405 I: Instruction,
406{
407 return_trap_address: Address<I>,
408 pc: Address<I>,
409 _phantom: PhantomData<CustomError>,
410}
411
412impl<I, Memory, CustomError> ProgramCounter<Address<I>, Memory, CustomError>
413 for BasicInstructionFetcher<I, CustomError>
414where
415 I: Instruction,
416 Memory: VirtualMemory,
417{
418 #[inline(always)]
419 fn get_pc(&self) -> Address<I> {
420 self.pc
421 }
422
423 #[inline]
424 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
425 fn set_pc(
426 &mut self,
427 memory: &Memory,
428 pc: Address<I>,
429 ) -> Result<ControlFlow<()>, ProgramCounterError<Address<I>, CustomError>> {
430 let _: &Memory = memory;
432 if pc == self.return_trap_address {
433 cold_path();
434 return Ok(ControlFlow::Break(()));
435 }
436
437 if !pc.as_u64().is_multiple_of(u64::from(I::alignment())) {
438 cold_path();
439 return Err(ProgramCounterError::UnalignedInstruction { address: pc });
440 }
441
442 self.pc = pc;
443
444 Ok(ControlFlow::Continue(()))
445 }
446}
447
448impl<I, Memory, CustomError> InstructionFetcher<I, Memory, CustomError>
449 for BasicInstructionFetcher<I, CustomError>
450where
451 I: Instruction,
452 Memory: VirtualMemory,
453{
454 #[inline]
455 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
456 fn fetch_instruction(
457 &mut self,
458 memory: &Memory,
459 ) -> Result<FetchInstructionResult<I>, ExecutionError<Address<I>, CustomError>> {
460 let instruction = match memory.read(self.pc.as_u64()).or_else(|error| {
461 cold_path();
462 if let Ok(instruction) = memory.read::<u16>(self.pc.as_u64())
464 && (instruction & 0b11) != 0b11
465 {
466 return Ok(u32::from(instruction));
467 }
468 Err(error)
469 }) {
470 Ok(instruction) => instruction,
471 Err(error) => {
472 cold_path();
473 return Err(ExecutionError::MemoryAccess(error));
474 }
475 };
476
477 let Some(instruction) = I::try_decode(instruction) else {
478 cold_path();
479 return Err(ExecutionError::IllegalInstruction { address: self.pc });
480 };
481 self.pc += instruction.size().into();
482
483 Ok(FetchInstructionResult::Instruction(instruction))
484 }
485}
486
487impl<I, CustomError> BasicInstructionFetcher<I, CustomError>
488where
489 I: Instruction,
490{
491 #[inline(always)]
496 pub fn new(return_trap_address: Address<I>, pc: Address<I>) -> Self {
497 Self {
498 return_trap_address,
499 pc,
500 _phantom: PhantomData,
501 }
502 }
503}
504
505#[derive(Debug, Default, Clone, Copy)]
508pub struct IllegalEcallSystemInstructionHandler;
509
510impl<Reg, Regs, Memory, PC, CustomError>
511 SystemInstructionHandler<Reg, Regs, Memory, PC, CustomError>
512 for IllegalEcallSystemInstructionHandler
513where
514 Reg: Register,
515 Regs: RegisterFile<Reg>,
516 PC: ProgramCounter<Reg::Type, Memory, CustomError>,
517{
518 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
519 fn handle_ecall(
520 &mut self,
521 regs: &mut Regs,
522 memory: &mut Memory,
523 program_counter: &mut PC,
524 ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type, CustomError>> {
525 let _: &Regs = regs;
527 let _: &Memory = memory;
528 Err(ExecutionError::IllegalInstruction {
529 address: program_counter.old_pc(size_of::<u32>() as u8),
530 })
531 }
532}