From 09d90643726d2a86952473e27b6e5d64543e41a0 Mon Sep 17 00:00:00 2001 From: taitep Date: Wed, 24 Dec 2025 13:56:41 +0100 Subject: [PATCH] EXCEPTION SYSTEM (initial version - may change later) --- Cargo.lock | 31 ++++++++ Cargo.toml | 1 + src/basic_uart.rs | 47 +++++------ src/core.rs | 50 +++++------- src/exceptions.rs | 30 ++++++++ src/instructions.rs | 88 ++++++++++++--------- src/instructions/rvi.rs | 77 +++++++++--------- src/instructions/rvi/mem.rs | 140 +++++++++++++-------------------- src/lib.rs | 1 + src/main.rs | 55 +++++-------- src/mem.rs | 150 +++++++++++++++++------------------- 11 files changed, 338 insertions(+), 332 deletions(-) create mode 100644 src/exceptions.rs diff --git a/Cargo.lock b/Cargo.lock index 11f7aa6..c50db93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,18 @@ dependencies = [ "scroll", ] +[[package]] +name = "int-enum" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366a1634cccc76b4cfd3e7580de9b605e4d93f1edac48d786c1f867c0def495" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + [[package]] name = "libc" version = "0.2.176" @@ -85,6 +97,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", +] + [[package]] name = "quote" version = "1.0.42" @@ -131,6 +155,7 @@ version = "0.0.0" dependencies = [ "anyhow", "goblin", + "int-enum", "memmap2", "nix", ] @@ -140,3 +165,9 @@ name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" diff --git a/Cargo.toml b/Cargo.toml index 91b641a..f76ab77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,6 @@ edition = "2024" [dependencies] anyhow = "1.0.100" goblin = "0.10.4" +int-enum = "1.2.0" memmap2 = "0.9.8" nix = { version = "0.30.1", features = ["fs"] } diff --git a/src/basic_uart.rs b/src/basic_uart.rs index 3380515..ec687e3 100644 --- a/src/basic_uart.rs +++ b/src/basic_uart.rs @@ -14,7 +14,8 @@ use std::time::Duration; use nix::fcntl::fcntl; use nix::fcntl::{FcntlArg, OFlag}; use trve::consts::{Byte, DWord, HWord, Word}; -use trve::mem::{MemAccessFault, MemDeviceInterface, PageNum}; +use trve::exceptions::ExceptionType; +use trve::mem::{MemDeviceInterface, PageNum}; /// byte 0: rx/tx /// byte 1: status (------rt, r=rxready, t=txready)/none @@ -82,12 +83,12 @@ impl MemDeviceInterface for BasicUart { _page: PageNum, _offset: u16, _value: DWord, - ) -> Result<(), MemAccessFault> { - Err(MemAccessFault) + ) -> Result<(), ExceptionType> { + Err(ExceptionType::StoreAmoAccessFault) } - fn write_word(&self, _page: PageNum, _offset: u16, _value: Word) -> Result<(), MemAccessFault> { - Err(MemAccessFault) + fn write_word(&self, _page: PageNum, _offset: u16, _value: Word) -> Result<(), ExceptionType> { + Err(ExceptionType::StoreAmoAccessFault) } fn write_hword( @@ -95,13 +96,13 @@ impl MemDeviceInterface for BasicUart { _page: PageNum, _offset: u16, _value: HWord, - ) -> Result<(), MemAccessFault> { - Err(MemAccessFault) + ) -> Result<(), ExceptionType> { + Err(ExceptionType::StoreAmoAccessFault) } - fn write_byte(&self, page: PageNum, offset: u16, value: Byte) -> Result<(), MemAccessFault> { + fn write_byte(&self, page: PageNum, offset: u16, value: Byte) -> Result<(), ExceptionType> { if page > 0 { - return Err(MemAccessFault); + return Err(ExceptionType::StoreAmoAccessFault); } match offset { @@ -109,31 +110,31 @@ impl MemDeviceInterface for BasicUart { self.write(value); Ok(()) } - _ => Err(MemAccessFault), + _ => Err(ExceptionType::StoreAmoAccessFault), } } - fn read_dword(&self, _page: PageNum, _offset: u16) -> Result { - Err(MemAccessFault) + fn read_dword(&self, _page: PageNum, _offset: u16) -> Result { + Err(ExceptionType::LoadAccessFault) } - fn read_word(&self, _page: PageNum, _offset: u16) -> Result { - Err(MemAccessFault) + fn read_word(&self, _page: PageNum, _offset: u16) -> Result { + Err(ExceptionType::LoadAccessFault) } - fn read_hword(&self, _page: PageNum, _offset: u16) -> Result { - Err(MemAccessFault) + fn read_hword(&self, _page: PageNum, _offset: u16) -> Result { + Err(ExceptionType::LoadAccessFault) } - fn read_byte(&self, page: PageNum, offset: u16) -> Result { + fn read_byte(&self, page: PageNum, offset: u16) -> Result { if page > 0 { - return Err(MemAccessFault); + return Err(ExceptionType::LoadAccessFault); } match offset { 0 => Ok(self.read()), 1 => Ok(1 | (self.can_read() as u8) << 1), - _ => Err(MemAccessFault), + _ => Err(ExceptionType::LoadAccessFault), } } @@ -141,15 +142,15 @@ impl MemDeviceInterface for BasicUart { &self, _page: PageNum, _offset: u16, - ) -> Result<&std::sync::atomic::AtomicU32, MemAccessFault> { - Err(MemAccessFault) + ) -> Result<&std::sync::atomic::AtomicU32, ExceptionType> { + Err(ExceptionType::StoreAmoAccessFault) } fn get_atomic_dword( &self, _page: PageNum, _offset: u16, - ) -> Result<&std::sync::atomic::AtomicU64, MemAccessFault> { - Err(MemAccessFault) + ) -> Result<&std::sync::atomic::AtomicU64, ExceptionType> { + Err(ExceptionType::StoreAmoAccessFault) } } diff --git a/src/core.rs b/src/core.rs index 8fe5520..7e477c1 100644 --- a/src/core.rs +++ b/src/core.rs @@ -7,19 +7,11 @@ use crate::{ consts::{Addr, RegId, RegValue}, decode::Instruction, + exceptions::ExceptionType, instructions::find_and_exec, mem::MemConfig, }; -// placeholder - change when exception system is in place -pub(crate) type Exception = (); - -pub(crate) enum InstructionResult { - Normal, - Exception(Exception), - // Pause, -} - pub struct Core { pub(crate) x_regs: [RegValue; 32], pub(crate) pc: Addr, @@ -40,50 +32,46 @@ impl Core { let page = (self.pc / 4096) as usize; let offset = (self.pc % 4096 / 4) as u16; if !self.pc.is_multiple_of(4) { - //replace eprint with logging, replace break with exception - eprintln!("PC not aligned"); + self.throw_exception(ExceptionType::InstructionAccessMisaligned); break; } let instr = match self.mem.read_word(page, offset) { Ok(i) => i, Err(_) => { - eprintln!("Memory access fault while fetching instruction"); - eprintln!("PC: {:x}", self.pc); + self.throw_exception(ExceptionType::InstructionAccessFault); break; } }; if instr == 0 { - eprintln!("Executing 0 instruction at {:x}", self.pc); + self.throw_exception(ExceptionType::IllegalInstruction); break; } - assert_eq!(instr & 3, 3, "Compressed instructions not supported"); + if instr & 3 != 3 { + self.throw_exception(ExceptionType::IllegalInstruction); + break; + } let instr = Instruction(instr); - let res = find_and_exec(instr, self); - - if let Some(res) = res { - match res { - InstructionResult::Normal => {} - InstructionResult::Exception(_e) => { - eprintln!("Exception from instruction"); - eprintln!("PC: {:016x}, instr: {:08x}", self.pc, instr.0); - break; - } // InstructionResult::Pause => { - // eprintln!("Instruction asked for pause"); - // break; - // } + match find_and_exec(instr, self) { + Ok(()) => {} + Err(e) => { + self.throw_exception(e); + eprintln!("instr: {:08x}", instr.0); + break; } - } else { - eprintln!("Invalid Instruction {:08x} at PC: {:x}", instr.0, self.pc); - break; } } } + fn throw_exception(&mut self, exception_type: ExceptionType) { + eprintln!("Exception: {exception_type:?}"); + dbg!(self.pc, self.x_regs); + } + pub fn reset(&mut self, pc: Addr) { self.pc = pc; } diff --git a/src/exceptions.rs b/src/exceptions.rs new file mode 100644 index 0000000..2929d65 --- /dev/null +++ b/src/exceptions.rs @@ -0,0 +1,30 @@ +// Copyright (c) 2025 taitep +// SPDX-License-Identifier: MIT +// +// This file is part of TRVE (https://gitea.taitep.se/taitep/trve) +// See LICENSE file in the project root for full license text. + +use int_enum::IntEnum; + +#[repr(u8)] +#[allow(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, IntEnum)] +pub enum ExceptionType { + InstructionAccessMisaligned = 0, + InstructionAccessFault = 1, + IllegalInstruction = 2, + Breakpoint = 3, + LoadAddressMisaligned = 4, + LoadAccessFault = 5, + StoreAmoAddressMisaligned = 6, + StoreAmoAccessFault = 7, + EnvironmentCallFromUMode = 8, + EnvironmentCallFromSMode = 9, + EnvironmentCallFromMMode = 11, + InstructionPageFault = 12, + LoadPageFault = 13, + StoreAmoPageFault = 15, + DoubleTrap = 16, + SoftwareCheck = 18, + HardwareError = 19, +} diff --git a/src/instructions.rs b/src/instructions.rs index e4d5b03..482ea5d 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -7,67 +7,81 @@ mod rvi; use crate::{ - core::{Core, InstructionResult}, + core::Core, decode::Instruction, + exceptions::ExceptionType::{self, IllegalInstruction}, }; -pub(crate) fn find_and_exec(instr: Instruction, core: &mut Core) -> Option { +pub(crate) fn find_and_exec(instr: Instruction, core: &mut Core) -> Result<(), ExceptionType> { match instr.opcode_noncompressed() { 0b01100 => match (instr.funct7(), instr.funct3()) { // OP - (0b0000000, 0b000) => Some(rvi::add(core, instr)), - (0b0100000, 0b000) => Some(rvi::sub(core, instr)), - (0b0000000, 0b111) => Some(rvi::and(core, instr)), - (0b0000000, 0b110) => Some(rvi::or(core, instr)), - _ => None, + (0b0000000, 0b000) => rvi::add(core, instr), + (0b0100000, 0b000) => rvi::sub(core, instr), + (0b0000000, 0b111) => rvi::and(core, instr), + (0b0000000, 0b110) => rvi::or(core, instr), + _ => Err(IllegalInstruction), }, 0b00100 => match instr.funct3() { // OP_IMM - 0b000 => Some(rvi::addi(core, instr)), - 0b001 => (instr.funct6() == 0).then(|| rvi::slli(core, instr)), + 0b000 => rvi::addi(core, instr), + 0b001 => { + if instr.funct6() == 0 { + rvi::slli(core, instr) + } else { + Err(IllegalInstruction) + } + } 0b101 => match instr.funct6() { // immediate right-shift - 0b000000 => Some(rvi::srli(core, instr)), - _ => None, + 0b000000 => rvi::srli(core, instr), + _ => Err(IllegalInstruction), }, - 0b111 => Some(rvi::andi(core, instr)), - _ => None, + 0b111 => rvi::andi(core, instr), + _ => Err(IllegalInstruction), }, 0b00110 => match instr.funct3() { // OP_IMM_32 - 0b000 => Some(rvi::addiw(core, instr)), - _ => None, + 0b000 => rvi::addiw(core, instr), + _ => Err(IllegalInstruction), }, 0b01000 => match instr.funct3() { // STORE - 0b000 => Some(rvi::sb(core, instr)), - 0b001 => Some(rvi::sh(core, instr)), - 0b010 => Some(rvi::sw(core, instr)), - 0b011 => Some(rvi::sd(core, instr)), - _ => None, + 0b000 => rvi::sb(core, instr), + 0b001 => rvi::sh(core, instr), + 0b010 => rvi::sw(core, instr), + 0b011 => rvi::sd(core, instr), + _ => Err(IllegalInstruction), }, 0b00000 => match instr.funct3() { // LOAD - 0b000 => Some(rvi::lb(core, instr)), - 0b100 => Some(rvi::lbu(core, instr)), - 0b001 => Some(rvi::lh(core, instr)), - 0b010 => Some(rvi::lw(core, instr)), - 0b011 => Some(rvi::ld(core, instr)), - _ => None, + 0b000 => rvi::lb(core, instr), + 0b100 => rvi::lbu(core, instr), + 0b001 => rvi::lh(core, instr), + 0b010 => rvi::lw(core, instr), + 0b011 => rvi::ld(core, instr), + _ => Err(IllegalInstruction), }, 0b11000 => match instr.funct3() { // BRANCH - 0b000 => Some(rvi::beq(core, instr)), - 0b001 => Some(rvi::bne(core, instr)), - 0b100 => Some(rvi::blt(core, instr)), - 0b110 => Some(rvi::bltu(core, instr)), - 0b111 => Some(rvi::bgeu(core, instr)), - _ => None, + 0b000 => rvi::beq(core, instr), + 0b001 => rvi::bne(core, instr), + 0b100 => rvi::blt(core, instr), + 0b110 => rvi::bltu(core, instr), + 0b111 => rvi::bgeu(core, instr), + _ => Err(IllegalInstruction), }, - 0b01101 => Some(rvi::lui(core, instr)), - 0b00101 => Some(rvi::auipc(core, instr)), - 0b11011 => Some(rvi::jal(core, instr)), - 0b11001 => (instr.funct3() == 0).then(|| rvi::jalr(core, instr)), - _ => None, + 0b01101 => rvi::lui(core, instr), + 0b00101 => rvi::auipc(core, instr), + 0b11011 => rvi::jal(core, instr), + // 0b11001 => (instr.funct3() == 0).then(|| rvi::jalr(core, instr)), + 0b11001 => { + if instr.funct3() == 0 { + rvi::jalr(core, instr) + } else { + Err(IllegalInstruction) + } + } + _ => Err(IllegalInstruction), } } diff --git a/src/instructions/rvi.rs b/src/instructions/rvi.rs index 6766fdf..d8439fa 100644 --- a/src/instructions/rvi.rs +++ b/src/instructions/rvi.rs @@ -4,157 +4,154 @@ // This file is part of TRVE (https://gitea.taitep.se/taitep/trve) // See LICENSE file in the project root for full license text. -use crate::{ - core::{Core, InstructionResult}, - decode::Instruction, -}; +use crate::{core::Core, decode::Instruction, exceptions::ExceptionType}; mod mem; pub use mem::*; -pub fn add(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn add(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write( instr.rd(), core.reg_read(instr.rs1()) .wrapping_add(core.reg_read(instr.rs2())), ); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn sub(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn sub(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write( instr.rd(), core.reg_read(instr.rs1()) .wrapping_sub(core.reg_read(instr.rs2())), ); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn addi(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn addi(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write( instr.rd(), core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()), ); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn addiw(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn addiw(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let res = core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()) as i32; core.reg_write(instr.rd(), res as i64 as u64); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn and(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn and(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write( instr.rd(), core.reg_read(instr.rs1()) & core.reg_read(instr.rs2()), ); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn andi(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn andi(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write(instr.rd(), core.reg_read(instr.rs1()) & instr.imm_i()); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn or(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn or(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write( instr.rd(), core.reg_read(instr.rs1()) | core.reg_read(instr.rs2()), ); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn slli(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn slli(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write(instr.rd(), core.reg_read(instr.rs1()) << instr.imm_shamt()); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn srli(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn srli(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write(instr.rd(), core.reg_read(instr.rs1()) >> instr.imm_shamt()); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn lui(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn lui(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write(instr.rd(), instr.imm_u()); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn auipc(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn auipc(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write(instr.rd(), core.pc.wrapping_add(instr.imm_u())); core.advance_pc(); - InstructionResult::Normal + Ok(()) } -pub fn jal(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn jal(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write(instr.rd(), core.pc.wrapping_add(4)); core.pc = core.pc.wrapping_add(instr.imm_j()); - InstructionResult::Normal + Ok(()) } -pub fn jalr(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn jalr(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write(instr.rd(), core.pc.wrapping_add(4)); core.pc = core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()); - InstructionResult::Normal + Ok(()) } -pub fn beq(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn beq(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { if core.reg_read(instr.rs1()) == core.reg_read(instr.rs2()) { core.pc = core.pc.wrapping_add(instr.imm_b()); } else { core.advance_pc(); } - InstructionResult::Normal + Ok(()) } -pub fn bne(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn bne(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { if core.reg_read(instr.rs1()) != core.reg_read(instr.rs2()) { core.pc = core.pc.wrapping_add(instr.imm_b()); } else { core.advance_pc(); } - InstructionResult::Normal + Ok(()) } -pub fn blt(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn blt(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { if (core.reg_read(instr.rs1()) as i64) < (core.reg_read(instr.rs2()) as i64) { core.pc = core.pc.wrapping_add(instr.imm_b()); } else { core.advance_pc(); } - InstructionResult::Normal + Ok(()) } -pub fn bgeu(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn bgeu(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { if core.reg_read(instr.rs1()) >= core.reg_read(instr.rs2()) { core.pc = core.pc.wrapping_add(instr.imm_b()); } else { core.advance_pc(); } - InstructionResult::Normal + Ok(()) } -pub fn bltu(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn bltu(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { if core.reg_read(instr.rs1()) < core.reg_read(instr.rs2()) { core.pc = core.pc.wrapping_add(instr.imm_b()); } else { core.advance_pc(); } - InstructionResult::Normal + Ok(()) } diff --git a/src/instructions/rvi/mem.rs b/src/instructions/rvi/mem.rs index c948519..ad938cb 100644 --- a/src/instructions/rvi/mem.rs +++ b/src/instructions/rvi/mem.rs @@ -7,177 +7,145 @@ use crate::{ consts::{Addr, Byte, DWord, HWord, Word}, core::Core, - instructions::{Instruction, InstructionResult}, + exceptions::ExceptionType, + instructions::Instruction, mem::PageNum, }; // TODO: Support misaligned memory access -pub fn sd(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn sd(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let addr = core.reg_read(instr.rs1()).wrapping_add(instr.imm_s()); if !addr.is_multiple_of(std::mem::size_of::() as Addr) { - return InstructionResult::Exception(()); + return Err(ExceptionType::StoreAmoAddressMisaligned); } let page = (addr / 4096) as PageNum; let offset = (addr / 8 & ((4096 / 8 as Addr) - 1)) as u16; let value = core.reg_read(instr.rs2()); - match core.mem.write_dword(page, offset, value) { - Ok(_) => { - core.advance_pc(); - InstructionResult::Normal - } - Err(_) => InstructionResult::Exception(()), - } + core.mem.write_dword(page, offset, value)?; + core.advance_pc(); + + Ok(()) } -pub fn ld(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn ld(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let addr = core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()); if !addr.is_multiple_of(std::mem::size_of::() as Addr) { - return InstructionResult::Exception(()); + return Err(ExceptionType::LoadAddressMisaligned); } let page = (addr / 4096) as PageNum; let offset = (addr / 8 & ((4096 / 8 as Addr) - 1)) as u16; - match core.mem.read_dword(page, offset) { - Ok(x) => { - core.reg_write(instr.rd(), x); - core.advance_pc(); - InstructionResult::Normal - } - Err(_) => InstructionResult::Exception(()), - } + core.reg_write(instr.rd(), core.mem.read_dword(page, offset)?); + core.advance_pc(); + Ok(()) } -pub fn sw(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn sw(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let addr = core.reg_read(instr.rs1()).wrapping_add(instr.imm_s()); if !addr.is_multiple_of(std::mem::size_of::() as Addr) { - return InstructionResult::Exception(()); + return Err(ExceptionType::StoreAmoAddressMisaligned); } let page = (addr / 4096) as PageNum; let offset = (addr / 4 & ((4096 / 4 as Addr) - 1)) as u16; let value = core.reg_read(instr.rs2()) as Word; - match core.mem.write_word(page, offset, value) { - Ok(_) => { - core.advance_pc(); - InstructionResult::Normal - } - Err(_) => InstructionResult::Exception(()), - } + core.mem.write_word(page, offset, value)?; + core.advance_pc(); + Ok(()) } -pub fn lw(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn lw(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let addr = core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()); if !addr.is_multiple_of(std::mem::size_of::() as Addr) { - return InstructionResult::Exception(()); + return Err(ExceptionType::LoadAddressMisaligned); } let page = (addr / 4096) as PageNum; let offset = (addr / 4 & ((4096 / 4 as Addr) - 1)) as u16; - match core.mem.read_word(page, offset) { - Ok(x) => { - core.reg_write(instr.rd(), x as i32 as i64 as DWord); - core.advance_pc(); - InstructionResult::Normal - } - Err(_) => InstructionResult::Exception(()), - } + core.reg_write( + instr.rd(), + core.mem.read_word(page, offset)? as i32 as i64 as DWord, + ); + core.advance_pc(); + Ok(()) } -pub fn sh(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn sh(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let addr = core.reg_read(instr.rs1()).wrapping_add(instr.imm_s()); if !addr.is_multiple_of(std::mem::size_of::() as Addr) { - return InstructionResult::Exception(()); + return Err(ExceptionType::StoreAmoAddressMisaligned); } let page = (addr / 4096) as PageNum; let offset = (addr / 2 & ((4096 / 2 as Addr) - 1)) as u16; let value = core.reg_read(instr.rs2()) as HWord; - match core.mem.write_hword(page, offset, value) { - Ok(_) => { - core.advance_pc(); - InstructionResult::Normal - } - Err(_) => InstructionResult::Exception(()), - } + core.mem.write_hword(page, offset, value)?; + core.advance_pc(); + Ok(()) } -pub fn lh(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn lh(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let addr = core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()); if !addr.is_multiple_of(std::mem::size_of::() as Addr) { - return InstructionResult::Exception(()); + return Err(ExceptionType::LoadAddressMisaligned); } let page = (addr / 4096) as PageNum; let offset = (addr / 2 & ((4096 / 2 as Addr) - 1)) as u16; - match core.mem.read_hword(page, offset) { - Ok(x) => { - core.reg_write(instr.rd(), x as i16 as i64 as DWord); - core.advance_pc(); - InstructionResult::Normal - } - Err(_) => InstructionResult::Exception(()), - } + core.reg_write( + instr.rd(), + core.mem.read_hword(page, offset)? as i16 as i64 as DWord, + ); + core.advance_pc(); + Ok(()) } -pub fn sb(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn sb(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let addr = core.reg_read(instr.rs1()).wrapping_add(instr.imm_s()); let page = (addr / 4096) as PageNum; let offset = (addr & (4096 as Addr - 1)) as u16; let value = core.reg_read(instr.rs2()) as Byte; - match core.mem.write_byte(page, offset, value) { - Ok(_) => { - core.advance_pc(); - InstructionResult::Normal - } - Err(_) => InstructionResult::Exception(()), - } + core.mem.write_byte(page, offset, value)?; + core.advance_pc(); + Ok(()) } -pub fn lb(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn lb(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let addr = core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()); let page = (addr / 4096) as PageNum; let offset = (addr & (4096 as Addr - 1)) as u16; - match core.mem.read_byte(page, offset) { - Ok(x) => { - let x = x as i8 as i64 as DWord; - core.reg_write(instr.rd(), x); - core.advance_pc(); - InstructionResult::Normal - } - Err(_) => InstructionResult::Exception(()), - } + core.reg_write( + instr.rd(), + core.mem.read_byte(page, offset)? as i8 as i64 as DWord, + ); + core.advance_pc(); + Ok(()) } -pub fn lbu(core: &mut Core, instr: Instruction) -> InstructionResult { +pub fn lbu(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { let addr = core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()); let page = (addr / 4096) as PageNum; let offset = (addr & (4096 as Addr - 1)) as u16; - match core.mem.read_byte(page, offset) { - Ok(x) => { - let x = x as DWord; - core.reg_write(instr.rd(), x); - core.advance_pc(); - InstructionResult::Normal - } - Err(_) => InstructionResult::Exception(()), - } + core.reg_write(instr.rd(), core.mem.read_byte(page, offset)? as DWord); + core.advance_pc(); + Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index cf59a4b..7a9ac37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod consts; pub mod core; mod decode; +pub mod exceptions; mod instructions; pub mod mem; diff --git a/src/main.rs b/src/main.rs index 90fffd3..615f3bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,8 @@ use std::{env, sync::Arc, time::Duration}; use trve::{ consts::{Byte, DWord, HWord, Word}, core::Core, - mem::{DeviceEntry, MemAccessFault, MemConfig, MemDeviceInterface, PageNum, Ram}, + exceptions::ExceptionType, + mem::{DeviceEntry, MemConfig, MemDeviceInterface, PageNum, Ram}, }; use anyhow::{Result, bail}; @@ -63,12 +64,7 @@ mod basic_uart; struct DbgOut; impl MemDeviceInterface for DbgOut { - fn write_dword( - &self, - page: PageNum, - offset: u16, - value: DWord, - ) -> Result<(), trve::mem::MemAccessFault> { + fn write_dword(&self, page: PageNum, offset: u16, value: DWord) -> Result<(), ExceptionType> { eprintln!( "Wrote DWord {value:016x} to Debug-Out page {page}, offset {offset} (byte {})", offset * 8 @@ -76,12 +72,7 @@ impl MemDeviceInterface for DbgOut { Ok(()) } - fn write_word( - &self, - page: PageNum, - offset: u16, - value: Word, - ) -> Result<(), trve::mem::MemAccessFault> { + fn write_word(&self, page: PageNum, offset: u16, value: Word) -> Result<(), ExceptionType> { eprintln!( "Wrote Word {value:08x} to Debug-Out page {page}, offset {offset} (byte {})", offset * 4 @@ -89,12 +80,7 @@ impl MemDeviceInterface for DbgOut { Ok(()) } - fn write_hword( - &self, - page: PageNum, - offset: u16, - value: HWord, - ) -> Result<(), trve::mem::MemAccessFault> { + fn write_hword(&self, page: PageNum, offset: u16, value: HWord) -> Result<(), ExceptionType> { eprintln!( "Wrote HWord {value:04x} to Debug-Out page {page}, offset {offset} (byte {})", offset * 2 @@ -102,45 +88,40 @@ impl MemDeviceInterface for DbgOut { Ok(()) } - fn write_byte( - &self, - page: PageNum, - offset: u16, - value: Byte, - ) -> Result<(), trve::mem::MemAccessFault> { + fn write_byte(&self, page: PageNum, offset: u16, value: Byte) -> Result<(), ExceptionType> { eprintln!("Wrote Byte {value:02x} to Debug-Out page {page}, offset {offset}"); Ok(()) } - fn read_dword(&self, _page: PageNum, _offset: u16) -> Result { - Err(MemAccessFault) + fn read_dword(&self, _page: PageNum, _offset: u16) -> Result { + Err(ExceptionType::LoadAccessFault) } - fn read_word(&self, _page: PageNum, _offset: u16) -> Result { - Err(MemAccessFault) + fn read_word(&self, _page: PageNum, _offset: u16) -> Result { + Err(ExceptionType::LoadAccessFault) } - fn read_hword(&self, _page: PageNum, _offset: u16) -> Result { - Err(MemAccessFault) + fn read_hword(&self, _page: PageNum, _offset: u16) -> Result { + Err(ExceptionType::LoadAccessFault) } - fn read_byte(&self, _page: PageNum, _offset: u16) -> Result { - Err(MemAccessFault) + fn read_byte(&self, _page: PageNum, _offset: u16) -> Result { + Err(ExceptionType::LoadAccessFault) } fn get_atomic_word( &self, _page: PageNum, _offset: u16, - ) -> Result<&std::sync::atomic::AtomicU32, trve::mem::MemAccessFault> { - Err(MemAccessFault) + ) -> Result<&std::sync::atomic::AtomicU32, ExceptionType> { + Err(ExceptionType::StoreAmoAccessFault) } fn get_atomic_dword( &self, _page: PageNum, _offset: u16, - ) -> Result<&std::sync::atomic::AtomicU64, trve::mem::MemAccessFault> { - Err(MemAccessFault) + ) -> Result<&std::sync::atomic::AtomicU64, ExceptionType> { + Err(ExceptionType::StoreAmoAccessFault) } } diff --git a/src/mem.rs b/src/mem.rs index 44e1dc7..21cabec 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -11,7 +11,10 @@ use std::sync::{ use memmap2::MmapMut; -use crate::consts::{Byte, DWord, HWord, Word}; +use crate::{ + consts::{Byte, DWord, HWord, Word}, + exceptions::ExceptionType, +}; pub type PageNum = usize; @@ -45,38 +48,46 @@ impl MemConfig { } } - pub fn read_dword(&self, page: PageNum, offset: u16) -> Result { + pub fn read_dword(&self, page: PageNum, offset: u16) -> Result { if page_in_range(page, self.ram_start, self.ram.pages) { self.ram.read_dword(page - self.ram_start, offset) } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::LoadAccessFault)?; entry.interface.read_dword(page - entry.base, offset) } } - pub fn read_word(&self, page: PageNum, offset: u16) -> Result { + pub fn read_word(&self, page: PageNum, offset: u16) -> Result { if page_in_range(page, self.ram_start, self.ram.pages) { self.ram.read_word(page - self.ram_start, offset) } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::LoadAccessFault)?; entry.interface.read_word(page - entry.base, offset) } } - pub fn read_hword(&self, page: PageNum, offset: u16) -> Result { + pub fn read_hword(&self, page: PageNum, offset: u16) -> Result { if page_in_range(page, self.ram_start, self.ram.pages) { self.ram.read_hword(page - self.ram_start, offset) } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::LoadAccessFault)?; entry.interface.read_hword(page - entry.base, offset) } } - pub fn read_byte(&self, page: PageNum, offset: u16) -> Result { + pub fn read_byte(&self, page: PageNum, offset: u16) -> Result { if page_in_range(page, self.ram_start, self.ram.pages) { self.ram.read_byte(page - self.ram_start, offset) } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::LoadAccessFault)?; entry.interface.read_byte(page - entry.base, offset) } @@ -87,26 +98,25 @@ impl MemConfig { page: PageNum, offset: u16, value: DWord, - ) -> Result<(), MemAccessFault> { + ) -> Result<(), ExceptionType> { if page_in_range(page, self.ram_start, self.ram.pages) { self.ram.write_dword(page - self.ram_start, offset, value) } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::StoreAmoAccessFault)?; entry .interface .write_dword(page - entry.base, offset, value) } } - pub fn write_word( - &self, - page: PageNum, - offset: u16, - value: Word, - ) -> Result<(), MemAccessFault> { + pub fn write_word(&self, page: PageNum, offset: u16, value: Word) -> Result<(), ExceptionType> { if page_in_range(page, self.ram_start, self.ram.pages) { self.ram.write_word(page - self.ram_start, offset, value) } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::StoreAmoAccessFault)?; entry.interface.write_word(page - entry.base, offset, value) } } @@ -115,26 +125,25 @@ impl MemConfig { page: PageNum, offset: u16, value: HWord, - ) -> Result<(), MemAccessFault> { + ) -> Result<(), ExceptionType> { if page_in_range(page, self.ram_start, self.ram.pages) { self.ram.write_hword(page - self.ram_start, offset, value) } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::StoreAmoAccessFault)?; entry .interface .write_hword(page - entry.base, offset, value) } } - pub fn write_byte( - &self, - page: PageNum, - offset: u16, - value: Byte, - ) -> Result<(), MemAccessFault> { + pub fn write_byte(&self, page: PageNum, offset: u16, value: Byte) -> Result<(), ExceptionType> { if page_in_range(page, self.ram_start, self.ram.pages) { self.ram.write_byte(page - self.ram_start, offset, value) } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::StoreAmoAccessFault)?; entry.interface.write_byte(page - entry.base, offset, value) } } @@ -143,7 +152,7 @@ impl MemConfig { &self, page: PageNum, offset: u16, - ) -> Result<&AtomicU64, MemAccessFault> { + ) -> Result<&AtomicU64, ExceptionType> { if page_in_range(page, self.ram_start, self.ram.pages) { debug_assert!(((offset * 8) as usize) < PAGE_SIZE); let index = page * (PAGE_SIZE / 8) + (offset as usize); @@ -151,18 +160,16 @@ impl MemConfig { self.ram .buf_transmuted::() .get(index) - .ok_or(MemAccessFault) + .ok_or(ExceptionType::HardwareError) } } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::StoreAmoAccessFault)?; entry.interface.get_atomic_dword(page - entry.base, offset) } } - pub fn get_atomic_word( - &self, - page: PageNum, - offset: u16, - ) -> Result<&AtomicU32, MemAccessFault> { + pub fn get_atomic_word(&self, page: PageNum, offset: u16) -> Result<&AtomicU32, ExceptionType> { if page_in_range(page, self.ram_start, self.ram.pages) { debug_assert!(((offset * 4) as usize) < PAGE_SIZE); let index = page * (PAGE_SIZE / 4) + (offset as usize); @@ -170,10 +177,12 @@ impl MemConfig { self.ram .buf_transmuted::() .get(index) - .ok_or(MemAccessFault) + .ok_or(ExceptionType::HardwareError) } } else { - let entry = self.find_device_by_page(page).ok_or(MemAccessFault)?; + let entry = self + .find_device_by_page(page) + .ok_or(ExceptionType::StoreAmoAccessFault)?; entry.interface.get_atomic_word(page - entry.base, offset) } } @@ -232,46 +241,46 @@ impl Ram { } #[inline] - pub fn read_dword(&self, page: PageNum, offset: u16) -> Result { + pub fn read_dword(&self, page: PageNum, offset: u16) -> Result { debug_assert!(((offset * 8) as usize) < PAGE_SIZE); let index = page * (PAGE_SIZE / 8) + (offset as usize); Ok(unsafe { self.buf_transmuted::() .get(index) - .ok_or(MemAccessFault) + .ok_or(ExceptionType::LoadAccessFault) }? .load(Relaxed)) } #[inline] - pub fn read_word(&self, page: PageNum, offset: u16) -> Result { + pub fn read_word(&self, page: PageNum, offset: u16) -> Result { debug_assert!(((offset * 4) as usize) < PAGE_SIZE); let index = page * (PAGE_SIZE / 4) + (offset as usize); Ok(unsafe { self.buf_transmuted::() .get(index) - .ok_or(MemAccessFault) + .ok_or(ExceptionType::LoadAccessFault) }? .load(Relaxed)) } #[inline] - pub fn read_hword(&self, page: PageNum, offset: u16) -> Result { + pub fn read_hword(&self, page: PageNum, offset: u16) -> Result { debug_assert!(((offset * 2) as usize) < PAGE_SIZE); let index = page * (PAGE_SIZE / 2) + (offset as usize); Ok(unsafe { self.buf_transmuted::() .get(index) - .ok_or(MemAccessFault) + .ok_or(ExceptionType::LoadAccessFault) }? .load(Relaxed)) } #[inline] - pub fn read_byte(&self, page: PageNum, offset: u16) -> Result { + pub fn read_byte(&self, page: PageNum, offset: u16) -> Result { debug_assert!((offset as usize) < PAGE_SIZE); let index = page * PAGE_SIZE + (offset as usize); Ok(self .buf_atomic() .get(index) - .ok_or(MemAccessFault)? + .ok_or(ExceptionType::LoadAccessFault)? .load(Relaxed)) } @@ -281,30 +290,25 @@ impl Ram { page: PageNum, offset: u16, value: DWord, - ) -> Result<(), MemAccessFault> { + ) -> Result<(), ExceptionType> { debug_assert!(((offset * 8) as usize) < PAGE_SIZE); let index = page * (PAGE_SIZE / 8) + (offset as usize); unsafe { self.buf_transmuted::() .get(index) - .ok_or(MemAccessFault) + .ok_or(ExceptionType::StoreAmoAccessFault) }? .store(value, Relaxed); Ok(()) } #[inline] - pub fn write_word( - &self, - page: PageNum, - offset: u16, - value: Word, - ) -> Result<(), MemAccessFault> { + pub fn write_word(&self, page: PageNum, offset: u16, value: Word) -> Result<(), ExceptionType> { debug_assert!(((offset * 4) as usize) < PAGE_SIZE); let index = page * (PAGE_SIZE / 4) + (offset as usize); unsafe { self.buf_transmuted::() .get(index) - .ok_or(MemAccessFault) + .ok_or(ExceptionType::StoreAmoAccessFault) }? .store(value, Relaxed); Ok(()) @@ -315,29 +319,24 @@ impl Ram { page: PageNum, offset: u16, value: HWord, - ) -> Result<(), MemAccessFault> { + ) -> Result<(), ExceptionType> { debug_assert!(((offset * 2) as usize) < PAGE_SIZE); let index = page * (PAGE_SIZE / 2) + (offset as usize); unsafe { self.buf_transmuted::() .get(index) - .ok_or(MemAccessFault) + .ok_or(ExceptionType::StoreAmoAccessFault) }? .store(value, Relaxed); Ok(()) } #[inline] - pub fn write_byte( - &self, - page: PageNum, - offset: u16, - value: Byte, - ) -> Result<(), MemAccessFault> { + pub fn write_byte(&self, page: PageNum, offset: u16, value: Byte) -> Result<(), ExceptionType> { debug_assert!((offset as usize) < PAGE_SIZE); let index = page * PAGE_SIZE + (offset as usize); self.buf_atomic() .get(index) - .ok_or(MemAccessFault)? + .ok_or(ExceptionType::StoreAmoAccessFault)? .store(value, Relaxed); Ok(()) } @@ -351,21 +350,16 @@ pub struct DeviceEntry { } pub trait MemDeviceInterface { - fn write_dword(&self, page: PageNum, offset: u16, value: DWord) -> Result<(), MemAccessFault>; - fn write_word(&self, page: PageNum, offset: u16, value: Word) -> Result<(), MemAccessFault>; - fn write_hword(&self, page: PageNum, offset: u16, value: HWord) -> Result<(), MemAccessFault>; - fn write_byte(&self, page: PageNum, offset: u16, value: Byte) -> Result<(), MemAccessFault>; + fn write_dword(&self, page: PageNum, offset: u16, value: DWord) -> Result<(), ExceptionType>; + fn write_word(&self, page: PageNum, offset: u16, value: Word) -> Result<(), ExceptionType>; + fn write_hword(&self, page: PageNum, offset: u16, value: HWord) -> Result<(), ExceptionType>; + fn write_byte(&self, page: PageNum, offset: u16, value: Byte) -> Result<(), ExceptionType>; - fn read_dword(&self, page: PageNum, offset: u16) -> Result; - fn read_word(&self, page: PageNum, offset: u16) -> Result; - fn read_hword(&self, page: PageNum, offset: u16) -> Result; - fn read_byte(&self, page: PageNum, offset: u16) -> Result; + fn read_dword(&self, page: PageNum, offset: u16) -> Result; + fn read_word(&self, page: PageNum, offset: u16) -> Result; + fn read_hword(&self, page: PageNum, offset: u16) -> Result; + fn read_byte(&self, page: PageNum, offset: u16) -> Result; - fn get_atomic_word(&self, page: PageNum, offset: u16) -> Result<&AtomicU32, MemAccessFault>; - fn get_atomic_dword(&self, page: PageNum, offset: u16) -> Result<&AtomicU64, MemAccessFault>; + fn get_atomic_word(&self, page: PageNum, offset: u16) -> Result<&AtomicU32, ExceptionType>; + fn get_atomic_dword(&self, page: PageNum, offset: u16) -> Result<&AtomicU64, ExceptionType>; } - -/// Error that means something has gone wrong accessing memory -/// Examples are: Accessing unmapped memory, accessing an MMIO register at the wrong size -#[derive(Debug)] -pub struct MemAccessFault;