use crate::core::exec::{Access as ExitAccess, Exit, ExitMask, ExitReason};
use crate::core::space::{AddressSpace, MemAttrs};
use crate::core::value::Width;
use super::csr::{self, Csrs, Lines, Priv, cause, irq, status};
use super::isa::{self, Op, Xlen};
use super::mmu::{self, Access, Tlb};
use super::{Config, PAGE_MASK};
use crate::float::{self, B32, B64, Env, Flags, Format, Round};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Trap {
pub cause: u64,
pub tval: u64,
}
impl Trap {
const fn bare(cause: u64) -> Trap {
Trap { cause, tval: 0 }
}
const fn illegal(encoding: u64) -> Trap {
Trap {
cause: cause::ILLEGAL_INSN,
tval: encoding,
}
}
}
#[derive(Debug, Clone)]
pub(super) struct State {
pub x: [u64; 32],
pub f: [u64; 32],
pub pc: u64,
pub csrs: Csrs,
pub reservation: Option<u64>,
pub cycles: u64,
pub debt: u64,
pub wfi: bool,
pub faults: u64,
}
impl State {
pub(super) fn new(cfg: &Config) -> State {
State {
x: [0; 32],
f: [0; 32],
pc: cfg.xlen.trunc(cfg.reset_vector),
csrs: Csrs::new(cfg.xlen, cfg.ext, cfg.hartid, cfg.pmp_count),
reservation: None,
cycles: 0,
debt: 0,
wfi: false,
faults: 0,
}
}
}
pub(super) struct Exec<'a> {
st: &'a mut State,
tlb: &'a mut Tlb,
space: &'a AddressSpace,
cfg: &'a Config,
lines: &'a Lines,
exits: ExitMask,
exit: Option<Exit>,
attrs: MemAttrs,
used: u64,
next_pc: u64,
this_pc: u64,
wrote_instret: bool,
}
struct Walker<'a> {
space: &'a AddressSpace,
attrs: MemAttrs,
accesses: u64,
}
impl mmu::ReadPte for Walker<'_> {
fn read_pte(&mut self, addr: u64, bytes: u32) -> Option<u64> {
self.accesses += 1;
let width = Width::from_bytes(u64::from(bytes))?;
self.space.read(addr, width, self.attrs).ok()
}
}
impl mmu::PhysMem for Walker<'_> {
fn write_pte(&mut self, addr: u64, bytes: u32, value: u64) -> Option<()> {
self.accesses += 1;
let width = Width::from_bytes(u64::from(bytes))?;
self.space.write(addr, width, value, self.attrs).ok()
}
}
pub(super) fn debug_translate(
st: &State,
space: &AddressSpace,
cfg: &Config,
va: u64,
) -> Option<u64> {
let mut walker = Walker {
space,
attrs: MemAttrs::DEBUG.with_requester(cfg.requester),
accesses: 0,
};
mmu::translate_debug(&st.csrs, &mut walker, va, st.csrs.priv_mode).ok()
}
impl<'a> Exec<'a> {
pub(super) fn new(
st: &'a mut State,
tlb: &'a mut Tlb,
space: &'a AddressSpace,
cfg: &'a Config,
lines: &'a Lines,
exits: ExitMask,
) -> Exec<'a> {
let attrs = MemAttrs::DEFAULT.with_requester(cfg.requester);
let this_pc = st.pc;
Exec {
st,
tlb,
space,
cfg,
lines,
exits,
exit: None,
attrs,
used: 0,
next_pc: this_pc,
this_pc,
wrote_instret: false,
}
}
pub(super) fn step(&mut self) -> u64 {
if let Some(code) = self.pending_interrupt() {
self.enter_trap(Trap::bare(code), true);
self.st.pc = self.next_pc;
return self.used.max(1);
}
if self.st.wfi {
if self.lines.pending() & self.st.csrs.mie == 0 {
self.charge();
return self.used;
}
self.st.wfi = false;
}
self.this_pc = self.st.pc;
self.next_pc = self.st.pc;
match self.execute() {
Ok(()) => {
if self.st.csrs.mcountinhibit & 0b100 == 0 && !self.wrote_instret {
self.st.csrs.minstret = self.st.csrs.minstret.wrapping_add(1);
}
self.st.pc = self.cfg.xlen.trunc(self.next_pc);
}
Err(trap) => match self.exit_for(&trap) {
Some(exit) => {
self.st.pc = self.cfg.xlen.trunc(exit.resume_pc());
self.exit = Some(exit);
}
None => {
self.enter_trap(trap, false);
self.st.pc = self.next_pc;
}
},
}
self.used.max(1)
}
pub(super) fn take_exit(&mut self) -> Option<Exit> {
self.exit.take()
}
fn exit_for(&self, trap: &Trap) -> Option<Exit> {
let (reason, access) = match trap.cause {
cause::ECALL_U | cause::ECALL_S | cause::ECALL_M => {
(ExitReason::SYSCALL, ExitAccess::None)
}
cause::BREAKPOINT => (ExitReason::BREAKPOINT, ExitAccess::None),
cause::ILLEGAL_INSN => (ExitReason::FAULT, ExitAccess::None),
cause::INSN_MISALIGNED | cause::INSN_ACCESS | cause::INSN_PAGE_FAULT => {
(ExitReason::FAULT, ExitAccess::Execute)
}
cause::LOAD_MISALIGNED | cause::LOAD_ACCESS | cause::LOAD_PAGE_FAULT => {
(ExitReason::FAULT, ExitAccess::Read)
}
cause::STORE_MISALIGNED | cause::STORE_ACCESS | cause::STORE_PAGE_FAULT => {
(ExitReason::FAULT, ExitAccess::Write)
}
_ => return None,
};
if !self.exits.contains(reason) {
return None;
}
let len = self.next_pc.wrapping_sub(self.this_pc);
let len = if len <= 4 { len as u8 } else { 0 };
let exit = Exit::new(reason, self.this_pc, len).with_detail(trap.cause);
Some(match access {
ExitAccess::None => exit,
_ => exit.with_access(trap.tval, access),
})
}
#[inline]
fn charge(&mut self) {
self.used += 1;
self.st.cycles = self.st.cycles.wrapping_add(1);
if self.st.csrs.mcountinhibit & 1 == 0 {
self.st.csrs.mcycle = self.st.csrs.mcycle.wrapping_add(1);
}
}
#[inline]
fn x(&self, i: u32) -> u64 {
self.st.x[i as usize]
}
#[inline]
fn set_x(&mut self, i: u32, value: u64) {
if i != 0 {
self.st.x[i as usize] = self.cfg.xlen.sext(value);
}
}
#[inline]
fn f(&self, i: u32) -> u64 {
self.st.f[i as usize]
}
#[inline]
fn set_f(&mut self, i: u32, value: u64) {
self.st.f[i as usize] = value;
self.st.csrs.dirty_fp();
}
#[inline]
fn fs(&self, i: u32) -> u64 {
let v = self.st.f[i as usize];
if v >> 32 == 0xffff_ffff {
v & 0xffff_ffff
} else {
B32::QUIET_NAN
}
}
#[inline]
fn set_fs(&mut self, i: u32, value: u64) {
self.set_f(i, 0xffff_ffff_0000_0000 | (value & 0xffff_ffff));
}
#[inline]
fn raise(&mut self, flags: Flags) {
if !flags.is_empty() {
self.st.csrs.fcsr |= u64::from(flags.to_fcsr());
self.st.csrs.dirty_fp();
}
}
fn effective_priv(&self, kind: Access) -> Priv {
if kind != Access::Fetch
&& self.st.csrs.priv_mode == Priv::Machine
&& self.st.csrs.mstatus & status::MPRV != 0
{
Priv::from_bits((self.st.csrs.mstatus & status::MPP) >> status::MPP_SHIFT)
.unwrap_or(Priv::Machine)
} else {
self.st.csrs.priv_mode
}
}
fn fault_cause(kind: Access, fault: mmu::Fault) -> u64 {
match (kind, fault) {
(Access::Fetch, mmu::Fault::Page) => cause::INSN_PAGE_FAULT,
(Access::Fetch, mmu::Fault::Access) => cause::INSN_ACCESS,
(Access::Load, mmu::Fault::Page) => cause::LOAD_PAGE_FAULT,
(Access::Load, mmu::Fault::Access) => cause::LOAD_ACCESS,
(Access::Store, mmu::Fault::Page) => cause::STORE_PAGE_FAULT,
(Access::Store, mmu::Fault::Access) => cause::STORE_ACCESS,
}
}
fn translate(&mut self, vaddr: u64, kind: Access, len: u64) -> Result<u64, Trap> {
let mode = self.effective_priv(kind);
let phys = if mmu::translation_active(&self.st.csrs, mode) {
let vpn = vaddr >> mmu::PAGE_BITS;
let asid = mmu::asid(&self.st.csrs);
let generation = self.st.csrs.translation_gen;
if let Some(base) = self.tlb.lookup(kind, vpn, asid, mode, generation) {
base | (vaddr & PAGE_MASK)
} else {
let mut walker = Walker {
space: self.space,
attrs: self.attrs,
accesses: 0,
};
let result = mmu::translate(&self.st.csrs, &mut walker, vaddr, kind, mode);
for _ in 0..walker.accesses {
self.charge();
}
let phys = result.map_err(|f| Trap {
cause: Self::fault_cause(kind, f),
tval: vaddr,
})?;
self.tlb
.insert(kind, vpn, asid, mode, generation, phys & !PAGE_MASK);
phys
}
} else {
vaddr
};
if !mmu::pmp_allows(&self.st.csrs, phys, len, kind, mode) {
return Err(Trap {
cause: Self::fault_cause(kind, mmu::Fault::Access),
tval: vaddr,
});
}
Ok(phys)
}
fn read_once(&mut self, vaddr: u64, width: Width, kind: Access) -> Result<u64, Trap> {
let phys = self.translate(vaddr, kind, width.bytes())?;
self.charge();
match self.space.read(phys, width, self.attrs) {
Ok(v) => Ok(v),
Err(_) => {
self.st.faults = self.st.faults.wrapping_add(1);
Err(Trap {
cause: Self::fault_cause(kind, mmu::Fault::Access),
tval: vaddr,
})
}
}
}
fn write_once(&mut self, vaddr: u64, width: Width, value: u64) -> Result<(), Trap> {
let phys = self.translate(vaddr, Access::Store, width.bytes())?;
self.charge();
match self.space.write(phys, width, value, self.attrs) {
Ok(()) => Ok(()),
Err(_) => {
self.st.faults = self.st.faults.wrapping_add(1);
Err(Trap {
cause: cause::STORE_ACCESS,
tval: vaddr,
})
}
}
}
fn load(&mut self, vaddr: u64, bytes: u64) -> Result<u64, Trap> {
let width = Width::from_bytes(bytes).ok_or(Trap::bare(cause::LOAD_ACCESS))?;
if vaddr.is_multiple_of(bytes) {
return self.read_once(vaddr, width, Access::Load);
}
if !self.cfg.misaligned {
return Err(Trap {
cause: cause::LOAD_MISALIGNED,
tval: vaddr,
});
}
let mut value = 0u64;
for i in 0..bytes {
let byte = self.read_once(vaddr.wrapping_add(i), Width::U8, Access::Load)?;
value |= (byte & 0xff) << (8 * i);
}
Ok(value)
}
fn store(&mut self, vaddr: u64, bytes: u64, value: u64) -> Result<(), Trap> {
let width = Width::from_bytes(bytes).ok_or(Trap::bare(cause::STORE_ACCESS))?;
if let Some(reserved) = self.st.reservation
&& reserved >> 3 == vaddr >> 3
{
self.st.reservation = None;
}
if vaddr.is_multiple_of(bytes) {
return self.write_once(vaddr, width, value);
}
if !self.cfg.misaligned {
return Err(Trap {
cause: cause::STORE_MISALIGNED,
tval: vaddr,
});
}
for i in 0..bytes {
self.write_once(vaddr.wrapping_add(i), Width::U8, value >> (8 * i))?;
}
Ok(())
}
fn fetch(&mut self) -> Result<(u32, u64, u64), Trap> {
let pc = self.st.pc;
let low = self.read_once(pc, Width::U16, Access::Fetch)? as u16;
if !isa::is_32bit(low) {
if !self.cfg.ext.c {
return Err(Trap::illegal(u64::from(low)));
}
let word = isa::expand(low, self.cfg.xlen).ok_or(Trap::illegal(u64::from(low)))?;
return Ok((word, u64::from(low), 2));
}
let high = self.read_once(pc.wrapping_add(2), Width::U16, Access::Fetch)? as u16;
let word = u32::from(low) | (u32::from(high) << 16);
Ok((word, u64::from(word), 4))
}
fn has(&self, ext: isa::Ext) -> bool {
match ext {
isa::Ext::I | isa::Ext::Priv | isa::Ext::Zicsr | isa::Ext::Zifencei => true,
isa::Ext::M => self.cfg.ext.m,
isa::Ext::A => self.cfg.ext.a,
isa::Ext::F => self.cfg.ext.f,
isa::Ext::D => self.cfg.ext.d,
isa::Ext::C => self.cfg.ext.c,
}
}
fn pending_interrupt(&self) -> Option<u64> {
let ready = self.lines.pending() & self.st.csrs.mie;
if ready == 0 {
return None;
}
let current = self.st.csrs.priv_mode;
for code in irq::PRIORITY {
let bit = 1u64 << code;
if ready & bit == 0 {
continue;
}
let target = if self.st.csrs.mideleg & bit != 0 {
Priv::Supervisor
} else {
Priv::Machine
};
let enabled = if current < target {
true
} else if current == target {
let mask = if target == Priv::Machine {
status::MIE
} else {
status::SIE
};
self.st.csrs.mstatus & mask != 0
} else {
false
};
if enabled {
return Some(code);
}
}
None
}
fn enter_trap(&mut self, trap: Trap, interrupt: bool) {
let csrs = &mut self.st.csrs;
let bit = 1u64 << trap.cause;
let delegated = if interrupt {
csrs.mideleg & bit != 0
} else {
csrs.medeleg & bit != 0
};
let to_supervisor = delegated && csrs.ext.s && csrs.priv_mode <= Priv::Supervisor;
let coded = trap.cause
| if interrupt {
1 << (csrs.xlen.bits() - 1)
} else {
0
};
let from = csrs.priv_mode;
let tvec = if to_supervisor {
csrs.sepc = self.this_pc;
csrs.scause = coded;
csrs.stval = trap.tval;
let sie = csrs.mstatus & status::SIE != 0;
csrs.mstatus &= !(status::SPIE | status::SIE | status::SPP);
if sie {
csrs.mstatus |= status::SPIE;
}
if from == Priv::Supervisor {
csrs.mstatus |= status::SPP;
}
csrs.priv_mode = Priv::Supervisor;
csrs.stvec
} else {
csrs.mepc = self.this_pc;
csrs.mcause = coded;
csrs.mtval = trap.tval;
let mie = csrs.mstatus & status::MIE != 0;
csrs.mstatus &= !(status::MPIE | status::MIE | status::MPP);
if mie {
csrs.mstatus |= status::MPIE;
}
csrs.mstatus |= from.bits() << status::MPP_SHIFT;
csrs.priv_mode = Priv::Machine;
csrs.mtvec
};
self.st.reservation = None;
let base = tvec & !3;
self.next_pc = if tvec & 3 == 1 && interrupt {
base.wrapping_add(4 * trap.cause)
} else {
base
};
self.next_pc = self.cfg.xlen.trunc(self.next_pc);
}
fn trap_return(&mut self, machine: bool) {
let csrs = &mut self.st.csrs;
let lowest = if csrs.ext.u {
Priv::User
} else {
Priv::Machine
};
let to = if machine {
let mpp = Priv::from_bits((csrs.mstatus & status::MPP) >> status::MPP_SHIFT)
.unwrap_or(Priv::Machine);
let mpie = csrs.mstatus & status::MPIE != 0;
csrs.mstatus &= !(status::MIE | status::MPP);
if mpie {
csrs.mstatus |= status::MIE;
}
csrs.mstatus |= status::MPIE;
csrs.mstatus |= lowest.bits() << status::MPP_SHIFT;
self.next_pc = csrs.mepc;
mpp
} else {
let spp = if csrs.mstatus & status::SPP != 0 {
Priv::Supervisor
} else {
Priv::User
};
let spie = csrs.mstatus & status::SPIE != 0;
csrs.mstatus &= !(status::SIE | status::SPP);
if spie {
csrs.mstatus |= status::SIE;
}
csrs.mstatus |= status::SPIE;
self.next_pc = csrs.sepc;
spp
};
if to != Priv::Machine {
csrs.mstatus &= !status::MPRV;
}
csrs.priv_mode = to;
csrs.bump_translation();
self.next_pc = self.cfg.xlen.trunc(self.next_pc);
}
fn check_target(&self, target: u64) -> Result<(), Trap> {
let mask = if self.cfg.ext.c { 1 } else { 3 };
if target & mask != 0 {
Err(Trap {
cause: cause::INSN_MISALIGNED,
tval: target,
})
} else {
Ok(())
}
}
#[inline]
fn shamt(&self, raw: u64) -> u32 {
(raw & u64::from(self.cfg.xlen.bits() - 1)) as u32
}
fn sll(&self, a: u64, sh: u32) -> u64 {
match self.cfg.xlen {
Xlen::Rv32 => u64::from((a as u32) << sh),
Xlen::Rv64 => a << sh,
}
}
fn srl(&self, a: u64, sh: u32) -> u64 {
match self.cfg.xlen {
Xlen::Rv32 => u64::from((a as u32) >> sh),
Xlen::Rv64 => a >> sh,
}
}
fn sra(&self, a: u64, sh: u32) -> u64 {
match self.cfg.xlen {
Xlen::Rv32 => ((a as u32 as i32) >> sh) as u64,
Xlen::Rv64 => ((a as i64) >> sh) as u64,
}
}
#[allow(clippy::too_many_lines)]
fn execute(&mut self) -> Result<(), Trap> {
let (word, encoding, len) = self.fetch()?;
self.next_pc = self.this_pc.wrapping_add(len);
let insn = isa::decode(word, self.cfg.xlen).ok_or(Trap::illegal(encoding))?;
if !self.has(insn.ext) {
return Err(Trap::illegal(encoding));
}
if matches!(insn.ext, isa::Ext::F | isa::Ext::D) && !self.st.csrs.fp_enabled() {
return Err(Trap::illegal(encoding));
}
let rd = isa::rd(word);
let rs1 = isa::rs1(word);
let rs2 = isa::rs2(word);
let a = self.x(rs1);
let b = self.x(rs2);
match insn.op {
Op::Lui => self.set_x(rd, isa::imm_u(word) as u64),
Op::Auipc => self.set_x(rd, self.this_pc.wrapping_add(isa::imm_u(word) as u64)),
Op::Addi => self.set_x(rd, a.wrapping_add(isa::imm_i(word) as u64)),
Op::Slti => self.set_x(rd, u64::from((a as i64) < isa::imm_i(word))),
Op::Sltiu => {
self.set_x(
rd,
u64::from(a < self.cfg.xlen.sext(isa::imm_i(word) as u64)),
);
}
Op::Xori => self.set_x(rd, a ^ (isa::imm_i(word) as u64)),
Op::Ori => self.set_x(rd, a | (isa::imm_i(word) as u64)),
Op::Andi => self.set_x(rd, a & (isa::imm_i(word) as u64)),
Op::Slli | Op::Srli | Op::Srai => {
let shamt = isa::shamt(word);
if shamt >= self.cfg.xlen.bits() {
return Err(Trap::illegal(encoding));
}
let v = match insn.op {
Op::Slli => self.sll(a, shamt),
Op::Srli => self.srl(a, shamt),
_ => self.sra(a, shamt),
};
self.set_x(rd, v);
}
Op::Add => self.set_x(rd, a.wrapping_add(b)),
Op::Sub => self.set_x(rd, a.wrapping_sub(b)),
Op::Sll => {
let sh = self.shamt(b);
self.set_x(rd, self.sll(a, sh));
}
Op::Slt => self.set_x(rd, u64::from((a as i64) < (b as i64))),
Op::Sltu => self.set_x(rd, u64::from(a < b)),
Op::Xor => self.set_x(rd, a ^ b),
Op::Srl => {
let sh = self.shamt(b);
self.set_x(rd, self.srl(a, sh));
}
Op::Sra => {
let sh = self.shamt(b);
self.set_x(rd, self.sra(a, sh));
}
Op::Or => self.set_x(rd, a | b),
Op::And => self.set_x(rd, a & b),
Op::Addiw => {
self.set_x(
rd,
(a as u32).wrapping_add(isa::imm_i(word) as u32) as i32 as u64,
);
}
Op::Slliw | Op::Srliw | Op::Sraiw => {
let shamt = isa::shamt(word) & 31;
let v = match insn.op {
Op::Slliw => (a as u32) << shamt,
Op::Srliw => (a as u32) >> shamt,
_ => ((a as i32) >> shamt) as u32,
};
self.set_x(rd, v as i32 as u64);
}
Op::Addw => self.set_x(rd, (a as u32).wrapping_add(b as u32) as i32 as u64),
Op::Subw => self.set_x(rd, (a as u32).wrapping_sub(b as u32) as i32 as u64),
Op::Sllw => self.set_x(rd, ((a as u32) << (b & 31)) as i32 as u64),
Op::Srlw => self.set_x(rd, ((a as u32) >> (b & 31)) as i32 as u64),
Op::Sraw => self.set_x(rd, ((a as i32) >> (b & 31)) as u64),
Op::Mul => self.set_x(rd, a.wrapping_mul(b)),
Op::Mulh => {
let v = match self.cfg.xlen {
Xlen::Rv32 => ((i64::from(a as i32) * i64::from(b as i32)) >> 32) as u64,
Xlen::Rv64 => ((i128::from(a as i64) * i128::from(b as i64)) >> 64) as u64,
};
self.set_x(rd, v);
}
Op::Mulhsu => {
let v = match self.cfg.xlen {
Xlen::Rv32 => ((i64::from(a as i32) * i64::from(b as u32)) >> 32) as u64,
Xlen::Rv64 => ((i128::from(a as i64) * i128::from(b)) >> 64) as u64,
};
self.set_x(rd, v);
}
Op::Mulhu => {
let v = match self.cfg.xlen {
Xlen::Rv32 => (u64::from(a as u32) * u64::from(b as u32)) >> 32,
Xlen::Rv64 => ((u128::from(a) * u128::from(b)) >> 64) as u64,
};
self.set_x(rd, v);
}
Op::Div | Op::Divu | Op::Rem | Op::Remu => {
let v = self.divide(insn.op, a, b, self.cfg.xlen.bits());
self.set_x(rd, v);
}
Op::Mulw => self.set_x(rd, (a as u32).wrapping_mul(b as u32) as i32 as u64),
Op::Divw | Op::Divuw | Op::Remw | Op::Remuw => {
let op = match insn.op {
Op::Divw => Op::Div,
Op::Divuw => Op::Divu,
Op::Remw => Op::Rem,
_ => Op::Remu,
};
let (a32, b32) = match op {
Op::Div | Op::Rem => (a as i32 as u64, b as i32 as u64),
_ => (u64::from(a as u32), u64::from(b as u32)),
};
let v = self.divide(op, a32, b32, 32);
self.set_x(rd, v as i32 as u64);
}
Op::Jal => {
let target = self
.cfg
.xlen
.trunc(self.this_pc.wrapping_add(isa::imm_j(word) as u64));
self.check_target(target)?;
self.set_x(rd, self.next_pc);
self.next_pc = target;
}
Op::Jalr => {
let target = self.cfg.xlen.trunc(a.wrapping_add(isa::imm_i(word) as u64)) & !1;
self.check_target(target)?;
let link = self.next_pc;
self.next_pc = target;
self.set_x(rd, link);
}
Op::Beq | Op::Bne | Op::Blt | Op::Bge | Op::Bltu | Op::Bgeu => {
let taken = match insn.op {
Op::Beq => a == b,
Op::Bne => a != b,
Op::Blt => (a as i64) < (b as i64),
Op::Bge => (a as i64) >= (b as i64),
Op::Bltu => a < b,
_ => a >= b,
};
if taken {
let target = self
.cfg
.xlen
.trunc(self.this_pc.wrapping_add(isa::imm_b(word) as u64));
self.check_target(target)?;
self.next_pc = target;
}
}
Op::Lb | Op::Lh | Op::Lw | Op::Ld | Op::Lbu | Op::Lhu | Op::Lwu => {
let addr = self.cfg.xlen.trunc(a.wrapping_add(isa::imm_i(word) as u64));
let (bytes, signed) = match insn.op {
Op::Lb => (1, true),
Op::Lbu => (1, false),
Op::Lh => (2, true),
Op::Lhu => (2, false),
Op::Lw => (4, true),
Op::Lwu => (4, false),
_ => (8, true),
};
let raw = self.load(addr, bytes)?;
let v = if signed {
sign_extend(raw, bytes * 8)
} else {
raw
};
self.set_x(rd, v);
}
Op::Sb | Op::Sh | Op::Sw | Op::Sd => {
let addr = self.cfg.xlen.trunc(a.wrapping_add(isa::imm_s(word) as u64));
let bytes = match insn.op {
Op::Sb => 1,
Op::Sh => 2,
Op::Sw => 4,
_ => 8,
};
self.store(addr, bytes, b)?;
}
Op::Fence | Op::FenceI => {}
Op::LrW | Op::LrD => {
let bytes = if insn.op == Op::LrW { 4 } else { 8 };
let a = self.cfg.xlen.trunc(a);
if !a.is_multiple_of(bytes) {
return Err(Trap {
cause: cause::LOAD_MISALIGNED,
tval: a,
});
}
let width = Width::from_bytes(bytes).expect("4 or 8");
let v = self.read_once(a, width, Access::Load)?;
self.st.reservation = Some(a);
self.set_x(rd, sign_extend(v, bytes * 8));
}
Op::ScW | Op::ScD => {
let bytes = if insn.op == Op::ScW { 4 } else { 8 };
let a = self.cfg.xlen.trunc(a);
if !a.is_multiple_of(bytes) {
return Err(Trap {
cause: cause::STORE_MISALIGNED,
tval: a,
});
}
let held = self.st.reservation == Some(a);
self.st.reservation = None;
if held {
let width = Width::from_bytes(bytes).expect("4 or 8");
self.write_once(a, width, b)?;
self.set_x(rd, 0);
} else {
self.set_x(rd, 1);
}
}
Op::AmoswapW
| Op::AmoaddW
| Op::AmoxorW
| Op::AmoandW
| Op::AmoorW
| Op::AmominW
| Op::AmomaxW
| Op::AmominuW
| Op::AmomaxuW
| Op::AmoswapD
| Op::AmoaddD
| Op::AmoxorD
| Op::AmoandD
| Op::AmoorD
| Op::AmominD
| Op::AmomaxD
| Op::AmominuD
| Op::AmomaxuD => {
self.amo(insn.op, rd, self.cfg.xlen.trunc(a), b)?;
}
Op::Ecall => {
let code = match self.st.csrs.priv_mode {
Priv::User => cause::ECALL_U,
Priv::Supervisor => cause::ECALL_S,
Priv::Machine => cause::ECALL_M,
};
return Err(Trap::bare(code));
}
Op::Ebreak => {
return Err(Trap {
cause: cause::BREAKPOINT,
tval: self.this_pc,
});
}
Op::Mret => {
if self.st.csrs.priv_mode != Priv::Machine {
return Err(Trap::illegal(encoding));
}
self.trap_return(true);
}
Op::Sret => {
if self.st.csrs.priv_mode < Priv::Supervisor || !self.cfg.ext.s {
return Err(Trap::illegal(encoding));
}
if self.st.csrs.priv_mode == Priv::Supervisor
&& self.st.csrs.mstatus & status::TSR != 0
{
return Err(Trap::illegal(encoding));
}
self.trap_return(false);
}
Op::Wfi => {
if self.st.csrs.priv_mode != Priv::Machine && self.st.csrs.mstatus & status::TW != 0
{
return Err(Trap::illegal(encoding));
}
self.st.wfi = true;
}
Op::SfenceVma => {
if self.st.csrs.priv_mode == Priv::User
|| (self.st.csrs.priv_mode == Priv::Supervisor
&& self.st.csrs.mstatus & status::TVM != 0)
{
return Err(Trap::illegal(encoding));
}
self.st.csrs.bump_translation();
}
Op::Csrrw | Op::Csrrs | Op::Csrrc | Op::Csrrwi | Op::Csrrsi | Op::Csrrci => {
self.csr_access(insn.op, word, encoding)?;
}
_ => self.float(insn.op, word, encoding)?,
}
Ok(())
}
fn divide(&self, op: Op, a: u64, b: u64, bits: u32) -> u64 {
let mask = if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
let min = 1u64 << (bits - 1);
match op {
Op::Div => {
if b & mask == 0 {
u64::MAX
} else if a & mask == min && b & mask == mask {
a
} else {
((a as i64).wrapping_div(b as i64)) as u64
}
}
Op::Divu => {
if b & mask == 0 {
u64::MAX
} else if bits >= 64 {
a / b
} else {
(a & mask) / (b & mask)
}
}
Op::Rem => {
if b & mask == 0 {
a
} else if a & mask == min && b & mask == mask {
0
} else {
((a as i64).wrapping_rem(b as i64)) as u64
}
}
_ => {
if b & mask == 0 {
a
} else if bits >= 64 {
a % b
} else {
(a & mask) % (b & mask)
}
}
}
}
fn amo(&mut self, op: Op, rd: u32, addr: u64, operand: u64) -> Result<(), Trap> {
let bytes: u64 = if matches!(
op,
Op::AmoswapW
| Op::AmoaddW
| Op::AmoxorW
| Op::AmoandW
| Op::AmoorW
| Op::AmominW
| Op::AmomaxW
| Op::AmominuW
| Op::AmomaxuW
) {
4
} else {
8
};
if !addr.is_multiple_of(bytes) {
return Err(Trap {
cause: cause::STORE_MISALIGNED,
tval: addr,
});
}
let width = Width::from_bytes(bytes).expect("4 or 8");
let old = self.read_once(addr, width, Access::Load)?;
let bits = (bytes * 8) as u32;
let s_old = sign_extend(old, bytes * 8) as i64;
let s_arg = sign_extend(operand, bytes * 8) as i64;
let u_old = old & mask_bits(bits);
let u_arg = operand & mask_bits(bits);
let new = match op {
Op::AmoswapW | Op::AmoswapD => operand,
Op::AmoaddW | Op::AmoaddD => old.wrapping_add(operand),
Op::AmoxorW | Op::AmoxorD => old ^ operand,
Op::AmoandW | Op::AmoandD => old & operand,
Op::AmoorW | Op::AmoorD => old | operand,
Op::AmominW | Op::AmominD => {
if s_old < s_arg {
old
} else {
operand
}
}
Op::AmomaxW | Op::AmomaxD => {
if s_old > s_arg {
old
} else {
operand
}
}
Op::AmominuW | Op::AmominuD => {
if u_old < u_arg {
old
} else {
operand
}
}
_ => {
if u_old > u_arg {
old
} else {
operand
}
}
};
if let Some(reserved) = self.st.reservation
&& reserved >> 3 == addr >> 3
{
self.st.reservation = None;
}
self.write_once(addr, width, new)?;
self.set_x(rd, sign_extend(old, bytes * 8));
Ok(())
}
fn csr_access(&mut self, op: Op, word: u32, encoding: u64) -> Result<(), Trap> {
let num = isa::csr(word);
let rd = isa::rd(word);
let rs1 = isa::rs1(word);
let immediate = matches!(op, Op::Csrrwi | Op::Csrrsi | Op::Csrrci);
let source = if immediate {
u64::from(rs1)
} else {
self.x(rs1)
};
let write_form = matches!(op, Op::Csrrw | Op::Csrrwi);
let will_write = write_form || rs1 != 0;
let will_read = !write_form || rd != 0;
let pending = self.lines.pending();
let old = if will_read {
Some(
self.st
.csrs
.read(num, pending)
.ok_or(Trap::illegal(encoding))?,
)
} else {
self.st
.csrs
.read(num, pending)
.ok_or(Trap::illegal(encoding))?;
None
};
if will_write {
let current = old.unwrap_or_else(|| self.st.csrs.read(num, pending).unwrap_or(0));
let value = match op {
Op::Csrrw | Op::Csrrwi => source,
Op::Csrrs | Op::Csrrsi => current | source,
_ => current & !source,
};
let updated = self
.st
.csrs
.write(num, value, pending)
.ok_or(Trap::illegal(encoding))?;
if let Some(bits) = updated {
self.lines.set_all_pending(bits);
}
if matches!(num, csr::num::MINSTRET | csr::num::MINSTRETH) {
self.wrote_instret = true;
}
}
if let Some(v) = old {
self.set_x(rd, v);
}
Ok(())
}
fn rounding(&self, word: u32) -> Option<Env> {
let field = isa::funct3(word);
let mode = if field == 7 {
((self.st.csrs.fcsr >> 5) & 7) as u32
} else {
field
};
Round::from_riscv_rm(mode).map(|r| Env::RISCV.round(r))
}
#[allow(clippy::too_many_lines)]
fn float(&mut self, op: Op, word: u32, encoding: u64) -> Result<(), Trap> {
let rd = isa::rd(word);
let rs1 = isa::rs1(word);
let rs2 = isa::rs2(word);
let rs3 = isa::rs3(word);
let rm = || self.rounding(word).ok_or(Trap::illegal(encoding));
match op {
Op::Flw | Op::Fld => {
let addr = self
.cfg
.xlen
.trunc(self.x(rs1).wrapping_add(isa::imm_i(word) as u64));
if op == Op::Flw {
let v = self.load(addr, 4)?;
self.set_fs(rd, v);
} else {
let v = self.load(addr, 8)?;
self.set_f(rd, v);
}
}
Op::Fsw | Op::Fsd => {
let addr = self
.cfg
.xlen
.trunc(self.x(rs1).wrapping_add(isa::imm_s(word) as u64));
if op == Op::Fsw {
let v = self.f(rs2);
self.store(addr, 4, v)?;
} else {
let v = self.f(rs2);
self.store(addr, 8, v)?;
}
}
Op::FaddS | Op::FsubS | Op::FmulS | Op::FdivS => {
let rm = rm()?;
let (x, y) = (self.fs(rs1), self.fs(rs2));
let (v, f) = match op {
Op::FaddS => float::add::<B32>(x, y, rm),
Op::FsubS => float::sub::<B32>(x, y, rm),
Op::FmulS => float::mul::<B32>(x, y, rm),
_ => float::div::<B32>(x, y, rm),
};
self.set_fs(rd, v);
self.raise(f);
}
Op::FaddD | Op::FsubD | Op::FmulD | Op::FdivD => {
let rm = rm()?;
let (x, y) = (self.f(rs1), self.f(rs2));
let (v, f) = match op {
Op::FaddD => float::add::<B64>(x, y, rm),
Op::FsubD => float::sub::<B64>(x, y, rm),
Op::FmulD => float::mul::<B64>(x, y, rm),
_ => float::div::<B64>(x, y, rm),
};
self.set_f(rd, v);
self.raise(f);
}
Op::FsqrtS => {
let rm = rm()?;
let (v, f) = float::sqrt::<B32>(self.fs(rs1), rm);
self.set_fs(rd, v);
self.raise(f);
}
Op::FsqrtD => {
let rm = rm()?;
let (v, f) = float::sqrt::<B64>(self.f(rs1), rm);
self.set_f(rd, v);
self.raise(f);
}
Op::FmaddS | Op::FmsubS | Op::FnmsubS | Op::FnmaddS => {
let rm = rm()?;
let sign = B32::SIGN;
let (x, y, z) = (self.fs(rs1), self.fs(rs2), self.fs(rs3));
let (x, z) = match op {
Op::FmaddS => (x, z),
Op::FmsubS => (x, z ^ sign),
Op::FnmsubS => (x ^ sign, z),
_ => (x ^ sign, z ^ sign),
};
let (v, f) = float::fma::<B32>(x, y, z, rm);
self.set_fs(rd, v);
self.raise(f);
}
Op::FmaddD | Op::FmsubD | Op::FnmsubD | Op::FnmaddD => {
let rm = rm()?;
let sign = B64::SIGN;
let (x, y, z) = (self.f(rs1), self.f(rs2), self.f(rs3));
let (x, z) = match op {
Op::FmaddD => (x, z),
Op::FmsubD => (x, z ^ sign),
Op::FnmsubD => (x ^ sign, z),
_ => (x ^ sign, z ^ sign),
};
let (v, f) = float::fma::<B64>(x, y, z, rm);
self.set_f(rd, v);
self.raise(f);
}
Op::FsgnjS | Op::FsgnjnS | Op::FsgnjxS => {
let (x, y) = (self.fs(rs1), self.fs(rs2));
let sign = match op {
Op::FsgnjS => y & B32::SIGN,
Op::FsgnjnS => !y & B32::SIGN,
_ => (x ^ y) & B32::SIGN,
};
self.set_fs(rd, (x & !B32::SIGN) | sign);
}
Op::FsgnjD | Op::FsgnjnD | Op::FsgnjxD => {
let (x, y) = (self.f(rs1), self.f(rs2));
let sign = match op {
Op::FsgnjD => y & B64::SIGN,
Op::FsgnjnD => !y & B64::SIGN,
_ => (x ^ y) & B64::SIGN,
};
self.set_f(rd, (x & !B64::SIGN) | sign);
}
Op::FminS | Op::FmaxS => {
let (x, y) = (self.fs(rs1), self.fs(rs2));
let (v, f) = if op == Op::FminS {
float::min::<B32>(x, y, Env::RISCV)
} else {
float::max::<B32>(x, y, Env::RISCV)
};
self.set_fs(rd, v);
self.raise(f);
}
Op::FminD | Op::FmaxD => {
let (x, y) = (self.f(rs1), self.f(rs2));
let (v, f) = if op == Op::FminD {
float::min::<B64>(x, y, Env::RISCV)
} else {
float::max::<B64>(x, y, Env::RISCV)
};
self.set_f(rd, v);
self.raise(f);
}
Op::FeqS | Op::FltS | Op::FleS => {
let (x, y) = (self.fs(rs1), self.fs(rs2));
let (v, f) = match op {
Op::FeqS => float::eq::<B32>(x, y),
Op::FltS => float::lt::<B32>(x, y),
_ => float::le::<B32>(x, y),
};
self.set_x(rd, u64::from(v));
self.raise(f);
}
Op::FeqD | Op::FltD | Op::FleD => {
let (x, y) = (self.f(rs1), self.f(rs2));
let (v, f) = match op {
Op::FeqD => float::eq::<B64>(x, y),
Op::FltD => float::lt::<B64>(x, y),
_ => float::le::<B64>(x, y),
};
self.set_x(rd, u64::from(v));
self.raise(f);
}
Op::FclassS => self.set_x(rd, float::classify::<B32>(self.fs(rs1)).riscv_fclass()),
Op::FclassD => self.set_x(rd, float::classify::<B64>(self.f(rs1)).riscv_fclass()),
Op::FcvtSD => {
let rm = rm()?;
let (v, f) = float::convert::<B64, B32>(self.f(rs1), rm);
self.set_fs(rd, v);
self.raise(f);
}
Op::FcvtDS => {
let rm = rm()?;
let (v, f) = float::convert::<B32, B64>(self.fs(rs1), rm);
self.set_f(rd, v);
self.raise(f);
}
Op::FcvtWS | Op::FcvtWuS | Op::FcvtLS | Op::FcvtLuS => {
let rm = rm()?;
let x = self.fs(rs1);
let (v, f) = match op {
Op::FcvtWS => {
let (v, f) = float::to_signed::<B32>(x, 32, rm);
(v as u64, f)
}
Op::FcvtWuS => {
let (v, f) = float::to_unsigned::<B32>(x, 32, rm);
(v as u32 as i32 as u64, f)
}
Op::FcvtLS => {
let (v, f) = float::to_signed::<B32>(x, 64, rm);
(v as u64, f)
}
_ => float::to_unsigned::<B32>(x, 64, rm),
};
self.set_x(rd, v);
self.raise(f);
}
Op::FcvtWD | Op::FcvtWuD | Op::FcvtLD | Op::FcvtLuD => {
let rm = rm()?;
let x = self.f(rs1);
let (v, f) = match op {
Op::FcvtWD => {
let (v, f) = float::to_signed::<B64>(x, 32, rm);
(v as u64, f)
}
Op::FcvtWuD => {
let (v, f) = float::to_unsigned::<B64>(x, 32, rm);
(v as u32 as i32 as u64, f)
}
Op::FcvtLD => {
let (v, f) = float::to_signed::<B64>(x, 64, rm);
(v as u64, f)
}
_ => float::to_unsigned::<B64>(x, 64, rm),
};
self.set_x(rd, v);
self.raise(f);
}
Op::FcvtSW | Op::FcvtSWu | Op::FcvtSL | Op::FcvtSLu => {
let rm = rm()?;
let x = self.x(rs1);
let (v, f) = match op {
Op::FcvtSW => float::from_signed::<B32>(x as i64, 32, rm),
Op::FcvtSWu => float::from_unsigned::<B32>(x, 32, rm),
Op::FcvtSL => float::from_signed::<B32>(x as i64, 64, rm),
_ => float::from_unsigned::<B32>(x, 64, rm),
};
self.set_fs(rd, v);
self.raise(f);
}
Op::FcvtDW | Op::FcvtDWu | Op::FcvtDL | Op::FcvtDLu => {
let rm = rm()?;
let x = self.x(rs1);
let (v, f) = match op {
Op::FcvtDW => float::from_signed::<B64>(x as i64, 32, rm),
Op::FcvtDWu => float::from_unsigned::<B64>(x, 32, rm),
Op::FcvtDL => float::from_signed::<B64>(x as i64, 64, rm),
_ => float::from_unsigned::<B64>(x, 64, rm),
};
self.set_f(rd, v);
self.raise(f);
}
Op::FmvXW => self.set_x(rd, self.f(rs1) as u32 as i32 as u64),
Op::FmvWX => self.set_fs(rd, self.x(rs1)),
Op::FmvXD => self.set_x(rd, self.f(rs1)),
Op::FmvDX => self.set_f(rd, self.x(rs1)),
_ => return Err(Trap::illegal(encoding)),
}
Ok(())
}
}
#[inline]
fn sign_extend(value: u64, bits: u64) -> u64 {
if bits >= 64 {
value
} else {
let shift = 64 - bits as u32;
(((value << shift) as i64) >> shift) as u64
}
}
#[inline]
fn mask_bits(bits: u32) -> u64 {
if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
}
}