jurubas/src/riscv/cpu.rs

3576 lines
155 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! The cpu module contains the privileged mode, registers, and CPU.
//!
//! This design is taken from rvemu, originally at:
//! https://raw.githubusercontent.com/d0iasm/rvemu/main/src/cpu.rs
use std::cmp;
use std::cmp::PartialEq;
use std::collections::BTreeMap;
use std::fmt;
use std::num::FpCategory;
use crate::riscv::{
// devices::{
// uart::UART_IRQ,
// virtio_blk::{Virtio, VIRTIO_IRQ},
// },
// dram::DRAM_SIZE,
bus::{Bus, XLen},
// bus::{Bus, DRAM_BASE},
csr::*,
exception::Exception,
// interrupt::Interrupt,
};
/// The number of registers.
pub const REGISTERS_COUNT: usize = 32;
/// The page size (4 KiB) for the virtual memory system.
const PAGE_SIZE: u64 = 4096;
/// 8 bits. 1 byte.
pub const BYTE: u8 = 8;
/// 16 bits. 2 bytes.
pub const HALFWORD: u8 = 16;
/// 32 bits. 4 bytes.
pub const WORD: u8 = 32;
/// 64 bits. 8 bytes.
pub const DOUBLEWORD: u8 = 64;
/// riscv-pk is passing x10 and x11 registers to kernel. x11 is expected to have the pointer to DTB.
/// https://github.com/riscv/riscv-pk/blob/master/machine/mentry.S#L233-L235
pub const POINTER_TO_DTB: u64 = 0x1020;
macro_rules! inst_count {
($cpu:ident, $inst_name:expr) => {
if $cpu.is_count {
*$cpu.inst_counter.entry($inst_name.to_string()).or_insert(0) += 1;
}
};
}
/// Access type that is used in the virtual address translation process. It decides which exception
/// should raises (InstructionPageFault, LoadPageFault or StoreAMOPageFault).
#[derive(Debug, PartialEq, PartialOrd)]
pub enum AccessType {
/// Raises the exception InstructionPageFault. It is used for an instruction fetch.
Instruction,
/// Raises the exception LoadPageFault.
Load,
/// Raises the exception StoreAMOPageFault.
Store,
}
/// The privileged mode.
#[derive(Debug, PartialEq, PartialOrd, Eq, Copy, Clone)]
pub enum Mode {
User = 0b00,
Supervisor = 0b01,
Machine = 0b11,
Debug,
}
/// The integer registers.
#[derive(Debug)]
pub struct XRegisters {
xregs: [u64; REGISTERS_COUNT],
}
impl XRegisters {
/// Create a new `XRegisters` object.
pub fn new() -> Self {
let mut xregs = [0; REGISTERS_COUNT];
// The stack pointer is set in the default maximum memory size + the start address of dram.
// xregs[2] = DRAM_BASE + DRAM_SIZE;
// From riscv-pk:
// https://github.com/riscv/riscv-pk/blob/master/machine/mentry.S#L233-L235
// save a0 and a1; arguments from previous boot loader stage:
// // li x10, 0
// // li x11, 0
//
// void init_first_hart(uintptr_t hartid, uintptr_t dtb)
// x10 (a0): hartid
// x11 (a1): pointer to dtb
//
// So, we need to set registers register to the state as they are when a bootloader finished.
xregs[10] = 0;
xregs[11] = POINTER_TO_DTB;
Self { xregs }
}
/// Read the value from a register.
pub fn read(&self, index: u64) -> u64 {
self.xregs[index as usize]
}
/// Write the value to a register.
pub fn write(&mut self, index: u64, value: u64) {
// Register x0 is hardwired with all bits equal to 0.
if index != 0 {
self.xregs[index as usize] = value;
}
}
}
impl fmt::Display for XRegisters {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let abi = [
"zero", " ra ", " sp ", " gp ", " tp ", " t0 ", " t1 ", " t2 ", " s0 ", " s1 ", " a0 ",
" a1 ", " a2 ", " a3 ", " a4 ", " a5 ", " a6 ", " a7 ", " s2 ", " s3 ", " s4 ", " s5 ",
" s6 ", " s7 ", " s8 ", " s9 ", " s10", " s11", " t3 ", " t4 ", " t5 ", " t6 ",
];
let mut output = String::from("");
for i in (0..REGISTERS_COUNT).step_by(4) {
output = format!(
"{}\n{}",
output,
format!(
"x{:02}({})={:>#18x} x{:02}({})={:>#18x} x{:02}({})={:>#18x} x{:02}({})={:>#18x}",
i,
abi[i],
self.read(i as u64),
i + 1,
abi[i + 1],
self.read(i as u64 + 1),
i + 2,
abi[i + 2],
self.read(i as u64 + 2),
i + 3,
abi[i + 3],
self.read(i as u64 + 3),
)
);
}
write!(f, "{}", output)
}
}
/// The floating-point registers.
#[derive(Debug)]
pub struct FRegisters {
fregs: [f64; REGISTERS_COUNT],
}
impl FRegisters {
/// Create a new `FRegisters` object.
pub fn new() -> Self {
Self {
fregs: [0.0; REGISTERS_COUNT],
}
}
/// Read the value from a register.
pub fn read(&self, index: u64) -> f64 {
self.fregs[index as usize]
}
/// Write the value to a register.
pub fn write(&mut self, index: u64, value: f64) {
self.fregs[index as usize] = value;
}
}
impl fmt::Display for FRegisters {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let abi = [
// ft0-7: FP temporaries
" ft0", " ft1", " ft2", " ft3", " ft4", " ft5", " ft6", " ft7",
// fs0-1: FP saved registers
" fs0", " fs1", // fa0-1: FP arguments/return values
" fa0", " fa1", // fa27: FP arguments
" fa2", " fa3", " fa4", " fa5", " fa6", " fa7",
// fs211: FP saved registers
" fs2", " fs3", " fs4", " fs5", " fs6", " fs7", " fs8", " fs9", "fs10", "fs11",
// ft811: FP temporaries
" ft8", " ft9", "ft10", "ft11",
];
let mut output = String::from("");
for i in (0..REGISTERS_COUNT).step_by(4) {
output = format!(
"{}\n{}",
output,
format!(
"f{:02}({})={:>width$.prec$} f{:02}({})={:>width$.prec$} f{:02}({})={:>width$.prec$} f{:02}({})={:>width$.prec$}",
i,
abi[i],
self.read(i as u64),
i + 1,
abi[i + 1],
self.read(i as u64 + 1),
i + 2,
abi[i + 2],
self.read(i as u64+ 2),
i + 3,
abi[i + 3],
self.read(i as u64 + 3),
width=18,
prec=8,
)
);
}
// Remove the first new line.
output.remove(0);
write!(f, "{}", output)
}
}
/// The CPU to contain registers, a program counter, status, and a privileged mode.
pub struct Cpu<Bus> where Bus: crate::riscv::bus::Bus<XLen> {
/// 64-bit integer registers.
pub xregs: XRegisters,
/// 64-bit floating-point registers.
pub fregs: FRegisters,
/// Program counter.
pub pc: u64,
/// Control and status registers (CSR).
pub state: State,
/// Privilege level.
pub mode: Mode,
/// System bus.
pub bus: Bus,
/// SV39 paging flag.
enable_paging: bool,
/// Physical page number (PPN) × PAGE_SIZE (4096).
page_table: u64,
/// A set of bytes that subsumes the bytes in the addressed word used in
/// load-reserved/store-conditional instructions.
reservation_set: Vec<u64>,
/// Idle state. True when WFI is called, and becomes false when an interrupt happens.
pub idle: bool,
/// Counter of each instructions for debug.
pub inst_counter: BTreeMap<String, u64>,
/// The count flag. Count the number of each instruction executed.
pub is_count: bool,
/// Previous instruction. This is for debug.
pub pre_inst: u64,
}
impl<Bus> Cpu<Bus> where Bus: crate::riscv::bus::Bus<XLen> {
/// Create a new `Cpu` object.
pub fn new(bus: Bus) -> Cpu<Bus> {
Cpu {
xregs: XRegisters::new(),
fregs: FRegisters::new(),
pc: 0,
state: State::new(),
mode: Mode::Machine,
bus,
enable_paging: false,
page_table: 0,
reservation_set: Vec::new(),
idle: false,
inst_counter: BTreeMap::new(),
is_count: false,
pre_inst: 0,
}
}
fn debug(&self, _inst: u64, _name: &str) {
/*
if (((0x20_0000_0000 & self.pc) >> 37) == 1) && (self.pc & 0xf0000000_00000000) == 0 {
println!(
"[user] {} pc: {:#x}, inst: {:#x}, is_inst 16? {} x[0x1c] {:#x}",
name,
self.pc,
inst,
// Check if an instruction is one of the compressed instructions.
inst & 0b11 == 0 || inst & 0b11 == 1 || inst & 0b11 == 2,
self.xregs.read(0x1c),
);
return;
}
if (self.pc & 0xf0000000_00000000) != 0 {
return;
/*
println!(
"[kernel] {} pc: {:#x}, inst: {:#x}, is_inst 16? {} x[0x1c] {:#x}",
name,
self.pc,
inst,
// Check if an instruction is one of the compressed instructions.
inst & 0b11 == 0 || inst & 0b11 == 1 || inst & 0b11 == 2,
self.xregs.read(0x1c),
);
return;
*/
}
println!(
"[machine] {} pc: {:#x}, inst: {:#x}, is_inst 16? {} x[0x1c] {:#x}",
name,
self.pc,
inst,
// Check if an instruction is one of the compressed instructions.
inst & 0b11 == 0 || inst & 0b11 == 1 || inst & 0b11 == 2,
self.xregs.read(0x1c),
);
*/
}
/// Reset CPU states.
pub fn reset(&mut self) {
self.pc = 0;
self.mode = Mode::Machine;
self.state.reset();
for i in 0..REGISTERS_COUNT {
self.xregs.write(i as u64, 0);
self.fregs.write(i as u64, 0.0);
}
}
// /// Check interrupt flags for all devices that can interrupt.
// pub fn check_pending_interrupt(&mut self) -> Option<Interrupt> {
// // global interrupt: PLIC (Platform Local Interrupt Controller) dispatches global
// // interrupts to multiple harts.
// // local interrupt: CLINT (Core Local Interrupter) dispatches local interrupts to a hart
// // which directly connected to CLINT.
// // 3.1.6.1 Privilege and Global Interrupt-Enable Stack in mstatus register
// // "When a hart is executing in privilege mode x, interrupts are globally enabled when
// // xIE=1 and globally disabled when xIE=0."
// match self.mode {
// Mode::Machine => {
// // Check if the MIE bit is enabled.
// if self.state.read_mstatus(MSTATUS_MIE) == 0 {
// return None;
// }
// }
// Mode::Supervisor => {
// // Check if the SIE bit is enabled.
// if self.state.read_sstatus(XSTATUS_SIE) == 0 {
// return None;
// }
// }
// _ => {}
// }
// // TODO: Take interrupts based on priorities.
// // Check external interrupt for uart and virtio.
// let irq;
// if self.bus.uart.is_interrupting() {
// irq = UART_IRQ;
// } else if self.bus.virtio.is_interrupting() {
// // An interrupt is raised after a disk access is done.
// Virtio::disk_access(self).expect("failed to access the disk");
// irq = VIRTIO_IRQ;
// } else {
// irq = 0;
// }
// if irq != 0 {
// // TODO: assume that hart is 0
// // TODO: write a value to MCLAIM if the mode is machine
// self.bus.plic.update_pending(irq);
// self.state.write(MIP, self.state.read(MIP) | SEIP_BIT);
// }
// // 3.1.9 Machine Interrupt Registers (mip and mie)
// // "An interrupt i will be taken if bit i is set in both mip and mie, and if interrupts are
// // globally enabled. By default, M-mode interrupts are globally enabled if the harts
// // current privilege mode is less than M, or if the current privilege mode is M and the MIE
// // bit in the mstatus register is set. If bit i in mideleg is set, however, interrupts are
// // considered to be globally enabled if the harts current privilege mode equals the
// // delegated privilege mode (S or U) and that modes interrupt enable bit (SIE or UIE in
// // mstatus) is set, or if the current privilege mode is less than the delegated privilege
// // mode."
// let pending = self.state.read(MIE) & self.state.read(MIP);
// if (pending & MEIP_BIT) != 0 {
// self.state.write(MIP, self.state.read(MIP) & !MEIP_BIT);
// return Some(Interrupt::MachineExternalInterrupt);
// }
// if (pending & MSIP_BIT) != 0 {
// self.state.write(MIP, self.state.read(MIP) & !MSIP_BIT);
// return Some(Interrupt::MachineSoftwareInterrupt);
// }
// if (pending & MTIP_BIT) != 0 {
// self.state.write(MIP, self.state.read(MIP) & !MTIP_BIT);
// return Some(Interrupt::MachineTimerInterrupt);
// }
// if (pending & SEIP_BIT) != 0 {
// self.state.write(MIP, self.state.read(MIP) & !SEIP_BIT);
// return Some(Interrupt::SupervisorExternalInterrupt);
// }
// if (pending & SSIP_BIT) != 0 {
// self.state.write(MIP, self.state.read(MIP) & !SSIP_BIT);
// return Some(Interrupt::SupervisorSoftwareInterrupt);
// }
// if (pending & STIP_BIT) != 0 {
// self.state.write(MIP, self.state.read(MIP) & !STIP_BIT);
// return Some(Interrupt::SupervisorTimerInterrupt);
// }
// return None;
// }
/// Update the physical page number (PPN) and the addressing mode.
fn update_paging(&mut self) {
// Read the physical page number (PPN) of the root page table, i.e., its
// supervisor physical address divided by 4 KiB.
self.page_table = self.state.read_bits(SATP, ..44) * PAGE_SIZE;
// Read the MODE field, which selects the current address-translation scheme.
let mode = self.state.read_bits(SATP, 60..);
// Enable the SV39 paging if the value of the mode field is 8.
if mode == 8 {
self.enable_paging = true;
} else {
self.enable_paging = false;
}
}
/// Translate a virtual address to a physical address for the paged virtual-memory system.
fn translate(&mut self, addr: u64, access_type: AccessType) -> Result<u64, Exception> {
if !self.enable_paging || self.mode == Mode::Machine {
return Ok(addr);
}
// 4.3.2 Virtual Address Translation Process
// (The RISC-V Instruction Set Manual Volume II-Privileged Architecture_20190608)
// A virtual address va is translated into a physical address pa as follows:
let levels = 3;
let vpn = [
(addr >> 12) & 0x1ff,
(addr >> 21) & 0x1ff,
(addr >> 30) & 0x1ff,
];
// 1. Let a be satp.ppn × PAGESIZE, and let i = LEVELS 1. (For Sv32, PAGESIZE=212
// and LEVELS=2.)
let mut a = self.page_table;
let mut i: i64 = levels - 1;
let mut pte;
loop {
// 2. Let pte be the value of the PTE at address a+va.vpn[i]×PTESIZE. (For Sv32,
// PTESIZE=4.) If accessing pte violates a PMA or PMP check, raise an access
// exception corresponding to the original access type.
pte = self.bus.read(a + vpn[i as usize] * 8, DOUBLEWORD)?;
// 3. If pte.v = 0, or if pte.r = 0 and pte.w = 1, stop and raise a page-fault
// exception corresponding to the original access type.
let v = pte & 1;
let r = (pte >> 1) & 1;
let w = (pte >> 2) & 1;
let x = (pte >> 3) & 1;
if v == 0 || (r == 0 && w == 1) {
match access_type {
AccessType::Instruction => return Err(Exception::InstructionPageFault(addr)),
AccessType::Load => return Err(Exception::LoadPageFault(addr)),
AccessType::Store => return Err(Exception::StoreAMOPageFault(addr)),
}
}
// 4. Otherwise, the PTE is valid. If pte.r = 1 or pte.x = 1, go to step 5.
// Otherwise, this PTE is a pointer to the next level of the page table.
// Let i = i 1. If i < 0, stop and raise a page-fault exception
// corresponding to the original access type. Otherwise,
// let a = pte.ppn × PAGESIZE and go to step 2.
if r == 1 || x == 1 {
break;
}
i -= 1;
let ppn = (pte >> 10) & 0x0fff_ffff_ffff;
a = ppn * PAGE_SIZE;
if i < 0 {
match access_type {
AccessType::Instruction => return Err(Exception::InstructionPageFault(addr)),
AccessType::Load => return Err(Exception::LoadPageFault(addr)),
AccessType::Store => return Err(Exception::StoreAMOPageFault(addr)),
}
}
}
// TODO: implement step 5
// 5. A leaf PTE has been found. Determine if the requested memory access is
// allowed by the pte.r, pte.w, pte.x, and pte.u bits, given the current
// privilege mode and the value of the SUM and MXR fields of the mstatus
// register. If not, stop and raise a page-fault exception corresponding
// to the original access type.
// 3.1.6.3 Memory Privilege in mstatus Register
// "The MXR (Make eXecutable Readable) bit modifies the privilege with which loads access
// virtual memory. When MXR=0, only loads from pages marked readable (R=1 in Figure 4.15)
// will succeed. When MXR=1, loads from pages marked either readable or executable
// (R=1 or X=1) will succeed. MXR has no effect when page-based virtual memory is not in
// effect. MXR is hardwired to 0 if S-mode is not supported."
// "The SUM (permit Supervisor User Memory access) bit modifies the privilege with which
// S-mode loads and stores access virtual memory. When SUM=0, S-mode memory accesses to
// pages that are accessible by U-mode (U=1 in Figure 4.15) will fault. When SUM=1, these
// accesses are permitted. SUM has no effect when page-based virtual memory is not in
// effect. Note that, while SUM is ordinarily ignored when not executing in S-mode, it is
// in effect when MPRV=1 and MPP=S. SUM is hardwired to 0 if S-mode is not supported."
// 6. If i > 0 and pte.ppn[i1:0] != 0, this is a misaligned superpage; stop and
// raise a page-fault exception corresponding to the original access type.
let ppn = [
(pte >> 10) & 0x1ff,
(pte >> 19) & 0x1ff,
(pte >> 28) & 0x03ff_ffff,
];
if i > 0 {
for j in (0..i).rev() {
if ppn[j as usize] != 0 {
// A misaligned superpage.
match access_type {
AccessType::Instruction => {
return Err(Exception::InstructionPageFault(addr))
}
AccessType::Load => return Err(Exception::LoadPageFault(addr)),
AccessType::Store => return Err(Exception::StoreAMOPageFault(addr)),
}
}
}
}
// 7. If pte.a = 0, or if the memory access is a store and pte.d = 0, either raise
// a page-fault exception corresponding to the original access type, or:
// • Set pte.a to 1 and, if the memory access is a store, also set pte.d to 1.
// • If this access violates a PMA or PMP check, raise an access exception
// corresponding to the original access type.
// • This update and the loading of pte in step 2 must be atomic; in particular,
// no intervening store to the PTE may be perceived to have occurred in-between.
let a = (pte >> 6) & 1;
let d = (pte >> 7) & 1;
if a == 0 || (access_type == AccessType::Store && d == 0) {
// Set pte.a to 1 and, if the memory access is a store, also set pte.d to 1.
pte = pte
| (1 << 6)
| if access_type == AccessType::Store {
1 << 7
} else {
0
};
// TODO: PMA or PMP check.
// Update the value of address satp.ppn × PAGESIZE + va.vpn[i] × PTESIZE with new pte
// value.
// TODO: If this is enabled, running xv6 fails.
//self.bus.write(self.page_table + vpn[i as usize] * 8, pte, 64)?;
}
// 8. The translation is successful. The translated physical address is given as
// follows:
// • pa.pgoff = va.pgoff.
// • If i > 0, then this is a superpage translation and pa.ppn[i1:0] =
// va.vpn[i1:0].
// • pa.ppn[LEVELS1:i] = pte.ppn[LEVELS1:i].
let offset = addr & 0xfff;
match i {
0 => {
let ppn = (pte >> 10) & 0x0fff_ffff_ffff;
Ok((ppn << 12) | offset)
}
1 => {
// Superpage translation. A superpage is a memory page of larger size than an
// ordinary page (4 KiB). It reduces TLB misses and improves performance.
Ok((ppn[2] << 30) | (ppn[1] << 21) | (vpn[0] << 12) | offset)
}
2 => {
// Superpage translation. A superpage is a memory page of larger size than an
// ordinary page (4 KiB). It reduces TLB misses and improves performance.
Ok((ppn[2] << 30) | (vpn[1] << 21) | (vpn[0] << 12) | offset)
}
_ => match access_type {
AccessType::Instruction => return Err(Exception::InstructionPageFault(addr)),
AccessType::Load => return Err(Exception::LoadPageFault(addr)),
AccessType::Store => return Err(Exception::StoreAMOPageFault(addr)),
},
}
}
/// Read `size`-bit data from the system bus with the translation a virtual address to a physical address
/// if it is enabled.
fn read(&mut self, v_addr: u64, size: u8) -> Result<u64, Exception> {
let previous_mode = self.mode;
// 3.1.6.3 Memory Privilege in mstatus Register
// "When MPRV=1, load and store memory addresses are translated and protected, and
// endianness is applied, as though the current privilege mode were set to MPP."
if self.state.read_mstatus(MSTATUS_MPRV) == 1 {
self.mode = match self.state.read_mstatus(MSTATUS_MPP) {
0b00 => Mode::User,
0b01 => Mode::Supervisor,
0b11 => Mode::Machine,
_ => Mode::Debug,
};
}
let p_addr = self.translate(v_addr, AccessType::Load)?;
let result = self.bus.read(p_addr, size);
if self.state.read_mstatus(MSTATUS_MPRV) == 1 {
self.mode = previous_mode;
}
result
}
/// Write `size`-bit data to the system bus with the translation a virtual address to a physical
/// address if it is enabled.
fn write(&mut self, v_addr: u64, value: u64, size: u8) -> Result<(), Exception> {
let previous_mode = self.mode;
// 3.1.6.3 Memory Privilege in mstatus Register
// "When MPRV=1, load and store memory addresses are translated and protected, and
// endianness is applied, as though the current privilege mode were set to MPP."
if self.state.read_mstatus(MSTATUS_MPRV) == 1 {
self.mode = match self.state.read_mstatus(MSTATUS_MPP) {
0b00 => Mode::User,
0b01 => Mode::Supervisor,
0b11 => Mode::Machine,
_ => Mode::Debug,
};
}
// "The SC must fail if a write from some other device to the bytes accessed by the LR can
// be observed to occur between the LR and SC."
if self.reservation_set.contains(&v_addr) {
self.reservation_set.retain(|&x| x != v_addr);
}
let p_addr = self.translate(v_addr, AccessType::Store)?;
let result = self.bus.write(p_addr, value, size);
if self.state.read_mstatus(MSTATUS_MPRV) == 1 {
self.mode = previous_mode;
}
result
}
/// Fetch the `size`-bit next instruction from the memory at the current program counter.
pub fn fetch(&mut self, size: u8) -> Result<u64, Exception> {
if size != HALFWORD && size != WORD {
return Err(Exception::InstructionAccessFault);
}
let p_pc = self.translate(self.pc, AccessType::Instruction)?;
// The result of the read method can be `Exception::LoadAccessFault`. In fetch(), an error
// should be `Exception::InstructionAccessFault`.
match self.bus.read(p_pc, size) {
Ok(value) => Ok(value),
Err(_) => Err(Exception::InstructionAccessFault),
}
}
// /// Execute a cycle on peripheral devices.
// pub fn devices_increment(&mut self) {
// // TODO: mtime in Clint and TIME in CSR should be the same value.
// // Increment the timer register (mtimer) in Clint.
// self.bus.clint.increment(&mut self.state);
// // Increment the value in the TIME and CYCLE registers in CSR.
// self.state.increment_time();
// }
/// Execute an instruction. Raises an exception if something is wrong, otherwise, returns
/// the instruction executed in this cycle.
pub fn execute(&mut self) -> Result<u64, Exception> {
// WFI is called and pending interrupts don't exist.
if self.idle {
return Ok(0);
}
// Fetch.
let inst16 = self.fetch(HALFWORD)?;
let inst;
match inst16 & 0b11 {
0 | 1 | 2 => {
if inst16 == 0 {
// Unimplemented instruction, since all bits are 0.
return Err(Exception::IllegalInstruction(inst16));
}
inst = inst16;
self.execute_compressed(inst)?;
// Add 2 bytes to the program counter.
self.pc += 2;
}
_ => {
inst = self.fetch(WORD)?;
self.execute_general(inst)?;
// Add 4 bytes to the program counter.
self.pc += 4;
}
}
self.pre_inst = inst;
Ok(inst)
}
/// Execute a compressed instruction. Raised an exception if something is wrong, otherwise,
/// returns a fetched instruction. It also increments the program counter by 2 bytes.
pub fn execute_compressed(&mut self, inst: u64) -> Result<(), Exception> {
// 2. Decode.
let opcode = inst & 0x3;
let funct3 = (inst >> 13) & 0x7;
// 3. Execute.
// Compressed instructions have 3-bit field for popular registers, which correspond to
// registers x8 to x15.
match opcode {
0 => {
// Quadrant 0.
match funct3 {
0x0 => {
// c.addi4spn
// Expands to addi rd, x2, nzuimm, where rd=rd'+8.
inst_count!(self, "c.addi4spn");
self.debug(inst, "c.addi4spn");
let rd = ((inst >> 2) & 0x7) + 8;
// nzuimm[5:4|9:6|2|3] = inst[12:11|10:7|6|5]
let nzuimm = ((inst >> 1) & 0x3c0) // znuimm[9:6]
| ((inst >> 7) & 0x30) // znuimm[5:4]
| ((inst >> 2) & 0x8) // znuimm[3]
| ((inst >> 4) & 0x4); // znuimm[2]
if nzuimm == 0 {
return Err(Exception::IllegalInstruction(inst));
}
self.xregs
.write(rd, self.xregs.read(2).wrapping_add(nzuimm));
}
0x1 => {
// c.fld
// Expands to fld rd, offset(rs1), where rd=rd'+8 and rs1=rs1'+8.
inst_count!(self, "c.fld");
self.debug(inst, "c.fld");
let rd = ((inst >> 2) & 0x7) + 8;
let rs1 = ((inst >> 7) & 0x7) + 8;
// offset[5:3|7:6] = isnt[12:10|6:5]
let offset = ((inst << 1) & 0xc0) // imm[7:6]
| ((inst >> 7) & 0x38); // imm[5:3]
let val = f64::from_bits(
self.read(self.xregs.read(rs1).wrapping_add(offset), DOUBLEWORD)?,
);
self.fregs.write(rd, val);
}
0x2 => {
// c.lw
// Expands to lw rd, offset(rs1), where rd=rd'+8 and rs1=rs1'+8.
inst_count!(self, "c.lw");
self.debug(inst, "c.lw");
let rd = ((inst >> 2) & 0x7) + 8;
let rs1 = ((inst >> 7) & 0x7) + 8;
// offset[5:3|2|6] = isnt[12:10|6|5]
let offset = ((inst << 1) & 0x40) // imm[6]
| ((inst >> 7) & 0x38) // imm[5:3]
| ((inst >> 4) & 0x4); // imm[2]
let addr = self.xregs.read(rs1).wrapping_add(offset);
let val = self.read(addr, WORD)?;
self.xregs.write(rd, val as i32 as i64 as u64);
}
0x3 => {
// c.ld
// Expands to ld rd, offset(rs1), where rd=rd'+8 and rs1=rs1'+8.
inst_count!(self, "c.ld");
self.debug(inst, "c.ld");
let rd = ((inst >> 2) & 0x7) + 8;
let rs1 = ((inst >> 7) & 0x7) + 8;
// offset[5:3|7:6] = isnt[12:10|6:5]
let offset = ((inst << 1) & 0xc0) // imm[7:6]
| ((inst >> 7) & 0x38); // imm[5:3]
let addr = self.xregs.read(rs1).wrapping_add(offset);
let val = self.read(addr, DOUBLEWORD)?;
self.xregs.write(rd, val);
}
0x4 => {
// Reserved.
panic!("reserved");
}
0x5 => {
// c.fsd
// Expands to fsd rs2, offset(rs1), where rs2=rs2'+8 and rs1=rs1'+8.
inst_count!(self, "c.fsd");
self.debug(inst, "c.fsd");
let rs2 = ((inst >> 2) & 0x7) + 8;
let rs1 = ((inst >> 7) & 0x7) + 8;
// offset[5:3|7:6] = isnt[12:10|6:5]
let offset = ((inst << 1) & 0xc0) // imm[7:6]
| ((inst >> 7) & 0x38); // imm[5:3]
let addr = self.xregs.read(rs1).wrapping_add(offset);
self.write(addr, self.fregs.read(rs2).to_bits() as u64, DOUBLEWORD)?;
}
0x6 => {
// c.sw
// Expands to sw rs2, offset(rs1), where rs2=rs2'+8 and rs1=rs1'+8.
inst_count!(self, "c.sw");
self.debug(inst, "c.sw");
let rs2 = ((inst >> 2) & 0x7) + 8;
let rs1 = ((inst >> 7) & 0x7) + 8;
// offset[5:3|2|6] = isnt[12:10|6|5]
let offset = ((inst << 1) & 0x40) // imm[6]
| ((inst >> 7) & 0x38) // imm[5:3]
| ((inst >> 4) & 0x4); // imm[2]
let addr = self.xregs.read(rs1).wrapping_add(offset);
self.write(addr, self.xregs.read(rs2), WORD)?;
}
0x7 => {
// c.sd
// Expands to sd rs2, offset(rs1), where rs2=rs2'+8 and rs1=rs1'+8.
inst_count!(self, "c.sd");
self.debug(inst, "c.sd");
let rs2 = ((inst >> 2) & 0x7) + 8;
let rs1 = ((inst >> 7) & 0x7) + 8;
// offset[5:3|7:6] = isnt[12:10|6:5]
let offset = ((inst << 1) & 0xc0) // imm[7:6]
| ((inst >> 7) & 0x38); // imm[5:3]
let addr = self.xregs.read(rs1).wrapping_add(offset);
self.write(addr, self.xregs.read(rs2), DOUBLEWORD)?;
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
1 => {
// Quadrant 1.
match funct3 {
0x0 => {
// c.addi
// Expands to addi rd, rd, nzimm.
inst_count!(self, "c.addi");
self.debug(inst, "c.addi");
let rd = (inst >> 7) & 0x1f;
// nzimm[5|4:0] = inst[12|6:2]
let mut nzimm = ((inst >> 7) & 0x20) | ((inst >> 2) & 0x1f);
// Sign-extended.
nzimm = match (nzimm & 0x20) == 0 {
true => nzimm,
false => (0xc0 | nzimm) as i8 as i64 as u64,
};
if rd != 0 {
self.xregs
.write(rd, self.xregs.read(rd).wrapping_add(nzimm));
}
}
0x1 => {
// c.addiw
// Expands to addiw rd, rd, imm
// "The immediate can be zero for C.ADDIW, where this corresponds to sext.w
// rd"
inst_count!(self, "c.addiw");
self.debug(inst, "c.addiw");
let rd = (inst >> 7) & 0x1f;
// imm[5|4:0] = inst[12|6:2]
let mut imm = ((inst >> 7) & 0x20) | ((inst >> 2) & 0x1f);
// Sign-extended.
imm = match (imm & 0x20) == 0 {
true => imm,
false => (0xc0 | imm) as i8 as i64 as u64,
};
if rd != 0 {
self.xregs.write(
rd,
self.xregs.read(rd).wrapping_add(imm) as i32 as i64 as u64,
);
}
}
0x2 => {
// c.li
// Expands to addi rd, x0, imm.
inst_count!(self, "c.li");
self.debug(inst, "c.li");
let rd = (inst >> 7) & 0x1f;
// imm[5|4:0] = inst[12|6:2]
let mut imm = ((inst >> 7) & 0x20) | ((inst >> 2) & 0x1f);
// Sign-extended.
imm = match (imm & 0x20) == 0 {
true => imm,
false => (0xc0 | imm) as i8 as i64 as u64,
};
if rd != 0 {
self.xregs.write(rd, imm);
}
}
0x3 => {
let rd = (inst >> 7) & 0x1f;
match rd {
0 => {}
2 => {
// c.addi16sp
// Expands to addi x2, x2, nzimm
inst_count!(self, "c.addi16sp");
self.debug(inst, "c.addi16sp");
// nzimm[9|4|6|8:7|5] = inst[12|6|5|4:3|2]
let mut nzimm = ((inst >> 3) & 0x200) // nzimm[9]
| ((inst >> 2) & 0x10) // nzimm[4]
| ((inst << 1) & 0x40) // nzimm[6]
| ((inst << 4) & 0x180) // nzimm[8:7]
| ((inst << 3) & 0x20); // nzimm[5]
nzimm = match (nzimm & 0x200) == 0 {
true => nzimm,
// Sign-extended.
false => (0xfc00 | nzimm) as i16 as i32 as i64 as u64,
};
if nzimm != 0 {
self.xregs.write(2, self.xregs.read(2).wrapping_add(nzimm));
}
}
_ => {
// c.lui
// Expands to lui rd, nzimm.
inst_count!(self, "c.lui");
self.debug(inst, "c.lui");
// nzimm[17|16:12] = inst[12|6:2]
let mut nzimm = ((inst << 5) & 0x20000) | ((inst << 10) & 0x1f000);
// Sign-extended.
nzimm = match (nzimm & 0x20000) == 0 {
true => nzimm,
false => (0xfffc0000 | nzimm) as i32 as i64 as u64,
};
if nzimm != 0 {
self.xregs.write(rd, nzimm);
}
}
}
}
0x4 => {
let funct2 = (inst >> 10) & 0x3;
match funct2 {
0x0 => {
// c.srli
// Expands to srli rd, rd, shamt, where rd=rd'+8.
inst_count!(self, "c.srli");
self.debug(inst, "c.srli");
let rd = ((inst >> 7) & 0b111) + 8;
// shamt[5|4:0] = inst[12|6:2]
let shamt = ((inst >> 7) & 0x20) | ((inst >> 2) & 0x1f);
self.xregs.write(rd, self.xregs.read(rd) >> shamt);
}
0x1 => {
// c.srai
// Expands to srai rd, rd, shamt, where rd=rd'+8.
inst_count!(self, "c.srai");
self.debug(inst, "c.srai");
let rd = ((inst >> 7) & 0b111) + 8;
// shamt[5|4:0] = inst[12|6:2]
let shamt = ((inst >> 7) & 0x20) | ((inst >> 2) & 0x1f);
self.xregs
.write(rd, ((self.xregs.read(rd) as i64) >> shamt) as u64);
}
0x2 => {
// c.andi
// Expands to andi rd, rd, imm, where rd=rd'+8.
inst_count!(self, "c.andi");
self.debug(inst, "c.andi");
let rd = ((inst >> 7) & 0b111) + 8;
// imm[5|4:0] = inst[12|6:2]
let mut imm = ((inst >> 7) & 0x20) | ((inst >> 2) & 0x1f);
// Sign-extended.
imm = match (imm & 0x20) == 0 {
true => imm,
false => (0xc0 | imm) as i8 as i64 as u64,
};
self.xregs.write(rd, self.xregs.read(rd) & imm);
}
0x3 => {
match ((inst >> 12) & 0b1, (inst >> 5) & 0b11) {
(0x0, 0x0) => {
// c.sub
// Expands to sub rd, rd, rs2, rd=rd'+8 and rs2=rs2'+8.
inst_count!(self, "c.sub");
self.debug(inst, "c.sub");
let rd = ((inst >> 7) & 0b111) + 8;
let rs2 = ((inst >> 2) & 0b111) + 8;
self.xregs.write(
rd,
self.xregs.read(rd).wrapping_sub(self.xregs.read(rs2)),
);
}
(0x0, 0x1) => {
// c.xor
// Expands to xor rd, rd, rs2, rd=rd'+8 and rs2=rs2'+8.
inst_count!(self, "c.xor");
self.debug(inst, "c.xor");
let rd = ((inst >> 7) & 0b111) + 8;
let rs2 = ((inst >> 2) & 0b111) + 8;
self.xregs
.write(rd, self.xregs.read(rd) ^ self.xregs.read(rs2));
}
(0x0, 0x2) => {
// c.or
// Expands to or rd, rd, rs2, rd=rd'+8 and rs2=rs2'+8.
inst_count!(self, "c.or");
self.debug(inst, "c.or");
let rd = ((inst >> 7) & 0b111) + 8;
let rs2 = ((inst >> 2) & 0b111) + 8;
self.xregs
.write(rd, self.xregs.read(rd) | self.xregs.read(rs2));
}
(0x0, 0x3) => {
// c.and
// Expands to and rd, rd, rs2, rd=rd'+8 and rs2=rs2'+8.
inst_count!(self, "c.and");
self.debug(inst, "c.and");
let rd = ((inst >> 7) & 0b111) + 8;
let rs2 = ((inst >> 2) & 0b111) + 8;
self.xregs
.write(rd, self.xregs.read(rd) & self.xregs.read(rs2));
}
(0x1, 0x0) => {
// c.subw
// Expands to subw rd, rd, rs2, rd=rd'+8 and rs2=rs2'+8.
inst_count!(self, "c.subw");
self.debug(inst, "c.subw");
let rd = ((inst >> 7) & 0b111) + 8;
let rs2 = ((inst >> 2) & 0b111) + 8;
self.xregs.write(
rd,
self.xregs.read(rd).wrapping_sub(self.xregs.read(rs2))
as i32
as i64
as u64,
);
}
(0x1, 0x1) => {
// c.addw
// Expands to addw rd, rd, rs2, rd=rd'+8 and rs2=rs2'+8.
inst_count!(self, "c.addw");
self.debug(inst, "c.andw");
let rd = ((inst >> 7) & 0b111) + 8;
let rs2 = ((inst >> 2) & 0b111) + 8;
self.xregs.write(
rd,
self.xregs.read(rd).wrapping_add(self.xregs.read(rs2))
as i32
as i64
as u64,
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x5 => {
// c.j
// Expands to jal x0, offset.
inst_count!(self, "c.j");
self.debug(inst, "c.j");
// offset[11|4|9:8|10|6|7|3:1|5] = inst[12|11|10:9|8|7|6|5:3|2]
let mut offset = ((inst >> 1) & 0x800) // offset[11]
| ((inst << 2) & 0x400) // offset[10]
| ((inst >> 1) & 0x300) // offset[9:8]
| ((inst << 1) & 0x80) // offset[7]
| ((inst >> 1) & 0x40) // offset[6]
| ((inst << 3) & 0x20) // offset[5]
| ((inst >> 7) & 0x10) // offset[4]
| ((inst >> 2) & 0xe); // offset[3:1]
// Sign-extended.
offset = match (offset & 0x800) == 0 {
true => offset,
false => (0xf000 | offset) as i16 as i64 as u64,
};
self.pc = self.pc.wrapping_add(offset).wrapping_sub(2);
}
0x6 => {
// c.beqz
// Expands to beq rs1, x0, offset, rs1=rs1'+8.
inst_count!(self, "c.beqz");
self.debug(inst, "c.beqz");
let rs1 = ((inst >> 7) & 0b111) + 8;
// offset[8|4:3|7:6|2:1|5] = inst[12|11:10|6:5|4:3|2]
let mut offset = ((inst >> 4) & 0x100) // offset[8]
| ((inst << 1) & 0xc0) // offset[7:6]
| ((inst << 3) & 0x20) // offset[5]
| ((inst >> 7) & 0x18) // offset[4:3]
| ((inst >> 2) & 0x6); // offset[2:1]
// Sign-extended.
offset = match (offset & 0x100) == 0 {
true => offset,
false => (0xfe00 | offset) as i16 as i64 as u64,
};
if self.xregs.read(rs1) == 0 {
self.pc = self.pc.wrapping_add(offset).wrapping_sub(2);
}
}
0x7 => {
// c.bnez
// Expands to bne rs1, x0, offset, rs1=rs1'+8.
inst_count!(self, "c.bnez");
self.debug(inst, "c.beez");
let rs1 = ((inst >> 7) & 0b111) + 8;
// offset[8|4:3|7:6|2:1|5] = inst[12|11:10|6:5|4:3|2]
let mut offset = ((inst >> 4) & 0x100) // offset[8]
| ((inst << 1) & 0xc0) // offset[7:6]
| ((inst << 3) & 0x20) // offset[5]
| ((inst >> 7) & 0x18) // offset[4:3]
| ((inst >> 2) & 0x6); // offset[2:1]
// Sign-extended.
offset = match (offset & 0x100) == 0 {
true => offset,
false => (0xfe00 | offset) as i16 as i64 as u64,
};
if self.xregs.read(rs1) != 0 {
self.pc = self.pc.wrapping_add(offset).wrapping_sub(2);
}
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
2 => {
// Quadrant 2.
match funct3 {
0x0 => {
// c.slli
// Expands to slli rd, rd, shamt.
inst_count!(self, "c.slli");
self.debug(inst, "c.slli");
let rd = (inst >> 7) & 0x1f;
// shamt[5|4:0] = inst[12|6:2]
let shamt = ((inst >> 7) & 0x20) | ((inst >> 2) & 0x1f);
if rd != 0 {
self.xregs.write(rd, self.xregs.read(rd) << shamt);
}
}
0x1 => {
// c.fldsp
// Expands to fld rd, offset(x2).
inst_count!(self, "c.fldsp");
self.debug(inst, "c.fldsp");
let rd = (inst >> 7) & 0x1f;
// offset[5|4:3|8:6] = inst[12|6:5|4:2]
let offset = ((inst << 4) & 0x1c0) // offset[8:6]
| ((inst >> 7) & 0x20) // offset[5]
| ((inst >> 2) & 0x18); // offset[4:3]
let val =
f64::from_bits(self.read(self.xregs.read(2) + offset, DOUBLEWORD)?);
self.fregs.write(rd, val);
}
0x2 => {
// c.lwsp
// Expands to lw rd, offset(x2).
inst_count!(self, "c.lwsp");
self.debug(inst, "c.lwsp");
let rd = (inst >> 7) & 0x1f;
// offset[5|4:2|7:6] = inst[12|6:4|3:2]
let offset = ((inst << 4) & 0xc0) // offset[7:6]
| ((inst >> 7) & 0x20) // offset[5]
| ((inst >> 2) & 0x1c); // offset[4:2]
let val = self.read(self.xregs.read(2).wrapping_add(offset), WORD)?;
self.xregs.write(rd, val as i32 as i64 as u64);
}
0x3 => {
// c.ldsp
// Expands to ld rd, offset(x2).
inst_count!(self, "c.ldsp");
self.debug(inst, "c.ldsp");
let rd = (inst >> 7) & 0x1f;
// offset[5|4:3|8:6] = inst[12|6:5|4:2]
let offset = ((inst << 4) & 0x1c0) // offset[8:6]
| ((inst >> 7) & 0x20) // offset[5]
| ((inst >> 2) & 0x18); // offset[4:3]
let val = self.read(self.xregs.read(2).wrapping_add(offset), DOUBLEWORD)?;
self.xregs.write(rd, val);
}
0x4 => {
match ((inst >> 12) & 0x1, (inst >> 2) & 0x1f) {
(0, 0) => {
// c.jr
// Expands to jalr x0, 0(rs1).
inst_count!(self, "c.jr");
self.debug(inst, "c.jr");
let rs1 = (inst >> 7) & 0x1f;
if rs1 != 0 {
self.pc = self.xregs.read(rs1).wrapping_sub(2);
}
}
(0, _) => {
// c.mv
// Expands to add rd, x0, rs2.
inst_count!(self, "c.mv");
self.debug(inst, "c.mv");
let rd = (inst >> 7) & 0x1f;
let rs2 = (inst >> 2) & 0x1f;
if rs2 != 0 {
self.xregs.write(rd, self.xregs.read(rs2));
}
}
(1, 0) => {
let rd = (inst >> 7) & 0x1f;
if rd == 0 {
// c.ebreak
// Expands to ebreak.
inst_count!(self, "c.ebreak");
self.debug(inst, "c.ebreak");
return Err(Exception::Breakpoint);
} else {
// c.jalr
// Expands to jalr x1, 0(rs1).
inst_count!(self, "c.jalr");
self.debug(inst, "c.jalr");
let rs1 = (inst >> 7) & 0x1f;
let t = self.pc.wrapping_add(2);
self.pc = self.xregs.read(rs1).wrapping_sub(2);
self.xregs.write(1, t);
}
}
(1, _) => {
// c.add
// Expands to add rd, rd, rs2.
inst_count!(self, "c.add");
self.debug(inst, "c.add");
let rd = (inst >> 7) & 0x1f;
let rs2 = (inst >> 2) & 0x1f;
if rs2 != 0 {
self.xregs.write(
rd,
self.xregs.read(rd).wrapping_add(self.xregs.read(rs2)),
);
}
}
(_, _) => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x5 => {
// c.fsdsp
// Expands to fsd rs2, offset(x2).
inst_count!(self, "c.fsdsp");
self.debug(inst, "c.fsdsp");
let rs2 = (inst >> 2) & 0x1f;
// offset[5:3|8:6] = isnt[12:10|9:7]
let offset = ((inst >> 1) & 0x1c0) // offset[8:6]
| ((inst >> 7) & 0x38); // offset[5:3]
let addr = self.xregs.read(2).wrapping_add(offset);
self.write(addr, self.fregs.read(rs2).to_bits(), DOUBLEWORD)?;
}
0x6 => {
// c.swsp
// Expands to sw rs2, offset(x2).
inst_count!(self, "c.swsp");
self.debug(inst, "c.swsp");
let rs2 = (inst >> 2) & 0x1f;
// offset[5:2|7:6] = inst[12:9|8:7]
let offset = ((inst >> 1) & 0xc0) // offset[7:6]
| ((inst >> 7) & 0x3c); // offset[5:2]
let addr = self.xregs.read(2).wrapping_add(offset);
self.write(addr, self.xregs.read(rs2), WORD)?;
}
0x7 => {
// c.sdsp
// Expands to sd rs2, offset(x2).
inst_count!(self, "c.sdsp");
self.debug(inst, "c.sdsp");
let rs2 = (inst >> 2) & 0x1f;
// offset[5:3|8:6] = isnt[12:10|9:7]
let offset = ((inst >> 1) & 0x1c0) // offset[8:6]
| ((inst >> 7) & 0x38); // offset[5:3]
let addr = self.xregs.read(2).wrapping_add(offset);
self.write(addr, self.xregs.read(rs2), DOUBLEWORD)?;
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
Ok(())
}
/// Execute a general-purpose instruction. Raises an exception if something is wrong,
/// otherwise, returns a fetched instruction. It also increments the program counter by 4 bytes.
fn execute_general(&mut self, inst: u64) -> Result<(), Exception> {
// 2. Decode.
let opcode = inst & 0x0000007f;
let rd = (inst & 0x00000f80) >> 7;
let rs1 = (inst & 0x000f8000) >> 15;
let rs2 = (inst & 0x01f00000) >> 20;
let funct3 = (inst & 0x00007000) >> 12;
let funct7 = (inst & 0xfe000000) >> 25;
// 3. Execute.
match opcode {
0x03 => {
// RV32I and RV64I
// imm[11:0] = inst[31:20]
let offset = ((inst as i32 as i64) >> 20) as u64;
let addr = self.xregs.read(rs1).wrapping_add(offset);
match funct3 {
0x0 => {
// lb
inst_count!(self, "lb");
self.debug(inst, "lb");
let val = self.read(addr, BYTE)?;
self.xregs.write(rd, val as i8 as i64 as u64);
}
0x1 => {
// lh
inst_count!(self, "lh");
self.debug(inst, "lh");
let val = self.read(addr, HALFWORD)?;
self.xregs.write(rd, val as i16 as i64 as u64);
}
0x2 => {
// lw
inst_count!(self, "lw");
self.debug(inst, "lw");
let val = self.read(addr, WORD)?;
self.xregs.write(rd, val as i32 as i64 as u64);
}
0x3 => {
// ld
inst_count!(self, "ld");
self.debug(inst, "ld");
let val = self.read(addr, DOUBLEWORD)?;
self.xregs.write(rd, val);
}
0x4 => {
// lbu
inst_count!(self, "lbu");
self.debug(inst, "lbu");
let val = self.read(addr, BYTE)?;
self.xregs.write(rd, val);
}
0x5 => {
// lhu
inst_count!(self, "lhu");
self.debug(inst, "lhu");
let val = self.read(addr, HALFWORD)?;
self.xregs.write(rd, val);
}
0x6 => {
// lwu
inst_count!(self, "lwu");
self.debug(inst, "lwu");
let val = self.read(addr, WORD)?;
self.xregs.write(rd, val);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x07 => {
// RV32D and RV64D
// imm[11:0] = inst[31:20]
let offset = ((inst as i32 as i64) >> 20) as u64;
let addr = self.xregs.read(rs1).wrapping_add(offset);
match funct3 {
0x2 => {
// flw
inst_count!(self, "flw");
self.debug(inst, "flw");
let val = f32::from_bits(self.read(addr, WORD)? as u32);
self.fregs.write(rd, val as f64);
}
0x3 => {
// fld
inst_count!(self, "fld");
self.debug(inst, "fld");
let val = f64::from_bits(self.read(addr, DOUBLEWORD)?);
self.fregs.write(rd, val);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x0f => {
// RV32I and RV64I
// fence instructions are not supported yet because this emulator executes an
// instruction sequentially on a single thread.
// fence.i is a part of the Zifencei extension.
match funct3 {
0x0 => {
// fence
inst_count!(self, "fence");
self.debug(inst, "fence");
}
0x1 => {
// fence.i
inst_count!(self, "fence.i");
self.debug(inst, "fence.i");
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x13 => {
// RV32I and RV64I
// imm[11:0] = inst[31:20]
let imm = ((inst as i32 as i64) >> 20) as u64;
let funct6 = funct7 >> 1;
match funct3 {
0x0 => {
// addi
inst_count!(self, "addi");
self.debug(inst, "addi");
self.xregs.write(rd, self.xregs.read(rs1).wrapping_add(imm));
}
0x1 => {
// slli
inst_count!(self, "slli");
self.debug(inst, "slli");
// shamt size is 5 bits for RV32I and 6 bits for RV64I.
let shamt = (inst >> 20) & 0x3f;
self.xregs.write(rd, self.xregs.read(rs1) << shamt);
}
0x2 => {
// slti
inst_count!(self, "slti");
self.debug(inst, "slti");
self.xregs.write(
rd,
if (self.xregs.read(rs1) as i64) < (imm as i64) {
1
} else {
0
},
);
}
0x3 => {
// sltiu
inst_count!(self, "sltiu");
self.debug(inst, "sltiu");
self.xregs
.write(rd, if self.xregs.read(rs1) < imm { 1 } else { 0 });
}
0x4 => {
// xori
inst_count!(self, "xori");
self.debug(inst, "xori");
self.xregs.write(rd, self.xregs.read(rs1) ^ imm);
}
0x5 => {
match funct6 {
0x00 => {
// srli
inst_count!(self, "srli");
self.debug(inst, "srli");
// shamt size is 5 bits for RV32I and 6 bits for RV64I.
let shamt = (inst >> 20) & 0x3f;
self.xregs.write(rd, self.xregs.read(rs1) >> shamt);
}
0x10 => {
// srai
inst_count!(self, "srai");
self.debug(inst, "srai");
// shamt size is 5 bits for RV32I and 6 bits for RV64I.
let shamt = (inst >> 20) & 0x3f;
self.xregs
.write(rd, ((self.xregs.read(rs1) as i64) >> shamt) as u64);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x6 => {
// ori
inst_count!(self, "ori");
self.debug(inst, "ori");
self.xregs.write(rd, self.xregs.read(rs1) | imm);
}
0x7 => {
// andi
inst_count!(self, "andi");
self.debug(inst, "andi");
self.xregs.write(rd, self.xregs.read(rs1) & imm);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x17 => {
// RV32I
// auipc
inst_count!(self, "auipc");
self.debug(inst, "auipc");
// AUIPC forms a 32-bit offset from the 20-bit U-immediate, filling
// in the lowest 12 bits with zeros.
// imm[31:12] = inst[31:12]
let imm = (inst & 0xfffff000) as i32 as i64 as u64;
self.xregs.write(rd, self.pc.wrapping_add(imm));
}
0x1b => {
// RV64I
// imm[11:0] = inst[31:20]
let imm = ((inst as i32 as i64) >> 20) as u64;
match funct3 {
0x0 => {
// addiw
inst_count!(self, "addiw");
self.debug(inst, "addiw");
self.xregs.write(
rd,
self.xregs.read(rs1).wrapping_add(imm) as i32 as i64 as u64,
);
}
0x1 => {
// slliw
inst_count!(self, "slliw");
self.debug(inst, "slliw");
// "SLLIW, SRLIW, and SRAIW encodings with imm[5] ̸= 0 are reserved."
let shamt = (imm & 0x1f) as u32;
self.xregs
.write(rd, (self.xregs.read(rs1) << shamt) as i32 as i64 as u64);
}
0x5 => {
match funct7 {
0x00 => {
// srliw
inst_count!(self, "srliw");
self.debug(inst, "srliw");
// "SLLIW, SRLIW, and SRAIW encodings with imm[5] ̸= 0 are reserved."
let shamt = (imm & 0x1f) as u32;
self.xregs.write(
rd,
((self.xregs.read(rs1) as u32) >> shamt) as i32 as i64 as u64,
)
}
0x20 => {
// sraiw
inst_count!(self, "sraiw");
self.debug(inst, "sraiw");
// "SLLIW, SRLIW, and SRAIW encodings with imm[5] ̸= 0 are reserved."
let shamt = (imm & 0x1f) as u32;
self.xregs.write(
rd,
((self.xregs.read(rs1) as i32) >> shamt) as i64 as u64,
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x23 => {
// RV32I
// offset[11:5|4:0] = inst[31:25|11:7]
let offset =
(((inst & 0xfe000000) as i32 as i64 >> 20) as u64) | ((inst >> 7) & 0x1f);
let addr = self.xregs.read(rs1).wrapping_add(offset);
match funct3 {
0x0 => {
// sb
inst_count!(self, "sb");
self.debug(inst, "sb");
self.write(addr, self.xregs.read(rs2), BYTE)?
}
0x1 => {
// sh
inst_count!(self, "sh");
self.debug(inst, "sh");
self.write(addr, self.xregs.read(rs2), HALFWORD)?
}
0x2 => {
// sw
inst_count!(self, "sw");
self.debug(inst, "sw");
self.write(addr, self.xregs.read(rs2), WORD)?
}
0x3 => {
// sd
inst_count!(self, "sd");
self.debug(inst, "sd");
self.write(addr, self.xregs.read(rs2), DOUBLEWORD)?
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x27 => {
// RV32F and RV64F
// offset[11:5|4:0] = inst[31:25|11:7]
let offset = ((((inst as i32 as i64) >> 20) as u64) & 0xfe0) | ((inst >> 7) & 0x1f);
let addr = self.xregs.read(rs1).wrapping_add(offset);
match funct3 {
0x2 => {
// fsw
inst_count!(self, "fsw");
self.debug(inst, "fsw");
self.write(addr, (self.fregs.read(rs2) as f32).to_bits() as u64, WORD)?
}
0x3 => {
// fsd
inst_count!(self, "fsd");
self.debug(inst, "fsd");
self.write(addr, self.fregs.read(rs2).to_bits() as u64, DOUBLEWORD)?
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x2f => {
// RV32A and RV64A
let funct5 = (funct7 & 0b1111100) >> 2;
// TODO: Handle `aq` and `rl`.
let _aq = (funct7 & 0b0000010) >> 1; // acquire access
let _rl = funct7 & 0b0000001; // release access
match (funct3, funct5) {
(0x2, 0x00) => {
// amoadd.w
inst_count!(self, "amoadd.w");
self.debug(inst, "amoadd.w");
let addr = self.xregs.read(rs1);
// "For AMOs, the A extension requires that the address held in rs1 be
// naturally aligned to the size of the operand (i.e., eight-byte aligned
// for 64-bit words and four-byte aligned for 32-bit words). If the
// address is not naturally aligned, an address-misaligned exception or
// an access-fault exception will be generated."
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, WORD)?;
self.write(addr, t.wrapping_add(self.xregs.read(rs2)), WORD)?;
self.xregs.write(rd, t as i32 as i64 as u64);
}
(0x3, 0x00) => {
// amoadd.d
inst_count!(self, "amoadd.d");
self.debug(inst, "amoadd.d");
let addr = self.xregs.read(rs1);
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, DOUBLEWORD)?;
self.write(addr, t.wrapping_add(self.xregs.read(rs2)), DOUBLEWORD)?;
self.xregs.write(rd, t);
}
(0x2, 0x01) => {
// amoswap.w
inst_count!(self, "amoswap.w");
self.debug(inst, "amoswap.w");
let addr = self.xregs.read(rs1);
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, WORD)?;
self.write(addr, self.xregs.read(rs2), WORD)?;
self.xregs.write(rd, t as i32 as i64 as u64);
}
(0x3, 0x01) => {
// amoswap.d
inst_count!(self, "amoswap.d");
self.debug(inst, "amoswap.d");
let addr = self.xregs.read(rs1);
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, DOUBLEWORD)?;
self.write(addr, self.xregs.read(rs2), DOUBLEWORD)?;
self.xregs.write(rd, t);
}
(0x2, 0x02) => {
// lr.w
inst_count!(self, "lr.w");
self.debug(inst, "lr.w");
let addr = self.xregs.read(rs1);
// "For LR and SC, the A extension requires that the address held in rs1 be
// naturally aligned to the size of the operand (i.e., eight-byte aligned
// for 64-bit words and four-byte aligned for 32-bit words)."
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let value = self.read(addr, WORD)?;
self.xregs.write(rd, value as i32 as i64 as u64);
self.reservation_set.push(addr);
}
(0x3, 0x02) => {
// lr.d
inst_count!(self, "lr.d");
self.debug(inst, "lr.d");
let addr = self.xregs.read(rs1);
// "For LR and SC, the A extension requires that the address held in rs1 be
// naturally aligned to the size of the operand (i.e., eight-byte aligned for
// 64-bit words and four-byte aligned for 32-bit words)."
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let value = self.read(addr, DOUBLEWORD)?;
self.xregs.write(rd, value);
self.reservation_set.push(addr);
}
(0x2, 0x03) => {
// sc.w
inst_count!(self, "sc.w");
self.debug(inst, "sc.w");
let addr = self.xregs.read(rs1);
// "For LR and SC, the A extension requires that the address held in rs1 be
// naturally aligned to the size of the operand (i.e., eight-byte aligned for
// 64-bit words and four-byte aligned for 32-bit words)."
if addr % 4 != 0 {
return Err(Exception::StoreAMOAddressMisaligned);
}
if self.reservation_set.contains(&addr) {
// "Regardless of success or failure, executing an SC.W instruction
// invalidates any reservation held by this hart. "
self.reservation_set.retain(|&x| x != addr);
self.write(addr, self.xregs.read(rs2), WORD)?;
self.xregs.write(rd, 0);
} else {
self.reservation_set.retain(|&x| x != addr);
self.xregs.write(rd, 1);
};
}
(0x3, 0x03) => {
// sc.d
inst_count!(self, "sc.d");
self.debug(inst, "sc.d");
let addr = self.xregs.read(rs1);
// "For LR and SC, the A extension requires that the address held in rs1 be
// naturally aligned to the size of the operand (i.e., eight-byte aligned for
// 64-bit words and four-byte aligned for 32-bit words)."
if addr % 8 != 0 {
return Err(Exception::StoreAMOAddressMisaligned);
}
if self.reservation_set.contains(&addr) {
self.reservation_set.retain(|&x| x != addr);
self.write(addr, self.xregs.read(rs2), DOUBLEWORD)?;
self.xregs.write(rd, 0);
} else {
self.reservation_set.retain(|&x| x != addr);
self.xregs.write(rd, 1);
}
}
(0x2, 0x04) => {
// amoxor.w
inst_count!(self, "amoxor.w");
self.debug(inst, "amoxor.w");
let addr = self.xregs.read(rs1);
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, WORD)?;
self.write(
addr,
(t as i32 ^ (self.xregs.read(rs2) as i32)) as i64 as u64,
WORD,
)?;
self.xregs.write(rd, t as i32 as i64 as u64);
}
(0x3, 0x04) => {
// amoxor.d
inst_count!(self, "amoxor.d");
self.debug(inst, "amoxor.d");
let addr = self.xregs.read(rs1);
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, DOUBLEWORD)?;
self.write(addr, t ^ self.xregs.read(rs2), DOUBLEWORD)?;
self.xregs.write(rd, t);
}
(0x2, 0x08) => {
// amoor.w
inst_count!(self, "amoor.w");
self.debug(inst, "amoor.w");
let addr = self.xregs.read(rs1);
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, WORD)?;
self.write(
addr,
(t as i32 | (self.xregs.read(rs2) as i32)) as i64 as u64,
WORD,
)?;
self.xregs.write(rd, t as i32 as i64 as u64);
}
(0x3, 0x08) => {
// amoor.d
inst_count!(self, "amoor.d");
self.debug(inst, "amoor.d");
let addr = self.xregs.read(rs1);
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, DOUBLEWORD)?;
self.write(addr, t | self.xregs.read(rs2), DOUBLEWORD)?;
self.xregs.write(rd, t);
}
(0x2, 0x0c) => {
// amoand.w
inst_count!(self, "amoand.w");
self.debug(inst, "amoand.w");
let addr = self.xregs.read(rs1);
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, WORD)?;
self.write(
addr,
(t as i32 & (self.xregs.read(rs2) as i32)) as u32 as u64,
WORD,
)?;
self.xregs.write(rd, t as i32 as i64 as u64);
}
(0x3, 0x0c) => {
// amoand.d
inst_count!(self, "amoand.d");
self.debug(inst, "amoand.d");
let addr = self.xregs.read(rs1);
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, DOUBLEWORD)?;
self.write(addr, t & self.xregs.read(rs2), DOUBLEWORD)?;
self.xregs.write(rd, t);
}
(0x2, 0x10) => {
// amomin.w
inst_count!(self, "amomin.w");
self.debug(inst, "amomin.w");
let addr = self.xregs.read(rs1);
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, WORD)?;
self.write(
addr,
cmp::min(t as i32, self.xregs.read(rs2) as i32) as i64 as u64,
WORD,
)?;
self.xregs.write(rd, t as i32 as i64 as u64);
}
(0x3, 0x10) => {
// amomin.d
inst_count!(self, "amomin.d");
self.debug(inst, "amomin.d");
let addr = self.xregs.read(rs1);
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, DOUBLEWORD)?;
self.write(
addr,
cmp::min(t as i64, self.xregs.read(rs2) as i64) as u64,
DOUBLEWORD,
)?;
self.xregs.write(rd, t as u64);
}
(0x2, 0x14) => {
// amomax.w
inst_count!(self, "amomax.w");
self.debug(inst, "amomax.w");
let addr = self.xregs.read(rs1);
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, WORD)?;
self.write(
addr,
cmp::max(t as i32, self.xregs.read(rs2) as i32) as i64 as u64,
WORD,
)?;
self.xregs.write(rd, t as i32 as i64 as u64);
}
(0x3, 0x14) => {
// amomax.d
inst_count!(self, "amomax.d");
self.debug(inst, "amomax.d");
let addr = self.xregs.read(rs1);
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, DOUBLEWORD)?;
self.write(
addr,
cmp::max(t as i64, self.xregs.read(rs2) as i64) as u64,
DOUBLEWORD,
)?;
self.xregs.write(rd, t);
}
(0x2, 0x18) => {
// amominu.w
inst_count!(self, "amominu.w");
self.debug(inst, "amominu.w");
let addr = self.xregs.read(rs1);
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, WORD)?;
self.write(
addr,
cmp::min(t as u32, self.xregs.read(rs2) as u32) as u64,
WORD,
)?;
self.xregs.write(rd, t as i32 as i64 as u64);
}
(0x3, 0x18) => {
// amominu.d
inst_count!(self, "amominu.d");
self.debug(inst, "amominu.d");
let addr = self.xregs.read(rs1);
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, DOUBLEWORD)?;
self.write(addr, cmp::min(t, self.xregs.read(rs2)), DOUBLEWORD)?;
self.xregs.write(rd, t);
}
(0x2, 0x1c) => {
// amomaxu.w
inst_count!(self, "amomaxu.w");
self.debug(inst, "amomaxu.w");
let addr = self.xregs.read(rs1);
if addr % 4 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, WORD)?;
self.write(
addr,
cmp::max(t as u32, self.xregs.read(rs2) as u32) as u64,
WORD,
)?;
self.xregs.write(rd, t as i32 as i64 as u64);
}
(0x3, 0x1c) => {
// amomaxu.d
inst_count!(self, "amomaxu.d");
self.debug(inst, "amomaxu.d");
let addr = self.xregs.read(rs1);
if addr % 8 != 0 {
return Err(Exception::LoadAddressMisaligned);
}
let t = self.read(addr, DOUBLEWORD)?;
self.write(addr, cmp::max(t, self.xregs.read(rs2)), DOUBLEWORD)?;
self.xregs.write(rd, t);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x33 => {
// RV64I and RV64M
match (funct3, funct7) {
(0x0, 0x00) => {
// add
inst_count!(self, "add");
self.debug(inst, "add");
self.xregs
.write(rd, self.xregs.read(rs1).wrapping_add(self.xregs.read(rs2)));
}
(0x0, 0x01) => {
// mul
inst_count!(self, "mul");
self.debug(inst, "mul");
self.xregs.write(
rd,
(self.xregs.read(rs1) as i64).wrapping_mul(self.xregs.read(rs2) as i64)
as u64,
);
}
(0x0, 0x20) => {
// sub
inst_count!(self, "sub");
self.debug(inst, "sub");
self.xregs
.write(rd, self.xregs.read(rs1).wrapping_sub(self.xregs.read(rs2)));
}
(0x1, 0x00) => {
// sll
inst_count!(self, "sll");
self.debug(inst, "sll");
// "SLL, SRL, and SRA perform logical left, logical right, and arithmetic
// right shifts on the value in register rs1 by the shift amount held in
// register rs2. In RV64I, only the low 6 bits of rs2 are considered for the
// shift amount."
let shamt = self.xregs.read(rs2) & 0x3f;
self.xregs.write(rd, self.xregs.read(rs1) << shamt);
}
(0x1, 0x01) => {
// mulh
inst_count!(self, "mulh");
self.debug(inst, "mulh");
// signed × signed
self.xregs.write(
rd,
((self.xregs.read(rs1) as i64 as i128)
.wrapping_mul(self.xregs.read(rs2) as i64 as i128)
>> 64) as u64,
);
}
(0x2, 0x00) => {
// slt
inst_count!(self, "slt");
self.debug(inst, "slt");
self.xregs.write(
rd,
if (self.xregs.read(rs1) as i64) < (self.xregs.read(rs2) as i64) {
1
} else {
0
},
);
}
(0x2, 0x01) => {
// mulhsu
inst_count!(self, "mulhsu");
self.debug(inst, "mulhsu");
// signed × unsigned
self.xregs.write(
rd,
((self.xregs.read(rs1) as i64 as i128 as u128)
.wrapping_mul(self.xregs.read(rs2) as u128)
>> 64) as u64,
);
}
(0x3, 0x00) => {
// sltu
inst_count!(self, "sltu");
self.debug(inst, "sltu");
self.xregs.write(
rd,
if self.xregs.read(rs1) < self.xregs.read(rs2) {
1
} else {
0
},
);
}
(0x3, 0x01) => {
// mulhu
inst_count!(self, "mulhu");
self.debug(inst, "mulhu");
// unsigned × unsigned
self.xregs.write(
rd,
((self.xregs.read(rs1) as u128)
.wrapping_mul(self.xregs.read(rs2) as u128)
>> 64) as u64,
);
}
(0x4, 0x00) => {
// xor
inst_count!(self, "xor");
self.debug(inst, "xor");
self.xregs
.write(rd, self.xregs.read(rs1) ^ self.xregs.read(rs2));
}
(0x4, 0x01) => {
// div
inst_count!(self, "div");
self.debug(inst, "div");
let dividend = self.xregs.read(rs1) as i64;
let divisor = self.xregs.read(rs2) as i64;
self.xregs.write(
rd,
if divisor == 0 {
// Division by zero
// Set DZ (Divide by Zero) flag to 1.
self.state.write_bit(FCSR, 3, 1);
// "The quotient of division by zero has all bits set"
u64::MAX
} else if dividend == i64::MIN && divisor == -1 {
// Overflow
// "The quotient of a signed division with overflow is equal to the
// dividend"
dividend as u64
} else {
// "division of rs1 by rs2, rounding towards zero"
dividend.wrapping_div(divisor) as u64
},
);
}
(0x5, 0x00) => {
// srl
inst_count!(self, "srl");
self.debug(inst, "srl");
// "SLL, SRL, and SRA perform logical left, logical right, and arithmetic
// right shifts on the value in register rs1 by the shift amount held in
// register rs2. In RV64I, only the low 6 bits of rs2 are considered for the
// shift amount."
let shamt = self.xregs.read(rs2) & 0x3f;
self.xregs.write(rd, self.xregs.read(rs1) >> shamt);
}
(0x5, 0x01) => {
// divu
inst_count!(self, "divu");
self.debug(inst, "divu");
let dividend = self.xregs.read(rs1);
let divisor = self.xregs.read(rs2);
self.xregs.write(
rd,
if divisor == 0 {
// Division by zero
// Set DZ (Divide by Zero) flag to 1.
self.state.write_bit(FCSR, 3, 1);
// "The quotient of division by zero has all bits set"
u64::MAX
} else {
// "division of rs1 by rs2, rounding towards zero"
dividend.wrapping_div(divisor)
},
);
}
(0x5, 0x20) => {
// sra
inst_count!(self, "sra");
self.debug(inst, "sra");
// "SLL, SRL, and SRA perform logical left, logical right, and arithmetic
// right shifts on the value in register rs1 by the shift amount held in
// register rs2. In RV64I, only the low 6 bits of rs2 are considered for the
// shift amount."
let shamt = self.xregs.read(rs2) & 0x3f;
self.xregs
.write(rd, ((self.xregs.read(rs1) as i64) >> shamt) as u64);
}
(0x6, 0x00) => {
// or
inst_count!(self, "or");
self.debug(inst, "or");
self.xregs
.write(rd, self.xregs.read(rs1) | self.xregs.read(rs2));
}
(0x6, 0x01) => {
// rem
inst_count!(self, "rem");
self.debug(inst, "rem");
let dividend = self.xregs.read(rs1) as i64;
let divisor = self.xregs.read(rs2) as i64;
self.xregs.write(
rd,
if divisor == 0 {
// Division by zero
// "the remainder of division by zero equals the dividend"
dividend as u64
} else if dividend == i64::MIN && divisor == -1 {
// Overflow
// "the remainder is zero"
0
} else {
// "provide the remainder of the corresponding division
// operation"
dividend.wrapping_rem(divisor) as u64
},
);
}
(0x7, 0x00) => {
// and
inst_count!(self, "and");
self.debug(inst, "and");
self.xregs
.write(rd, self.xregs.read(rs1) & self.xregs.read(rs2));
}
(0x7, 0x01) => {
// remu
inst_count!(self, "remu");
self.debug(inst, "remu");
let dividend = self.xregs.read(rs1);
let divisor = self.xregs.read(rs2);
self.xregs.write(
rd,
if divisor == 0 {
// Division by zero
// "the remainder of division by zero equals the dividend"
dividend
} else {
// "provide the remainder of the corresponding division
// operation"
dividend.wrapping_rem(divisor)
},
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
};
}
0x37 => {
// RV32I
// lui
inst_count!(self, "lui");
self.debug(inst, "lui");
// "LUI places the U-immediate value in the top 20 bits of the destination
// register rd, filling in the lowest 12 bits with zeros."
self.xregs
.write(rd, (inst & 0xfffff000) as i32 as i64 as u64);
}
0x3b => {
// RV64I and RV64M
match (funct3, funct7) {
(0x0, 0x00) => {
// addw
inst_count!(self, "addw");
self.debug(inst, "addw");
self.xregs.write(
rd,
self.xregs.read(rs1).wrapping_add(self.xregs.read(rs2)) as i32 as i64
as u64,
);
}
(0x0, 0x01) => {
// mulw
inst_count!(self, "mulw");
self.debug(inst, "mulw");
let n1 = self.xregs.read(rs1) as i32;
let n2 = self.xregs.read(rs2) as i32;
let result = n1.wrapping_mul(n2);
self.xregs.write(rd, result as i64 as u64);
}
(0x0, 0x20) => {
// subw
inst_count!(self, "subw");
self.debug(inst, "subw");
self.xregs.write(
rd,
((self.xregs.read(rs1).wrapping_sub(self.xregs.read(rs2))) as i32)
as u64,
);
}
(0x1, 0x00) => {
// sllw
inst_count!(self, "sllw");
self.debug(inst, "sllw");
// The shift amount is given by rs2[4:0].
let shamt = self.xregs.read(rs2) & 0x1f;
self.xregs
.write(rd, ((self.xregs.read(rs1)) << shamt) as i32 as i64 as u64);
}
(0x4, 0x01) => {
// divw
inst_count!(self, "divw");
self.debug(inst, "divw");
let dividend = self.xregs.read(rs1) as i32;
let divisor = self.xregs.read(rs2) as i32;
self.xregs.write(
rd,
if divisor == 0 {
// Division by zero
// Set DZ (Divide by Zero) flag to 1.
self.state.write_bit(FCSR, 3, 1);
// "The quotient of division by zero has all bits set"
u64::MAX
} else if dividend == i32::MIN && divisor == -1 {
// Overflow
// "The quotient of a signed division with overflow is equal to the
// dividend"
dividend as i64 as u64
} else {
// "division of rs1 by rs2, rounding towards zero"
dividend.wrapping_div(divisor) as i64 as u64
},
);
}
(0x5, 0x00) => {
// srlw
inst_count!(self, "srlw");
self.debug(inst, "srlw");
// The shift amount is given by rs2[4:0].
let shamt = self.xregs.read(rs2) & 0x1f;
self.xregs.write(
rd,
((self.xregs.read(rs1) as u32) >> shamt) as i32 as i64 as u64,
);
}
(0x5, 0x01) => {
// divuw
inst_count!(self, "divuw");
self.debug(inst, "divuw");
let dividend = self.xregs.read(rs1) as u32;
let divisor = self.xregs.read(rs2) as u32;
self.xregs.write(
rd,
if divisor == 0 {
// Division by zero
// Set DZ (Divide by Zero) flag to 1.
self.state.write_bit(FCSR, 3, 1);
// "The quotient of division by zero has all bits set"
u64::MAX
} else {
// "division of rs1 by rs2, rounding towards zero"
dividend.wrapping_div(divisor) as i32 as i64 as u64
},
);
}
(0x5, 0x20) => {
// sraw
inst_count!(self, "sraw");
self.debug(inst, "sraw");
// The shift amount is given by rs2[4:0].
let shamt = self.xregs.read(rs2) & 0x1f;
self.xregs
.write(rd, ((self.xregs.read(rs1) as i32) >> shamt) as i64 as u64);
}
(0x6, 0x01) => {
// remw
inst_count!(self, "remw");
self.debug(inst, "remw");
let dividend = self.xregs.read(rs1) as i32;
let divisor = self.xregs.read(rs2) as i32;
self.xregs.write(
rd,
if divisor == 0 {
// Division by zero
// "the remainder of division by zero equals the dividend"
dividend as i64 as u64
} else if dividend == i32::MIN && divisor == -1 {
// Overflow
// "the remainder is zero"
0
} else {
// "provide the remainder of the corresponding division
// operation"
dividend.wrapping_rem(divisor) as i64 as u64
},
);
}
(0x7, 0x01) => {
// remuw
inst_count!(self, "remuw");
self.debug(inst, "remuw");
let dividend = self.xregs.read(rs1) as u32;
let divisor = self.xregs.read(rs2) as u32;
self.xregs.write(
rd,
if divisor == 0 {
// Division by zero
// "the remainder of division by zero equals the dividend"
dividend as i32 as i64 as u64
} else {
// "provide the remainder of the corresponding division
// operation"
dividend.wrapping_rem(divisor) as i32 as i64 as u64
},
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x43 => {
// RV32F and RV64F
// TODO: support the rounding mode encoding (rm).
let rs3 = ((inst & 0xf8000000) >> 27) as u64;
let funct2 = (inst & 0x03000000) >> 25;
match funct2 {
0x0 => {
// fmadd.s
inst_count!(self, "fmadd.s");
self.debug(inst, "fmadd.s");
self.fregs.write(
rd,
(self.fregs.read(rs1) as f32)
.mul_add(self.fregs.read(rs2) as f32, self.fregs.read(rs3) as f32)
as f64,
);
}
0x1 => {
// fmadd.d
inst_count!(self, "fmadd.d");
self.debug(inst, "fmadd.d");
self.fregs.write(
rd,
self.fregs
.read(rs1)
.mul_add(self.fregs.read(rs2), self.fregs.read(rs3)),
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x47 => {
// RV32F and RV64F
// TODO: support the rounding mode encoding (rm).
let rs3 = ((inst & 0xf8000000) >> 27) as u64;
let funct2 = (inst & 0x03000000) >> 25;
match funct2 {
0x0 => {
// fmsub.s
inst_count!(self, "fmsub.s");
self.debug(inst, "fmsub.s");
self.fregs.write(
rd,
(self.fregs.read(rs1) as f32)
.mul_add(self.fregs.read(rs2) as f32, -self.fregs.read(rs3) as f32)
as f64,
);
}
0x1 => {
// fmsub.d
inst_count!(self, "fmsub.d");
self.debug(inst, "fmsub.d");
self.fregs.write(
rd,
self.fregs
.read(rs1)
.mul_add(self.fregs.read(rs2), -self.fregs.read(rs3)),
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x4b => {
// RV32F and RV64F
// TODO: support the rounding mode encoding (rm).
let rs3 = ((inst & 0xf8000000) >> 27) as u64;
let funct2 = (inst & 0x03000000) >> 25;
match funct2 {
0x0 => {
// fnmadd.s
inst_count!(self, "fnmadd.s");
self.debug(inst, "fnmadd.s");
self.fregs.write(
rd,
(-self.fregs.read(rs1) as f32)
.mul_add(self.fregs.read(rs2) as f32, self.fregs.read(rs3) as f32)
as f64,
);
}
0x1 => {
// fnmadd.d
inst_count!(self, "fnmadd.d");
self.debug(inst, "fnmadd.d");
self.fregs.write(
rd,
(-self.fregs.read(rs1))
.mul_add(self.fregs.read(rs2), self.fregs.read(rs3)),
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x4f => {
// RV32F and RV64F
// TODO: support the rounding mode encoding (rm).
let rs3 = ((inst & 0xf8000000) >> 27) as u64;
let funct2 = (inst & 0x03000000) >> 25;
match funct2 {
0x0 => {
// fnmsub.s
inst_count!(self, "fnmsub.s");
self.debug(inst, "fnmsub.s");
self.fregs.write(
rd,
(-self.fregs.read(rs1) as f32)
.mul_add(self.fregs.read(rs2) as f32, -self.fregs.read(rs3) as f32)
as f64,
);
}
0x1 => {
// fnmsub.d
inst_count!(self, "fnmsub.d");
self.debug(inst, "fnmsub.d");
self.fregs.write(
rd,
(-self.fregs.read(rs1))
.mul_add(self.fregs.read(rs2), -self.fregs.read(rs3)),
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x53 => {
// RV32F and RV64F
// TODO: support the rounding mode encoding (rm).
// TODO: NaN Boxing of Narrower Values (Spec 12.2).
// TODO: set exception flags.
/*
* Floating-point instructions align with the IEEE 754 (1985).
* The format consist of three fields: a sign bit, a biased exponent, and a fraction.
*
* | sign(1) | exponent(8) | fraction(23) |
* Ok => {}
* 31 0
*
*/
// Check the frm field is valid.
match self.state.read_bits(FCSR, 5..8) {
0b000 => {}
0b001 => {}
0b010 => {}
0b011 => {}
0b100 => {}
0b111 => {}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
match funct7 {
0x00 => {
// fadd.s
inst_count!(self, "fadd.s");
self.debug(inst, "fadd.s");
self.fregs.write(
rd,
(self.fregs.read(rs1) as f32 + self.fregs.read(rs2) as f32) as f64,
)
}
0x01 => {
// fadd.d
inst_count!(self, "fadd.d");
self.debug(inst, "fadd.d");
self.fregs
.write(rd, self.fregs.read(rs1) + self.fregs.read(rs2));
}
0x04 => {
// fsub.s
inst_count!(self, "fsub.s");
self.debug(inst, "fsub.s");
self.fregs.write(
rd,
(self.fregs.read(rs1) as f32 - self.fregs.read(rs2) as f32) as f64,
)
}
0x05 => {
// fsub.d
inst_count!(self, "fsub.d");
self.debug(inst, "fsub.d");
self.fregs
.write(rd, self.fregs.read(rs1) - self.fregs.read(rs2));
}
0x08 => {
// fmul.s
inst_count!(self, "fmul.s");
self.debug(inst, "fmul.s");
self.fregs.write(
rd,
(self.fregs.read(rs1) as f32 * self.fregs.read(rs2) as f32) as f64,
)
}
0x09 => {
// fmul.d
inst_count!(self, "fmul.d");
self.debug(inst, "fmul.d");
self.fregs
.write(rd, self.fregs.read(rs1) * self.fregs.read(rs2));
}
0x0c => {
// fdiv.s
inst_count!(self, "fdiv.s");
self.debug(inst, "fdiv.s");
self.fregs.write(
rd,
(self.fregs.read(rs1) as f32 / self.fregs.read(rs2) as f32) as f64,
)
}
0x0d => {
// fdiv.d
inst_count!(self, "fdiv.d");
self.debug(inst, "fdiv.d");
self.fregs
.write(rd, self.fregs.read(rs1) / self.fregs.read(rs2));
}
0x10 => {
match funct3 {
0x0 => {
// fsgnj.s
inst_count!(self, "fsgnj.s");
self.debug(inst, "fsgnj.s");
self.fregs
.write(rd, self.fregs.read(rs1).copysign(self.fregs.read(rs2)));
}
0x1 => {
// fsgnjn.s
inst_count!(self, "fsgnjn.s");
self.debug(inst, "fsgnjn.s");
self.fregs.write(
rd,
self.fregs.read(rs1).copysign(-self.fregs.read(rs2)),
);
}
0x2 => {
// fsgnjx.s
inst_count!(self, "fsgnjx.s");
self.debug(inst, "fsgnjx.s");
let sign1 = (self.fregs.read(rs1) as f32).to_bits() & 0x80000000;
let sign2 = (self.fregs.read(rs2) as f32).to_bits() & 0x80000000;
let other = (self.fregs.read(rs1) as f32).to_bits() & 0x7fffffff;
self.fregs
.write(rd, f32::from_bits((sign1 ^ sign2) | other) as f64);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x11 => {
match funct3 {
0x0 => {
// fsgnj.d
inst_count!(self, "fsgnj.d");
self.debug(inst, "fsgnj.d");
self.fregs
.write(rd, self.fregs.read(rs1).copysign(self.fregs.read(rs2)));
}
0x1 => {
// fsgnjn.d
inst_count!(self, "fsgnjn.d");
self.debug(inst, "fsgnjn.d");
self.fregs.write(
rd,
self.fregs.read(rs1).copysign(-self.fregs.read(rs2)),
);
}
0x2 => {
// fsgnjx.d
inst_count!(self, "fsgnjx.d");
self.debug(inst, "fsgnjx.d");
let sign1 = self.fregs.read(rs1).to_bits() & 0x80000000_00000000;
let sign2 = self.fregs.read(rs2).to_bits() & 0x80000000_00000000;
let other = self.fregs.read(rs1).to_bits() & 0x7fffffff_ffffffff;
self.fregs
.write(rd, f64::from_bits((sign1 ^ sign2) | other));
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x14 => {
match funct3 {
0x0 => {
// fmin.s
inst_count!(self, "fmin.s");
self.debug(inst, "fmin.s");
self.fregs
.write(rd, self.fregs.read(rs1).min(self.fregs.read(rs2)));
}
0x1 => {
// fmax.s
inst_count!(self, "fmax.s");
self.debug(inst, "fmax.s");
self.fregs
.write(rd, self.fregs.read(rs1).max(self.fregs.read(rs2)));
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x15 => {
match funct3 {
0x0 => {
// fmin.d
inst_count!(self, "fmin.d");
self.debug(inst, "fmin.d");
self.fregs
.write(rd, self.fregs.read(rs1).min(self.fregs.read(rs2)));
}
0x1 => {
// fmax.d
inst_count!(self, "fmax.d");
self.debug(inst, "fmax.d");
self.fregs
.write(rd, self.fregs.read(rs1).max(self.fregs.read(rs2)));
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x20 => {
// fcvt.s.d
inst_count!(self, "fcvt.s.d");
self.debug(inst, "fcvt.s.d");
self.fregs.write(rd, self.fregs.read(rs1));
}
0x21 => {
// fcvt.d.s
inst_count!(self, "fcvt.d.s");
self.debug(inst, "fcvt.d.s");
self.fregs.write(rd, (self.fregs.read(rs1) as f32) as f64);
}
0x2c => {
// fsqrt.s
inst_count!(self, "fsqrt.s");
self.debug(inst, "fsqrt.s");
self.fregs
.write(rd, (self.fregs.read(rs1) as f32).sqrt() as f64);
}
0x2d => {
// fsqrt.d
inst_count!(self, "fsqrt.d");
self.debug(inst, "fsqrt.d");
self.fregs.write(rd, self.fregs.read(rs1).sqrt());
}
0x50 => {
match funct3 {
0x0 => {
// fle.s
inst_count!(self, "fle.s");
self.debug(inst, "fle.s");
self.xregs.write(
rd,
if self.fregs.read(rs1) <= self.fregs.read(rs2) {
1
} else {
0
},
);
}
0x1 => {
// flt.s
inst_count!(self, "flt.s");
self.debug(inst, "flt.s");
self.xregs.write(
rd,
if self.fregs.read(rs1) < self.fregs.read(rs2) {
1
} else {
0
},
);
}
0x2 => {
// feq.s
inst_count!(self, "feq.s");
self.debug(inst, "feq.s");
self.xregs.write(
rd,
if self.fregs.read(rs1) == self.fregs.read(rs2) {
1
} else {
0
},
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x51 => {
match funct3 {
0x0 => {
// fle.d
inst_count!(self, "fle.d");
self.debug(inst, "fle.d");
self.xregs.write(
rd,
if self.fregs.read(rs1) <= self.fregs.read(rs2) {
1
} else {
0
},
);
}
0x1 => {
// flt.d
inst_count!(self, "flt.d");
self.debug(inst, "flt.d");
self.xregs.write(
rd,
if self.fregs.read(rs1) < self.fregs.read(rs2) {
1
} else {
0
},
);
}
0x2 => {
// feq.d
inst_count!(self, "feq.d");
self.debug(inst, "feq.d");
self.xregs.write(
rd,
if self.fregs.read(rs1) == self.fregs.read(rs2) {
1
} else {
0
},
);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x60 => {
match rs2 {
0x0 => {
// fcvt.w.s
inst_count!(self, "fcvt.w.s");
self.debug(inst, "fcvt.w.s");
self.xregs.write(
rd,
((self.fregs.read(rs1) as f32).round() as i32) as u64,
);
}
0x1 => {
// fcvt.wu.s
inst_count!(self, "fcvt.wu.s");
self.debug(inst, "fcvt.wu.s");
self.xregs.write(
rd,
(((self.fregs.read(rs1) as f32).round() as u32) as i32) as u64,
);
}
0x2 => {
// fcvt.l.s
inst_count!(self, "fcvt.l.s");
self.debug(inst, "fcvt.l.s");
self.xregs
.write(rd, (self.fregs.read(rs1) as f32).round() as u64);
}
0x3 => {
// fcvt.lu.s
inst_count!(self, "fcvt.lu.s");
self.debug(inst, "fcvt.lu.s");
self.xregs
.write(rd, (self.fregs.read(rs1) as f32).round() as u64);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x61 => {
match rs2 {
0x0 => {
// fcvt.w.d
inst_count!(self, "fcvt.w.d");
self.debug(inst, "fcvt.w.d");
self.xregs
.write(rd, (self.fregs.read(rs1).round() as i32) as u64);
}
0x1 => {
// fcvt.wu.d
inst_count!(self, "fcvt.wu.d");
self.debug(inst, "fcvt.wu.d");
self.xregs.write(
rd,
((self.fregs.read(rs1).round() as u32) as i32) as u64,
);
}
0x2 => {
// fcvt.l.d
inst_count!(self, "fcvt.l.d");
self.debug(inst, "fcvt.l.d");
self.xregs.write(rd, self.fregs.read(rs1).round() as u64);
}
0x3 => {
// fcvt.lu.d
inst_count!(self, "fcvt.lu.d");
self.debug(inst, "fcvt.lu.d");
self.xregs.write(rd, self.fregs.read(rs1).round() as u64);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x68 => {
match rs2 {
0x0 => {
// fcvt.s.w
inst_count!(self, "fcvt.s.w");
self.debug(inst, "fcvt.s.w");
self.fregs
.write(rd, ((self.xregs.read(rs1) as i32) as f32) as f64);
}
0x1 => {
// fcvt.s.wu
inst_count!(self, "fcvt.s.wu");
self.debug(inst, "fcvt.s.wu");
self.fregs
.write(rd, ((self.xregs.read(rs1) as u32) as f32) as f64);
}
0x2 => {
// fcvt.s.l
inst_count!(self, "fcvt.s.l");
self.debug(inst, "fcvt.s.l");
self.fregs.write(rd, (self.xregs.read(rs1) as f32) as f64);
}
0x3 => {
// fcvt.s.lu
inst_count!(self, "fcvt.s.lu");
self.debug(inst, "fcvt.s.lu");
self.fregs
.write(rd, ((self.xregs.read(rs1) as u64) as f32) as f64);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x69 => {
match rs2 {
0x0 => {
// fcvt.d.w
inst_count!(self, "fcvt.d.w");
self.debug(inst, "fcvt.d.w");
self.fregs.write(rd, (self.xregs.read(rs1) as i32) as f64);
}
0x1 => {
// fcvt.d.wu
inst_count!(self, "fcvt.d.wu");
self.debug(inst, "fcvt.d.wu");
self.fregs.write(rd, (self.xregs.read(rs1) as u32) as f64);
}
0x2 => {
// fcvt.d.l
inst_count!(self, "fcvt.d.l");
self.debug(inst, "fcvt.d.l");
self.fregs.write(rd, self.xregs.read(rs1) as f64);
}
0x3 => {
// fcvt.d.lu
inst_count!(self, "fcvt.d.lu");
self.debug(inst, "fcvt.d.lu");
self.fregs.write(rd, self.xregs.read(rs1) as f64);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x70 => {
match funct3 {
0x0 => {
// fmv.x.w
inst_count!(self, "fmv.x.w");
self.debug(inst, "fmv.x.w");
self.xregs.write(
rd,
(self.fregs.read(rs1).to_bits() & 0xffffffff) as i32 as i64
as u64,
);
}
0x1 => {
// fclass.s
inst_count!(self, "fclass.s");
self.debug(inst, "fclass.s");
let f = self.fregs.read(rs1);
match f.classify() {
FpCategory::Infinite => {
self.xregs
.write(rd, if f.is_sign_negative() { 0 } else { 7 });
}
FpCategory::Normal => {
self.xregs
.write(rd, if f.is_sign_negative() { 1 } else { 6 });
}
FpCategory::Subnormal => {
self.xregs
.write(rd, if f.is_sign_negative() { 2 } else { 5 });
}
FpCategory::Zero => {
self.xregs
.write(rd, if f.is_sign_negative() { 3 } else { 4 });
}
// don't support a signaling nan, only support a quiet nan.
FpCategory::Nan => self.xregs.write(rd, 9),
}
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x71 => {
match funct3 {
0x0 => {
// fmv.x.d
inst_count!(self, "fmv.x.d");
self.debug(inst, "fmv.x.d");
// "FMV.X.D and FMV.D.X do not modify the bits being transferred"
self.xregs.write(rd, self.fregs.read(rs1).to_bits());
}
0x1 => {
// fclass.d
inst_count!(self, "fclass.d");
self.debug(inst, "fclass.d");
let f = self.fregs.read(rs1);
match f.classify() {
FpCategory::Infinite => {
self.xregs
.write(rd, if f.is_sign_negative() { 0 } else { 7 });
}
FpCategory::Normal => {
self.xregs
.write(rd, if f.is_sign_negative() { 1 } else { 6 });
}
FpCategory::Subnormal => {
self.xregs
.write(rd, if f.is_sign_negative() { 2 } else { 5 });
}
FpCategory::Zero => {
self.xregs
.write(rd, if f.is_sign_negative() { 3 } else { 4 });
}
// don't support a signaling nan, only support a quiet nan.
FpCategory::Nan => self.xregs.write(rd, 9),
}
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x78 => {
// fmv.w.x
inst_count!(self, "fmv.w.x");
self.debug(inst, "fmv.w.x");
self.fregs
.write(rd, f64::from_bits(self.xregs.read(rs1) & 0xffffffff));
}
0x79 => {
// fmv.d.x
inst_count!(self, "fmv.d.x");
self.debug(inst, "fmv.d.x");
// "FMV.X.D and FMV.D.X do not modify the bits being transferred"
self.fregs.write(rd, f64::from_bits(self.xregs.read(rs1)));
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x63 => {
// RV32I
// imm[12|10:5|4:1|11] = inst[31|30:25|11:8|7]
let imm = (((inst & 0x80000000) as i32 as i64 >> 19) as u64)
| ((inst & 0x80) << 4) // imm[11]
| ((inst >> 20) & 0x7e0) // imm[10:5]
| ((inst >> 7) & 0x1e); // imm[4:1]
match funct3 {
0x0 => {
// beq
inst_count!(self, "beq");
self.debug(inst, "beq");
if self.xregs.read(rs1) == self.xregs.read(rs2) {
self.pc = self.pc.wrapping_add(imm).wrapping_sub(4);
}
}
0x1 => {
// bne
inst_count!(self, "bne");
self.debug(inst, "bne");
if self.xregs.read(rs1) != self.xregs.read(rs2) {
self.pc = self.pc.wrapping_add(imm).wrapping_sub(4);
}
}
0x4 => {
// blt
inst_count!(self, "blt");
self.debug(inst, "blt");
if (self.xregs.read(rs1) as i64) < (self.xregs.read(rs2) as i64) {
self.pc = self.pc.wrapping_add(imm).wrapping_sub(4);
}
}
0x5 => {
// bge
inst_count!(self, "bge");
self.debug(inst, "bge");
if (self.xregs.read(rs1) as i64) >= (self.xregs.read(rs2) as i64) {
self.pc = self.pc.wrapping_add(imm).wrapping_sub(4);
}
}
0x6 => {
// bltu
inst_count!(self, "bltu");
self.debug(inst, "bltu");
if self.xregs.read(rs1) < self.xregs.read(rs2) {
self.pc = self.pc.wrapping_add(imm).wrapping_sub(4);
}
}
0x7 => {
// bgeu
inst_count!(self, "bgeu");
self.debug(inst, "bgeu");
if self.xregs.read(rs1) >= self.xregs.read(rs2) {
self.pc = self.pc.wrapping_add(imm).wrapping_sub(4);
}
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x67 => {
// jalr
inst_count!(self, "jalr");
self.debug(inst, "jalr");
let t = self.pc.wrapping_add(4);
let offset = (inst as i32 as i64) >> 20;
let target = ((self.xregs.read(rs1) as i64).wrapping_add(offset)) & !1;
self.pc = (target as u64).wrapping_sub(4);
self.xregs.write(rd, t);
}
0x6F => {
// jal
inst_count!(self, "jal");
self.debug(inst, "jal");
self.xregs.write(rd, self.pc.wrapping_add(4));
// imm[20|10:1|11|19:12] = inst[31|30:21|20|19:12]
let offset = (((inst & 0x80000000) as i32 as i64 >> 11) as u64) // imm[20]
| (inst & 0xff000) // imm[19:12]
| ((inst >> 9) & 0x800) // imm[11]
| ((inst >> 20) & 0x7fe); // imm[10:1]
self.pc = self.pc.wrapping_add(offset).wrapping_sub(4);
}
0x73 => {
// RV32I, RVZicsr, and supervisor ISA
let csr_addr = ((inst >> 20) & 0xfff) as u16;
match funct3 {
0x0 => {
match (rs2, funct7) {
(0x0, 0x0) => {
// ecall
inst_count!(self, "ecall");
self.debug(inst, "ecall");
// Makes a request of the execution environment by raising an
// environment call exception.
match self.mode {
Mode::User => {
return Err(Exception::EnvironmentCallFromUMode);
}
Mode::Supervisor => {
return Err(Exception::EnvironmentCallFromSMode);
}
Mode::Machine => {
return Err(Exception::EnvironmentCallFromMMode);
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
(0x1, 0x0) => {
// ebreak
inst_count!(self, "ebreak");
self.debug(inst, "ebreak");
// Makes a request of the debugger bu raising a Breakpoint
// exception.
return Err(Exception::Breakpoint);
}
(0x2, 0x0) => {
// uret
inst_count!(self, "uret");
self.debug(inst, "uret");
panic!("uret: not implemented yet. pc {}", self.pc);
}
(0x2, 0x8) => {
// sret
inst_count!(self, "sret");
self.debug(inst, "sret");
// "The RISC-V Reader" book says:
// "Returns from a supervisor-mode exception handler. Sets the pc to
// CSRs[sepc], the privilege mode to CSRs[sstatus].SPP,
// CSRs[sstatus].SIE to CSRs[sstatus].SPIE, CSRs[sstatus].SPIE to
// 1, and CSRs[sstatus].SPP to 0.", but the implementation in QEMU
// and Spike use `mstatus` instead of `sstatus`.
// Set the program counter to the supervisor exception program
// counter (SEPC).
self.pc = self.state.read(SEPC).wrapping_sub(4);
// TODO: Check TSR field
// Set the current privileged mode depending on a previous
// privilege mode for supervisor mode (SPP, 8).
self.mode = match self.state.read_sstatus(XSTATUS_SPP) {
0b0 => Mode::User,
0b1 => {
// If SPP != M-mode, SRET also sets MPRV=0.
self.state.write_mstatus(MSTATUS_MPRV, 0);
Mode::Supervisor
}
_ => Mode::Debug,
};
// Read a previous interrupt-enable bit for supervisor mode (SPIE,
// 5), and set a global interrupt-enable bit for supervisor mode
// (SIE, 1) to it.
self.state.write_sstatus(
XSTATUS_SIE,
self.state.read_sstatus(XSTATUS_SPIE),
);
// Set a previous interrupt-enable bit for supervisor mode (SPIE,
// 5) to 1.
self.state.write_sstatus(XSTATUS_SPIE, 1);
// Set a previous privilege mode for supervisor mode (SPP, 8) to 0.
self.state.write_sstatus(XSTATUS_SPP, 0);
}
(0x2, 0x18) => {
// mret
inst_count!(self, "mret");
self.debug(inst, "mret");
// "The RISC-V Reader" book says:
// "Returns from a machine-mode exception handler. Sets the pc to
// CSRs[mepc], the privilege mode to CSRs[mstatus].MPP,
// CSRs[mstatus].MIE to CSRs[mstatus].MPIE, and CSRs[mstatus].MPIE
// to 1; and, if user mode is supported, sets CSRs[mstatus].MPP to
// 0".
// Set the program counter to the machine exception program
// counter (MEPC).
self.pc = self.state.read(MEPC).wrapping_sub(4);
// Set the current privileged mode depending on a previous
// privilege mode for machine mode (MPP, 11..13).
self.mode = match self.state.read_mstatus(MSTATUS_MPP) {
0b00 => {
// If MPP != M-mode, MRET also sets MPRV=0.
self.state.write_mstatus(MSTATUS_MPRV, 0);
Mode::User
}
0b01 => {
// If MPP != M-mode, MRET also sets MPRV=0.
self.state.write_mstatus(MSTATUS_MPRV, 0);
Mode::Supervisor
}
0b11 => Mode::Machine,
_ => Mode::Debug,
};
// Read a previous interrupt-enable bit for machine mode (MPIE, 7),
// and set a global interrupt-enable bit for machine mode (MIE, 3)
// to it.
self.state.write_mstatus(
MSTATUS_MIE,
self.state.read_mstatus(MSTATUS_MPIE),
);
// Set a previous interrupt-enable bit for machine mode (MPIE, 7)
// to 1.
self.state.write_mstatus(MSTATUS_MPIE, 1);
// Set a previous privilege mode for machine mode (MPP, 11..13) to
// 0.
self.state.write_mstatus(MSTATUS_MPP, Mode::User as u64);
}
(0x5, 0x8) => {
// wfi
inst_count!(self, "wfi");
self.debug(inst, "wfi");
// "provides a hint to the implementation that the current
// hart can be stalled until an interrupt might need servicing."
self.idle = true;
}
(_, 0x9) => {
// sfence.vma
inst_count!(self, "sfence.vma");
self.debug(inst, "sfence.vma");
// "SFENCE.VMA is used to synchronize updates to in-memory
// memory-management data structures with current execution"
}
(_, 0x11) => {
// hfence.bvma
inst_count!(self, "hfence.bvma");
self.debug(inst, "hfence.bvma");
}
(_, 0x51) => {
// hfence.gvma
inst_count!(self, "hfence.gvma");
self.debug(inst, "hfence.gvma");
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
0x1 => {
// csrrw
inst_count!(self, "csrrw");
self.debug(inst, "csrrw");
let t = self.state.read(csr_addr);
self.state.write(csr_addr, self.xregs.read(rs1));
self.xregs.write(rd, t);
if csr_addr == SATP {
self.update_paging();
}
}
0x2 => {
// csrrs
inst_count!(self, "csrrs");
self.debug(inst, "csrrs");
let t = self.state.read(csr_addr);
self.state.write(csr_addr, t | self.xregs.read(rs1));
self.xregs.write(rd, t);
if csr_addr == SATP {
self.update_paging();
}
}
0x3 => {
// csrrc
inst_count!(self, "csrrc");
self.debug(inst, "csrrc");
let t = self.state.read(csr_addr);
self.state.write(csr_addr, t & (!self.xregs.read(rs1)));
self.xregs.write(rd, t);
if csr_addr == SATP {
self.update_paging();
}
}
0x5 => {
// csrrwi
inst_count!(self, "csrrwi");
self.debug(inst, "csrrwi");
let zimm = rs1;
self.xregs.write(rd, self.state.read(csr_addr));
self.state.write(csr_addr, zimm);
if csr_addr == SATP {
self.update_paging();
}
}
0x6 => {
// csrrsi
inst_count!(self, "csrrsi");
self.debug(inst, "csrrsi");
let zimm = rs1;
let t = self.state.read(csr_addr);
self.state.write(csr_addr, t | zimm);
self.xregs.write(rd, t);
if csr_addr == SATP {
self.update_paging();
}
}
0x7 => {
// csrrci
inst_count!(self, "csrrci");
self.debug(inst, "csrrci");
let zimm = rs1;
let t = self.state.read(csr_addr);
self.state.write(csr_addr, t & (!zimm));
self.xregs.write(rd, t);
if csr_addr == SATP {
self.update_paging();
}
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
}
_ => {
return Err(Exception::IllegalInstruction(inst));
}
}
Ok(())
}
}