diff --git a/src/bus.rs b/src/bus.rs index 33ce2bf..4fc91ee 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -46,59 +46,59 @@ const DRAM_END: u64 = DRAM_BASE + DRAM_SIZE; /// The system bus. pub struct Bus { - pub clint: Clint, - pub plic: Plic, - pub uart: Uart, - pub virtio: Virtio, - dram: Dram, - pub rom: Rom, + pub clint: Clint, + pub plic: Plic, + pub uart: Uart, + pub virtio: Virtio, + dram: Dram, + pub rom: Rom, } impl Bus { - /// Create a new bus object. - pub fn new() -> Bus { - Self { - clint: Clint::new(), - plic: Plic::new(), - uart: Uart::new(), - virtio: Virtio::new(), - dram: Dram::new(), - rom: Rom::new(), - } + /// Create a new bus object. + pub fn new() -> Bus { + Self { + clint: Clint::new(), + plic: Plic::new(), + uart: Uart::new(), + virtio: Virtio::new(), + dram: Dram::new(), + rom: Rom::new(), } + } - /// Set the binary data to the memory. - pub fn initialize_dram(&mut self, data: Vec) { - self.dram.initialize(data); - } + /// Set the binary data to the memory. + pub fn initialize_dram(&mut self, data: Vec) { + self.dram.initialize(data); + } - /// Set the binary data to the virtIO disk. - pub fn initialize_disk(&mut self, data: Vec) { - self.virtio.initialize(data); - } + /// Set the binary data to the virtIO disk. + pub fn initialize_disk(&mut self, data: Vec) { + self.virtio.initialize(data); + } - /// Load a `size`-bit data from the device that connects to the system bus. - pub fn read(&mut self, addr: u64, size: u8) -> Result { - match addr { - MROM_BASE..=MROM_END => self.rom.read(addr, size), - CLINT_BASE..=CLINT_END => self.clint.read(addr, size), - PLIC_BASE..=PLIC_END => self.plic.read(addr, size), - UART_BASE..=UART_END => self.uart.read(addr, size), - VIRTIO_BASE..=VIRTIO_END => self.virtio.read(addr, size), - DRAM_BASE..=DRAM_END => self.dram.read(addr, size), - _ => Err(Exception::LoadAccessFault), - } + /// Load a `size`-bit data from the device that connects to the system bus. + pub fn read(&mut self, addr: u64, size: u8) -> Result { + match addr { + MROM_BASE..=MROM_END => self.rom.read(addr, size), + CLINT_BASE..=CLINT_END => self.clint.read(addr, size), + PLIC_BASE..=PLIC_END => self.plic.read(addr, size), + UART_BASE..=UART_END => self.uart.read(addr, size), + VIRTIO_BASE..=VIRTIO_END => self.virtio.read(addr, size), + DRAM_BASE..=DRAM_END => self.dram.read(addr, size), + _ => Err(Exception::LoadAccessFault), } + } - /// Store a `size`-bit data to the device that connects to the system bus. - pub fn write(&mut self, addr: u64, value: u64, size: u8) -> Result<(), Exception> { - match addr { - CLINT_BASE..=CLINT_END => self.clint.write(addr, value, size), - PLIC_BASE..=PLIC_END => self.plic.write(addr, value, size), - UART_BASE..=UART_END => self.uart.write(addr, value as u8, size), - VIRTIO_BASE..=VIRTIO_END => self.virtio.write(addr, value as u32, size), - DRAM_BASE..=DRAM_END => self.dram.write(addr, value, size), - _ => Err(Exception::StoreAMOAccessFault), - } + /// Store a `size`-bit data to the device that connects to the system bus. + pub fn write(&mut self, addr: u64, value: u64, size: u8) -> Result<(), Exception> { + match addr { + CLINT_BASE..=CLINT_END => self.clint.write(addr, value, size), + PLIC_BASE..=PLIC_END => self.plic.write(addr, value, size), + UART_BASE..=UART_END => self.uart.write(addr, value as u8, size), + VIRTIO_BASE..=VIRTIO_END => self.virtio.write(addr, value as u32, size), + DRAM_BASE..=DRAM_END => self.dram.write(addr, value, size), + _ => Err(Exception::StoreAMOAccessFault), } + } } diff --git a/src/cpu.rs b/src/cpu.rs index 759dc40..2eb1257 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -7,15 +7,15 @@ use std::fmt; use std::num::FpCategory; use crate::{ - bus::{Bus, DRAM_BASE}, - csr::*, - devices::{ - uart::UART_IRQ, - virtio_blk::{Virtio, VIRTIO_IRQ}, - }, - dram::DRAM_SIZE, - exception::Exception, - interrupt::Interrupt, + bus::{Bus, DRAM_BASE}, + csr::*, + devices::{ + uart::UART_IRQ, + virtio_blk::{Virtio, VIRTIO_IRQ}, + }, + dram::DRAM_SIZE, + exception::Exception, + interrupt::Interrupt, }; /// The number of registers. @@ -37,151 +37,149 @@ pub const DOUBLEWORD: u8 = 64; 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; - } - }; + ($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, + /// 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, + User = 0b00, + Supervisor = 0b01, + Machine = 0b11, + Debug, } /// The integer registers. #[derive(Debug)] pub struct XRegisters { - xregs: [u64; REGISTERS_COUNT], + 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; - } + /// 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) + 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], + fregs: [f64; REGISTERS_COUNT], } impl FRegisters { - /// Create a new `FRegisters` object. - pub fn new() -> Self { - Self { - fregs: [0.0; REGISTERS_COUNT], - } + /// 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] - } + /// 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; - } + /// 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", // fa2–7: FP arguments - " fa2", " fa3", " fa4", " fa5", " fa6", " fa7", - // fs2–11: FP saved registers - " fs2", " fs3", " fs4", " fs5", " fs6", " fs7", " fs8", " fs9", "fs10", "fs11", - // ft8–11: FP temporaries - " ft8", " ft9", "ft10", "ft11", - ]; - let mut output = String::from(""); - for i in (0..REGISTERS_COUNT).step_by(4) { - output = format!( + 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", // fa2–7: FP arguments + " fa2", " fa3", " fa4", " fa5", " fa6", " fa7", // fs2–11: FP saved registers + " fs2", " fs3", " fs4", " fs5", " fs6", " fs7", " fs8", " fs9", "fs10", "fs11", + // ft8–11: 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!( @@ -202,97 +200,84 @@ impl fmt::Display for FRegisters { prec=8, ) ); - } - // Remove the first new line. - output.remove(0); - write!(f, "{}", output) } + // 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 { - /// 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, - /// 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, - /// The count flag. Count the number of each instruction executed. - pub is_count: bool, - /// Previous instruction. This is for debug. - pub pre_inst: u64, + /// 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, + /// 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, + /// 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 Cpu { - /// Create a new `Cpu` object. - pub fn new() -> Cpu { - Cpu { - xregs: XRegisters::new(), - fregs: FRegisters::new(), - pc: 0, - state: State::new(), - mode: Mode::Machine, - bus: Bus::new(), - enable_paging: false, - page_table: 0, - reservation_set: Vec::new(), - idle: false, - inst_counter: BTreeMap::new(), - is_count: false, - pre_inst: 0, - } + /// Create a new `Cpu` object. + pub fn new() -> Cpu { + Cpu { + xregs: XRegisters::new(), + fregs: FRegisters::new(), + pc: 0, + state: State::new(), + mode: Mode::Machine, + bus: Bus::new(), + 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; - */ - } + 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!( - "[machine] {} pc: {:#x}, inst: {:#x}, is_inst 16? {} x[0x1c] {:#x}", + "[kernel] {} pc: {:#x}, inst: {:#x}, is_inst 16? {} x[0x1c] {:#x}", name, self.pc, inst, @@ -300,779 +285,760 @@ impl Cpu { inst & 0b11 == 0 || inst & 0b11 == 1 || inst & 0b11 == 2, self.xregs.read(0x1c), ); + return; */ } - /// 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); - } + 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 { - // 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; - } - } - _ => {} + } + + /// Check interrupt flags for all devices that can interrupt. + pub fn check_pending_interrupt(&mut self) -> Option { + // 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; } - - // 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; + } + Mode::Supervisor => { + // Check if the SIE bit is enabled. + if self.state.read_sstatus(XSTATUS_SIE) == 0 { + return None; } + } + _ => {} + } - 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); - } + // 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; + } - // 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 hart’s - // 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 hart’s current privilege mode equals the - // delegated privilege mode (S or U) and that mode’s 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); - } + 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); + } - return None; + // 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 hart’s + // 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 hart’s current privilege mode equals the + // delegated privilege mode (S or U) and that mode’s 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); } - /// 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; + return None; + } - // Read the MODE field, which selects the current address-translation scheme. - let mode = self.state.read_bits(SATP, 60..); + /// 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; - // 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; - } - } + // Read the MODE field, which selects the current address-translation scheme. + let mode = self.state.read_bits(SATP, 60..); - /// Translate a virtual address to a physical address for the paged virtual-memory system. - fn translate(&mut self, addr: u64, access_type: AccessType) -> Result { - if !self.enable_paging || self.mode == Mode::Machine { - return Ok(addr); - } + // 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; + } + } - // 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)), - } - } + /// Translate a virtual address to a physical address for the paged virtual-memory system. + fn translate(&mut self, addr: u64, access_type: AccessType) -> Result { + if !self.enable_paging || self.mode == Mode::Machine { + return Ok(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)), - } - } + // 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)), } - // 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[i−1: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)), - } - } - } + } + + // 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)), } - - // 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)?; + } + } + // 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[i−1: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)), + } } + } + } - // 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[i−1:0] = - // va.vpn[i−1:0]. - // • pa.ppn[LEVELS−1:i] = pte.ppn[LEVELS−1: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)), - }, - } + // 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)?; } - /// 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 { - 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, - }; - } + // 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[i−1:0] = + // va.vpn[i−1:0]. + // • pa.ppn[LEVELS−1:i] = pte.ppn[LEVELS−1: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 { + 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); + 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; - } + if self.state.read_mstatus(MSTATUS_MPRV) == 1 { + self.mode = previous_mode; + } - result + 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, + }; } - /// 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); + } - // "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); - 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; + } - if self.state.read_mstatus(MSTATUS_MPRV) == 1 { - self.mode = previous_mode; - } + result + } - result + /// Fetch the `size`-bit next instruction from the memory at the current program counter. + pub fn fetch(&mut self, size: u8) -> Result { + if size != HALFWORD && size != WORD { + return Err(Exception::InstructionAccessFault); } - /// Fetch the `size`-bit next instruction from the memory at the current program counter. - pub fn fetch(&mut self, size: u8) -> Result { - if size != HALFWORD && size != WORD { - return Err(Exception::InstructionAccessFault); - } - - let p_pc = self.translate(self.pc, AccessType::Instruction)?; + 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), - } + // 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 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 { + // WFI is called and pending interrupts don't exist. + if self.idle { + return Ok(0); } - /// Execute an instruction. Raises an exception if something is wrong, otherwise, returns - /// the instruction executed in this cycle. - pub fn execute(&mut self) -> Result { - // 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)); } - - // 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) + 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; + } } - - /// 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] + 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] + 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] + 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] + 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] + 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] + 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] + 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)); - } - } + 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)); } - 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] + } + 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] + 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] @@ -1081,2491 +1047,2397 @@ impl Cpu { | ((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] + // 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] + 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)); - } - } + 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); } - 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] + } + _ => { + 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] + 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] + 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] + 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] + 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] + 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)); - } - } + 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)); + } } - 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)); - } - } + } + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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); } - 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)); + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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); } - 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)); - } - }; + } + (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); } - 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); + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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); } - 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)); - } - } + 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)); - } + 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)); + } } - 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) + } + 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)); - } - } + 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); } - 0x67 => { - // jalr - inst_count!(self, "jalr"); - self.debug(inst, "jalr"); + } + 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 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; + 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.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, t); + } + 0x6F => { + // jal + inst_count!(self, "jal"); + self.debug(inst, "jal"); - self.xregs.write(rd, self.pc.wrapping_add(4)); + 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] + // 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)); - } + 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)); + } } - Ok(()) + } + _ => { + return Err(Exception::IllegalInstruction(inst)); + } } + Ok(()) + } } diff --git a/src/csr.rs b/src/csr.rs index a2634ef..c91b80e 100644 --- a/src/csr.rs +++ b/src/csr.rs @@ -82,15 +82,15 @@ const SSTATUS_MXR_MASK: u64 = 0x80000; // sstatus[19] const SSTATUS_UXL_MASK: u64 = 0x3_00000000; // sstatus[33:32] const SSTATUS_SD_MASK: u64 = 0x80000000_00000000; // sstatus[63] const SSTATUS_MASK: u64 = SSTATUS_SIE_MASK - | SSTATUS_SPIE_MASK - | SSTATUS_UBE_MASK - | SSTATUS_SPP_MASK - | SSTATUS_FS_MASK - | SSTATUS_XS_MASK - | SSTATUS_SUM_MASK - | SSTATUS_MXR_MASK - | SSTATUS_UXL_MASK - | SSTATUS_SD_MASK; + | SSTATUS_SPIE_MASK + | SSTATUS_UBE_MASK + | SSTATUS_SPP_MASK + | SSTATUS_FS_MASK + | SSTATUS_XS_MASK + | SSTATUS_SUM_MASK + | SSTATUS_MXR_MASK + | SSTATUS_UXL_MASK + | SSTATUS_SD_MASK; /// Global interrupt-enable bit for supervisor mode. pub const XSTATUS_SIE: CsrFieldRange = 1..=1; /// Previous interrupt-enable bit for supervisor mode. @@ -171,51 +171,51 @@ pub const MEIP_BIT: u64 = 1 << 11; /// The state to contains all the CSRs. pub struct State { - csrs: [u64; CSR_SIZE], + csrs: [u64; CSR_SIZE], } impl fmt::Display for State { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - format!( - "{}\n{}\n{}", - format!( - "mstatus={:>#18x} mtvec={:>#18x} mepc={:>#18x}\n mcause={:>#18x} medeleg={:>#18x} mideleg={:>#18x}", - self.read(MSTATUS), - self.read(MTVEC), - self.read(MEPC), - self.read(MCAUSE), - self.read(MEDELEG), - self.read(MIDELEG), - ), - format!( - "sstatus={:>#18x} stvec={:>#18x} sepc={:>#18x}\n scause={:>#18x} sedeleg={:>#18x} sideleg={:>#18x}", - self.read(SSTATUS), - self.read(STVEC), - self.read(SEPC), - self.read(SCAUSE), - self.read(SEDELEG), - self.read(SIDELEG), - ), - format!( - "ustatus={:>#18x} utvec={:>#18x} uepc={:>#18x}\n ucause={:>#18x}", - self.read(USTATUS), - self.read(UTVEC), - self.read(UEPC), - self.read(UCAUSE), - ), - ) - ) - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + format!( + "{}\n{}\n{}", + format!( + "mstatus={:>#18x} mtvec={:>#18x} mepc={:>#18x}\n mcause={:>#18x} medeleg={:>#18x} mideleg={:>#18x}", + self.read(MSTATUS), + self.read(MTVEC), + self.read(MEPC), + self.read(MCAUSE), + self.read(MEDELEG), + self.read(MIDELEG), + ), + format!( + "sstatus={:>#18x} stvec={:>#18x} sepc={:>#18x}\n scause={:>#18x} sedeleg={:>#18x} sideleg={:>#18x}", + self.read(SSTATUS), + self.read(STVEC), + self.read(SEPC), + self.read(SCAUSE), + self.read(SEDELEG), + self.read(SIDELEG), + ), + format!( + "ustatus={:>#18x} utvec={:>#18x} uepc={:>#18x}\n ucause={:>#18x}", + self.read(USTATUS), + self.read(UTVEC), + self.read(UEPC), + self.read(UCAUSE), + ), + ) + ) + } } impl State { - /// Create a new `state` object. - pub fn new() -> Self { - let mut csrs = [0; CSR_SIZE]; - let misa: u64 = (2 << 62) | // MXL[1:0]=2 (XLEN is 64) + /// Create a new `state` object. + pub fn new() -> Self { + let mut csrs = [0; CSR_SIZE]; + let misa: u64 = (2 << 62) | // MXL[1:0]=2 (XLEN is 64) (1 << 20) | // Extensions[20] (User mode implemented) (1 << 18) | // Extensions[18] (Supervisor mode implemented) (1 << 12) | // Extensions[12] (Integer Multiply/Divide extension) @@ -224,151 +224,150 @@ impl State { (1 << 3) | // Extensions[3] (Double-precision floating-point extension) (1 << 2) | // Extensions[2] (Compressed extension) 1; // Extensions[0] (Atomic extension) - csrs[MISA as usize] = misa; - - Self { csrs } + csrs[MISA as usize] = misa; + + Self { csrs } + } + + /// Increment the value in the TIME register. + pub fn increment_time(&mut self) { + self.csrs[TIME as usize] = self.csrs[TIME as usize].wrapping_add(1); + } + + /// Read the val from the CSR. + pub fn read(&self, addr: CsrAddress) -> u64 { + // 4.1 Supervisor CSRs + // "The supervisor should only view CSR state that should be visible to a supervisor-level + // operating system. In particular, there is no information about the existence (or + // non-existence) of higher privilege levels (machine level or other) visible in the CSRs + // accessible by the supervisor. Many supervisor CSRs are a subset of the equivalent + // machine-mode CSR, and the machinemode chapter should be read first to help understand + // the supervisor-level CSR descriptions." + match addr { + SSTATUS => self.csrs[MSTATUS as usize] & SSTATUS_MASK, + SIE => self.csrs[MIE as usize] & self.csrs[MIDELEG as usize], + SIP => self.csrs[MIP as usize] & self.csrs[MIDELEG as usize], + _ => self.csrs[addr as usize], } - - /// Increment the value in the TIME register. - pub fn increment_time(&mut self) { - self.csrs[TIME as usize] = self.csrs[TIME as usize].wrapping_add(1); + } + + /// Write the val to the CSR. + pub fn write(&mut self, addr: CsrAddress, val: u64) { + // 4.1 Supervisor CSRs + // "The supervisor should only view CSR state that should be visible to a supervisor-level + // operating system. In particular, there is no information about the existence (or + // non-existence) of higher privilege levels (machine level or other) visible in the CSRs + // accessible by the supervisor. Many supervisor CSRs are a subset of the equivalent + // machine-mode CSR, and the machinemode chapter should be read first to help understand + // the supervisor-level CSR descriptions." + match addr { + MVENDORID => {} + MARCHID => {} + MIMPID => {} + MHARTID => {} + SSTATUS => { + self.csrs[MSTATUS as usize] = (self.csrs[MSTATUS as usize] & !SSTATUS_MASK) | (val & SSTATUS_MASK); + } + SIE => { + self.csrs[MIE as usize] = + (self.csrs[MIE as usize] & !self.csrs[MIDELEG as usize]) | (val & self.csrs[MIDELEG as usize]); + } + SIP => { + let mask = SSIP_BIT & self.csrs[MIDELEG as usize]; + self.csrs[MIP as usize] = (self.csrs[MIP as usize] & !mask) | (val & mask); + } + _ => self.csrs[addr as usize] = val, } + } - /// Read the val from the CSR. - pub fn read(&self, addr: CsrAddress) -> u64 { - // 4.1 Supervisor CSRs - // "The supervisor should only view CSR state that should be visible to a supervisor-level - // operating system. In particular, there is no information about the existence (or - // non-existence) of higher privilege levels (machine level or other) visible in the CSRs - // accessible by the supervisor. Many supervisor CSRs are a subset of the equivalent - // machine-mode CSR, and the machinemode chapter should be read first to help understand - // the supervisor-level CSR descriptions." - match addr { - SSTATUS => self.csrs[MSTATUS as usize] & SSTATUS_MASK, - SIE => self.csrs[MIE as usize] & self.csrs[MIDELEG as usize], - SIP => self.csrs[MIP as usize] & self.csrs[MIDELEG as usize], - _ => self.csrs[addr as usize], - } + /// Read a bit from the CSR. + pub fn read_bit(&self, addr: CsrAddress, bit: usize) -> u64 { + if bit >= MXLEN { + // TODO: raise exception? } - /// Write the val to the CSR. - pub fn write(&mut self, addr: CsrAddress, val: u64) { - // 4.1 Supervisor CSRs - // "The supervisor should only view CSR state that should be visible to a supervisor-level - // operating system. In particular, there is no information about the existence (or - // non-existence) of higher privilege levels (machine level or other) visible in the CSRs - // accessible by the supervisor. Many supervisor CSRs are a subset of the equivalent - // machine-mode CSR, and the machinemode chapter should be read first to help understand - // the supervisor-level CSR descriptions." - match addr { - MVENDORID => {} - MARCHID => {} - MIMPID => {} - MHARTID => {} - SSTATUS => { - self.csrs[MSTATUS as usize] = - (self.csrs[MSTATUS as usize] & !SSTATUS_MASK) | (val & SSTATUS_MASK); - } - SIE => { - self.csrs[MIE as usize] = (self.csrs[MIE as usize] & !self.csrs[MIDELEG as usize]) - | (val & self.csrs[MIDELEG as usize]); - } - SIP => { - let mask = SSIP_BIT & self.csrs[MIDELEG as usize]; - self.csrs[MIP as usize] = (self.csrs[MIP as usize] & !mask) | (val & mask); - } - _ => self.csrs[addr as usize] = val, - } + if (self.read(addr) & (1 << bit)) != 0 { + 1 + } else { + 0 } + } - /// Read a bit from the CSR. - pub fn read_bit(&self, addr: CsrAddress, bit: usize) -> u64 { - if bit >= MXLEN { - // TODO: raise exception? - } - - if (self.read(addr) & (1 << bit)) != 0 { - 1 - } else { - 0 - } - } + /// Read a arbitrary length of bits from the CSR. + pub fn read_bits>(&self, addr: CsrAddress, range: T) -> u64 { + let range = to_range(&range, MXLEN); - /// Read a arbitrary length of bits from the CSR. - pub fn read_bits>(&self, addr: CsrAddress, range: T) -> u64 { - let range = to_range(&range, MXLEN); + if (range.start >= MXLEN) | (range.end > MXLEN) | (range.start >= range.end) { + // TODO: ranse exception? + } - if (range.start >= MXLEN) | (range.end > MXLEN) | (range.start >= range.end) { - // TODO: ranse exception? - } + // Bitmask for high bits. + let mut bitmask = 0; + if range.end != 64 { + bitmask = !0 << range.end; + } - // Bitmask for high bits. - let mut bitmask = 0; - if range.end != 64 { - bitmask = !0 << range.end; - } + // Shift away low bits. + (self.read(addr) as u64 & !bitmask) >> range.start + } - // Shift away low bits. - (self.read(addr) as u64 & !bitmask) >> range.start + /// Write a bit to the CSR. + pub fn write_bit(&mut self, addr: CsrAddress, bit: usize, val: u64) { + if bit >= MXLEN { + // TODO: raise exception? } - - /// Write a bit to the CSR. - pub fn write_bit(&mut self, addr: CsrAddress, bit: usize, val: u64) { - if bit >= MXLEN { - // TODO: raise exception? - } - if val > 1 { - // TODO: raise exception - } - - if val == 1 { - self.write(addr, self.read(addr) | 1 << bit); - } else if val == 0 { - self.write(addr, self.read(addr) & !(1 << bit)); - } + if val > 1 { + // TODO: raise exception } - /// Write an arbitrary length of bits to the CSR. - pub fn write_bits>(&mut self, addr: CsrAddress, range: T, val: u64) { - let range = to_range(&range, MXLEN); + if val == 1 { + self.write(addr, self.read(addr) | 1 << bit); + } else if val == 0 { + self.write(addr, self.read(addr) & !(1 << bit)); + } + } - if (range.start >= MXLEN) | (range.end > MXLEN) | (range.start >= range.end) { - // TODO: ranse exception? - } - if (val >> (range.end - range.start)) != 0 { - // TODO: raise exception - } + /// Write an arbitrary length of bits to the CSR. + pub fn write_bits>(&mut self, addr: CsrAddress, range: T, val: u64) { + let range = to_range(&range, MXLEN); - let bitmask = (!0 << range.end) | !(!0 << range.start); - // Set bits. - self.write(addr, (self.read(addr) & bitmask) | (val << range.start)) + if (range.start >= MXLEN) | (range.end > MXLEN) | (range.start >= range.end) { + // TODO: ranse exception? } - - /// Read bit(s) from a given field in the SSTATUS register. - pub fn read_sstatus(&self, range: CsrFieldRange) -> u64 { - self.read_bits(SSTATUS, range) + if (val >> (range.end - range.start)) != 0 { + // TODO: raise exception } - /// Read bit(s) from a given field in the MSTATUS register. - pub fn read_mstatus(&self, range: CsrFieldRange) -> u64 { - self.read_bits(MSTATUS, range) - } + let bitmask = (!0 << range.end) | !(!0 << range.start); + // Set bits. + self.write(addr, (self.read(addr) & bitmask) | (val << range.start)) + } - /// Write bit(s) to a given field in the SSTATUS register. - pub fn write_sstatus(&mut self, range: CsrFieldRange, val: u64) { - self.write_bits(SSTATUS, range, val); - } + /// Read bit(s) from a given field in the SSTATUS register. + pub fn read_sstatus(&self, range: CsrFieldRange) -> u64 { + self.read_bits(SSTATUS, range) + } - /// Write bit(s) to a given field in the MSTATUS register. - pub fn write_mstatus(&mut self, range: CsrFieldRange, val: u64) { - self.write_bits(MSTATUS, range, val); - } + /// Read bit(s) from a given field in the MSTATUS register. + pub fn read_mstatus(&self, range: CsrFieldRange) -> u64 { + self.read_bits(MSTATUS, range) + } - /// Reset all the CSRs. - pub fn reset(&mut self) { - self.csrs = [0; CSR_SIZE]; + /// Write bit(s) to a given field in the SSTATUS register. + pub fn write_sstatus(&mut self, range: CsrFieldRange, val: u64) { + self.write_bits(SSTATUS, range, val); + } - let misa: u64 = (2 << 62) | // MXL[1:0]=2 (XLEN is 64) + /// Write bit(s) to a given field in the MSTATUS register. + pub fn write_mstatus(&mut self, range: CsrFieldRange, val: u64) { + self.write_bits(MSTATUS, range, val); + } + + /// Reset all the CSRs. + pub fn reset(&mut self) { + self.csrs = [0; CSR_SIZE]; + + let misa: u64 = (2 << 62) | // MXL[1:0]=2 (XLEN is 64) (1 << 18) | // Extensions[18] (Supervisor mode implemented) (1 << 12) | // Extensions[12] (Integer Multiply/Divide extension) (1 << 8) | // Extensions[8] (RV32I/64I/128I base ISA) @@ -376,22 +375,22 @@ impl State { (1 << 3) | // Extensions[3] (Double-precision floating-point extension) (1 << 2) | // Extensions[2] (Compressed extension) 1; // Extensions[0] (Atomic extension) - self.csrs[MISA as usize] = misa; - } + self.csrs[MISA as usize] = misa; + } } /// Convert the val implement `RangeBounds` to the `Range` struct. fn to_range>(generic_range: &T, bit_length: usize) -> Range { - let start = match generic_range.start_bound() { - Bound::Excluded(&val) => val + 1, - Bound::Included(&val) => val, - Bound::Unbounded => 0, - }; - let end = match generic_range.end_bound() { - Bound::Excluded(&val) => val, - Bound::Included(&val) => val + 1, - Bound::Unbounded => bit_length, - }; - - start..end + let start = match generic_range.start_bound() { + Bound::Excluded(&val) => val + 1, + Bound::Included(&val) => val, + Bound::Unbounded => 0, + }; + let end = match generic_range.end_bound() { + Bound::Excluded(&val) => val, + Bound::Included(&val) => val + 1, + Bound::Unbounded => bit_length, + }; + + start..end } diff --git a/src/devices/clint.rs b/src/devices/clint.rs index 14ee554..ff0f23c 100644 --- a/src/devices/clint.rs +++ b/src/devices/clint.rs @@ -40,116 +40,116 @@ const MTIME_END: u64 = MTIME + 0x8; /// 0x4000 mtimecmp for hart 0 (8 bytes) /// 0xbff8 mtime (8 bytes) pub struct Clint { - /// Machine mode software interrupt pending register, used to assert a software interrupt for - /// a CPU. - msip: u32, - /// Memory mapped machine mode timer compare register, used to trigger an interrupt when - /// mtimecmp is greater than or equal to mtime. There is an mtimecmp dedicated to each CPU. - mtimecmp: u64, - /// Machine mode timer register which runs at a constant frequency. - mtime: u64, + /// Machine mode software interrupt pending register, used to assert a software interrupt for + /// a CPU. + msip: u32, + /// Memory mapped machine mode timer compare register, used to trigger an interrupt when + /// mtimecmp is greater than or equal to mtime. There is an mtimecmp dedicated to each CPU. + mtimecmp: u64, + /// Machine mode timer register which runs at a constant frequency. + mtime: u64, } impl Clint { - /// Create a new CLINT object. - pub fn new() -> Self { - Self { - msip: 0, - mtimecmp: 0, - mtime: 0, - } + /// Create a new CLINT object. + pub fn new() -> Self { + Self { + msip: 0, + mtimecmp: 0, + mtime: 0, + } + } + + /// Increment the mtimer register. It's not a real-time value. The MTIP bit (MIP, 7) is enabled + /// when `mtime` is greater than or equal to `mtimecmp`. + pub fn increment(&mut self, state: &mut State) { + self.mtime = self.mtime.wrapping_add(1); + // Sync TIME csr. + //state.write(TIME, self.mtime); + + if (self.msip & 1) != 0 { + // Enable the MSIP bit (MIP, 3). + state.write(MIP, state.read(MIP) | MSIP_BIT); } - /// Increment the mtimer register. It's not a real-time value. The MTIP bit (MIP, 7) is enabled - /// when `mtime` is greater than or equal to `mtimecmp`. - pub fn increment(&mut self, state: &mut State) { - self.mtime = self.mtime.wrapping_add(1); - // Sync TIME csr. - //state.write(TIME, self.mtime); - - if (self.msip & 1) != 0 { - // Enable the MSIP bit (MIP, 3). - state.write(MIP, state.read(MIP) | MSIP_BIT); - } - - // 3.1.10 Machine Timer Registers (mtime and mtimecmp) - // "The interrupt remains posted until mtimecmp becomes greater than mtime (typically as a - // result of writing mtimecmp)." - if self.mtimecmp > self.mtime { - // Clear the MTIP bit (MIP, 7). - state.write(MIP, state.read(MIP) & !MTIP_BIT); - } - - // 3.1.10 Machine Timer Registers (mtime and mtimecmp) - // "A timer interrupt becomes pending whenever mtime contains a value greater than or equal - // to mtimecmp, treating the values as unsigned integers." - if self.mtime >= self.mtimecmp { - // Enable the MTIP bit (MIP, 7). - state.write(MIP, state.read(MIP) | MTIP_BIT); - } + // 3.1.10 Machine Timer Registers (mtime and mtimecmp) + // "The interrupt remains posted until mtimecmp becomes greater than mtime (typically as a + // result of writing mtimecmp)." + if self.mtimecmp > self.mtime { + // Clear the MTIP bit (MIP, 7). + state.write(MIP, state.read(MIP) & !MTIP_BIT); } - /// Load `size`-bit data from a register located at `addr` in CLINT. - pub fn read(&self, addr: u64, size: u8) -> Result { - // `reg` is the value of a target register in CLINT and `offset` is the byte of the start - // position in the register. - let (reg, offset) = match addr { - MSIP..=MSIP_END => (self.msip as u64, addr - MSIP), - MTIMECMP..=MTIMECMP_END => (self.mtimecmp, addr - MTIMECMP), - MTIME..=MTIME_END => (self.mtime, addr - MTIME), - _ => return Err(Exception::LoadAccessFault), - }; - - match size { - BYTE => Ok((reg >> (offset * 8)) & 0xff), - HALFWORD => Ok((reg >> (offset * 8)) & 0xffff), - WORD => Ok((reg >> (offset * 8)) & 0xffffffff), - DOUBLEWORD => Ok(reg), - _ => return Err(Exception::LoadAccessFault), - } + // 3.1.10 Machine Timer Registers (mtime and mtimecmp) + // "A timer interrupt becomes pending whenever mtime contains a value greater than or equal + // to mtimecmp, treating the values as unsigned integers." + if self.mtime >= self.mtimecmp { + // Enable the MTIP bit (MIP, 7). + state.write(MIP, state.read(MIP) | MTIP_BIT); + } + } + + /// Load `size`-bit data from a register located at `addr` in CLINT. + pub fn read(&self, addr: u64, size: u8) -> Result { + // `reg` is the value of a target register in CLINT and `offset` is the byte of the start + // position in the register. + let (reg, offset) = match addr { + MSIP..=MSIP_END => (self.msip as u64, addr - MSIP), + MTIMECMP..=MTIMECMP_END => (self.mtimecmp, addr - MTIMECMP), + MTIME..=MTIME_END => (self.mtime, addr - MTIME), + _ => return Err(Exception::LoadAccessFault), + }; + + match size { + BYTE => Ok((reg >> (offset * 8)) & 0xff), + HALFWORD => Ok((reg >> (offset * 8)) & 0xffff), + WORD => Ok((reg >> (offset * 8)) & 0xffffffff), + DOUBLEWORD => Ok(reg), + _ => return Err(Exception::LoadAccessFault), + } + } + + /// Store `size`-bit data to a register located at `addr` in CLINT. + pub fn write(&mut self, addr: u64, value: u64, size: u8) -> Result<(), Exception> { + // `reg` is the value of a target register in CLINT and `offset` is the byte of the start + // position in the register. + let (mut reg, offset) = match addr { + MSIP..=MSIP_END => (self.msip as u64, addr - MSIP), + MTIMECMP..=MTIMECMP_END => (self.mtimecmp, addr - MTIMECMP), + MTIME..=MTIME_END => (self.mtime, addr - MTIME), + _ => return Err(Exception::StoreAMOAccessFault), + }; + + // Calculate the new value of the target register based on `size` and `offset`. + match size { + BYTE => { + // Clear the target byte. + reg = reg & (!(0xff << (offset * 8))); + // Set the new `value` to the target byte. + reg = reg | ((value & 0xff) << (offset * 8)); + } + HALFWORD => { + reg = reg & (!(0xffff << (offset * 8))); + reg = reg | ((value & 0xffff) << (offset * 8)); + } + WORD => { + reg = reg & (!(0xffffffff << (offset * 8))); + reg = reg | ((value & 0xffffffff) << (offset * 8)); + } + DOUBLEWORD => { + reg = value; + } + _ => return Err(Exception::StoreAMOAccessFault), } - /// Store `size`-bit data to a register located at `addr` in CLINT. - pub fn write(&mut self, addr: u64, value: u64, size: u8) -> Result<(), Exception> { - // `reg` is the value of a target register in CLINT and `offset` is the byte of the start - // position in the register. - let (mut reg, offset) = match addr { - MSIP..=MSIP_END => (self.msip as u64, addr - MSIP), - MTIMECMP..=MTIMECMP_END => (self.mtimecmp, addr - MTIMECMP), - MTIME..=MTIME_END => (self.mtime, addr - MTIME), - _ => return Err(Exception::StoreAMOAccessFault), - }; - - // Calculate the new value of the target register based on `size` and `offset`. - match size { - BYTE => { - // Clear the target byte. - reg = reg & (!(0xff << (offset * 8))); - // Set the new `value` to the target byte. - reg = reg | ((value & 0xff) << (offset * 8)); - } - HALFWORD => { - reg = reg & (!(0xffff << (offset * 8))); - reg = reg | ((value & 0xffff) << (offset * 8)); - } - WORD => { - reg = reg & (!(0xffffffff << (offset * 8))); - reg = reg | ((value & 0xffffffff) << (offset * 8)); - } - DOUBLEWORD => { - reg = value; - } - _ => return Err(Exception::StoreAMOAccessFault), - } - - // Store the new value to the target register. - match addr { - MSIP..=MSIP_END => self.msip = reg as u32, - MTIMECMP..=MTIMECMP_END => self.mtimecmp = reg, - MTIME..=MTIME_END => self.mtime = reg, - _ => return Err(Exception::StoreAMOAccessFault), - } - - Ok(()) + // Store the new value to the target register. + match addr { + MSIP..=MSIP_END => self.msip = reg as u32, + MTIMECMP..=MTIMECMP_END => self.mtimecmp = reg, + MTIME..=MTIME_END => self.mtime = reg, + _ => return Err(Exception::StoreAMOAccessFault), } + + Ok(()) + } } diff --git a/src/devices/mod.rs b/src/devices/mod.rs index 232a1ef..a7d4344 100644 --- a/src/devices/mod.rs +++ b/src/devices/mod.rs @@ -2,7 +2,5 @@ pub mod clint; pub mod plic; -pub mod virtio_blk; pub mod uart; - - +pub mod virtio_blk; diff --git a/src/devices/plic.rs b/src/devices/plic.rs index 2d0ee44..c9786b6 100644 --- a/src/devices/plic.rs +++ b/src/devices/plic.rs @@ -65,157 +65,157 @@ const SOURCE_NUM: u64 = 1024; /// The platform-level-interrupt controller (PLIC). pub struct Plic { - /// The interrupt priority for each interrupt source. A priority value of 0 is reserved to mean - /// "never interrupt" and effectively disables the interrupt. Priority 1 is the lowest active - /// priority, and priority 7 is the highest. - priority: [u32; SOURCE_NUM as usize], - /// Interrupt pending bits. If bit 1 is set, a global interrupt 1 is pending. A pending bit in - /// the PLIC core can be cleared by setting the associated enable bit then performing a claim. - pending: [u32; 32], - /// Interrupt Enable Bit of Interrupt Source #0 to #1023 for 2 contexts. - enable: [u32; 64], - /// The settings of a interrupt priority threshold of each context. The PLIC will mask all PLIC - /// interrupts of a priority less than or equal to `threshold`. - threshold: [u32; 2], - /// The ID of the highest priority pending interrupt or zero if there is no pending interrupt - /// for each context. - claim: [u32; 2], + /// The interrupt priority for each interrupt source. A priority value of 0 is reserved to mean + /// "never interrupt" and effectively disables the interrupt. Priority 1 is the lowest active + /// priority, and priority 7 is the highest. + priority: [u32; SOURCE_NUM as usize], + /// Interrupt pending bits. If bit 1 is set, a global interrupt 1 is pending. A pending bit in + /// the PLIC core can be cleared by setting the associated enable bit then performing a claim. + pending: [u32; 32], + /// Interrupt Enable Bit of Interrupt Source #0 to #1023 for 2 contexts. + enable: [u32; 64], + /// The settings of a interrupt priority threshold of each context. The PLIC will mask all PLIC + /// interrupts of a priority less than or equal to `threshold`. + threshold: [u32; 2], + /// The ID of the highest priority pending interrupt or zero if there is no pending interrupt + /// for each context. + claim: [u32; 2], } impl Plic { - /// Create a new PLIC object. - pub fn new() -> Self { - Self { - priority: [0; 1024], - pending: [0; 32], - enable: [0; 64], - threshold: [0; 2], - claim: [0; 2], - } + /// Create a new PLIC object. + pub fn new() -> Self { + Self { + priority: [0; 1024], + pending: [0; 32], + enable: [0; 64], + threshold: [0; 2], + claim: [0; 2], } - - /// Sets IRQ bit in `pending`. - pub fn update_pending(&mut self, irq: u64) { - let index = irq.wrapping_div(WORD_SIZE); - self.pending[index as usize] = self.pending[index as usize] | (1 << irq); - - self.update_claim(irq); + } + + /// Sets IRQ bit in `pending`. + pub fn update_pending(&mut self, irq: u64) { + let index = irq.wrapping_div(WORD_SIZE); + self.pending[index as usize] = self.pending[index as usize] | (1 << irq); + + self.update_claim(irq); + } + + /// Clears IRQ bit in `pending`. + fn clear_pending(&mut self, irq: u64) { + let index = irq.wrapping_div(WORD_SIZE); + self.pending[index as usize] = self.pending[index as usize] & !(1 << irq); + + self.update_claim(0); + } + + /// Sets IRQ bit in `claim` for context 1. + fn update_claim(&mut self, irq: u64) { + // TODO: Support highest priority to the `claim` register. + // claim[1] is claim/complete registers for S-mode (context 1). SCLAIM. + if self.is_enable(1, irq) || irq == 0 { + self.claim[1] = irq as u32; } - - /// Clears IRQ bit in `pending`. - fn clear_pending(&mut self, irq: u64) { - let index = irq.wrapping_div(WORD_SIZE); - self.pending[index as usize] = self.pending[index as usize] & !(1 << irq); - - self.update_claim(0); + } + + /// Returns true if the enable bit for the `irq` of the `context` is set. + fn is_enable(&self, context: u64, irq: u64) -> bool { + let index = (irq.wrapping_rem(SOURCE_NUM)).wrapping_div(WORD_SIZE * 8); + let offset = (irq.wrapping_rem(SOURCE_NUM)).wrapping_rem(WORD_SIZE * 8); + return ((self.enable[(context * 32 + index) as usize] >> offset) & 1) == 1; + } + + /// Load `size`-bit data from a register located at `addr` in PLIC. + pub fn read(&self, addr: u64, size: u8) -> Result { + // TODO: should support byte-base access. + if size != WORD { + return Err(Exception::LoadAccessFault); } - /// Sets IRQ bit in `claim` for context 1. - fn update_claim(&mut self, irq: u64) { - // TODO: Support highest priority to the `claim` register. - // claim[1] is claim/complete registers for S-mode (context 1). SCLAIM. - if self.is_enable(1, irq) || irq == 0 { - self.claim[1] = irq as u32; + match addr { + SOURCE_PRIORITY..=SOURCE_PRIORITY_END => { + if (addr - SOURCE_PRIORITY).wrapping_rem(WORD_SIZE) != 0 { + return Err(Exception::LoadAccessFault); + } + let index = (addr - SOURCE_PRIORITY).wrapping_div(WORD_SIZE); + Ok(self.priority[index as usize] as u64) + } + PENDING..=PENDING_END => { + if (addr - PENDING).wrapping_rem(WORD_SIZE) != 0 { + return Err(Exception::LoadAccessFault); + } + let index = (addr - PENDING).wrapping_div(WORD_SIZE); + Ok(self.pending[index as usize] as u64) + } + ENABLE..=ENABLE_END => { + if (addr - ENABLE).wrapping_rem(WORD_SIZE) != 0 { + return Err(Exception::LoadAccessFault); } + let index = (addr - ENABLE).wrapping_div(WORD_SIZE); + Ok(self.enable[index as usize] as u64) + } + THRESHOLD_AND_CLAIM..=THRESHOLD_AND_CLAIM_END => { + let context = (addr - THRESHOLD_AND_CLAIM).wrapping_div(CONTEXT_OFFSET); + let offset = addr - (THRESHOLD_AND_CLAIM + CONTEXT_OFFSET * context); + if offset == 0 { + Ok(self.threshold[context as usize] as u64) + } else if offset == 4 { + Ok(self.claim[context as usize] as u64) + } else { + return Err(Exception::LoadAccessFault); + } + } + _ => return Err(Exception::LoadAccessFault), } + } - /// Returns true if the enable bit for the `irq` of the `context` is set. - fn is_enable(&self, context: u64, irq: u64) -> bool { - let index = (irq.wrapping_rem(SOURCE_NUM)).wrapping_div(WORD_SIZE * 8); - let offset = (irq.wrapping_rem(SOURCE_NUM)).wrapping_rem(WORD_SIZE * 8); - return ((self.enable[(context * 32 + index) as usize] >> offset) & 1) == 1; + /// Store `size`-bit data to a register located at `addr` in PLIC. + pub fn write(&mut self, addr: u64, value: u64, size: u8) -> Result<(), Exception> { + // TODO: should support byte-base access. + if size != WORD { + return Err(Exception::StoreAMOAccessFault); } - /// Load `size`-bit data from a register located at `addr` in PLIC. - pub fn read(&self, addr: u64, size: u8) -> Result { - // TODO: should support byte-base access. - if size != WORD { - return Err(Exception::LoadAccessFault); + match addr { + SOURCE_PRIORITY..=SOURCE_PRIORITY_END => { + if (addr - SOURCE_PRIORITY).wrapping_rem(WORD_SIZE) != 0 { + return Err(Exception::StoreAMOAccessFault); } - - match addr { - SOURCE_PRIORITY..=SOURCE_PRIORITY_END => { - if (addr - SOURCE_PRIORITY).wrapping_rem(WORD_SIZE) != 0 { - return Err(Exception::LoadAccessFault); - } - let index = (addr - SOURCE_PRIORITY).wrapping_div(WORD_SIZE); - Ok(self.priority[index as usize] as u64) - } - PENDING..=PENDING_END => { - if (addr - PENDING).wrapping_rem(WORD_SIZE) != 0 { - return Err(Exception::LoadAccessFault); - } - let index = (addr - PENDING).wrapping_div(WORD_SIZE); - Ok(self.pending[index as usize] as u64) - } - ENABLE..=ENABLE_END => { - if (addr - ENABLE).wrapping_rem(WORD_SIZE) != 0 { - return Err(Exception::LoadAccessFault); - } - let index = (addr - ENABLE).wrapping_div(WORD_SIZE); - Ok(self.enable[index as usize] as u64) - } - THRESHOLD_AND_CLAIM..=THRESHOLD_AND_CLAIM_END => { - let context = (addr - THRESHOLD_AND_CLAIM).wrapping_div(CONTEXT_OFFSET); - let offset = addr - (THRESHOLD_AND_CLAIM + CONTEXT_OFFSET * context); - if offset == 0 { - Ok(self.threshold[context as usize] as u64) - } else if offset == 4 { - Ok(self.claim[context as usize] as u64) - } else { - return Err(Exception::LoadAccessFault); - } - } - _ => return Err(Exception::LoadAccessFault), + let index = (addr - SOURCE_PRIORITY).wrapping_div(WORD_SIZE); + self.priority[index as usize] = value as u32; + } + PENDING..=PENDING_END => { + if (addr - PENDING).wrapping_rem(WORD_SIZE) != 0 { + return Err(Exception::StoreAMOAccessFault); } - } - - /// Store `size`-bit data to a register located at `addr` in PLIC. - pub fn write(&mut self, addr: u64, value: u64, size: u8) -> Result<(), Exception> { - // TODO: should support byte-base access. - if size != WORD { - return Err(Exception::StoreAMOAccessFault); + let index = (addr - PENDING).wrapping_div(WORD_SIZE); + self.pending[index as usize] = value as u32; + } + ENABLE..=ENABLE_END => { + if (addr - ENABLE).wrapping_rem(WORD_SIZE) != 0 { + return Err(Exception::StoreAMOAccessFault); } - - match addr { - SOURCE_PRIORITY..=SOURCE_PRIORITY_END => { - if (addr - SOURCE_PRIORITY).wrapping_rem(WORD_SIZE) != 0 { - return Err(Exception::StoreAMOAccessFault); - } - let index = (addr - SOURCE_PRIORITY).wrapping_div(WORD_SIZE); - self.priority[index as usize] = value as u32; - } - PENDING..=PENDING_END => { - if (addr - PENDING).wrapping_rem(WORD_SIZE) != 0 { - return Err(Exception::StoreAMOAccessFault); - } - let index = (addr - PENDING).wrapping_div(WORD_SIZE); - self.pending[index as usize] = value as u32; - } - ENABLE..=ENABLE_END => { - if (addr - ENABLE).wrapping_rem(WORD_SIZE) != 0 { - return Err(Exception::StoreAMOAccessFault); - } - let index = (addr - ENABLE).wrapping_div(WORD_SIZE); - self.enable[index as usize] = value as u32; - } - THRESHOLD_AND_CLAIM..=THRESHOLD_AND_CLAIM_END => { - let context = (addr - THRESHOLD_AND_CLAIM).wrapping_div(CONTEXT_OFFSET); - let offset = addr - (THRESHOLD_AND_CLAIM + CONTEXT_OFFSET * context); - if offset == 0 { - self.threshold[context as usize] = value as u32; - } else if offset == 4 { - //self.claim[context as usize] = value as u32; - - // Clear pending bit. - self.clear_pending(value); - } else { - return Err(Exception::StoreAMOAccessFault); - } - } - _ => return Err(Exception::StoreAMOAccessFault), + let index = (addr - ENABLE).wrapping_div(WORD_SIZE); + self.enable[index as usize] = value as u32; + } + THRESHOLD_AND_CLAIM..=THRESHOLD_AND_CLAIM_END => { + let context = (addr - THRESHOLD_AND_CLAIM).wrapping_div(CONTEXT_OFFSET); + let offset = addr - (THRESHOLD_AND_CLAIM + CONTEXT_OFFSET * context); + if offset == 0 { + self.threshold[context as usize] = value as u32; + } else if offset == 4 { + //self.claim[context as usize] = value as u32; + + // Clear pending bit. + self.clear_pending(value); + } else { + return Err(Exception::StoreAMOAccessFault); } - - Ok(()) + } + _ => return Err(Exception::StoreAMOAccessFault), } + + Ok(()) + } } diff --git a/src/devices/uart.rs b/src/devices/uart.rs index 97dee47..a43106e 100644 --- a/src/devices/uart.rs +++ b/src/devices/uart.rs @@ -5,8 +5,8 @@ use std::io; use std::io::prelude::*; use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Condvar, Mutex, + atomic::{AtomicBool, Ordering}, + Arc, Condvar, Mutex, }; use std::thread; @@ -49,99 +49,99 @@ const UART_LSR_TX: u8 = 1 << 5; /// The UART, the size of which is 0x100 (2**8). pub struct Uart { - uart: Arc<(Mutex<[u8; UART_SIZE as usize]>, Condvar)>, - interrupting: Arc, + uart: Arc<(Mutex<[u8; UART_SIZE as usize]>, Condvar)>, + interrupting: Arc, } impl Uart { - /// Create a new UART object. - pub fn new() -> Self { - let uart = Arc::new((Mutex::new([0; UART_SIZE as usize]), Condvar::new())); - let interrupting = Arc::new(AtomicBool::new(false)); - { - let (uart, _cvar) = &*uart; - let mut uart = uart.lock().expect("failed to get an UART object"); - // Transmitter hold register is empty. It allows input anytime. - uart[(UART_LSR - UART_BASE) as usize] |= UART_LSR_TX; + /// Create a new UART object. + pub fn new() -> Self { + let uart = Arc::new((Mutex::new([0; UART_SIZE as usize]), Condvar::new())); + let interrupting = Arc::new(AtomicBool::new(false)); + { + let (uart, _cvar) = &*uart; + let mut uart = uart.lock().expect("failed to get an UART object"); + // Transmitter hold register is empty. It allows input anytime. + uart[(UART_LSR - UART_BASE) as usize] |= UART_LSR_TX; + } + + // Create a new thread for waiting for input. + let mut byte = [0; 1]; + let cloned_uart = uart.clone(); + let cloned_interrupting = interrupting.clone(); + let _uart_thread_for_read = thread::spawn(move || loop { + match io::stdin().read(&mut byte) { + Ok(_) => { + let (uart, cvar) = &*cloned_uart; + let mut uart = uart.lock().expect("failed to get an UART object"); + // Wait for the thread to start up. + while (uart[(UART_LSR - UART_BASE) as usize] & UART_LSR_RX) == 1 { + uart = cvar.wait(uart).expect("the mutex is poisoned"); + } + uart[0] = byte[0]; + cloned_interrupting.store(true, Ordering::Release); + // Data has been receive. + uart[(UART_LSR - UART_BASE) as usize] |= UART_LSR_RX; + } + Err(e) => { + println!("input via UART is error: {}", e); } + } + }); - // Create a new thread for waiting for input. - let mut byte = [0; 1]; - let cloned_uart = uart.clone(); - let cloned_interrupting = interrupting.clone(); - let _uart_thread_for_read = thread::spawn(move || loop { - match io::stdin().read(&mut byte) { - Ok(_) => { - let (uart, cvar) = &*cloned_uart; - let mut uart = uart.lock().expect("failed to get an UART object"); - // Wait for the thread to start up. - while (uart[(UART_LSR - UART_BASE) as usize] & UART_LSR_RX) == 1 { - uart = cvar.wait(uart).expect("the mutex is poisoned"); - } - uart[0] = byte[0]; - cloned_interrupting.store(true, Ordering::Release); - // Data has been receive. - uart[(UART_LSR - UART_BASE) as usize] |= UART_LSR_RX; - } - Err(e) => { - println!("input via UART is error: {}", e); - } - } - }); + Self { uart, interrupting } + } - Self { uart, interrupting } - } + /// Return true if an interrupt is pending. Clear the interrupting flag by swapping a value. + pub fn is_interrupting(&self) -> bool { + self.interrupting.swap(false, Ordering::Acquire) + } - /// Return true if an interrupt is pending. Clear the interrupting flag by swapping a value. - pub fn is_interrupting(&self) -> bool { - self.interrupting.swap(false, Ordering::Acquire) + /// Read a byte from the receive holding register. + pub fn read(&mut self, index: u64, size: u8) -> Result { + if size != BYTE { + return Err(Exception::LoadAccessFault); } - /// Read a byte from the receive holding register. - pub fn read(&mut self, index: u64, size: u8) -> Result { - if size != BYTE { - return Err(Exception::LoadAccessFault); - } - - let (uart, cvar) = &*self.uart; - let mut uart = uart.lock().expect("failed to get an UART object"); - match index { - UART_RHR => { - cvar.notify_one(); - uart[(UART_LSR - UART_BASE) as usize] &= !UART_LSR_RX; - Ok(uart[(UART_RHR - UART_BASE) as usize] as u64) - } - _ => Ok(uart[(index - UART_BASE) as usize] as u64), - } + let (uart, cvar) = &*self.uart; + let mut uart = uart.lock().expect("failed to get an UART object"); + match index { + UART_RHR => { + cvar.notify_one(); + uart[(UART_LSR - UART_BASE) as usize] &= !UART_LSR_RX; + Ok(uart[(UART_RHR - UART_BASE) as usize] as u64) + } + _ => Ok(uart[(index - UART_BASE) as usize] as u64), } + } - /// Write a byte to the transmit holding register. - pub fn write(&mut self, index: u64, value: u8, size: u8) -> Result<(), Exception> { - if size != BYTE { - return Err(Exception::StoreAMOAccessFault); - } + /// Write a byte to the transmit holding register. + pub fn write(&mut self, index: u64, value: u8, size: u8) -> Result<(), Exception> { + if size != BYTE { + return Err(Exception::StoreAMOAccessFault); + } - // An OS allows to write a byte to a UART when UART_LSR_TX is 1. - // e.g. (xv6): - // // wait for Transmit Holding Empty to be set in LSR. - // while((ReadReg(LSR) & (1 << 5)) == 0) - // ; - // WriteReg(THR, c); - // - // e.g. (riscv-pk): - // while ((uart16550[UART_REG_LSR << uart16550_reg_shift] & UART_REG_STATUS_TX) == 0); - // uart16550[UART_REG_QUEUE << uart16550_reg_shift] = ch; - let (uart, _cvar) = &*self.uart; - let mut uart = uart.lock().expect("failed to get an UART object"); - match index { - UART_THR => { - print!("{}", value as char); - io::stdout().flush().expect("failed to flush stdout"); - } - _ => { - uart[(index - UART_BASE) as usize] = value; - } - } - Ok(()) + // An OS allows to write a byte to a UART when UART_LSR_TX is 1. + // e.g. (xv6): + // // wait for Transmit Holding Empty to be set in LSR. + // while((ReadReg(LSR) & (1 << 5)) == 0) + // ; + // WriteReg(THR, c); + // + // e.g. (riscv-pk): + // while ((uart16550[UART_REG_LSR << uart16550_reg_shift] & UART_REG_STATUS_TX) == 0); + // uart16550[UART_REG_QUEUE << uart16550_reg_shift] = ch; + let (uart, _cvar) = &*self.uart; + let mut uart = uart.lock().expect("failed to get an UART object"); + match index { + UART_THR => { + print!("{}", value as char); + io::stdout().flush().expect("failed to flush stdout"); + } + _ => { + uart[(index - UART_BASE) as usize] = value; + } } + Ok(()) + } } diff --git a/src/devices/virtio_blk.rs b/src/devices/virtio_blk.rs index d5414c9..ac71511 100644 --- a/src/devices/virtio_blk.rs +++ b/src/devices/virtio_blk.rs @@ -150,37 +150,37 @@ const CONFIG_END: u64 = VIRTIO_BASE + 0x107; /// ``` #[derive(Debug, Copy, Clone)] struct VirtqueueAddr { - /// The address that starts actual descriptors (16 bytes each). - desc_addr: u64, - /// The address that starts a ring of available descriptors. - avail_addr: u64, - /// The address that starts a ring of used descriptors. - used_addr: u64, + /// The address that starts actual descriptors (16 bytes each). + desc_addr: u64, + /// The address that starts a ring of available descriptors. + avail_addr: u64, + /// The address that starts a ring of used descriptors. + used_addr: u64, } impl VirtqueueAddr { - /// Create a new virtqueue descriptor based on the address that stores the content of the - /// descriptor. - fn new(virtio: &Virtio) -> Self { - // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-240006 - // Virtqueue Part | Alignment | Size - // ------------------------------------------------- - // Descriptor Table | 16 | 16∗(Queue Size) - // Available Ring | 2 | 6 + 2∗(Queue Size) - // Used Ring | 4 | 6 + 8∗(Queue Size) - - let base_addr = virtio.queue_pfn as u64 * virtio.guest_page_size as u64; - let align = virtio.queue_align as u64; - let size = virtio.queue_num as u64; - let avail_ring_end = base_addr + (16 * size) + (6 + 2 * size); - - Self { - desc_addr: base_addr, - avail_addr: base_addr + 16 * size, - // Used ring starts with the `queue_align` boundary after the available ring ends. - used_addr: (avail_ring_end.wrapping_div(align) + 1).wrapping_mul(align), - } + /// Create a new virtqueue descriptor based on the address that stores the content of the + /// descriptor. + fn new(virtio: &Virtio) -> Self { + // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-240006 + // Virtqueue Part | Alignment | Size + // ------------------------------------------------- + // Descriptor Table | 16 | 16∗(Queue Size) + // Available Ring | 2 | 6 + 2∗(Queue Size) + // Used Ring | 4 | 6 + 8∗(Queue Size) + + let base_addr = virtio.queue_pfn as u64 * virtio.guest_page_size as u64; + let align = virtio.queue_align as u64; + let size = virtio.queue_num as u64; + let avail_ring_end = base_addr + (16 * size) + (6 + 2 * size); + + Self { + desc_addr: base_addr, + avail_addr: base_addr + 16 * size, + // Used ring starts with the `queue_align` boundary after the available ring ends. + used_addr: (avail_ring_end.wrapping_div(align) + 1).wrapping_mul(align), } + } } /// "The descriptor table refers to the buffers the driver is using for the device. addr is a @@ -208,27 +208,27 @@ impl VirtqueueAddr { /// ``` #[derive(Debug)] struct VirtqDesc { - /// Address (guest-physical). - addr: u64, - /// Length. - len: u64, - /// The flags as indicated VIRTQ_DESC_F_NEXT/VIRTQ_DESC_F_WRITE/VIRTQ_DESC_F_INDIRECT. - flags: u64, - /// Next field if flags & NEXT. - next: u64, + /// Address (guest-physical). + addr: u64, + /// Length. + len: u64, + /// The flags as indicated VIRTQ_DESC_F_NEXT/VIRTQ_DESC_F_WRITE/VIRTQ_DESC_F_INDIRECT. + flags: u64, + /// Next field if flags & NEXT. + next: u64, } impl VirtqDesc { - /// Creates a new virtqueue descriptor based on the address that stores the content of the - /// descriptor. - fn new(cpu: &mut Cpu, addr: u64) -> Result { - Ok(Self { - addr: cpu.bus.read(addr, DOUBLEWORD)?, - len: cpu.bus.read(addr.wrapping_add(8), WORD)?, - flags: cpu.bus.read(addr.wrapping_add(12), HALFWORD)?, - next: cpu.bus.read(addr.wrapping_add(14), HALFWORD)?, - }) - } + /// Creates a new virtqueue descriptor based on the address that stores the content of the + /// descriptor. + fn new(cpu: &mut Cpu, addr: u64) -> Result { + Ok(Self { + addr: cpu.bus.read(addr, DOUBLEWORD)?, + len: cpu.bus.read(addr.wrapping_add(8), WORD)?, + flags: cpu.bus.read(addr.wrapping_add(12), HALFWORD)?, + next: cpu.bus.read(addr.wrapping_add(14), HALFWORD)?, + }) + } } /// "The driver uses the available ring to offer buffers to the device: each ring entry refers to @@ -247,369 +247,361 @@ impl VirtqDesc { /// ``` #[derive(Debug)] struct VirtqAvail { - idx: u16, - ring_start_addr: u64, + idx: u16, + ring_start_addr: u64, } impl VirtqAvail { - fn new(cpu: &mut Cpu, addr: u64) -> Result { - Ok(Self { - idx: cpu.bus.read(addr.wrapping_add(2), HALFWORD)? as u16, - ring_start_addr: addr.wrapping_add(4), - }) - } + fn new(cpu: &mut Cpu, addr: u64) -> Result { + Ok(Self { + idx: cpu.bus.read(addr.wrapping_add(2), HALFWORD)? as u16, + ring_start_addr: addr.wrapping_add(4), + }) + } } /// Paravirtualized drivers for IO virtualization. pub struct Virtio { - id: u64, - device_features: [u32; 2], - device_features_sel: u32, - driver_features: [u32; 2], - driver_features_sel: u32, - guest_page_size: u32, - queue_num: u32, - queue_align: u32, - queue_pfn: u32, - queue_notify: u32, - interrupt_status: u32, - status: u32, - config: [u8; 8], - disk: Vec, - virtqueue: Option, + id: u64, + device_features: [u32; 2], + device_features_sel: u32, + driver_features: [u32; 2], + driver_features_sel: u32, + guest_page_size: u32, + queue_num: u32, + queue_align: u32, + queue_pfn: u32, + queue_notify: u32, + interrupt_status: u32, + status: u32, + config: [u8; 8], + disk: Vec, + virtqueue: Option, } impl Virtio { - /// Creates a new virtio object. - pub fn new() -> Self { - let mut config = [0; 8]; - // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2440004 - // 5.2.4 Device configuration layout - // struct virtio_blk_config { - // le64 capacity; - // } - // - // The value is based on QEMU's output: - // "virtio_blk virtio0: [vda] 204800 512-byte logical blocks (105 MB/100 MiB)" - // 204800 --> 0x32000 - config[1] = 0x20; - config[2] = 0x03; - - Self { - id: 0, - device_features: Virtio::device_features(), - device_features_sel: 0, - driver_features: [0; 2], - driver_features_sel: 0, - guest_page_size: 0, - queue_num: 0, - // default value to avoid division by 0. - queue_align: 0x1000, - queue_pfn: 0, - queue_notify: u32::MAX, - interrupt_status: 0, - status: 0, - config, - disk: Vec::new(), - virtqueue: None, - } + /// Creates a new virtio object. + pub fn new() -> Self { + let mut config = [0; 8]; + // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2440004 + // 5.2.4 Device configuration layout + // struct virtio_blk_config { + // le64 capacity; + // } + // + // The value is based on QEMU's output: + // "virtio_blk virtio0: [vda] 204800 512-byte logical blocks (105 MB/100 MiB)" + // 204800 --> 0x32000 + config[1] = 0x20; + config[2] = 0x03; + + Self { + id: 0, + device_features: Virtio::device_features(), + device_features_sel: 0, + driver_features: [0; 2], + driver_features_sel: 0, + guest_page_size: 0, + queue_num: 0, + // default value to avoid division by 0. + queue_align: 0x1000, + queue_pfn: 0, + queue_notify: u32::MAX, + interrupt_status: 0, + status: 0, + config, + disk: Vec::new(), + virtqueue: None, } - - /// Returns device features. - fn device_features() -> [u32; 2] { - let mut features = [0; 2]; - // VIRTIO_F_IN_ORDER(Bit 35). This feature indicates that all buffers are used by the device - // in the same order in which they have been made available. - features[1] = features[1] | (1 << 3); - return features; + } + + /// Returns device features. + fn device_features() -> [u32; 2] { + let mut features = [0; 2]; + // VIRTIO_F_IN_ORDER(Bit 35). This feature indicates that all buffers are used by the device + // in the same order in which they have been made available. + features[1] = features[1] | (1 << 3); + return features; + } + + /// Initializes a virtqueue once the device initialization is finished by setting the DRIVER_OK + /// status bit (0x4). + fn init_virtqueue(&mut self) { + let queue = VirtqueueAddr::new(self); + self.virtqueue = Some(queue); + } + + /// Gets `VirtqueueAddr` struct if it exists. If not, creates a new one based on the virtio + /// configuration values. + fn virtqueue(&self) -> VirtqueueAddr { + match self.virtqueue { + Some(queue) => queue, + None => VirtqueueAddr::new(self), } - - /// Initializes a virtqueue once the device initialization is finished by setting the DRIVER_OK - /// status bit (0x4). - fn init_virtqueue(&mut self) { - let queue = VirtqueueAddr::new(self); - self.virtqueue = Some(queue); + } + + /// Resets the device when `status` is written to 0. + fn reset(&mut self) { + self.id = 0; + // 4.2.2.1 Device Requirements: MMIO Device Register Layout + // "Upon reset, the device MUST clear all bits in InterruptStatus and ready bits in the + // QueueReady register for all queues in the device." + self.interrupt_status = 0; + } + + /// Returns true if an interrupt is pending. + pub fn is_interrupting(&mut self) -> bool { + if self.queue_notify != u32::MAX { + self.queue_notify = u32::MAX; + return true; } - - /// Gets `VirtqueueAddr` struct if it exists. If not, creates a new one based on the virtio - /// configuration values. - fn virtqueue(&self) -> VirtqueueAddr { - match self.virtqueue { - Some(queue) => queue, - None => VirtqueueAddr::new(self), + false + } + + /// Sets the binary in the virtio disk. + pub fn initialize(&mut self, binary: Vec) { + self.disk.extend(binary.iter().cloned()); + } + + /// Loads `size`-bit data from a register located at `addr` in the virtio block device. + pub fn read(&self, addr: u64, size: u8) -> Result { + // `reg` is the value of a target register in the virtio block device and `offset` is the + // byte of the start position in the register. + let (reg, offset) = match addr { + // A Little Endian equivalent of the “virt” string. + MAGIC..=MAGIC_END => (0x74726976, addr - MAGIC), + // Legacy devices (see 4.2.4 Legacy interface) used 0x1. + VERSION..=VERSION_END => (0x1, addr - VERSION), + // Block device. + DEVICE_ID..=DEVICE_ID_END => (0x2, addr - DEVICE_ID), + // See https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/virtio_disk.c#L86 + VENDOR_ID..=VENDOR_ID_END => (0x554d4551, addr - VENDOR_ID), + DEVICE_FEATURES..=DEVICE_FEATURES_END => ( + self.device_features[self.device_features_sel as usize], + addr - DEVICE_FEATURES, + ), + QUEUE_NUM_MAX..=QUEUE_NUM_MAX_END => (QUEUE_SIZE as u32, addr - QUEUE_NUM_MAX), + QUEUE_PFN..=QUEUE_PFN_END => (self.queue_pfn, addr - QUEUE_PFN), + INTERRUPT_STATUS..=INTERRUPT_STATUS_END => (self.interrupt_status, addr - INTERRUPT_STATUS), + STATUS..=STATUS_END => (self.status, addr - STATUS), + CONFIG..=CONFIG_END => { + if size != BYTE { + return Err(Exception::StoreAMOAccessFault); } - } - - /// Resets the device when `status` is written to 0. - fn reset(&mut self) { - self.id = 0; - // 4.2.2.1 Device Requirements: MMIO Device Register Layout - // "Upon reset, the device MUST clear all bits in InterruptStatus and ready bits in the - // QueueReady register for all queues in the device." - self.interrupt_status = 0; - } - - /// Returns true if an interrupt is pending. - pub fn is_interrupting(&mut self) -> bool { - if self.queue_notify != u32::MAX { - self.queue_notify = u32::MAX; - return true; + let index = addr - CONFIG; + (self.config[index as usize] as u32, 0) + } + _ => return Err(Exception::LoadAccessFault), + }; + + let value = match size { + BYTE => (reg >> (offset * 8)) & 0xff, + HALFWORD => (reg >> (offset * 8)) & 0xffff, + WORD => (reg >> (offset * 8)) & 0xffffffff, + _ => return Err(Exception::LoadAccessFault), + }; + + Ok(value as u64) + } + + /// Stores `size`-bit data to a register located at `addr` in the virtio block device. + pub fn write(&mut self, addr: u64, value: u32, size: u8) -> Result<(), Exception> { + // `reg` is the value of a target register in the virtio block device and `offset` is the + // byte of the start position in the register. + let (mut reg, offset) = match addr { + DEVICE_FEATURES_SEL..=DEVICE_FEATURES_SEL_END => (self.device_features_sel, addr - DEVICE_FEATURES_SEL), + DRIVER_FEATURES..=DRIVER_FEATURES_END => ( + self.driver_features[self.driver_features_sel as usize], + addr - DRIVER_FEATURES, + ), + DRIVER_FEATURES_SEL..=DRIVER_FEATURES_SEL_END => (self.driver_features_sel, addr - DRIVER_FEATURES_SEL), + GUEST_PAGE_SIZE..=GUEST_PAGE_SIZE_END => (self.guest_page_size, addr - GUEST_PAGE_SIZE), + QUEUE_SEL..=QUEUE_SEL_END => { + if value != 0 { + panic!("Multiple virtual queues are not supported."); } - false - } - - /// Sets the binary in the virtio disk. - pub fn initialize(&mut self, binary: Vec) { - self.disk.extend(binary.iter().cloned()); - } - - /// Loads `size`-bit data from a register located at `addr` in the virtio block device. - pub fn read(&self, addr: u64, size: u8) -> Result { - // `reg` is the value of a target register in the virtio block device and `offset` is the - // byte of the start position in the register. - let (reg, offset) = match addr { - // A Little Endian equivalent of the “virt” string. - MAGIC..=MAGIC_END => (0x74726976, addr - MAGIC), - // Legacy devices (see 4.2.4 Legacy interface) used 0x1. - VERSION..=VERSION_END => (0x1, addr - VERSION), - // Block device. - DEVICE_ID..=DEVICE_ID_END => (0x2, addr - DEVICE_ID), - // See https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/virtio_disk.c#L86 - VENDOR_ID..=VENDOR_ID_END => (0x554d4551, addr - VENDOR_ID), - DEVICE_FEATURES..=DEVICE_FEATURES_END => ( - self.device_features[self.device_features_sel as usize], - addr - DEVICE_FEATURES, - ), - QUEUE_NUM_MAX..=QUEUE_NUM_MAX_END => (QUEUE_SIZE as u32, addr - QUEUE_NUM_MAX), - QUEUE_PFN..=QUEUE_PFN_END => (self.queue_pfn, addr - QUEUE_PFN), - INTERRUPT_STATUS..=INTERRUPT_STATUS_END => { - (self.interrupt_status, addr - INTERRUPT_STATUS) - } - STATUS..=STATUS_END => (self.status, addr - STATUS), - CONFIG..=CONFIG_END => { - if size != BYTE { - return Err(Exception::StoreAMOAccessFault); - } - let index = addr - CONFIG; - (self.config[index as usize] as u32, 0) - } - _ => return Err(Exception::LoadAccessFault), - }; - - let value = match size { - BYTE => (reg >> (offset * 8)) & 0xff, - HALFWORD => (reg >> (offset * 8)) & 0xffff, - WORD => (reg >> (offset * 8)) & 0xffffffff, - _ => return Err(Exception::LoadAccessFault), - }; - - Ok(value as u64) - } - - /// Stores `size`-bit data to a register located at `addr` in the virtio block device. - pub fn write(&mut self, addr: u64, value: u32, size: u8) -> Result<(), Exception> { - // `reg` is the value of a target register in the virtio block device and `offset` is the - // byte of the start position in the register. - let (mut reg, offset) = match addr { - DEVICE_FEATURES_SEL..=DEVICE_FEATURES_SEL_END => { - (self.device_features_sel, addr - DEVICE_FEATURES_SEL) - } - DRIVER_FEATURES..=DRIVER_FEATURES_END => ( - self.driver_features[self.driver_features_sel as usize], - addr - DRIVER_FEATURES, - ), - DRIVER_FEATURES_SEL..=DRIVER_FEATURES_SEL_END => { - (self.driver_features_sel, addr - DRIVER_FEATURES_SEL) - } - GUEST_PAGE_SIZE..=GUEST_PAGE_SIZE_END => (self.guest_page_size, addr - GUEST_PAGE_SIZE), - QUEUE_SEL..=QUEUE_SEL_END => { - if value != 0 { - panic!("Multiple virtual queues are not supported."); - } - return Ok(()); - } - QUEUE_NUM..=QUEUE_NUM_END => (self.queue_num, addr - QUEUE_NUM), - QUEUE_ALIGN..=QUEUE_ALIGN_END => (self.queue_align, addr - QUEUE_ALIGN), - QUEUE_PFN..=QUEUE_PFN_END => (self.queue_pfn, addr - QUEUE_PFN), - QUEUE_NOTIFY..=QUEUE_NOTIFY_END => (self.queue_notify, addr - QUEUE_NOTIFY), - INTERRUPT_ACK..=INTERRUPT_ACK_END => { - (self.interrupt_status, addr - INTERRUPT_ACK) - /* - if (value & 0x1) == 1 { - self.interrupt_status &= !0x1; - } else { - panic!("unexpected value for INTERRUPT_ACK: {:#x}", value); - } - return Ok(()); - */ - } - STATUS..=STATUS_END => (self.status, addr - STATUS), - CONFIG..=CONFIG_END => { - if size != BYTE { - return Err(Exception::StoreAMOAccessFault); - } - let index = addr - CONFIG; - self.config[index as usize] = (value >> (index * 8)) as u8; - return Ok(()); - } - _ => return Err(Exception::StoreAMOAccessFault), - }; - - // Calculate the new value of the target register based on `size` and `offset`. - match size { - BYTE => { - // Clear the target byte. - reg = reg & (!(0xff << (offset * 8))); - // Set the new `value` to the target byte. - reg = reg | ((value & 0xff) << (offset * 8)); - } - HALFWORD => { - reg = reg & (!(0xffff << (offset * 8))); - reg = reg | ((value & 0xffff) << (offset * 8)); - } - WORD => { - reg = value; - } - _ => return Err(Exception::StoreAMOAccessFault), + return Ok(()); + } + QUEUE_NUM..=QUEUE_NUM_END => (self.queue_num, addr - QUEUE_NUM), + QUEUE_ALIGN..=QUEUE_ALIGN_END => (self.queue_align, addr - QUEUE_ALIGN), + QUEUE_PFN..=QUEUE_PFN_END => (self.queue_pfn, addr - QUEUE_PFN), + QUEUE_NOTIFY..=QUEUE_NOTIFY_END => (self.queue_notify, addr - QUEUE_NOTIFY), + INTERRUPT_ACK..=INTERRUPT_ACK_END => { + (self.interrupt_status, addr - INTERRUPT_ACK) + /* + if (value & 0x1) == 1 { + self.interrupt_status &= !0x1; + } else { + panic!("unexpected value for INTERRUPT_ACK: {:#x}", value); } - - // Store the new register value to the target register. - match addr { - DEVICE_FEATURES_SEL..=DEVICE_FEATURES_SEL_END => self.device_features_sel = reg, - DRIVER_FEATURES..=DRIVER_FEATURES_END => { - self.driver_features[self.driver_features_sel as usize] = reg - } - DRIVER_FEATURES_SEL..=DRIVER_FEATURES_SEL_END => self.driver_features_sel = reg, - GUEST_PAGE_SIZE..=GUEST_PAGE_SIZE_END => self.guest_page_size = reg, - QUEUE_NUM..=QUEUE_NUM_END => self.queue_num = reg, - QUEUE_ALIGN..=QUEUE_ALIGN_END => self.queue_align = reg, - QUEUE_PFN..=QUEUE_PFN_END => self.queue_pfn = reg, - QUEUE_NOTIFY..=QUEUE_NOTIFY_END => self.queue_notify = reg, - INTERRUPT_ACK..=INTERRUPT_ACK_END => self.interrupt_status = reg, - STATUS..=STATUS_END => { - self.status = reg; - // "Writing 0 into this field resets the device." - if self.status == 0 { - self.reset(); - } - // DRIVER_OK bit (4) was set, so initialize `VirtqueueAddr`. - if self.status & 0x4 == 1 { - self.init_virtqueue(); - } - // FAILED (128) bit. Indicates that something went wrong in the guest. - if (self.status & 128) == 1 { - panic!("virtio: device status FAILED"); - } - } - _ => return Err(Exception::StoreAMOAccessFault), + return Ok(()); + */ + } + STATUS..=STATUS_END => (self.status, addr - STATUS), + CONFIG..=CONFIG_END => { + if size != BYTE { + return Err(Exception::StoreAMOAccessFault); } - - Ok(()) - } - - fn read_disk(&self, addr: u64) -> u64 { - self.disk[addr as usize] as u64 + let index = addr - CONFIG; + self.config[index as usize] = (value >> (index * 8)) as u8; + return Ok(()); + } + _ => return Err(Exception::StoreAMOAccessFault), + }; + + // Calculate the new value of the target register based on `size` and `offset`. + match size { + BYTE => { + // Clear the target byte. + reg = reg & (!(0xff << (offset * 8))); + // Set the new `value` to the target byte. + reg = reg | ((value & 0xff) << (offset * 8)); + } + HALFWORD => { + reg = reg & (!(0xffff << (offset * 8))); + reg = reg | ((value & 0xffff) << (offset * 8)); + } + WORD => { + reg = value; + } + _ => return Err(Exception::StoreAMOAccessFault), } - fn write_disk(&mut self, addr: u64, value: u64) { - self.disk[addr as usize] = value as u8 + // Store the new register value to the target register. + match addr { + DEVICE_FEATURES_SEL..=DEVICE_FEATURES_SEL_END => self.device_features_sel = reg, + DRIVER_FEATURES..=DRIVER_FEATURES_END => self.driver_features[self.driver_features_sel as usize] = reg, + DRIVER_FEATURES_SEL..=DRIVER_FEATURES_SEL_END => self.driver_features_sel = reg, + GUEST_PAGE_SIZE..=GUEST_PAGE_SIZE_END => self.guest_page_size = reg, + QUEUE_NUM..=QUEUE_NUM_END => self.queue_num = reg, + QUEUE_ALIGN..=QUEUE_ALIGN_END => self.queue_align = reg, + QUEUE_PFN..=QUEUE_PFN_END => self.queue_pfn = reg, + QUEUE_NOTIFY..=QUEUE_NOTIFY_END => self.queue_notify = reg, + INTERRUPT_ACK..=INTERRUPT_ACK_END => self.interrupt_status = reg, + STATUS..=STATUS_END => { + self.status = reg; + // "Writing 0 into this field resets the device." + if self.status == 0 { + self.reset(); + } + // DRIVER_OK bit (4) was set, so initialize `VirtqueueAddr`. + if self.status & 0x4 == 1 { + self.init_virtqueue(); + } + // FAILED (128) bit. Indicates that something went wrong in the guest. + if (self.status & 128) == 1 { + panic!("virtio: device status FAILED"); + } + } + _ => return Err(Exception::StoreAMOAccessFault), } - /// Accesses the disk via virtio. This is an associated function which takes a `cpu` object to - /// read and write with a memory directly (DMA). - pub fn disk_access(cpu: &mut Cpu) -> Result<(), Exception> { - // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-1460002 - // "Used Buffer Notification - // - bit 0 - the interrupt was asserted because the device has used a buffer in at - // least one of the active virtual queues." - cpu.bus.virtio.interrupt_status |= 0x1; - - let virtq = cpu.bus.virtio.virtqueue(); - - let avail = VirtqAvail::new(cpu, virtq.avail_addr)?; - - let head_index = cpu.bus.read( - avail.ring_start_addr + avail.idx as u64 % QUEUE_SIZE, - HALFWORD, - )?; - - // First descriptor. - let desc0 = VirtqDesc::new(cpu, virtq.desc_addr + VRING_DESC_SIZE * head_index)?; - assert_eq!(desc0.flags & VIRTQ_DESC_F_NEXT, 1); - - // Second descriptor. - let desc1 = VirtqDesc::new(cpu, virtq.desc_addr + VRING_DESC_SIZE * desc0.next)?; - assert_eq!(desc1.flags & VIRTQ_DESC_F_NEXT, 1); - - // 5.2.6 Device Operation - // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2500006 - // struct virtio_blk_req { - // le32 type; - // le32 reserved; - // le64 sector; - // u8 data[][512]; - // u8 status; - // }; - let sector = cpu.bus.read(desc0.addr.wrapping_add(8), DOUBLEWORD)?; - - // Write to a device if the second bit of `flags` is set. - match (desc1.flags & VIRTQ_DESC_F_WRITE) == 0 { - true => { - // Read memory data and write it to a disk. - for i in 0..desc1.len { - let data = cpu.bus.read(desc1.addr + i, BYTE)?; - cpu.bus.virtio.write_disk(sector * SECTOR_SIZE + i, data); - } - } - false => { - // Read disk data and write it to memory. - for i in 0..desc1.len { - let data = cpu.bus.virtio.read_disk(sector * SECTOR_SIZE + i); - cpu.bus.write(desc1.addr + i, data, BYTE)?; - } - } - }; - - // Third descriptor address. - let desc2 = VirtqDesc::new(cpu, virtq.desc_addr + VRING_DESC_SIZE * desc1.next)?; - assert_eq!(desc2.flags & VIRTQ_DESC_F_NEXT, 0); - // Tell success. - cpu.bus.write(desc2.addr, 0, BYTE)?; - - // 2.6.7.2 Device Requirements: Used Buffer Notification Suppression - // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-400007 - // After the device writes a descriptor index into the used ring: - // If flags is 1, the device SHOULD NOT send a notification. - // If flags is 0, the device MUST send a notification. - // TODO: check the flags in the available ring. - - // "The used ring is where the device returns buffers once it is done with them: it is only - // written to by the device, and read by the driver." - // - // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-430008 - // - // ```c - // #define VIRTQ_USED_F_NO_NOTIFY 1 - // struct virtq_used { - // le16 flags; - // le16 idx; - // struct virtq_used_elem ring[ /* Queue Size */]; - // le16 avail_event; /* Only if VIRTIO_F_EVENT_IDX */ - // }; - // ``` - cpu.bus.write( - virtq - .used_addr - .wrapping_add(4) - .wrapping_add((cpu.bus.virtio.id as u64 % QUEUE_SIZE) * 8), - head_index, - WORD, - )?; - - cpu.bus.virtio.id = cpu.bus.virtio.id.wrapping_add(1); - cpu.bus - .write(virtq.used_addr.wrapping_add(2), cpu.bus.virtio.id, HALFWORD)?; - - Ok(()) - } + Ok(()) + } + + fn read_disk(&self, addr: u64) -> u64 { + self.disk[addr as usize] as u64 + } + + fn write_disk(&mut self, addr: u64, value: u64) { + self.disk[addr as usize] = value as u8 + } + + /// Accesses the disk via virtio. This is an associated function which takes a `cpu` object to + /// read and write with a memory directly (DMA). + pub fn disk_access(cpu: &mut Cpu) -> Result<(), Exception> { + // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-1460002 + // "Used Buffer Notification + // - bit 0 - the interrupt was asserted because the device has used a buffer in at + // least one of the active virtual queues." + cpu.bus.virtio.interrupt_status |= 0x1; + + let virtq = cpu.bus.virtio.virtqueue(); + + let avail = VirtqAvail::new(cpu, virtq.avail_addr)?; + + let head_index = cpu + .bus + .read(avail.ring_start_addr + avail.idx as u64 % QUEUE_SIZE, HALFWORD)?; + + // First descriptor. + let desc0 = VirtqDesc::new(cpu, virtq.desc_addr + VRING_DESC_SIZE * head_index)?; + assert_eq!(desc0.flags & VIRTQ_DESC_F_NEXT, 1); + + // Second descriptor. + let desc1 = VirtqDesc::new(cpu, virtq.desc_addr + VRING_DESC_SIZE * desc0.next)?; + assert_eq!(desc1.flags & VIRTQ_DESC_F_NEXT, 1); + + // 5.2.6 Device Operation + // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2500006 + // struct virtio_blk_req { + // le32 type; + // le32 reserved; + // le64 sector; + // u8 data[][512]; + // u8 status; + // }; + let sector = cpu.bus.read(desc0.addr.wrapping_add(8), DOUBLEWORD)?; + + // Write to a device if the second bit of `flags` is set. + match (desc1.flags & VIRTQ_DESC_F_WRITE) == 0 { + true => { + // Read memory data and write it to a disk. + for i in 0..desc1.len { + let data = cpu.bus.read(desc1.addr + i, BYTE)?; + cpu.bus.virtio.write_disk(sector * SECTOR_SIZE + i, data); + } + } + false => { + // Read disk data and write it to memory. + for i in 0..desc1.len { + let data = cpu.bus.virtio.read_disk(sector * SECTOR_SIZE + i); + cpu.bus.write(desc1.addr + i, data, BYTE)?; + } + } + }; + + // Third descriptor address. + let desc2 = VirtqDesc::new(cpu, virtq.desc_addr + VRING_DESC_SIZE * desc1.next)?; + assert_eq!(desc2.flags & VIRTQ_DESC_F_NEXT, 0); + // Tell success. + cpu.bus.write(desc2.addr, 0, BYTE)?; + + // 2.6.7.2 Device Requirements: Used Buffer Notification Suppression + // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-400007 + // After the device writes a descriptor index into the used ring: + // If flags is 1, the device SHOULD NOT send a notification. + // If flags is 0, the device MUST send a notification. + // TODO: check the flags in the available ring. + + // "The used ring is where the device returns buffers once it is done with them: it is only + // written to by the device, and read by the driver." + // + // https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-430008 + // + // ```c + // #define VIRTQ_USED_F_NO_NOTIFY 1 + // struct virtq_used { + // le16 flags; + // le16 idx; + // struct virtq_used_elem ring[ /* Queue Size */]; + // le16 avail_event; /* Only if VIRTIO_F_EVENT_IDX */ + // }; + // ``` + cpu.bus.write( + virtq + .used_addr + .wrapping_add(4) + .wrapping_add((cpu.bus.virtio.id as u64 % QUEUE_SIZE) * 8), + head_index, + WORD, + )?; + + cpu.bus.virtio.id = cpu.bus.virtio.id.wrapping_add(1); + cpu + .bus + .write(virtq.used_addr.wrapping_add(2), cpu.bus.virtio.id, HALFWORD)?; + + Ok(()) + } } diff --git a/src/dram.rs b/src/dram.rs index f5673a2..c2d421c 100644 --- a/src/dram.rs +++ b/src/dram.rs @@ -10,114 +10,114 @@ pub const DRAM_SIZE: u64 = 1024 * 1024 * 1024; /// The memory used by the emulator. #[derive(Debug)] pub struct Dram { - pub dram: Vec, - code_size: u64, + pub dram: Vec, + code_size: u64, } impl Dram { - /// Create a new memory object with default memory size. - pub fn new() -> Self { - Self { - dram: vec![0; DRAM_SIZE as usize], - code_size: 0, - } + /// Create a new memory object with default memory size. + pub fn new() -> Self { + Self { + dram: vec![0; DRAM_SIZE as usize], + code_size: 0, } + } - /// Set the binary in the memory. - pub fn initialize(&mut self, binary: Vec) { - self.code_size = binary.len() as u64; - self.dram.splice(..binary.len(), binary.iter().cloned()); - } + /// Set the binary in the memory. + pub fn initialize(&mut self, binary: Vec) { + self.code_size = binary.len() as u64; + self.dram.splice(..binary.len(), binary.iter().cloned()); + } - /// Load `size`-bit data from the memory. - pub fn read(&self, addr: u64, size: u8) -> Result { - match size { - BYTE => Ok(self.read8(addr)), - HALFWORD => Ok(self.read16(addr)), - WORD => Ok(self.read32(addr)), - DOUBLEWORD => Ok(self.read64(addr)), - _ => return Err(Exception::LoadAccessFault), - } + /// Load `size`-bit data from the memory. + pub fn read(&self, addr: u64, size: u8) -> Result { + match size { + BYTE => Ok(self.read8(addr)), + HALFWORD => Ok(self.read16(addr)), + WORD => Ok(self.read32(addr)), + DOUBLEWORD => Ok(self.read64(addr)), + _ => return Err(Exception::LoadAccessFault), } + } - /// Store `size`-bit data to the memory. - pub fn write(&mut self, addr: u64, value: u64, size: u8) -> Result<(), Exception> { - match size { - BYTE => self.write8(addr, value), - HALFWORD => self.write16(addr, value), - WORD => self.write32(addr, value), - DOUBLEWORD => self.write64(addr, value), - _ => return Err(Exception::StoreAMOAccessFault), - } - Ok(()) + /// Store `size`-bit data to the memory. + pub fn write(&mut self, addr: u64, value: u64, size: u8) -> Result<(), Exception> { + match size { + BYTE => self.write8(addr, value), + HALFWORD => self.write16(addr, value), + WORD => self.write32(addr, value), + DOUBLEWORD => self.write64(addr, value), + _ => return Err(Exception::StoreAMOAccessFault), } + Ok(()) + } - /// Write a byte to the memory. - fn write8(&mut self, addr: u64, val: u64) { - let index = (addr - DRAM_BASE) as usize; - self.dram[index] = val as u8 - } + /// Write a byte to the memory. + fn write8(&mut self, addr: u64, val: u64) { + let index = (addr - DRAM_BASE) as usize; + self.dram[index] = val as u8 + } - /// Write 2 bytes to the memory with little endian. - fn write16(&mut self, addr: u64, val: u64) { - let index = (addr - DRAM_BASE) as usize; - self.dram[index] = (val & 0xff) as u8; - self.dram[index + 1] = ((val >> 8) & 0xff) as u8; - } + /// Write 2 bytes to the memory with little endian. + fn write16(&mut self, addr: u64, val: u64) { + let index = (addr - DRAM_BASE) as usize; + self.dram[index] = (val & 0xff) as u8; + self.dram[index + 1] = ((val >> 8) & 0xff) as u8; + } - /// Write 4 bytes to the memory with little endian. - fn write32(&mut self, addr: u64, val: u64) { - let index = (addr - DRAM_BASE) as usize; - self.dram[index] = (val & 0xff) as u8; - self.dram[index + 1] = ((val >> 8) & 0xff) as u8; - self.dram[index + 2] = ((val >> 16) & 0xff) as u8; - self.dram[index + 3] = ((val >> 24) & 0xff) as u8; - } + /// Write 4 bytes to the memory with little endian. + fn write32(&mut self, addr: u64, val: u64) { + let index = (addr - DRAM_BASE) as usize; + self.dram[index] = (val & 0xff) as u8; + self.dram[index + 1] = ((val >> 8) & 0xff) as u8; + self.dram[index + 2] = ((val >> 16) & 0xff) as u8; + self.dram[index + 3] = ((val >> 24) & 0xff) as u8; + } - /// Write 8 bytes to the memory with little endian. - fn write64(&mut self, addr: u64, val: u64) { - let index = (addr - DRAM_BASE) as usize; - self.dram[index] = (val & 0xff) as u8; - self.dram[index + 1] = ((val >> 8) & 0xff) as u8; - self.dram[index + 2] = ((val >> 16) & 0xff) as u8; - self.dram[index + 3] = ((val >> 24) & 0xff) as u8; - self.dram[index + 4] = ((val >> 32) & 0xff) as u8; - self.dram[index + 5] = ((val >> 40) & 0xff) as u8; - self.dram[index + 6] = ((val >> 48) & 0xff) as u8; - self.dram[index + 7] = ((val >> 56) & 0xff) as u8; - } + /// Write 8 bytes to the memory with little endian. + fn write64(&mut self, addr: u64, val: u64) { + let index = (addr - DRAM_BASE) as usize; + self.dram[index] = (val & 0xff) as u8; + self.dram[index + 1] = ((val >> 8) & 0xff) as u8; + self.dram[index + 2] = ((val >> 16) & 0xff) as u8; + self.dram[index + 3] = ((val >> 24) & 0xff) as u8; + self.dram[index + 4] = ((val >> 32) & 0xff) as u8; + self.dram[index + 5] = ((val >> 40) & 0xff) as u8; + self.dram[index + 6] = ((val >> 48) & 0xff) as u8; + self.dram[index + 7] = ((val >> 56) & 0xff) as u8; + } - /// Read a byte from the memory. - fn read8(&self, addr: u64) -> u64 { - let index = (addr - DRAM_BASE) as usize; - self.dram[index] as u64 - } + /// Read a byte from the memory. + fn read8(&self, addr: u64) -> u64 { + let index = (addr - DRAM_BASE) as usize; + self.dram[index] as u64 + } - /// Read 2 bytes from the memory with little endian. - fn read16(&self, addr: u64) -> u64 { - let index = (addr - DRAM_BASE) as usize; - return (self.dram[index] as u64) | ((self.dram[index + 1] as u64) << 8); - } + /// Read 2 bytes from the memory with little endian. + fn read16(&self, addr: u64) -> u64 { + let index = (addr - DRAM_BASE) as usize; + return (self.dram[index] as u64) | ((self.dram[index + 1] as u64) << 8); + } - /// Read 4 bytes from the memory with little endian. - fn read32(&self, addr: u64) -> u64 { - let index = (addr - DRAM_BASE) as usize; - return (self.dram[index] as u64) - | ((self.dram[index + 1] as u64) << 8) - | ((self.dram[index + 2] as u64) << 16) - | ((self.dram[index + 3] as u64) << 24); - } + /// Read 4 bytes from the memory with little endian. + fn read32(&self, addr: u64) -> u64 { + let index = (addr - DRAM_BASE) as usize; + return (self.dram[index] as u64) + | ((self.dram[index + 1] as u64) << 8) + | ((self.dram[index + 2] as u64) << 16) + | ((self.dram[index + 3] as u64) << 24); + } - /// Read 8 bytes from the memory with little endian. - fn read64(&self, addr: u64) -> u64 { - let index = (addr - DRAM_BASE) as usize; - return (self.dram[index] as u64) - | ((self.dram[index + 1] as u64) << 8) - | ((self.dram[index + 2] as u64) << 16) - | ((self.dram[index + 3] as u64) << 24) - | ((self.dram[index + 4] as u64) << 32) - | ((self.dram[index + 5] as u64) << 40) - | ((self.dram[index + 6] as u64) << 48) - | ((self.dram[index + 7] as u64) << 56); - } + /// Read 8 bytes from the memory with little endian. + fn read64(&self, addr: u64) -> u64 { + let index = (addr - DRAM_BASE) as usize; + return (self.dram[index] as u64) + | ((self.dram[index + 1] as u64) << 8) + | ((self.dram[index + 2] as u64) << 16) + | ((self.dram[index + 3] as u64) << 24) + | ((self.dram[index + 4] as u64) << 32) + | ((self.dram[index + 5] as u64) << 40) + | ((self.dram[index + 6] as u64) << 48) + | ((self.dram[index + 7] as u64) << 56); + } } diff --git a/src/emulator.rs b/src/emulator.rs index 14af18b..177d038 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -5,148 +5,148 @@ use crate::exception::Trap; /// The emulator to hold a CPU. pub struct Emulator { - /// The CPU which is the core implementation of this emulator. - pub cpu: Cpu, - /// The debug flag. Output messages if it's true, otherwise output nothing. - pub is_debug: bool, + /// The CPU which is the core implementation of this emulator. + pub cpu: Cpu, + /// The debug flag. Output messages if it's true, otherwise output nothing. + pub is_debug: bool, } impl Emulator { - /// Constructor for an emulator. - pub fn new() -> Emulator { - Self { - cpu: Cpu::new(), - is_debug: false, - } - } - - /// Reset CPU state. - pub fn reset(&mut self) { - self.cpu.reset() - } - - /// Set binary data to the beginning of the DRAM from the emulator console. - pub fn initialize_dram(&mut self, data: Vec) { - self.cpu.bus.initialize_dram(data); - } - - /// Set binary data to the virtio disk from the emulator console. - pub fn initialize_disk(&mut self, data: Vec) { - self.cpu.bus.initialize_disk(data); + /// Constructor for an emulator. + pub fn new() -> Emulator { + Self { + cpu: Cpu::new(), + is_debug: false, } - - /// Set the program counter to the CPU field. - pub fn initialize_pc(&mut self, pc: u64) { - self.cpu.pc = pc; + } + + /// Reset CPU state. + pub fn reset(&mut self) { + self.cpu.reset() + } + + /// Set binary data to the beginning of the DRAM from the emulator console. + pub fn initialize_dram(&mut self, data: Vec) { + self.cpu.bus.initialize_dram(data); + } + + /// Set binary data to the virtio disk from the emulator console. + pub fn initialize_disk(&mut self, data: Vec) { + self.cpu.bus.initialize_disk(data); + } + + /// Set the program counter to the CPU field. + pub fn initialize_pc(&mut self, pc: u64) { + self.cpu.pc = pc; + } + + /// Start executing the emulator with limited range of program. This method is for test. + /// No interrupts happen. + pub fn test_start(&mut self, start: u64, end: u64) { + println!("----- test start -----"); + let mut count = 0; + loop { + count += 1; + if self.cpu.pc < start || end <= self.cpu.pc { + return; + } + // This is a workaround for unit tests to finish the execution. + if count > 1000 { + return; + } + + match self.cpu.execute() { + Ok(inst) => { + println!("pc: {:#x}, inst: {:#x}", self.cpu.pc.wrapping_sub(4), inst); + Trap::Requested + } + Err(exception) => { + println!("pc: {:#x}, exception: {:?}", self.cpu.pc, exception); + exception.take_trap(&mut self.cpu) + } + }; } + } + + /// Start executing the emulator for debug. + fn debug_start(&mut self) { + let mut count = 0; + loop { + count += 1; + if self.cpu.is_count && count > 50000000 { + return; + } + + // Run a cycle on peripheral devices. + self.cpu.devices_increment(); + + // Take an interrupt. + match self.cpu.check_pending_interrupt() { + Some(interrupt) => interrupt.take_trap(&mut self.cpu), + None => {} + } + + // Execute an instruction. + let trap = match self.cpu.execute() { + Ok(inst) => { + if self.is_debug { + println!( + "pc: {:#x}, inst: {:#x}, is_inst 16? {} pre_inst: {:#x}", + self.cpu.pc.wrapping_sub(4), + inst, + // Check if an instruction is one of the compressed instructions. + inst & 0b11 == 0 || inst & 0b11 == 1 || inst & 0b11 == 2, + self.cpu.pre_inst, + ); + } + // Return a placeholder trap. + Trap::Requested + } + Err(exception) => exception.take_trap(&mut self.cpu), + }; - /// Start executing the emulator with limited range of program. This method is for test. - /// No interrupts happen. - pub fn test_start(&mut self, start: u64, end: u64) { - println!("----- test start -----"); - let mut count = 0; - loop { - count += 1; - if self.cpu.pc < start || end <= self.cpu.pc { - return; - } - // This is a workaround for unit tests to finish the execution. - if count > 1000 { - return; - } - - match self.cpu.execute() { - Ok(inst) => { - println!("pc: {:#x}, inst: {:#x}", self.cpu.pc.wrapping_sub(4), inst); - Trap::Requested - } - Err(exception) => { - println!("pc: {:#x}, exception: {:?}", self.cpu.pc, exception); - exception.take_trap(&mut self.cpu) - } - }; + match trap { + Trap::Fatal => { + println!("pc: {:#x}, trap {:#?}", self.cpu.pc, trap); + return; } + _ => {} + } } + } - /// Start executing the emulator for debug. - fn debug_start(&mut self) { - let mut count = 0; - loop { - count += 1; - if self.cpu.is_count && count > 50000000 { - return; - } - - // Run a cycle on peripheral devices. - self.cpu.devices_increment(); - - // Take an interrupt. - match self.cpu.check_pending_interrupt() { - Some(interrupt) => interrupt.take_trap(&mut self.cpu), - None => {} - } - - // Execute an instruction. - let trap = match self.cpu.execute() { - Ok(inst) => { - if self.is_debug { - println!( - "pc: {:#x}, inst: {:#x}, is_inst 16? {} pre_inst: {:#x}", - self.cpu.pc.wrapping_sub(4), - inst, - // Check if an instruction is one of the compressed instructions. - inst & 0b11 == 0 || inst & 0b11 == 1 || inst & 0b11 == 2, - self.cpu.pre_inst, - ); - } - // Return a placeholder trap. - Trap::Requested - } - Err(exception) => exception.take_trap(&mut self.cpu), - }; - - match trap { - Trap::Fatal => { - println!("pc: {:#x}, trap {:#?}", self.cpu.pc, trap); - return; - } - _ => {} - } - } + /// Start executing the emulator. + pub fn start(&mut self) { + if self.is_debug || self.cpu.is_count { + self.debug_start(); } - /// Start executing the emulator. - pub fn start(&mut self) { - if self.is_debug || self.cpu.is_count { - self.debug_start(); + loop { + // Run a cycle on peripheral devices. + self.cpu.devices_increment(); + + // Take an interrupt. + match self.cpu.check_pending_interrupt() { + Some(interrupt) => interrupt.take_trap(&mut self.cpu), + None => {} + } + + // Execute an instruction. + let trap = match self.cpu.execute() { + Ok(_) => { + // Return a placeholder trap. + Trap::Requested } + Err(exception) => exception.take_trap(&mut self.cpu), + }; - loop { - // Run a cycle on peripheral devices. - self.cpu.devices_increment(); - - // Take an interrupt. - match self.cpu.check_pending_interrupt() { - Some(interrupt) => interrupt.take_trap(&mut self.cpu), - None => {} - } - - // Execute an instruction. - let trap = match self.cpu.execute() { - Ok(_) => { - // Return a placeholder trap. - Trap::Requested - } - Err(exception) => exception.take_trap(&mut self.cpu), - }; - - match trap { - Trap::Fatal => { - println!("pc: {:#x}, trap {:#?}", self.cpu.pc, trap); - return; - } - _ => {} - } + match trap { + Trap::Fatal => { + println!("pc: {:#x}, trap {:#?}", self.cpu.pc, trap); + return; } + _ => {} + } } + } } diff --git a/src/exception.rs b/src/exception.rs index 5f3092f..8355f93 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,75 +1,75 @@ //! The exception module contains all the exception kinds and the function to handle exceptions. use crate::{ - cpu::{Cpu, Mode}, - csr::*, + cpu::{Cpu, Mode}, + csr::*, }; /// All the exception kinds. #[derive(Debug, PartialEq)] pub enum Exception { - /// With the addition of the C extension, no instructions can raise - /// instruction-address-misaligned exceptions. - InstructionAddressMisaligned, - InstructionAccessFault, - IllegalInstruction(u64), - Breakpoint, - LoadAddressMisaligned, - LoadAccessFault, - StoreAMOAddressMisaligned, - StoreAMOAccessFault, - EnvironmentCallFromUMode, - EnvironmentCallFromSMode, - EnvironmentCallFromMMode, - // Stores a trap value (the faulting address) for page fault exceptions. - InstructionPageFault(u64), - LoadPageFault(u64), - StoreAMOPageFault(u64), + /// With the addition of the C extension, no instructions can raise + /// instruction-address-misaligned exceptions. + InstructionAddressMisaligned, + InstructionAccessFault, + IllegalInstruction(u64), + Breakpoint, + LoadAddressMisaligned, + LoadAccessFault, + StoreAMOAddressMisaligned, + StoreAMOAccessFault, + EnvironmentCallFromUMode, + EnvironmentCallFromSMode, + EnvironmentCallFromMMode, + // Stores a trap value (the faulting address) for page fault exceptions. + InstructionPageFault(u64), + LoadPageFault(u64), + StoreAMOPageFault(u64), } /// All the trap kinds. #[derive(Debug)] pub enum Trap { - /// The trap is visible to, and handled by, software running inside the execution - /// environment. - Contained, - /// The trap is a synchronous exception that is an explicit call to the execution - /// environment requesting an action on behalf of software inside the execution environment. - Requested, - /// The trap is handled transparently by the execution environment and execution - /// resumes normally after the trap is handled. - Invisible, - /// The trap represents a fatal failure and causes the execution environment to terminate - /// execution. - Fatal, + /// The trap is visible to, and handled by, software running inside the execution + /// environment. + Contained, + /// The trap is a synchronous exception that is an explicit call to the execution + /// environment requesting an action on behalf of software inside the execution environment. + Requested, + /// The trap is handled transparently by the execution environment and execution + /// resumes normally after the trap is handled. + Invisible, + /// The trap represents a fatal failure and causes the execution environment to terminate + /// execution. + Fatal, } impl Exception { - fn exception_code(&self) -> u64 { - match self { - Exception::InstructionAddressMisaligned => 0, - Exception::InstructionAccessFault => 1, - Exception::IllegalInstruction(_) => 2, - Exception::Breakpoint => 3, - Exception::LoadAddressMisaligned => 4, - Exception::LoadAccessFault => 5, - Exception::StoreAMOAddressMisaligned => 6, - Exception::StoreAMOAccessFault => 7, - Exception::EnvironmentCallFromUMode => 8, - Exception::EnvironmentCallFromSMode => 9, - Exception::EnvironmentCallFromMMode => 11, - Exception::InstructionPageFault(_) => 12, - Exception::LoadPageFault(_) => 13, - Exception::StoreAMOPageFault(_) => 15, - } + fn exception_code(&self) -> u64 { + match self { + Exception::InstructionAddressMisaligned => 0, + Exception::InstructionAccessFault => 1, + Exception::IllegalInstruction(_) => 2, + Exception::Breakpoint => 3, + Exception::LoadAddressMisaligned => 4, + Exception::LoadAccessFault => 5, + Exception::StoreAMOAddressMisaligned => 6, + Exception::StoreAMOAccessFault => 7, + Exception::EnvironmentCallFromUMode => 8, + Exception::EnvironmentCallFromSMode => 9, + Exception::EnvironmentCallFromMMode => 11, + Exception::InstructionPageFault(_) => 12, + Exception::LoadPageFault(_) => 13, + Exception::StoreAMOPageFault(_) => 15, } - - fn epc(&self, pc: u64) -> u64 { - // 3.2.1 Environment Call and Breakpoint - // "ECALL and EBREAK cause the receiving privilege mode’s epc register to be set to the - // address of the ECALL or EBREAK instruction itself, not the address of the following - // instruction." - match self { + } + + fn epc(&self, pc: u64) -> u64 { + // 3.2.1 Environment Call and Breakpoint + // "ECALL and EBREAK cause the receiving privilege mode’s epc register to be set to the + // address of the ECALL or EBREAK instruction itself, not the address of the following + // instruction." + match self { Exception::Breakpoint | Exception::EnvironmentCallFromUMode | Exception::EnvironmentCallFromSMode @@ -80,155 +80,151 @@ impl Exception { | Exception::StoreAMOPageFault(_) => pc, _ => pc.wrapping_add(4), } + } + + fn trap_value(&self, pc: u64) -> u64 { + // 3.1.17 Machine Trap Value Register (mtval) + // 4.1.9 Supervisor Trap Value Register (stval) + // "When a hardware breakpoint is triggered, or an address-misaligned, access-fault, or + // page-fault exception occurs on an instruction fetch, load, or store, mtval (stval) is + // written with the faulting virtual address. On an illegal instruction trap, mtval (stval) + // may be written with the first XLEN or ILEN bits of the faulting instruction as described + // below. For other traps, mtval (stval) is set to zero, but a future standard may redefine + // mtval's (stval's) setting for other traps." + match self { + Exception::InstructionAddressMisaligned + | Exception::InstructionAccessFault + | Exception::Breakpoint + | Exception::LoadAddressMisaligned + | Exception::LoadAccessFault + | Exception::StoreAMOAddressMisaligned + | Exception::StoreAMOAccessFault => pc, + Exception::InstructionPageFault(val) | Exception::LoadPageFault(val) | Exception::StoreAMOPageFault(val) => *val, + Exception::IllegalInstruction(val) => *val, + _ => 0, } - - fn trap_value(&self, pc: u64) -> u64 { - // 3.1.17 Machine Trap Value Register (mtval) - // 4.1.9 Supervisor Trap Value Register (stval) - // "When a hardware breakpoint is triggered, or an address-misaligned, access-fault, or - // page-fault exception occurs on an instruction fetch, load, or store, mtval (stval) is - // written with the faulting virtual address. On an illegal instruction trap, mtval (stval) - // may be written with the first XLEN or ILEN bits of the faulting instruction as described - // below. For other traps, mtval (stval) is set to zero, but a future standard may redefine - // mtval's (stval's) setting for other traps." - match self { - Exception::InstructionAddressMisaligned - | Exception::InstructionAccessFault - | Exception::Breakpoint - | Exception::LoadAddressMisaligned - | Exception::LoadAccessFault - | Exception::StoreAMOAddressMisaligned - | Exception::StoreAMOAccessFault => pc, - Exception::InstructionPageFault(val) - | Exception::LoadPageFault(val) - | Exception::StoreAMOPageFault(val) => *val, - Exception::IllegalInstruction(val) => *val, - _ => 0, - } + } + + /// Update CSRs and the program counter depending on an exception. + pub fn take_trap(&self, cpu: &mut Cpu) -> Trap { + // 1.2 Privilege Levels + // "Traps that increase privilege level are termed vertical traps, while traps that remain + // at the same privilege level are termed horizontal traps." + + let exception_pc = self.epc(cpu.pc); + let previous_mode = cpu.mode; + let cause = self.exception_code(); + + // 3.1.8 Machine Trap Delegation Registers (medeleg and mideleg) + // "By default, all traps at any privilege level are handled in machine mode" + // "To increase performance, implementations can provide individual read/write bits within + // medeleg and mideleg to indicate that certain exceptions and interrupts should be + // processed directly by a lower privilege level." + // + // "medeleg has a bit position allocated for every synchronous exception shown in Table 3.6 + // on page 37, with the index of the bit position equal to the value returned in the mcause + // register (i.e., setting bit 8 allows user-mode environment calls to be delegated to a + // lower-privilege trap handler)." + if previous_mode <= Mode::Supervisor && ((cpu.state.read(MEDELEG) >> cause) & 1) == 1 { + // Handle the trap in S-mode. + cpu.mode = Mode::Supervisor; + + // Set the program counter to the supervisor trap-handler base address (stvec). + cpu.pc = (cpu.state.read(STVEC) & !1) as u64; + + // 4.1.9 Supervisor Exception Program Counter (sepc) + // "The low bit of sepc (sepc[0]) is always zero." + // "When a trap is taken into S-mode, sepc is written with the virtual address of + // the instruction that was interrupted or that encountered the exception. + // Otherwise, sepc is never written by the implementation, though it may be + // explicitly written by software." + cpu.state.write(SEPC, exception_pc & !1); + + // 4.1.10 Supervisor Cause Register (scause) + // "When a trap is taken into S-mode, scause is written with a code indicating + // the event that caused the trap. Otherwise, scause is never written by the + // implementation, though it may be explicitly written by software." + cpu.state.write(SCAUSE, cause); + + // 4.1.11 Supervisor Trap Value (stval) Register + // "When a trap is taken into S-mode, stval is written with exception-specific + // information to assist software in handling the trap. Otherwise, stval is never + // written by the implementation, though it may be explicitly written by software." + cpu.state.write(STVAL, self.trap_value(exception_pc)); + + // Set a previous interrupt-enable bit for supervisor mode (SPIE, 5) to the value + // of a global interrupt-enable bit for supervisor mode (SIE, 1). + cpu + .state + .write_sstatus(XSTATUS_SPIE, cpu.state.read_sstatus(XSTATUS_SIE)); + // Set a global interrupt-enable bit for supervisor mode (SIE, 1) to 0. + cpu.state.write_sstatus(XSTATUS_SIE, 0); + // 4.1.1 Supervisor Status Register (sstatus) + // "When a trap is taken, SPP is set to 0 if the trap originated from user mode, or + // 1 otherwise." + match previous_mode { + Mode::User => cpu.state.write_sstatus(XSTATUS_SPP, 0), + _ => cpu.state.write_sstatus(XSTATUS_SPP, 1), + } + } else { + // Handle the trap in M-mode. + cpu.mode = Mode::Machine; + + // Set the program counter to the machine trap-handler base address (mtvec). + cpu.pc = (cpu.state.read(MTVEC) & !1) as u64; + + // 3.1.15 Machine Exception Program Counter (mepc) + // "The low bit of mepc (mepc[0]) is always zero." + // "When a trap is taken into M-mode, mepc is written with the virtual address of + // the instruction that was interrupted or that encountered the exception. + // Otherwise, mepc is never written by the implementation, though it may be + // explicitly written by software." + cpu.state.write(MEPC, exception_pc & !1); + + // 3.1.16 Machine Cause Register (mcause) + // "When a trap is taken into M-mode, mcause is written with a code indicating + // the event that caused the trap. Otherwise, mcause is never written by the + // implementation, though it may be explicitly written by software." + cpu.state.write(MCAUSE, cause); + + // 3.1.17 Machine Trap Value (mtval) Register + // "When a trap is taken into M-mode, mtval is either set to zero or written with + // exception-specific information to assist software in handling the trap. + // Otherwise, mtval is never written by the implementation, though it may be + // explicitly written by software." + cpu.state.write(MTVAL, self.trap_value(exception_pc)); + + // Set a previous interrupt-enable bit for machine mode (MPIE, 7) to the value + // of a global interrupt-enable bit for machine mode (MIE, 3). + cpu + .state + .write_mstatus(MSTATUS_MPIE, cpu.state.read_mstatus(MSTATUS_MIE)); + // Set a global interrupt-enable bit for machine mode (MIE, 3) to 0. + cpu.state.write_mstatus(MSTATUS_MIE, 0); + // When a trap is taken from privilege mode y into privilege mode x, xPIE is set + // to the value of x IE; x IE is set to 0; and xPP is set to y. + match previous_mode { + Mode::User => cpu.state.write_mstatus(MSTATUS_MPP, Mode::User as u64), + Mode::Supervisor => cpu.state.write_mstatus(MSTATUS_MPP, Mode::Supervisor as u64), + Mode::Machine => cpu.state.write_mstatus(MSTATUS_MPP, Mode::Machine as u64), + _ => panic!("previous privilege mode is invalid"), + } } - /// Update CSRs and the program counter depending on an exception. - pub fn take_trap(&self, cpu: &mut Cpu) -> Trap { - // 1.2 Privilege Levels - // "Traps that increase privilege level are termed vertical traps, while traps that remain - // at the same privilege level are termed horizontal traps." - - let exception_pc = self.epc(cpu.pc); - let previous_mode = cpu.mode; - let cause = self.exception_code(); - - // 3.1.8 Machine Trap Delegation Registers (medeleg and mideleg) - // "By default, all traps at any privilege level are handled in machine mode" - // "To increase performance, implementations can provide individual read/write bits within - // medeleg and mideleg to indicate that certain exceptions and interrupts should be - // processed directly by a lower privilege level." - // - // "medeleg has a bit position allocated for every synchronous exception shown in Table 3.6 - // on page 37, with the index of the bit position equal to the value returned in the mcause - // register (i.e., setting bit 8 allows user-mode environment calls to be delegated to a - // lower-privilege trap handler)." - if previous_mode <= Mode::Supervisor && ((cpu.state.read(MEDELEG) >> cause) & 1) == 1 { - // Handle the trap in S-mode. - cpu.mode = Mode::Supervisor; - - // Set the program counter to the supervisor trap-handler base address (stvec). - cpu.pc = (cpu.state.read(STVEC) & !1) as u64; - - // 4.1.9 Supervisor Exception Program Counter (sepc) - // "The low bit of sepc (sepc[0]) is always zero." - // "When a trap is taken into S-mode, sepc is written with the virtual address of - // the instruction that was interrupted or that encountered the exception. - // Otherwise, sepc is never written by the implementation, though it may be - // explicitly written by software." - cpu.state.write(SEPC, exception_pc & !1); - - // 4.1.10 Supervisor Cause Register (scause) - // "When a trap is taken into S-mode, scause is written with a code indicating - // the event that caused the trap. Otherwise, scause is never written by the - // implementation, though it may be explicitly written by software." - cpu.state.write(SCAUSE, cause); - - // 4.1.11 Supervisor Trap Value (stval) Register - // "When a trap is taken into S-mode, stval is written with exception-specific - // information to assist software in handling the trap. Otherwise, stval is never - // written by the implementation, though it may be explicitly written by software." - cpu.state.write(STVAL, self.trap_value(exception_pc)); - - // Set a previous interrupt-enable bit for supervisor mode (SPIE, 5) to the value - // of a global interrupt-enable bit for supervisor mode (SIE, 1). - cpu.state - .write_sstatus(XSTATUS_SPIE, cpu.state.read_sstatus(XSTATUS_SIE)); - // Set a global interrupt-enable bit for supervisor mode (SIE, 1) to 0. - cpu.state.write_sstatus(XSTATUS_SIE, 0); - // 4.1.1 Supervisor Status Register (sstatus) - // "When a trap is taken, SPP is set to 0 if the trap originated from user mode, or - // 1 otherwise." - match previous_mode { - Mode::User => cpu.state.write_sstatus(XSTATUS_SPP, 0), - _ => cpu.state.write_sstatus(XSTATUS_SPP, 1), - } - } else { - // Handle the trap in M-mode. - cpu.mode = Mode::Machine; - - // Set the program counter to the machine trap-handler base address (mtvec). - cpu.pc = (cpu.state.read(MTVEC) & !1) as u64; - - // 3.1.15 Machine Exception Program Counter (mepc) - // "The low bit of mepc (mepc[0]) is always zero." - // "When a trap is taken into M-mode, mepc is written with the virtual address of - // the instruction that was interrupted or that encountered the exception. - // Otherwise, mepc is never written by the implementation, though it may be - // explicitly written by software." - cpu.state.write(MEPC, exception_pc & !1); - - // 3.1.16 Machine Cause Register (mcause) - // "When a trap is taken into M-mode, mcause is written with a code indicating - // the event that caused the trap. Otherwise, mcause is never written by the - // implementation, though it may be explicitly written by software." - cpu.state.write(MCAUSE, cause); - - // 3.1.17 Machine Trap Value (mtval) Register - // "When a trap is taken into M-mode, mtval is either set to zero or written with - // exception-specific information to assist software in handling the trap. - // Otherwise, mtval is never written by the implementation, though it may be - // explicitly written by software." - cpu.state.write(MTVAL, self.trap_value(exception_pc)); - - // Set a previous interrupt-enable bit for machine mode (MPIE, 7) to the value - // of a global interrupt-enable bit for machine mode (MIE, 3). - cpu.state - .write_mstatus(MSTATUS_MPIE, cpu.state.read_mstatus(MSTATUS_MIE)); - // Set a global interrupt-enable bit for machine mode (MIE, 3) to 0. - cpu.state.write_mstatus(MSTATUS_MIE, 0); - // When a trap is taken from privilege mode y into privilege mode x, xPIE is set - // to the value of x IE; x IE is set to 0; and xPP is set to y. - match previous_mode { - Mode::User => cpu.state.write_mstatus(MSTATUS_MPP, Mode::User as u64), - Mode::Supervisor => cpu - .state - .write_mstatus(MSTATUS_MPP, Mode::Supervisor as u64), - Mode::Machine => cpu.state.write_mstatus(MSTATUS_MPP, Mode::Machine as u64), - _ => panic!("previous privilege mode is invalid"), - } - } - - match self { - Exception::InstructionAddressMisaligned | Exception::InstructionAccessFault => { - Trap::Fatal - } - Exception::IllegalInstruction(_) => Trap::Invisible, - Exception::Breakpoint => Trap::Requested, - Exception::LoadAddressMisaligned - | Exception::LoadAccessFault - | Exception::StoreAMOAddressMisaligned - | Exception::StoreAMOAccessFault => Trap::Fatal, - Exception::EnvironmentCallFromUMode - | Exception::EnvironmentCallFromSMode - | Exception::EnvironmentCallFromMMode => Trap::Fatal, - Exception::InstructionPageFault(_) - | Exception::LoadPageFault(_) - | Exception::StoreAMOPageFault(_) => Trap::Invisible, - } + match self { + Exception::InstructionAddressMisaligned | Exception::InstructionAccessFault => Trap::Fatal, + Exception::IllegalInstruction(_) => Trap::Invisible, + Exception::Breakpoint => Trap::Requested, + Exception::LoadAddressMisaligned + | Exception::LoadAccessFault + | Exception::StoreAMOAddressMisaligned + | Exception::StoreAMOAccessFault => Trap::Fatal, + Exception::EnvironmentCallFromUMode + | Exception::EnvironmentCallFromSMode + | Exception::EnvironmentCallFromMMode => Trap::Fatal, + Exception::InstructionPageFault(_) | Exception::LoadPageFault(_) | Exception::StoreAMOPageFault(_) => { + Trap::Invisible + } } + } } diff --git a/src/interrupt.rs b/src/interrupt.rs index c4b9805..75761c6 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -1,168 +1,168 @@ //! The interrupt module contains all the interrupt kinds and the function to handle interrupts. use crate::{ - cpu::{Cpu, Mode}, - csr::*, + cpu::{Cpu, Mode}, + csr::*, }; /// All the interrupt kinds. #[derive(Debug)] pub enum Interrupt { - UserSoftwareInterrupt, - SupervisorSoftwareInterrupt, - MachineSoftwareInterrupt, - UserTimerInterrupt, - SupervisorTimerInterrupt, - MachineTimerInterrupt, - UserExternalInterrupt, - SupervisorExternalInterrupt, - MachineExternalInterrupt, + UserSoftwareInterrupt, + SupervisorSoftwareInterrupt, + MachineSoftwareInterrupt, + UserTimerInterrupt, + SupervisorTimerInterrupt, + MachineTimerInterrupt, + UserExternalInterrupt, + SupervisorExternalInterrupt, + MachineExternalInterrupt, } impl Interrupt { - fn exception_code(&self) -> u64 { - match self { - Interrupt::UserSoftwareInterrupt => 0, - Interrupt::SupervisorSoftwareInterrupt => 1, - Interrupt::MachineSoftwareInterrupt => 3, - Interrupt::UserTimerInterrupt => 4, - Interrupt::SupervisorTimerInterrupt => 5, - Interrupt::MachineTimerInterrupt => 7, - Interrupt::UserExternalInterrupt => 8, - Interrupt::SupervisorExternalInterrupt => 9, - Interrupt::MachineExternalInterrupt => 11, - } + fn exception_code(&self) -> u64 { + match self { + Interrupt::UserSoftwareInterrupt => 0, + Interrupt::SupervisorSoftwareInterrupt => 1, + Interrupt::MachineSoftwareInterrupt => 3, + Interrupt::UserTimerInterrupt => 4, + Interrupt::SupervisorTimerInterrupt => 5, + Interrupt::MachineTimerInterrupt => 7, + Interrupt::UserExternalInterrupt => 8, + Interrupt::SupervisorExternalInterrupt => 9, + Interrupt::MachineExternalInterrupt => 11, } - - /// Update CSRs and the program counter depending on an interrupt. - pub fn take_trap(&self, cpu: &mut Cpu) { - // 1.2 Privilege Levels - // "Traps that increase privilege level are termed vertical traps, while traps that remain - // at the same privilege level are termed horizontal traps." - - cpu.idle = false; - - let exception_pc = cpu.pc; - let previous_mode = cpu.mode; - let cause = self.exception_code(); - - // 3.1.8 Machine Trap Delegation Registers (medeleg and mideleg) - // "By default, all traps at any privilege level are handled in machine mode To increase - // performance, implementations can provide individual read/write bits within medeleg and - // mideleg to indicate that certain exceptions and interrupts should be processed directly - // by a lower privilege level." - // - // "mideleg holds trap delegation bits for individual interrupts, with the layout of bits - // matching those in the mip register (i.e., STIP interrupt delegation control is located - // in bit 5)." - // TODO: Why should a M-mode timer interrupt be taken in M-mode? - if previous_mode <= Mode::Supervisor - && ((cpu.state.read(MIDELEG) >> cause) & 1) == 1 - && cause != Interrupt::MachineTimerInterrupt.exception_code() - { - // Handle the trap in S-mode. - cpu.mode = Mode::Supervisor; - - // Set the program counter to the supervisor trap-handler base address (stvec) - // depending on the mode. - let vector = match cpu.state.read_bit(STVEC, 0) { - 1 => 4 * cause, // vectored mode - _ => 0, // direct mode - }; - cpu.pc = ((cpu.state.read(STVEC) & !1) + vector) as u64; - - // 4.1.9 Supervisor Exception Program Counter (sepc) - // "The low bit of sepc (sepc[0]) is always zero." - // "When a trap is taken into S-mode, sepc is written with the virtual address of - // the instruction that was interrupted or that encountered the exception. - // Otherwise, sepc is never written by the implementation, though it may be - // explicitly written by software." - cpu.state.write(SEPC, exception_pc & !1); - - // 4.1.10 Supervisor Cause Register (scause) - // "When a trap is taken into S-mode, scause is written with a code indicating - // the event that caused the trap. Otherwise, scause is never written by the - // implementation, though it may be explicitly written by software." - cpu.state.write(SCAUSE, 1 << 63 | cause); - - // 4.1.11 Supervisor Trap Value (stval) Register - // "When a trap is taken into S-mode, stval is written with exception-specific - // information to assist software in handling the trap. Otherwise, stval is never - // written by the implementation, though it may be explicitly written by software." - // "When a hardware breakpoint is triggered, or an instruction-fetch, load, or - // store address-misaligned, access, or page-fault exception occurs, stval is - // written with the faulting virtual address. On an illegal instruction trap, - // stval may be written with the first XLEN or ILEN bits of the faulting - // instruction as described below. For other exceptions, stval is set to zero." - cpu.state.write(STVAL, 0); - - // Set a privious interrupt-enable bit for supervisor mode (SPIE, 5) to the value - // of a global interrupt-enable bit for supervisor mode (SIE, 1). - cpu.state - .write_sstatus(XSTATUS_SPIE, cpu.state.read_sstatus(XSTATUS_SIE)); - // Set a global interrupt-enable bit for supervisor mode (SIE, 1) to 0. - cpu.state.write_sstatus(XSTATUS_SIE, 0); - // 4.1.1 Supervisor Status Register (sstatus) - // "When a trap is taken, SPP is set to 0 if the trap originated from user mode, or - // 1 otherwise." - match previous_mode { - Mode::User => cpu.state.write_sstatus(XSTATUS_SPP, 0), - _ => cpu.state.write_sstatus(XSTATUS_SPP, 1), - } - } else { - // Handle the trap in M-mode. - cpu.mode = Mode::Machine; - - // Set the program counter to the machine trap-handler base address (mtvec) - // depending on the mode. - let vector = match cpu.state.read_bit(MTVEC, 0) { - 1 => 4 * cause, // vectored mode - _ => 0, // direct mode - }; - cpu.pc = ((cpu.state.read(MTVEC) & !1) + vector) as u64; - - // 3.1.15 Machine Exception Program Counter (mepc) - // "The low bit of mepc (mepc[0]) is always zero." - // "When a trap is taken into M-mode, mepc is written with the virtual address of - // the instruction that was interrupted or that encountered the exception. - // Otherwise, mepc is never written by the implementation, though it may be - // explicitly written by software." - cpu.state.write(MEPC, exception_pc & !1); - - // 3.1.16 Machine Cause Register (mcause) - // "When a trap is taken into M-mode, mcause is written with a code indicating - // the event that caused the trap. Otherwise, mcause is never written by the - // implementation, though it may be explicitly written by software." - cpu.state.write(MCAUSE, 1 << 63 | cause); - - // 3.1.17 Machine Trap Value (mtval) Register - // "When a trap is taken into M-mode, mtval is either set to zero or written with - // exception-specific information to assist software in handling the trap. - // Otherwise, mtval is never written by the implementation, though it may be - // explicitly written by software." - // "When a hardware breakpoint is triggered, or an instruction-fetch, load, or - // store address-misaligned, access, or page-fault exception occurs, mtval is - // written with the faulting virtual address. On an illegal instruction trap, - // mtval may be written with the first XLEN or ILEN bits of the faulting - // instruction as described below. For other traps, mtval is set to zero." - cpu.state.write(MTVAL, 0); - - // Set a previous interrupt-enable bit for machine mode (MPIE, 7) to the value - // of a global interrupt-enable bit for machine mode (MIE, 3). - cpu.state - .write_mstatus(MSTATUS_MPIE, cpu.state.read_mstatus(MSTATUS_MIE)); - // Set a global interrupt-enable bit for machine mode (MIE, 3) to 0. - cpu.state.write_mstatus(MSTATUS_MIE, 0); - // When a trap is taken from privilege mode y into privilege mode x, xPIE is set - // to the value of x IE; x IE is set to 0; and xPP is set to y. - match previous_mode { - Mode::User => cpu.state.write_mstatus(MSTATUS_MPP, Mode::User as u64), - Mode::Supervisor => cpu - .state - .write_mstatus(MSTATUS_MPP, Mode::Supervisor as u64), - Mode::Machine => cpu.state.write_mstatus(MSTATUS_MPP, Mode::Machine as u64), - _ => panic!("previous privilege mode is invalid"), - } - } + } + + /// Update CSRs and the program counter depending on an interrupt. + pub fn take_trap(&self, cpu: &mut Cpu) { + // 1.2 Privilege Levels + // "Traps that increase privilege level are termed vertical traps, while traps that remain + // at the same privilege level are termed horizontal traps." + + cpu.idle = false; + + let exception_pc = cpu.pc; + let previous_mode = cpu.mode; + let cause = self.exception_code(); + + // 3.1.8 Machine Trap Delegation Registers (medeleg and mideleg) + // "By default, all traps at any privilege level are handled in machine mode To increase + // performance, implementations can provide individual read/write bits within medeleg and + // mideleg to indicate that certain exceptions and interrupts should be processed directly + // by a lower privilege level." + // + // "mideleg holds trap delegation bits for individual interrupts, with the layout of bits + // matching those in the mip register (i.e., STIP interrupt delegation control is located + // in bit 5)." + // TODO: Why should a M-mode timer interrupt be taken in M-mode? + if previous_mode <= Mode::Supervisor + && ((cpu.state.read(MIDELEG) >> cause) & 1) == 1 + && cause != Interrupt::MachineTimerInterrupt.exception_code() + { + // Handle the trap in S-mode. + cpu.mode = Mode::Supervisor; + + // Set the program counter to the supervisor trap-handler base address (stvec) + // depending on the mode. + let vector = match cpu.state.read_bit(STVEC, 0) { + 1 => 4 * cause, // vectored mode + _ => 0, // direct mode + }; + cpu.pc = ((cpu.state.read(STVEC) & !1) + vector) as u64; + + // 4.1.9 Supervisor Exception Program Counter (sepc) + // "The low bit of sepc (sepc[0]) is always zero." + // "When a trap is taken into S-mode, sepc is written with the virtual address of + // the instruction that was interrupted or that encountered the exception. + // Otherwise, sepc is never written by the implementation, though it may be + // explicitly written by software." + cpu.state.write(SEPC, exception_pc & !1); + + // 4.1.10 Supervisor Cause Register (scause) + // "When a trap is taken into S-mode, scause is written with a code indicating + // the event that caused the trap. Otherwise, scause is never written by the + // implementation, though it may be explicitly written by software." + cpu.state.write(SCAUSE, 1 << 63 | cause); + + // 4.1.11 Supervisor Trap Value (stval) Register + // "When a trap is taken into S-mode, stval is written with exception-specific + // information to assist software in handling the trap. Otherwise, stval is never + // written by the implementation, though it may be explicitly written by software." + // "When a hardware breakpoint is triggered, or an instruction-fetch, load, or + // store address-misaligned, access, or page-fault exception occurs, stval is + // written with the faulting virtual address. On an illegal instruction trap, + // stval may be written with the first XLEN or ILEN bits of the faulting + // instruction as described below. For other exceptions, stval is set to zero." + cpu.state.write(STVAL, 0); + + // Set a privious interrupt-enable bit for supervisor mode (SPIE, 5) to the value + // of a global interrupt-enable bit for supervisor mode (SIE, 1). + cpu + .state + .write_sstatus(XSTATUS_SPIE, cpu.state.read_sstatus(XSTATUS_SIE)); + // Set a global interrupt-enable bit for supervisor mode (SIE, 1) to 0. + cpu.state.write_sstatus(XSTATUS_SIE, 0); + // 4.1.1 Supervisor Status Register (sstatus) + // "When a trap is taken, SPP is set to 0 if the trap originated from user mode, or + // 1 otherwise." + match previous_mode { + Mode::User => cpu.state.write_sstatus(XSTATUS_SPP, 0), + _ => cpu.state.write_sstatus(XSTATUS_SPP, 1), + } + } else { + // Handle the trap in M-mode. + cpu.mode = Mode::Machine; + + // Set the program counter to the machine trap-handler base address (mtvec) + // depending on the mode. + let vector = match cpu.state.read_bit(MTVEC, 0) { + 1 => 4 * cause, // vectored mode + _ => 0, // direct mode + }; + cpu.pc = ((cpu.state.read(MTVEC) & !1) + vector) as u64; + + // 3.1.15 Machine Exception Program Counter (mepc) + // "The low bit of mepc (mepc[0]) is always zero." + // "When a trap is taken into M-mode, mepc is written with the virtual address of + // the instruction that was interrupted or that encountered the exception. + // Otherwise, mepc is never written by the implementation, though it may be + // explicitly written by software." + cpu.state.write(MEPC, exception_pc & !1); + + // 3.1.16 Machine Cause Register (mcause) + // "When a trap is taken into M-mode, mcause is written with a code indicating + // the event that caused the trap. Otherwise, mcause is never written by the + // implementation, though it may be explicitly written by software." + cpu.state.write(MCAUSE, 1 << 63 | cause); + + // 3.1.17 Machine Trap Value (mtval) Register + // "When a trap is taken into M-mode, mtval is either set to zero or written with + // exception-specific information to assist software in handling the trap. + // Otherwise, mtval is never written by the implementation, though it may be + // explicitly written by software." + // "When a hardware breakpoint is triggered, or an instruction-fetch, load, or + // store address-misaligned, access, or page-fault exception occurs, mtval is + // written with the faulting virtual address. On an illegal instruction trap, + // mtval may be written with the first XLEN or ILEN bits of the faulting + // instruction as described below. For other traps, mtval is set to zero." + cpu.state.write(MTVAL, 0); + + // Set a previous interrupt-enable bit for machine mode (MPIE, 7) to the value + // of a global interrupt-enable bit for machine mode (MIE, 3). + cpu + .state + .write_mstatus(MSTATUS_MPIE, cpu.state.read_mstatus(MSTATUS_MIE)); + // Set a global interrupt-enable bit for machine mode (MIE, 3) to 0. + cpu.state.write_mstatus(MSTATUS_MIE, 0); + // When a trap is taken from privilege mode y into privilege mode x, xPIE is set + // to the value of x IE; x IE is set to 0; and xPP is set to y. + match previous_mode { + Mode::User => cpu.state.write_mstatus(MSTATUS_MPP, Mode::User as u64), + Mode::Supervisor => cpu.state.write_mstatus(MSTATUS_MPP, Mode::Supervisor as u64), + Mode::Machine => cpu.state.write_mstatus(MSTATUS_MPP, Mode::Machine as u64), + _ => panic!("previous privilege mode is invalid"), + } } + } }