1#[cfg(test)]
4mod tests;
5
6use crate::zawrs::WrsHandler;
7use crate::{
8 Address, BasicInt, ExecutableInstruction, ExecutionError, ExecutionResult,
9 FetchInstructionResult, InstructionFetcher, PackedAddress, ProgramCounter, RegisterFile,
10 Rs1Rs2OperandValues, Rs1Rs2Operands, SystemInstructionHandler, VirtualMemory,
11 VirtualMemoryError,
12};
13use ab_riscv_primitives::prelude::*;
14#[cfg(feature = "alloc")]
15use alloc::boxed::Box;
16use core::hint::cold_path;
17use core::ops::ControlFlow;
18use replace_with::replace_with_or_abort_and_return;
19
20pub const unsafe trait BasicRegister
26where
27 Self: [const] Register,
28{
29 const N: usize;
33
34 fn offset(self) -> u8;
36}
37
38const unsafe impl<Type> BasicRegister for EReg<Type>
40where
41 Self: [const] Register,
42{
43 const N: usize = 16;
44
45 #[inline(always)]
46 fn offset(self) -> u8 {
47 unsafe { core::mem::transmute::<Self, u8>(self) }
49 }
50}
51
52const unsafe impl<Type> BasicRegister for Reg<Type>
54where
55 Self: [const] Register,
56{
57 const N: usize = 32;
58
59 #[inline(always)]
60 fn offset(self) -> u8 {
61 unsafe { core::mem::transmute::<Self, u8>(self) }
63 }
64}
65
66#[derive(Debug, Clone, Copy)]
72#[repr(align(16))]
73pub struct BasicRegisters<Reg, const ZEROSTORE: bool = false>
74where
75 Reg: BasicRegister,
76{
77 regs: [Reg::Type; Reg::N],
78}
79
80impl<Reg, const ZEROSTORE: bool> Default for BasicRegisters<Reg, ZEROSTORE>
81where
82 Reg: BasicRegister,
83{
84 #[inline(always)]
85 fn default() -> Self {
86 Self {
87 regs: [Reg::Type::default(); _],
88 }
89 }
90}
91
92const impl<Reg, const ZEROSTORE: bool> RegisterFile<Reg> for BasicRegisters<Reg, ZEROSTORE>
93where
94 Reg: [const] BasicRegister,
95{
96 #[inline(always)]
97 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
98 fn read(&self, reg: Reg) -> Reg::Type {
99 if reg == Reg::ZERO && !ZEROSTORE {
100 return Reg::Type::default();
102 }
103
104 *unsafe { self.regs.get_unchecked(usize::from(reg.offset())) }
106 }
107
108 #[inline(always)]
109 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
110 fn write(&mut self, reg: Reg, value: Reg::Type) {
111 *unsafe { self.regs.get_unchecked_mut(usize::from(reg.offset())) } = value;
113 if ZEROSTORE {
114 *unsafe { self.regs.get_unchecked_mut(0) } = Reg::Type::default();
116 }
117 }
118}
119
120#[derive(Debug)]
125pub struct BasicInterpreterState<Regs, Env, Memory, IF> {
126 pub regs: Regs,
128 pub env: Env,
134 pub memory: Memory,
136 pub instruction_fetcher: IF,
138}
139
140impl<Regs, Env, Memory, IF> BasicInterpreterState<Regs, Env, Memory, IF> {
141 #[rustc_align(64)]
151 pub fn execute<I>(&mut self) -> Result<(), ExecutionError<Address<I>>>
152 where
153 Regs: RegisterFile<<I as Instruction>::Reg>,
154 I: ExecutableInstruction<Regs, Env, Memory, IF>,
155 Memory: VirtualMemory,
156 IF: InstructionFetcher<I, Memory> + ProgramCounter<Address<I>, Memory>,
157 {
158 replace_with_or_abort_and_return(
159 &mut self.instruction_fetcher,
160 #[inline(always)]
161 |mut instruction_fetcher| {
162 loop {
163 let instruction = match instruction_fetcher.fetch_instruction(&self.memory) {
164 FetchInstructionResult::Instruction(instruction) => instruction,
165 FetchInstructionResult::Continue => {
166 cold_path();
167 continue;
168 }
169 FetchInstructionResult::Break => {
170 cold_path();
171 break;
172 }
173 FetchInstructionResult::Err(error) => {
174 cold_path();
175 return (Err(error), instruction_fetcher);
176 }
177 };
178
179 let Rs1Rs2Operands { rs1, rs2 } = instruction.get_rs1_rs2_operands();
180 let rs1rs2_values = Rs1Rs2OperandValues {
181 rs1_value: self.regs.read(rs1),
182 rs2_value: self.regs.read(rs2),
183 };
184
185 let outcome = instruction.execute(
186 rs1rs2_values,
187 &mut self.regs,
188 &mut self.env,
189 &mut self.memory,
190 &mut instruction_fetcher,
191 );
192
193 let control_flow =
194 match outcome {
195 ExecutionResult::Continue { rd, value } => {
196 self.regs.write(rd, value);
197 continue;
198 }
199 ExecutionResult::ContinueNoWrite => {
200 continue;
201 }
202 ExecutionResult::Branch { offset } => instruction_fetcher
203 .set_pc_relative(&self.memory, instruction.size(), offset),
204 ExecutionResult::Jump { target } => {
205 instruction_fetcher.set_pc(&self.memory, target)
206 }
207 ExecutionResult::Break => {
208 cold_path();
209 break;
210 }
211 ExecutionResult::Err(error) => {
212 cold_path();
213 return (Err(error), instruction_fetcher);
214 }
215 };
216
217 match control_flow {
218 Ok(ControlFlow::Continue(())) => {}
219 Ok(ControlFlow::Break(())) => {
220 cold_path();
221 break;
222 }
223 Err(error) => {
224 cold_path();
225 return (Err(error), instruction_fetcher);
226 }
227 }
228 }
229
230 (Ok(()), instruction_fetcher)
231 },
232 )
233 }
234}
235
236#[derive(Debug, Copy, Clone)]
250#[repr(align(16))]
251pub struct BasicMemory<const BASE_ADDR: u64, const SIZE: usize> {
252 data: [u8; SIZE],
253}
254
255const impl<const BASE_ADDR: u64, const SIZE: usize> VirtualMemory for BasicMemory<BASE_ADDR, SIZE> {
256 #[inline(always)]
257 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
258 fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
259 where
260 T: BasicInt,
261 {
262 let Some(offset) = address.checked_sub(BASE_ADDR) else {
263 cold_path();
264 return Err(VirtualMemoryError::OutOfBoundsRead { address });
265 };
266
267 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
268 cold_path();
269 return Err(VirtualMemoryError::OutOfBoundsRead { address });
270 }
271
272 unsafe {
274 Ok(self
275 .data
276 .as_ptr()
277 .cast::<T>()
278 .byte_add(offset as usize)
279 .read_unaligned())
280 }
281 }
282
283 #[inline(always)]
284 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
285 unsafe fn read_unchecked<T>(&self, address: u64) -> T
286 where
287 T: BasicInt,
288 {
289 unsafe {
291 let offset = address.unchecked_sub(BASE_ADDR) as usize;
292 self.data
293 .as_ptr()
294 .cast::<T>()
295 .byte_add(offset)
296 .read_unaligned()
297 }
298 }
299
300 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
301 fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError> {
302 let Some(offset) = address.checked_sub(BASE_ADDR) else {
303 cold_path();
304 return Err(VirtualMemoryError::OutOfBoundsRead { address });
305 };
306
307 if offset > self.data.len() as u64 {
308 cold_path();
309 return Err(VirtualMemoryError::OutOfBoundsRead { address });
310 }
311
312 self.data
313 .get(offset as usize..)
314 .and_then(const |data| data.get(..len as usize))
315 .ok_or(VirtualMemoryError::OutOfBoundsRead { address })
316 }
317
318 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
319 fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8] {
320 let Some(offset) = address.checked_sub(BASE_ADDR) else {
321 cold_path();
322 return &[];
323 };
324
325 if offset > self.data.len() as u64 {
326 cold_path();
327 return &[];
328 }
329
330 let remaining = self.data.get(offset as usize..).unwrap_or_default();
331 remaining.get(..len as usize).unwrap_or(remaining)
332 }
333
334 #[inline(always)]
335 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
336 fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
337 where
338 T: BasicInt,
339 {
340 let Some(offset) = address.checked_sub(BASE_ADDR) else {
341 cold_path();
342 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
343 };
344
345 if offset.saturating_add(size_of::<T>() as u64) > self.data.len() as u64 {
346 cold_path();
347 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
348 }
349
350 unsafe {
352 self.data
353 .as_mut_ptr()
354 .cast::<T>()
355 .byte_add(offset as usize)
356 .write_unaligned(value);
357 }
358
359 Ok(())
360 }
361
362 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
363 fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError> {
364 let Some(offset) = address.checked_sub(BASE_ADDR) else {
365 cold_path();
366 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
367 };
368
369 if offset > self.data.len() as u64 {
370 cold_path();
371 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
372 }
373
374 let len = data.len();
375 let Some(target_data) = self
376 .data
377 .get_mut(offset as usize..)
378 .and_then(const |data| data.get_mut(..len))
379 else {
380 cold_path();
381 return Err(VirtualMemoryError::OutOfBoundsWrite { address });
382 };
383
384 target_data.copy_from_slice(data);
385
386 Ok(())
387 }
388}
389
390impl<const BASE_ADDR: u64, const SIZE: usize> Default for BasicMemory<BASE_ADDR, SIZE> {
391 #[inline(always)]
392 fn default() -> Self {
393 Self { data: [0; _] }
394 }
395}
396
397impl<const BASE_ADDR: u64, const SIZE: usize> BasicMemory<BASE_ADDR, SIZE> {
398 #[cfg(feature = "alloc")]
399 pub fn new_boxed() -> Box<Self> {
400 unsafe { Box::<Self>::new_zeroed().assume_init() }
402 }
403
404 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
408 pub const fn get_mut_bytes(
409 &mut self,
410 address: u64,
411 size: usize,
412 ) -> Result<&mut [u8], VirtualMemoryError> {
413 let Some(offset) = address.checked_sub(BASE_ADDR) else {
414 cold_path();
415 return Err(VirtualMemoryError::OutOfBoundsRead { address });
416 };
417 let offset = offset as usize;
418
419 let Some(slice) = self
420 .data
421 .get_mut(offset..)
422 .and_then(const |data| data.get_mut(..size))
423 else {
424 cold_path();
425 return Err(VirtualMemoryError::OutOfBoundsRead { address });
426 };
427
428 Ok(slice)
429 }
430}
431
432#[derive(Debug, Copy, Clone)]
441pub struct BasicInstructionFetcher<I>
442where
443 I: Instruction,
444{
445 return_trap_address: Address<I>,
446 pc: Address<I>,
447}
448
449const impl<I, Memory> ProgramCounter<Address<I>, Memory> for BasicInstructionFetcher<I>
450where
451 I: [const] Instruction,
452 Memory: [const] VirtualMemory,
453{
454 #[inline(always)]
455 fn get_pc(&self) -> Address<I> {
456 self.pc
457 }
458
459 #[inline(always)]
460 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
461 unsafe fn try_set_pc_relative(&mut self, instruction_size: u8, offset: i32) -> bool {
462 let old_pc = <Self as ProgramCounter<_, Memory>>::old_pc(self, instruction_size);
463 let pc = old_pc.wrapping_add_signed(offset);
464 self.pc = pc;
467
468 pc != self.return_trap_address && pc.as_u64().is_multiple_of(u64::from(I::alignment()))
469 }
470
471 #[cold]
472 #[inline(never)]
473 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
474 unsafe fn failed_branch(
475 &mut self,
476 memory: &Memory,
477 ) -> Result<ControlFlow<()>, ExecutionError<Address<I>>> {
478 self.set_pc(memory, self.pc)
481 }
482
483 #[inline]
484 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
485 fn set_pc(
486 &mut self,
487 _memory: &Memory,
488 pc: Address<I>,
489 ) -> Result<ControlFlow<()>, ExecutionError<Address<I>>> {
490 if pc == self.return_trap_address {
491 cold_path();
492 return Ok(ControlFlow::Break(()));
493 }
494
495 if !pc.as_u64().is_multiple_of(u64::from(I::alignment())) {
496 cold_path();
497 return Err(ExecutionError::UnalignedInstruction {
498 address: PackedAddress::new(pc),
499 });
500 }
501
502 self.pc = pc;
503
504 Ok(ControlFlow::Continue(()))
505 }
506}
507
508const impl<I, Memory> InstructionFetcher<I, Memory> for BasicInstructionFetcher<I>
509where
510 I: [const] Instruction,
511 Memory: [const] VirtualMemory,
512{
513 #[inline]
514 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
515 fn peek_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I> {
516 let instruction = match memory.read(self.pc.as_u64()).or_else(const |error| {
517 cold_path();
518 if let Ok(instruction) = memory.read::<u16>(self.pc.as_u64())
520 && (instruction & 0b11) != 0b11
521 {
522 return Ok(u32::from(instruction));
523 }
524 Err(error)
525 }) {
526 Ok(instruction) => instruction,
527 Err(error) => {
528 cold_path();
529 return FetchInstructionResult::Err(ExecutionError::from(error));
530 }
531 };
532
533 let Some(instruction) = I::try_decode(instruction) else {
534 cold_path();
535 return FetchInstructionResult::Err(ExecutionError::IllegalInstruction {
536 address: PackedAddress::new(self.pc),
537 });
538 };
539 FetchInstructionResult::Instruction(instruction)
540 }
541
542 #[inline(always)]
543 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
544 unsafe fn advance(&mut self, instruction_size: u8) {
545 self.pc = self.pc.wrapping_add_signed(i32::from(instruction_size));
546 }
547
548 #[inline]
549 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
550 fn fetch_instruction(&mut self, memory: &Memory) -> FetchInstructionResult<I> {
551 let result = InstructionFetcher::<I, Memory>::peek_instruction(self, memory);
552
553 if let FetchInstructionResult::Instruction(instruction) = result {
554 unsafe {
557 InstructionFetcher::<I, Memory>::advance(self, instruction.size());
558 }
559 }
560
561 result
562 }
563}
564
565impl<I> BasicInstructionFetcher<I>
566where
567 I: Instruction,
568{
569 #[inline(always)]
574 pub const fn new(return_trap_address: Address<I>, pc: Address<I>) -> Self {
575 Self {
576 return_trap_address,
577 pc,
578 }
579 }
580}
581
582#[derive(Debug, Default, Clone, Copy)]
588pub struct IllegalEcallSystemInstructionHandler;
589
590const impl<Reg, Regs, Memory, PC> SystemInstructionHandler<Reg, Regs, Memory, PC>
591 for IllegalEcallSystemInstructionHandler
592where
593 Reg: [const] Register,
594 PC: [const] ProgramCounter<Reg::Type, Memory>,
595{
596 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
597 fn handle_ecall(
598 &mut self,
599 _regs: &mut Regs,
600 _memory: &mut Memory,
601 program_counter: &mut PC,
602 ) -> Result<ControlFlow<()>, ExecutionError<Reg::Type>> {
603 Err(ExecutionError::IllegalInstruction {
604 address: PackedAddress::new(program_counter.old_pc(size_of::<u32>() as u8)),
605 })
606 }
607}
608
609const impl WrsHandler for IllegalEcallSystemInstructionHandler {}