mirror of
https://github.com/Noratrieb/rustv32i.git
synced 2026-01-14 21:35:02 +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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "owo-colors"
|
||||||
|
version = "4.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
@ -95,6 +101,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eyre",
|
"eyre",
|
||||||
"libc",
|
"libc",
|
||||||
|
"owo-colors",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ edition = "2024"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
libc = "0.2.170"
|
libc = "0.2.170"
|
||||||
|
owo-colors = "4.2.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = 1
|
debug = 1
|
||||||
|
|
|
||||||
163
src/emu.rs
163
src/emu.rs
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::inst::{AmoOp, Inst, InstCode, IsCompressed};
|
use crate::inst::{AmoOp, Inst, InstCode, IsCompressed};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
|
io::Write,
|
||||||
ops::{Index, IndexMut},
|
ops::{Index, IndexMut},
|
||||||
u32,
|
u32,
|
||||||
};
|
};
|
||||||
|
|
@ -86,6 +87,8 @@ pub struct Emulator {
|
||||||
/// to make SC fail if it's not to this address.
|
/// to make SC fail if it's not to this address.
|
||||||
pub reservation_set: Option<u32>,
|
pub reservation_set: Option<u32>,
|
||||||
|
|
||||||
|
pub is_breaking: bool,
|
||||||
|
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
pub ecall_handler: Box<dyn FnMut(&mut Memory, &mut [u32; 32]) -> Result<(), Status>>,
|
pub ecall_handler: Box<dyn FnMut(&mut Memory, &mut [u32; 32]) -> Result<(), Status>>,
|
||||||
}
|
}
|
||||||
|
|
@ -129,12 +132,20 @@ impl Reg {
|
||||||
|
|
||||||
impl Display for Reg {
|
impl Display for Reg {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
if self.0 == Self::SP.0 {
|
let n = self.0;
|
||||||
write!(f, "sp")
|
match n {
|
||||||
} else if self.0 == Self::RA.0 {
|
0 => write!(f, "zero"),
|
||||||
write!(f, "ra")
|
1 => write!(f, "ra"),
|
||||||
} else {
|
2 => write!(f, "sp"),
|
||||||
write!(f, "x{}", self.0)
|
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 code = self.mem.load_u32(self.pc)?;
|
||||||
let (inst, was_compressed) = Inst::decode(code)?;
|
let (inst, was_compressed) = Inst::decode(code)?;
|
||||||
|
|
||||||
|
if self.is_breaking {
|
||||||
|
self.debug_interactive();
|
||||||
|
}
|
||||||
|
|
||||||
if self.debug {
|
if self.debug {
|
||||||
println!("executing 0x{:x} {inst:?}", self.pc);
|
println!("executing 0x{:x} {inst:?}", self.pc);
|
||||||
}
|
}
|
||||||
|
|
@ -314,7 +329,13 @@ impl Emulator {
|
||||||
Inst::Ecall => {
|
Inst::Ecall => {
|
||||||
(self.ecall_handler)(&mut self.mem, &mut self.xreg)?;
|
(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 } => {
|
Inst::Mul { dest, src1, src2 } => {
|
||||||
self[dest] = ((self[src1] as i32).wrapping_mul(self[src2] as i32)) as u32;
|
self[dest] = ((self[src1] as i32).wrapping_mul(self[src2] as i32)) as u32;
|
||||||
}
|
}
|
||||||
|
|
@ -357,8 +378,7 @@ impl Emulator {
|
||||||
if self[src2] == 0 {
|
if self[src2] == 0 {
|
||||||
self[dest] = self[src1];
|
self[dest] = self[src1];
|
||||||
} else {
|
} else {
|
||||||
dbg!(self[src1], self[src2]);
|
self[dest] = self[src1] % self[src2];
|
||||||
self[dest] = dbg!(self[src1] % self[src2]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Inst::AmoW {
|
Inst::AmoW {
|
||||||
|
|
@ -414,4 +434,129 @@ impl Emulator {
|
||||||
}
|
}
|
||||||
Ok(())
|
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
|
// C.LI -> addi \rd, zero, \imm
|
||||||
0b010 => Inst::Addi {
|
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(),
|
dest: code.rd(),
|
||||||
src1: Reg::ZERO,
|
src1: Reg::ZERO,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,8 @@ pub fn execute_linux_elf(
|
||||||
pc: start,
|
pc: start,
|
||||||
reservation_set: None,
|
reservation_set: None,
|
||||||
|
|
||||||
|
is_breaking: false,
|
||||||
|
|
||||||
debug,
|
debug,
|
||||||
ecall_handler,
|
ecall_handler,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,5 @@ init: init.c
|
||||||
init1: init1.rs
|
init1: init1.rs
|
||||||
$(RUSTC) init1.rs
|
$(RUSTC) init1.rs
|
||||||
|
|
||||||
x: x.s
|
x: x.S
|
||||||
$(CC_STATIC) x.s -o x
|
$(CC_STATIC) x.S -o x
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
fs::DirEntry,
|
||||||
io::Write,
|
io::Write,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
@ -25,50 +26,55 @@ fn check() -> eyre::Result<()> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(std::io::stdout(), "test {name} ...")?;
|
test_case(tmpdir.path(), &file, name, "rv32ima")?;
|
||||||
std::io::stdout().flush()?;
|
test_case(tmpdir.path(), &file, name, "rv32imac")?;
|
||||||
|
|
||||||
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:?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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 out_path = tmpdir.join(Path::new(src.file_name().unwrap()).with_extension(""));
|
||||||
|
|
||||||
let mut cmd = std::process::Command::new("clang");
|
let mut cmd = std::process::Command::new("clang");
|
||||||
cmd.args([
|
cmd.args(["-target", "riscv32-unknown-none-elf", "-nostdlib"]);
|
||||||
"-target",
|
cmd.arg(format!("-march={march}"));
|
||||||
"riscv32-unknown-none-elf",
|
|
||||||
"-nostdlib",
|
|
||||||
"-march=rv32ima",
|
|
||||||
]);
|
|
||||||
cmd.arg(src);
|
cmd.arg(src);
|
||||||
cmd.arg("-o");
|
cmd.arg("-o");
|
||||||
cmd.arg(&out_path);
|
cmd.arg(&out_path);
|
||||||
|
|
|
||||||
|
|
@ -43,14 +43,14 @@ branch7:
|
||||||
# Test link registers being set correctly:
|
# Test link registers being set correctly:
|
||||||
auipc t1, 0
|
auipc t1, 0
|
||||||
jal t0, link2
|
jal t0, link2
|
||||||
j fail
|
jal t5, fail # force uncompressed
|
||||||
link2:
|
link2:
|
||||||
addi t1, t1, 8 # the instruction following the jump
|
addi t1, t1, 8 # the instruction following the jump
|
||||||
bne t1, t0, fail
|
bne t1, t0, fail
|
||||||
|
|
||||||
auipc t1, 0
|
auipc t1, 0
|
||||||
jalr t0, 12(t1) # 12 is the three instructions, so to the addi
|
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
|
addi t1, t1, 8 # the instruction following the jump
|
||||||
bne t0, t1, fail
|
bne t0, t1, fail
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
ebreak
|
||||||
li a7, -1
|
li a7, -1
|
||||||
li a0, 0
|
li a0, 0
|
||||||
ecall
|
ecall
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue