#![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
)]
#![cfg_attr(
any(
all(
target_arch = "riscv32",
any(
target_feature = "zbb",
target_feature = "zbc",
target_feature = "zbkb",
target_feature = "zbkx",
target_feature = "zknd",
target_feature = "zkne",
target_feature = "zknh"
)
),
all(
target_arch = "riscv64",
any(
target_feature = "zbb",
target_feature = "zbc",
target_feature = "zbkx",
target_feature = "zknd",
target_feature = "zkne",
target_feature = "zknh"
)
)
),
feature(riscv_ext_intrinsics)
)]
#![no_std]
pub mod basic;
pub mod prelude;
mod private;
pub mod rv32;
pub mod rv64;
pub mod v;
pub mod zicond;
pub mod zicsr;
use crate::private::BasicIntSealed;
use ab_riscv_primitives::prelude::*;
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, 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,
{
#[inline(always)]
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)
}
#[inline(always)]
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>>;
}