mirror of
https://github.com/Noratrieb/rustv32i.git
synced 2026-01-14 13:25:01 +01:00
debugger and fixes
This commit is contained in:
parent
de03390dab
commit
7ae26ddc85
9 changed files with 212 additions and 50 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ edition = "2024"
|
|||
[dependencies]
|
||||
eyre = "0.6.12"
|
||||
libc = "0.2.170"
|
||||
owo-colors = "4.2.0"
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
||||
|
|
|
|||
163
src/emu.rs
163
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<u32>,
|
||||
|
||||
pub is_breaking: bool,
|
||||
|
||||
pub debug: bool,
|
||||
pub ecall_handler: Box<dyn FnMut(&mut Memory, &mut [u32; 32]) -> 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"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ pub fn execute_linux_elf(
|
|||
pc: start,
|
||||
reservation_set: None,
|
||||
|
||||
is_breaking: false,
|
||||
|
||||
debug,
|
||||
ecall_handler,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@ init: init.c
|
|||
init1: init1.rs
|
||||
$(RUSTC) init1.rs
|
||||
|
||||
x: x.s
|
||||
$(CC_STATIC) x.s -o x
|
||||
x: x.S
|
||||
$(CC_STATIC) x.S -o x
|
||||
|
|
|
|||
|
|
@ -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<PathBuf> {
|
||||
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<PathBuf> {
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
.endm
|
||||
|
||||
fail:
|
||||
ebreak
|
||||
li a7, -1
|
||||
li a0, 0
|
||||
ecall
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue