From 7ae26ddc857d6783ef930461aca46c26a3cde6af Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Mon, 10 Mar 2025 18:08:00 +0100 Subject: [PATCH] debugger and fixes --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/emu.rs | 163 ++++++++++++++++++++++++++++++++++++++++--- src/inst.rs | 2 +- src/lib.rs | 2 + test/Makefile | 4 +- tests/check.rs | 78 +++++++++++---------- tests/check/branch.S | 4 +- tests/helper.S | 1 + 9 files changed, 212 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfa4624..b9aacfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,6 +76,12 @@ version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +[[package]] +name = "owo-colors" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" + [[package]] name = "rustix" version = "1.0.1" @@ -95,6 +101,7 @@ version = "0.1.0" dependencies = [ "eyre", "libc", + "owo-colors", "tempfile", ] diff --git a/Cargo.toml b/Cargo.toml index 27f8bc6..122b49f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] eyre = "0.6.12" libc = "0.2.170" +owo-colors = "4.2.0" [profile.release] debug = 1 diff --git a/src/emu.rs b/src/emu.rs index 5e0abfd..83dcd2e 100644 --- a/src/emu.rs +++ b/src/emu.rs @@ -1,6 +1,7 @@ use crate::inst::{AmoOp, Inst, InstCode, IsCompressed}; use std::{ fmt::{Debug, Display}, + io::Write, ops::{Index, IndexMut}, u32, }; @@ -86,6 +87,8 @@ pub struct Emulator { /// to make SC fail if it's not to this address. pub reservation_set: Option, + pub is_breaking: bool, + pub debug: bool, pub ecall_handler: Box Result<(), Status>>, } @@ -129,12 +132,20 @@ impl Reg { impl Display for Reg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.0 == Self::SP.0 { - write!(f, "sp") - } else if self.0 == Self::RA.0 { - write!(f, "ra") - } else { - write!(f, "x{}", self.0) + let n = self.0; + match n { + 0 => write!(f, "zero"), + 1 => write!(f, "ra"), + 2 => write!(f, "sp"), + 3 => write!(f, "gp"), + 4 => write!(f, "tp"), + 5..=7 => write!(f, "t{}", n - 5), + 8 => write!(f, "s0"), + 9 => write!(f, "s1"), + 10..=17 => write!(f, "a{}", n - 10), + 18..=27 => write!(f, "s{}", n - 18 + 2), + 28..=31 => write!(f, "t{}", n - 28 + 3), + _ => unreachable!("invalid register"), } } } @@ -164,6 +175,10 @@ impl Emulator { let code = self.mem.load_u32(self.pc)?; let (inst, was_compressed) = Inst::decode(code)?; + if self.is_breaking { + self.debug_interactive(); + } + if self.debug { println!("executing 0x{:x} {inst:?}", self.pc); } @@ -314,7 +329,13 @@ impl Emulator { Inst::Ecall => { (self.ecall_handler)(&mut self.mem, &mut self.xreg)?; } - Inst::Ebreak => return Err(Status::Ebreak), + Inst::Ebreak => { + if self.debug { + self.is_breaking = true; + } else { + return Err(Status::Ebreak); + } + } Inst::Mul { dest, src1, src2 } => { self[dest] = ((self[src1] as i32).wrapping_mul(self[src2] as i32)) as u32; } @@ -357,8 +378,7 @@ impl Emulator { if self[src2] == 0 { self[dest] = self[src1]; } else { - dbg!(self[src1], self[src2]); - self[dest] = dbg!(self[src1] % self[src2]); + self[dest] = self[src1] % self[src2]; } } Inst::AmoW { @@ -414,4 +434,129 @@ impl Emulator { } Ok(()) } + + fn debug_interactive(&mut self) { + use owo_colors::OwoColorize; + loop { + print!("> "); + std::io::stdout().flush().unwrap(); + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let input = input.trim(); + match input { + "c" => { + self.is_breaking = false; + return; + } + "s" => { + return; + } + "p" => { + let format_value = |v: u32| { + if v == 0 { + format!("{:0>8x}", v.black()) + } else { + format!("{:0>8x}", v) + } + }; + let r = |i| format_value(self.xreg[i]); + println!( + "{}: {} | {}: {} | {}: {} | {}: {}", + "ra".red(), + r(1), + "sp".red(), + r(2), + "gp".red(), + r(3), + "tp".red(), + r(4) + ); + println!( + "{}: {} | {}: {} | {}: {} | {}: {}", + "a0".green(), + r(10), + "a1".green(), + r(11), + "a2".green(), + r(12), + "a3".green(), + r(13) + ); + println!( + "{}: {} | {}: {} | {}: {} | {}: {}", + "a4".green(), + r(14), + "a5".green(), + r(15), + "a6".green(), + r(16), + "a7".green(), + r(17) + ); + println!( + "{}: {} | {}: {} | {}: {} | {}: {}", + "s0".cyan(), + r(8), + "s1".cyan(), + r(9), + "s2".cyan(), + r(18), + "s3".cyan(), + r(19) + ); + println!( + "{}: {} | {}: {} | {}: {} | {}: {}", + "s4".cyan(), + r(20), + "s5".cyan(), + r(21), + "s6".cyan(), + r(22), + "s7".cyan(), + r(23) + ); + println!( + "{}: {} | {}: {} | {}: {} | {}: {}", + "s8".cyan(), + r(24), + "s9".cyan(), + r(25), + "s10".cyan(), + r(26), + "s11".cyan(), + r(27) + ); + println!( + "{}: {} | {}: {} | {}: {} | {}: {}", + "t0".yellow(), + r(5), + "t1".yellow(), + r(6), + "t2".yellow(), + r(7), + "t3".yellow(), + r(28) + ); + println!( + "{}: {} | {}: {} | {}: {} | {}: {}", + "t4".yellow(), + r(29), + "t5".yellow(), + r(30), + "t6".yellow(), + r(31), + "pc".red(), + format_value(self.pc) + ); + } + _ => println!( + "commands: +- ?: help +- p: print registers +- c: continue until next breakpoint +- s: step one instruction" + ), + } + } + } } diff --git a/src/inst.rs b/src/inst.rs index 8f45cf4..782dbb8 100644 --- a/src/inst.rs +++ b/src/inst.rs @@ -602,7 +602,7 @@ impl Inst { }, // C.LI -> addi \rd, zero, \imm 0b010 => Inst::Addi { - imm: code.immediate_s(&[(2..=4, 0), (12..=12, 5)]), + imm: code.immediate_s(&[(2..=6, 0), (12..=12, 5)]), dest: code.rd(), src1: Reg::ZERO, }, diff --git a/src/lib.rs b/src/lib.rs index df7cfea..5151fb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,8 @@ pub fn execute_linux_elf( xreg0_value: 0, pc: start, reservation_set: None, + + is_breaking: false, debug, ecall_handler, diff --git a/test/Makefile b/test/Makefile index a952773..1910c02 100644 --- a/test/Makefile +++ b/test/Makefile @@ -11,5 +11,5 @@ init: init.c init1: init1.rs $(RUSTC) init1.rs -x: x.s - $(CC_STATIC) x.s -o x \ No newline at end of file +x: x.S + $(CC_STATIC) x.S -o x diff --git a/tests/check.rs b/tests/check.rs index 305e74b..7388bec 100644 --- a/tests/check.rs +++ b/tests/check.rs @@ -1,4 +1,5 @@ use std::{ + fs::DirEntry, io::Write, path::{Path, PathBuf}, }; @@ -25,50 +26,55 @@ fn check() -> eyre::Result<()> { continue; } - write!(std::io::stdout(), "test {name} ...")?; - std::io::stdout().flush()?; - - let output = build(&tmpdir.path(), &file.path()).wrap_err(format!("building {name}"))?; - let content = - std::fs::read(&output).wrap_err(format!("reading output from {}", output.display()))?; - - let status = rustv32i::execute_linux_elf( - &content, - true, - Box::new(|_, xreg| { - if xreg[Reg::A7.0 as usize] == u32::MAX { - if xreg[Reg::A0.0 as usize] == 1 { - Err(rustv32i::emu::Status::Exit { code: 0 }) - } else { - Err(rustv32i::emu::Status::Trap("wrong exit code")) - } - } else { - Err(rustv32i::emu::Status::Trap("wrong syscall")) - } - }), - ) - .wrap_err(format!("{name} failed"))?; - - if let Status::Exit { code: 0 } = status { - writeln!(std::io::stdout(), " ✅")?; - } else { - bail!("{name} returned an error: {status:?}"); - } + test_case(tmpdir.path(), &file, name, "rv32ima")?; + test_case(tmpdir.path(), &file, name, "rv32imac")?; } Ok(()) } -fn build(tmpdir: &Path, src: &Path) -> eyre::Result { +fn test_case(tmpdir: &Path, file: &DirEntry, name: &str, march: &str) -> eyre::Result<()> { + let name = format!("{name} ({march})"); + write!(std::io::stdout(), "test {name} ...")?; + std::io::stdout().flush()?; + + eprintln!("---- START TEST {name} -----"); + + let output = build(&tmpdir, &file.path(), march).wrap_err(format!("building {name}"))?; + let content = + std::fs::read(&output).wrap_err(format!("reading output from {}", output.display()))?; + + let status = rustv32i::execute_linux_elf( + &content, + true, + Box::new(|_, xreg| { + if xreg[Reg::A7.0 as usize] == u32::MAX { + if xreg[Reg::A0.0 as usize] == 1 { + Err(rustv32i::emu::Status::Exit { code: 0 }) + } else { + Err(rustv32i::emu::Status::Trap("fail")) + } + } else { + Err(rustv32i::emu::Status::Trap("wrong syscall")) + } + }), + ) + .wrap_err(format!("{name} failed"))?; + + if let Status::Exit { code: 0 } = status { + writeln!(std::io::stdout(), " ✅")?; + Ok(()) + } else { + bail!("{name} returned an error: {status:?}"); + } +} + +fn build(tmpdir: &Path, src: &Path, march: &str) -> eyre::Result { let out_path = tmpdir.join(Path::new(src.file_name().unwrap()).with_extension("")); let mut cmd = std::process::Command::new("clang"); - cmd.args([ - "-target", - "riscv32-unknown-none-elf", - "-nostdlib", - "-march=rv32ima", - ]); + cmd.args(["-target", "riscv32-unknown-none-elf", "-nostdlib"]); + cmd.arg(format!("-march={march}")); cmd.arg(src); cmd.arg("-o"); cmd.arg(&out_path); diff --git a/tests/check/branch.S b/tests/check/branch.S index 1938d67..6e1c299 100644 --- a/tests/check/branch.S +++ b/tests/check/branch.S @@ -43,14 +43,14 @@ branch7: # Test link registers being set correctly: auipc t1, 0 jal t0, link2 - j fail + jal t5, fail # force uncompressed link2: addi t1, t1, 8 # the instruction following the jump bne t1, t0, fail auipc t1, 0 jalr t0, 12(t1) # 12 is the three instructions, so to the addi - j fail + jal t5, fail # force uncompressed addi t1, t1, 8 # the instruction following the jump bne t0, t1, fail diff --git a/tests/helper.S b/tests/helper.S index de1e7ca..286a001 100644 --- a/tests/helper.S +++ b/tests/helper.S @@ -20,6 +20,7 @@ .endm fail: + ebreak li a7, -1 li a0, 0 ecall