#![expect(incomplete_features, reason = "generic_const_exprs")]
#![feature(
const_convert,
const_default,
const_trait_impl,
generic_const_exprs,
result_option_map_or_default,
widening_mul
)]
#![no_std]
mod private;
pub mod rv32;
pub mod rv64;
pub mod v;
pub mod zicsr;
use crate::private::BasicIntSealed;
use ab_riscv_primitives::instructions::Instruction;
use ab_riscv_primitives::privilege::PrivilegeLevel;
use ab_riscv_primitives::registers::general_purpose::{RegType, Register, Registers};
use core::fmt;
use core::marker::PhantomData;
use core::ops::{ControlFlow, Sub};
type RegisterType<I> = <<I as Instruction>::Reg as Register>::Type;
type Address<I> = RegisterType<I>;
#[derive(Debug, thiserror::Error)]
pub enum VirtualMemoryError {
#[error("Out-of-bounds read at address {address}")]
OutOfBoundsRead {
address: u64,
},
#[error("Out-of-bounds write at address {address}")]
OutOfBoundsWrite {
address: u64,
},
}
pub trait BasicInt: Sized + Copy + BasicIntSealed + 'static {}
impl BasicIntSealed for u8 {}
impl BasicIntSealed for u16 {}
impl BasicIntSealed for u32 {}
impl BasicIntSealed for u64 {}
impl BasicIntSealed for i8 {}
impl BasicIntSealed for i16 {}
impl BasicIntSealed for i32 {}
impl BasicIntSealed for i64 {}
impl BasicInt for u8 {}
impl BasicInt for u16 {}
impl BasicInt for u32 {}
impl BasicInt for u64 {}
impl BasicInt for i8 {}
impl BasicInt for i16 {}
impl BasicInt for i32 {}
impl BasicInt for i64 {}
pub trait VirtualMemory {
fn read<T>(&self, address: u64) -> Result<T, VirtualMemoryError>
where
T: BasicInt;
unsafe fn read_unchecked<T>(&self, address: u64) -> T
where
T: BasicInt;
fn read_slice(&self, address: u64, len: u32) -> Result<&[u8], VirtualMemoryError>;
fn read_slice_up_to(&self, address: u64, len: u32) -> &[u8];
fn write<T>(&mut self, address: u64, value: T) -> Result<(), VirtualMemoryError>
where
T: BasicInt;
fn write_slice(&mut self, address: u64, data: &[u8]) -> Result<(), VirtualMemoryError>;
}
#[derive(Debug, Copy, Clone)]
pub struct CustomErrorPlaceholder;
impl fmt::Display for CustomErrorPlaceholder {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum ProgramCounterError<Address, CustomError = CustomErrorPlaceholder> {
#[error("Unaligned instruction at address {address}")]
UnalignedInstruction {
address: Address,
},
#[error("Memory access error: {0}")]
MemoryAccess(#[from] VirtualMemoryError),
#[error("Custom error: {0}")]
Custom(CustomError),
}
pub trait ProgramCounter<Address, Memory, CustomError = CustomErrorPlaceholder> {
fn get_pc(&self) -> Address;
#[inline(always)]
fn old_pc(&self, instruction_size: u8) -> Address
where
Address: From<u8> + Sub<Output = Address>,
{
self.get_pc() - Address::from(instruction_size)
}
fn set_pc(
&mut self,
memory: &Memory,
pc: Address,
) -> Result<ControlFlow<()>, ProgramCounterError<Address, CustomError>>;
}
#[derive(Debug, thiserror::Error)]
pub enum ExecutionError<Address, CustomError = CustomErrorPlaceholder> {
#[error("Unaligned instruction fetch at address {address:#x}")]
UnalignedInstructionFetch {
address: Address,
},
#[error("Program counter error: {0}")]
ProgramCounter(#[from] ProgramCounterError<Address, CustomError>),
#[error("Memory access error: {0}")]
MemoryAccess(#[from] VirtualMemoryError),
#[error("Unsupported `ecall` instruction at address {address:#x}")]
EcallUnsupported {
address: Address,
},
#[error("Unimplemented/illegal instruction at address {address:#x}")]
IllegalInstruction {
address: Address,
},
#[error("Invalid instruction at address {address:#x}: {instruction:#010x}")]
InvalidInstruction {
address: Address,
instruction: u32,
},
#[error("CSR error: {0}")]
CsrError(#[from] CsrError<CustomError>),
#[error("Custom error: {0}")]
Custom(CustomError),
}
#[derive(Debug, Copy, Clone)]
pub enum FetchInstructionResult<Instruction> {
Instruction(Instruction),
ControlFlow(ControlFlow<()>),
}
pub trait InstructionFetcher<I, Memory, CustomError = CustomErrorPlaceholder>
where
Self: ProgramCounter<Address<I>, Memory, CustomError>,
I: Instruction,
{
fn fetch_instruction(
&mut self,
memory: &Memory,
) -> Result<FetchInstructionResult<I>, ExecutionError<Address<I>, CustomError>>;
}
#[derive(Debug, Copy, Clone)]
pub struct BasicInstructionFetcher<I, CustomError>
where
I: Instruction,
{
return_trap_address: Address<I>,
pc: Address<I>,
_phantom: PhantomData<CustomError>,
}
impl<I, Memory, CustomError> ProgramCounter<Address<I>, Memory, CustomError>
for BasicInstructionFetcher<I, CustomError>
where
I: Instruction,
Memory: VirtualMemory,
{
#[inline(always)]
fn get_pc(&self) -> Address<I> {
self.pc
}
#[inline]
fn set_pc(
&mut self,
memory: &Memory,
pc: Address<I>,
) -> Result<ControlFlow<()>, ProgramCounterError<Address<I>, CustomError>> {
if pc == self.return_trap_address {
return Ok(ControlFlow::Break(()));
}
if !pc.as_u64().is_multiple_of(u64::from(I::alignment())) {
return Err(ProgramCounterError::UnalignedInstruction { address: pc });
}
memory.read::<u32>(pc.as_u64())?;
self.pc = pc;
Ok(ControlFlow::Continue(()))
}
}
impl<I, Memory, CustomError> InstructionFetcher<I, Memory, CustomError>
for BasicInstructionFetcher<I, CustomError>
where
I: Instruction,
Memory: VirtualMemory,
{
#[inline]
fn fetch_instruction(
&mut self,
memory: &Memory,
) -> Result<FetchInstructionResult<I>, ExecutionError<Address<I>, CustomError>> {
let instruction = unsafe { memory.read_unchecked(self.pc.as_u64()) };
let instruction = unsafe { I::try_decode(instruction).unwrap_unchecked() };
self.pc += instruction.size().into();
Ok(FetchInstructionResult::Instruction(instruction))
}
}
impl<I, CustomError> BasicInstructionFetcher<I, CustomError>
where
I: Instruction,
{
#[inline(always)]
pub unsafe fn new(return_trap_address: Address<I>, pc: Address<I>) -> Self {
Self {
return_trap_address,
pc,
_phantom: PhantomData,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum CsrError<CustomError = CustomErrorPlaceholder> {
#[error("Read only CSR {csr_index:#x}")]
ReadOnly {
csr_index: u16,
},
#[error("Illegal read access to CSR {csr_index:#x}")]
IllegalRead {
csr_index: u16,
},
#[error("Illegal write access to CSR {csr_index:#x}")]
IllegalWrite {
csr_index: u16,
},
#[error("Unknown CSR {csr_index:#x}")]
Unknown {
csr_index: u16,
},
#[error(
"Insufficient privilege level for CSR {csr_index:#x}: required {required:?}, \
current {current:?}"
)]
InsufficientPrivilege {
csr_index: u16,
required: PrivilegeLevel,
current: PrivilegeLevel,
},
#[error("Custom error: {0}")]
Custom(CustomError),
}
pub trait Csrs<Reg, CustomError = CustomErrorPlaceholder>
where
Reg: Register,
{
#[inline(always)]
fn privilege_level(&self) -> PrivilegeLevel {
PrivilegeLevel::Machine
}
fn read_csr(&self, csr_index: u16) -> Result<Reg::Type, CsrError<CustomError>>;
fn write_csr(&mut self, csr_index: u16, value: Reg::Type) -> Result<(), CsrError<CustomError>>;
fn process_csr_read(
&self,
csr_index: u16,
raw_value: Reg::Type,
) -> Result<Reg::Type, CsrError<CustomError>>;
fn process_csr_write(
&mut self,
csr_index: u16,
write_value: Reg::Type,
) -> Result<Reg::Type, CsrError<CustomError>>;
}
pub trait SystemInstructionHandler<Reg, Memory, PC, CustomError = CustomErrorPlaceholder>
where
Reg: Register,
[(); Reg::N]:,
{
#[inline(always)]
fn handle_fence(&mut self, pred: u8, succ: u8) {
let _ = pred;
let _ = succ;
}
#[inline(always)]
fn handle_fence_tso(&mut self) {
}
fn handle_ecall(
&mut self,
regs: &mut Registers<Reg>,
memory: &mut Memory,
program_counter: &mut PC,
) -> Result<ControlFlow<()>, ExecutionError<Reg::Type, CustomError>>;
#[inline(always)]
fn handle_ebreak(&mut self, regs: &mut Registers<Reg>, memory: &mut Memory, pc: Reg::Type) {
let _ = regs;
let _ = memory;
let _ = pc;
}
}
#[derive(Debug)]
pub struct InterpreterState<
Reg,
ExtState,
Memory,
IF,
InstructionHandler,
CustomError = CustomErrorPlaceholder,
> where
Reg: Register,
[(); Reg::N]:,
{
pub regs: Registers<Reg>,
pub ext_state: ExtState,
pub memory: Memory,
pub instruction_fetcher: IF,
pub system_instruction_handler: InstructionHandler,
pub custom_error: PhantomData<CustomError>,
}
pub trait ExecutableInstruction<State, CustomError = CustomErrorPlaceholder>
where
Self: Instruction,
{
fn prepare_csr_read<C>(
csrs: &C,
csr_index: u16,
raw_value: RegisterType<Self>,
output_value: &mut RegisterType<Self>,
) -> Result<bool, CsrError<CustomError>>
where
C: Csrs<Self::Reg, CustomError>,
{
let _ = csrs;
let _ = csr_index;
let _ = raw_value;
let _ = output_value;
Ok(false)
}
fn prepare_csr_write<C>(
csrs: &mut C,
csr_index: u16,
write_value: RegisterType<Self>,
output_value: &mut RegisterType<Self>,
) -> Result<bool, CsrError<CustomError>>
where
C: Csrs<Self::Reg, CustomError>,
{
let _ = csrs;
let _ = csr_index;
let _ = write_value;
let _ = output_value;
Ok(false)
}
fn execute(
self,
state: &mut State,
) -> Result<ControlFlow<()>, ExecutionError<Address<Self>, CustomError>>;
}