mirror of
https://github.com/Noratrieb/rustv32i.git
synced 2026-01-17 06:45:00 +01:00
Compare commits
2 commits
e9a689aa1a
...
8d01c9f1d7
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d01c9f1d7 | |||
| f0c04f1466 |
7 changed files with 356 additions and 68 deletions
52
Cargo.lock
generated
52
Cargo.lock
generated
|
|
@ -29,6 +29,37 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.10"
|
version = "0.3.10"
|
||||||
|
|
@ -133,6 +164,26 @@ version = "4.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
|
checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
@ -171,6 +222,7 @@ name = "rvdc"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"object",
|
"object",
|
||||||
|
"rayon",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,5 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(slow_tests)'] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
object = "0.36.7"
|
object = "0.36.7"
|
||||||
|
rayon = "1.10.0"
|
||||||
tempfile = "3.19.1"
|
tempfile = "3.19.1"
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ More extensions may be implemented in the future.
|
||||||
let x = 0x1101_u32;
|
let x = 0x1101_u32;
|
||||||
let expected = rvdc::Inst::Addi { imm: rvdc::Imm::new_i32(-0x20), dest: rvdc::Reg::SP, src1: rvdc::Reg::SP };
|
let expected = rvdc::Inst::Addi { imm: rvdc::Imm::new_i32(-0x20), dest: rvdc::Reg::SP, src1: rvdc::Reg::SP };
|
||||||
|
|
||||||
let (inst, is_compressed) = rvdc::Inst::decode(x).unwrap();
|
let (inst, is_compressed) = rvdc::Inst::decode(x, rvdc::Xlen::Rv32).unwrap();
|
||||||
assert_eq!(inst, expected);
|
assert_eq!(inst, expected);
|
||||||
assert_eq!(is_compressed, rvdc::IsCompressed::Yes);
|
assert_eq!(is_compressed, rvdc::IsCompressed::Yes);
|
||||||
assert_eq!(format!("{inst}"), "addi sp, sp, -32")
|
assert_eq!(format!("{inst}"), "addi sp, sp, -32")
|
||||||
|
|
@ -49,7 +49,7 @@ assert_eq!(format!("{inst}"), "addi sp, sp, -32")
|
||||||
let x = 0x0000a317;
|
let x = 0x0000a317;
|
||||||
let expected = rvdc::Inst::Auipc { uimm: rvdc::Imm::new_u32(0xa << 12), dest: rvdc::Reg::T1 };
|
let expected = rvdc::Inst::Auipc { uimm: rvdc::Imm::new_u32(0xa << 12), dest: rvdc::Reg::T1 };
|
||||||
|
|
||||||
let (inst, is_compressed) = rvdc::Inst::decode(x).unwrap();
|
let (inst, is_compressed) = rvdc::Inst::decode(x, rvdc::Xlen::Rv32).unwrap();
|
||||||
assert_eq!(inst, expected);
|
assert_eq!(inst, expected);
|
||||||
assert_eq!(is_compressed, rvdc::IsCompressed::No);
|
assert_eq!(is_compressed, rvdc::IsCompressed::No);
|
||||||
assert_eq!(format!("{inst}"), "auipc t1, 10")
|
assert_eq!(format!("{inst}"), "auipc t1, 10")
|
||||||
|
|
|
||||||
165
rvdc/src/lib.rs
165
rvdc/src/lib.rs
|
|
@ -6,7 +6,7 @@ use core::fmt::{self, Debug, Display};
|
||||||
use core::ops::RangeInclusive;
|
use core::ops::RangeInclusive;
|
||||||
|
|
||||||
/// The register size of the ISA, RV32 or RV64.
|
/// The register size of the ISA, RV32 or RV64.
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Xlen {
|
pub enum Xlen {
|
||||||
/// 32 bit
|
/// 32 bit
|
||||||
Rv32,
|
Rv32,
|
||||||
|
|
@ -274,10 +274,16 @@ pub enum Inst {
|
||||||
|
|
||||||
/// Add
|
/// Add
|
||||||
Add { dest: Reg, src1: Reg, src2: Reg },
|
Add { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Add 32-bit (**RV64 only**)
|
||||||
|
AddW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Subtract
|
/// Subtract
|
||||||
Sub { dest: Reg, src1: Reg, src2: Reg },
|
Sub { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Subtract 32-bit (**RV64 only**)
|
||||||
|
SubW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Shift Left Logical
|
/// Shift Left Logical
|
||||||
Sll { dest: Reg, src1: Reg, src2: Reg },
|
Sll { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Shift Left Logical 32-bit (**RV64 only**)
|
||||||
|
SllW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Set Less Than (signed)
|
/// Set Less Than (signed)
|
||||||
Slt { dest: Reg, src1: Reg, src2: Reg },
|
Slt { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Set Less Than Unsigned
|
/// Set Less Than Unsigned
|
||||||
|
|
@ -286,8 +292,12 @@ pub enum Inst {
|
||||||
Xor { dest: Reg, src1: Reg, src2: Reg },
|
Xor { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Shift Right Logical (unsigned)
|
/// Shift Right Logical (unsigned)
|
||||||
Srl { dest: Reg, src1: Reg, src2: Reg },
|
Srl { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Shift Right Logical (unsigned) 32-bit (**RV64 only**)
|
||||||
|
SrlW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Shift Right Arithmetic (unsigned)
|
/// Shift Right Arithmetic (unsigned)
|
||||||
Sra { dest: Reg, src1: Reg, src2: Reg },
|
Sra { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Shift Right Arithmetic (unsigned) 32-bit (**RV64 only**)
|
||||||
|
SraW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// OR
|
/// OR
|
||||||
Or { dest: Reg, src1: Reg, src2: Reg },
|
Or { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// AND
|
/// AND
|
||||||
|
|
@ -303,6 +313,8 @@ pub enum Inst {
|
||||||
// ------------- M extension -------------
|
// ------------- M extension -------------
|
||||||
/// Multiply
|
/// Multiply
|
||||||
Mul { dest: Reg, src1: Reg, src2: Reg },
|
Mul { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Multiply 32-bit (**RV64 only**)
|
||||||
|
MulW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Mul Upper Half Signed-Signed
|
/// Mul Upper Half Signed-Signed
|
||||||
Mulh { dest: Reg, src1: Reg, src2: Reg },
|
Mulh { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Mul Upper Half Signed-Unsigned
|
/// Mul Upper Half Signed-Unsigned
|
||||||
|
|
@ -311,12 +323,20 @@ pub enum Inst {
|
||||||
Mulhu { dest: Reg, src1: Reg, src2: Reg },
|
Mulhu { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Divide (signed)
|
/// Divide (signed)
|
||||||
Div { dest: Reg, src1: Reg, src2: Reg },
|
Div { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Divide (signed) 32-bit (**RV64 only**)
|
||||||
|
DivW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Divide Unsigned
|
/// Divide Unsigned
|
||||||
Divu { dest: Reg, src1: Reg, src2: Reg },
|
Divu { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Divide Unsigned 32-bit (**RV64 only**)
|
||||||
|
DivuW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Remainder (signed)
|
/// Remainder (signed)
|
||||||
Rem { dest: Reg, src1: Reg, src2: Reg },
|
Rem { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Remainder (signed) 32-bit (**RV64 only**)
|
||||||
|
RemW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
/// Remainder Unsigned
|
/// Remainder Unsigned
|
||||||
Remu { dest: Reg, src1: Reg, src2: Reg },
|
Remu { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
/// Remainder Unsigned 32-bit (**RV64 only**)
|
||||||
|
RemuW { dest: Reg, src1: Reg, src2: Reg },
|
||||||
|
|
||||||
// ------------- A extension -------------
|
// ------------- A extension -------------
|
||||||
/// Load-Reserved Word
|
/// Load-Reserved Word
|
||||||
|
|
@ -545,7 +565,7 @@ impl Display for Inst {
|
||||||
if imm.as_u32() == 0 {
|
if imm.as_u32() == 0 {
|
||||||
write!(f, "sext.w {dest}, {src1}")
|
write!(f, "sext.w {dest}, {src1}")
|
||||||
} else {
|
} else {
|
||||||
write!(f, "addi.w {dest}, {src1}, {}", imm.as_i32())
|
write!(f, "addiw {dest}, {src1}, {}", imm.as_i32())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Inst::Slti {
|
Inst::Slti {
|
||||||
|
|
@ -606,13 +626,20 @@ impl Display for Inst {
|
||||||
Inst::Add { dest, src1, src2 } => {
|
Inst::Add { dest, src1, src2 } => {
|
||||||
write!(f, "add {dest}, {src1}, {src2}")
|
write!(f, "add {dest}, {src1}, {src2}")
|
||||||
}
|
}
|
||||||
|
Inst::AddW { dest, src1, src2 } => {
|
||||||
|
write!(f, "addw {dest}, {src1}, {src2}")
|
||||||
|
}
|
||||||
Inst::Sub { dest, src1, src2 } => write!(f, "sub {dest}, {src1}, {src2}"),
|
Inst::Sub { dest, src1, src2 } => write!(f, "sub {dest}, {src1}, {src2}"),
|
||||||
|
Inst::SubW { dest, src1, src2 } => write!(f, "subw {dest}, {src1}, {src2}"),
|
||||||
Inst::Sll { dest, src1, src2 } => write!(f, "sll {dest}, {src1}, {src2}"),
|
Inst::Sll { dest, src1, src2 } => write!(f, "sll {dest}, {src1}, {src2}"),
|
||||||
|
Inst::SllW { dest, src1, src2 } => write!(f, "sllw {dest}, {src1}, {src2}"),
|
||||||
Inst::Slt { dest, src1, src2 } => write!(f, "slt {dest}, {src1}, {src2}"),
|
Inst::Slt { dest, src1, src2 } => write!(f, "slt {dest}, {src1}, {src2}"),
|
||||||
Inst::Sltu { dest, src1, src2 } => write!(f, "sltu {dest}, {src1}, {src2}"),
|
Inst::Sltu { dest, src1, src2 } => write!(f, "sltu {dest}, {src1}, {src2}"),
|
||||||
Inst::Xor { dest, src1, src2 } => write!(f, "xor {dest}, {src1}, {src2}"),
|
Inst::Xor { dest, src1, src2 } => write!(f, "xor {dest}, {src1}, {src2}"),
|
||||||
Inst::Srl { dest, src1, src2 } => write!(f, "srl {dest}, {src1}, {src2}"),
|
Inst::Srl { dest, src1, src2 } => write!(f, "srl {dest}, {src1}, {src2}"),
|
||||||
|
Inst::SrlW { dest, src1, src2 } => write!(f, "srlw {dest}, {src1}, {src2}"),
|
||||||
Inst::Sra { dest, src1, src2 } => write!(f, "sra {dest}, {src1}, {src2}"),
|
Inst::Sra { dest, src1, src2 } => write!(f, "sra {dest}, {src1}, {src2}"),
|
||||||
|
Inst::SraW { dest, src1, src2 } => write!(f, "sraw {dest}, {src1}, {src2}"),
|
||||||
Inst::Or { dest, src1, src2 } => write!(f, "or {dest}, {src1}, {src2}"),
|
Inst::Or { dest, src1, src2 } => write!(f, "or {dest}, {src1}, {src2}"),
|
||||||
Inst::And { dest, src1, src2 } => write!(f, "and {dest}, {src1}, {src2}"),
|
Inst::And { dest, src1, src2 } => write!(f, "and {dest}, {src1}, {src2}"),
|
||||||
Inst::Fence { fence } => match fence.fm {
|
Inst::Fence { fence } => match fence.fm {
|
||||||
|
|
@ -625,13 +652,18 @@ impl Display for Inst {
|
||||||
Inst::Ecall => write!(f, "ecall"),
|
Inst::Ecall => write!(f, "ecall"),
|
||||||
Inst::Ebreak => write!(f, "ebreak"),
|
Inst::Ebreak => write!(f, "ebreak"),
|
||||||
Inst::Mul { dest, src1, src2 } => write!(f, "mul {dest}, {src1}, {src2}"),
|
Inst::Mul { dest, src1, src2 } => write!(f, "mul {dest}, {src1}, {src2}"),
|
||||||
|
Inst::MulW { dest, src1, src2 } => write!(f, "mulw {dest}, {src1}, {src2}"),
|
||||||
Inst::Mulh { dest, src1, src2 } => write!(f, "mulh {dest}, {src1}, {src2}"),
|
Inst::Mulh { dest, src1, src2 } => write!(f, "mulh {dest}, {src1}, {src2}"),
|
||||||
Inst::Mulhsu { dest, src1, src2 } => write!(f, "mulhsu {dest}, {src1}, {src2}"),
|
Inst::Mulhsu { dest, src1, src2 } => write!(f, "mulhsu {dest}, {src1}, {src2}"),
|
||||||
Inst::Mulhu { dest, src1, src2 } => write!(f, "mulhu {dest}, {src1}, {src2}"),
|
Inst::Mulhu { dest, src1, src2 } => write!(f, "mulhu {dest}, {src1}, {src2}"),
|
||||||
Inst::Div { dest, src1, src2 } => write!(f, "div {dest}, {src1}, {src2}"),
|
Inst::Div { dest, src1, src2 } => write!(f, "div {dest}, {src1}, {src2}"),
|
||||||
|
Inst::DivW { dest, src1, src2 } => write!(f, "divw {dest}, {src1}, {src2}"),
|
||||||
Inst::Divu { dest, src1, src2 } => write!(f, "divu {dest}, {src1}, {src2}"),
|
Inst::Divu { dest, src1, src2 } => write!(f, "divu {dest}, {src1}, {src2}"),
|
||||||
|
Inst::DivuW { dest, src1, src2 } => write!(f, "divuw {dest}, {src1}, {src2}"),
|
||||||
Inst::Rem { dest, src1, src2 } => write!(f, "rem {dest}, {src1}, {src2}"),
|
Inst::Rem { dest, src1, src2 } => write!(f, "rem {dest}, {src1}, {src2}"),
|
||||||
|
Inst::RemW { dest, src1, src2 } => write!(f, "remw {dest}, {src1}, {src2}"),
|
||||||
Inst::Remu { dest, src1, src2 } => write!(f, "remu {dest}, {src1}, {src2}"),
|
Inst::Remu { dest, src1, src2 } => write!(f, "remu {dest}, {src1}, {src2}"),
|
||||||
|
Inst::RemuW { dest, src1, src2 } => write!(f, "remuw {dest}, {src1}, {src2}"),
|
||||||
Inst::LrW { order, dest, addr } => write!(f, "lr.w{order} {dest}, ({addr})",),
|
Inst::LrW { order, dest, addr } => write!(f, "lr.w{order} {dest}, ({addr})",),
|
||||||
Inst::ScW {
|
Inst::ScW {
|
||||||
order,
|
order,
|
||||||
|
|
@ -737,14 +769,6 @@ impl InstCode {
|
||||||
let end_span = 32 - (range.end() + 1);
|
let end_span = 32 - (range.end() + 1);
|
||||||
(self.0 << (end_span)) >> (end_span + range.start())
|
(self.0 << (end_span)) >> (end_span + range.start())
|
||||||
}
|
}
|
||||||
fn immediate_u(self, mappings: &[(RangeInclusive<u32>, u32)]) -> Imm {
|
|
||||||
let mut imm = 0;
|
|
||||||
for (from, to) in mappings {
|
|
||||||
let value = self.extract(from.clone());
|
|
||||||
imm |= value << to;
|
|
||||||
}
|
|
||||||
Imm::new_u32(imm)
|
|
||||||
}
|
|
||||||
fn immediate_s(self, mappings: &[(RangeInclusive<u32>, u32)]) -> Imm {
|
fn immediate_s(self, mappings: &[(RangeInclusive<u32>, u32)]) -> Imm {
|
||||||
let mut imm = 0;
|
let mut imm = 0;
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
|
|
@ -775,6 +799,10 @@ impl InstCode {
|
||||||
fn rs2_imm(self) -> u32 {
|
fn rs2_imm(self) -> u32 {
|
||||||
self.extract(20..=24)
|
self.extract(20..=24)
|
||||||
}
|
}
|
||||||
|
// shifts on RV64 have one extra bit
|
||||||
|
fn rs2_imm_plus(self) -> u32 {
|
||||||
|
self.extract(20..=25)
|
||||||
|
}
|
||||||
fn rd(self) -> Reg {
|
fn rd(self) -> Reg {
|
||||||
Reg(self.extract(7..=11) as u8)
|
Reg(self.extract(7..=11) as u8)
|
||||||
}
|
}
|
||||||
|
|
@ -788,7 +816,8 @@ impl InstCode {
|
||||||
self.immediate_s(&[(31..=31, 12), (7..=7, 11), (25..=30, 5), (8..=11, 1)])
|
self.immediate_s(&[(31..=31, 12), (7..=7, 11), (25..=30, 5), (8..=11, 1)])
|
||||||
}
|
}
|
||||||
fn imm_u(self) -> Imm {
|
fn imm_u(self) -> Imm {
|
||||||
self.immediate_u(&[(12..=31, 12)])
|
// Don't be fooled by the "u", LUI/AUIPC immediates are sign-extended on RV64.
|
||||||
|
self.immediate_s(&[(12..=31, 12)])
|
||||||
}
|
}
|
||||||
fn imm_j(self) -> Imm {
|
fn imm_j(self) -> Imm {
|
||||||
self.immediate_s(&[(31..=31, 20), (21..=30, 1), (20..=20, 11), (12..=19, 12)])
|
self.immediate_s(&[(31..=31, 20), (21..=30, 1), (20..=20, 11), (12..=19, 12)])
|
||||||
|
|
@ -928,10 +957,10 @@ impl Inst {
|
||||||
/// let x = 0x1101_u16;
|
/// let x = 0x1101_u16;
|
||||||
/// let expected = rvdc::Inst::Addi { imm: rvdc::Imm::new_i32(-0x20), dest: rvdc::Reg::SP, src1: rvdc::Reg::SP };
|
/// let expected = rvdc::Inst::Addi { imm: rvdc::Imm::new_i32(-0x20), dest: rvdc::Reg::SP, src1: rvdc::Reg::SP };
|
||||||
///
|
///
|
||||||
/// let inst = rvdc::Inst::decode_compressed(x).unwrap();
|
/// let inst = rvdc::Inst::decode_compressed(x, rvdc::Xlen::Rv32).unwrap();
|
||||||
/// assert_eq!(inst, expected);
|
/// assert_eq!(inst, expected);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn decode_compressed(code: u16, xlen: Xlen) -> Result<Inst, DecodeError> {
|
pub fn decode_compressed(code: u16, _xlen: Xlen) -> Result<Inst, DecodeError> {
|
||||||
let code = InstCodeC(code);
|
let code = InstCodeC(code);
|
||||||
if code.0 == 0 {
|
if code.0 == 0 {
|
||||||
return Err(decode_error(code, "null instruction"));
|
return Err(decode_error(code, "null instruction"));
|
||||||
|
|
@ -1096,7 +1125,7 @@ impl Inst {
|
||||||
_ => {
|
_ => {
|
||||||
let uimm = code.immediate_s(&[(2..=6, 12), (12..=12, 17)]);
|
let uimm = code.immediate_s(&[(2..=6, 12), (12..=12, 17)]);
|
||||||
if uimm.as_u32() == 0 {
|
if uimm.as_u32() == 0 {
|
||||||
return Err(decode_error(code, "C.LUI imm"));
|
return Err(decode_error(code, "C.LUI zero immediate"));
|
||||||
}
|
}
|
||||||
Inst::Lui {
|
Inst::Lui {
|
||||||
uimm,
|
uimm,
|
||||||
|
|
@ -1352,7 +1381,13 @@ impl Inst {
|
||||||
src1: code.rs1(),
|
src1: code.rs1(),
|
||||||
},
|
},
|
||||||
0b001 => {
|
0b001 => {
|
||||||
if code.funct7() != 0 {
|
// For RV32, bit 25 must be zero as well.
|
||||||
|
let left_zeroes = code.funct7()
|
||||||
|
>> match xlen {
|
||||||
|
Xlen::Rv32 => 0,
|
||||||
|
Xlen::Rv64 => 1,
|
||||||
|
};
|
||||||
|
if left_zeroes != 0 {
|
||||||
return Err(decode_error(code, "slli shift overflow"));
|
return Err(decode_error(code, "slli shift overflow"));
|
||||||
}
|
}
|
||||||
Inst::Slli {
|
Inst::Slli {
|
||||||
|
|
@ -1361,7 +1396,8 @@ impl Inst {
|
||||||
src1: code.rs1(),
|
src1: code.rs1(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0b101 => match code.funct7() {
|
0b101 => match xlen {
|
||||||
|
Xlen::Rv32 => match code.funct7() {
|
||||||
0b0000000 => Inst::Srli {
|
0b0000000 => Inst::Srli {
|
||||||
imm: Imm::new_u32(code.rs2_imm()),
|
imm: Imm::new_u32(code.rs2_imm()),
|
||||||
dest: code.rd(),
|
dest: code.rd(),
|
||||||
|
|
@ -1372,13 +1408,30 @@ impl Inst {
|
||||||
dest: code.rd(),
|
dest: code.rd(),
|
||||||
src1: code.rs1(),
|
src1: code.rs1(),
|
||||||
},
|
},
|
||||||
_ => return Err(decode_error(code, "OP-IMM funct7")),
|
_ => return Err(decode_error(code, "srli shift overflow")),
|
||||||
|
},
|
||||||
|
Xlen::Rv64 => {
|
||||||
|
let upper = code.funct7() >> 1;
|
||||||
|
match upper {
|
||||||
|
0b010000 => Inst::Srai {
|
||||||
|
imm: Imm::new_u32(code.rs2_imm_plus()),
|
||||||
|
dest: code.rd(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
},
|
||||||
|
0b000000 => Inst::Srli {
|
||||||
|
imm: Imm::new_u32(code.rs2_imm_plus()),
|
||||||
|
dest: code.rd(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
},
|
||||||
|
_ => return Err(decode_error(code, "srai/srli upper bits")),
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => return Err(decode_error(code, "OP-IMM funct3")),
|
_ => return Err(decode_error(code, "OP-IMM funct3")),
|
||||||
},
|
},
|
||||||
// OP-IMM-32
|
// OP-IMM-32
|
||||||
0b0011011 => {
|
0b0011011 => {
|
||||||
if !xlen.is_64() {
|
if xlen.is_32() {
|
||||||
return Err(decode_error(code, "OP-IMM-32 only on RV64"));
|
return Err(decode_error(code, "OP-IMM-32 only on RV64"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1443,6 +1496,28 @@ impl Inst {
|
||||||
_ => return Err(decode_error(code, "OP funct3/funct7")),
|
_ => return Err(decode_error(code, "OP funct3/funct7")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// OP-32
|
||||||
|
0b0111011 => {
|
||||||
|
if xlen.is_32() {
|
||||||
|
return Err(decode_error(code, "OP-IMM-32 only on RV64"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (dest, src1, src2) = (code.rd(), code.rs1(), code.rs2());
|
||||||
|
match (code.funct3(), code.funct7()) {
|
||||||
|
(0b000, 0b0000000) => Inst::AddW { dest, src1, src2 },
|
||||||
|
(0b000, 0b0100000) => Inst::SubW { dest, src1, src2 },
|
||||||
|
(0b001, 0b0000000) => Inst::SllW { dest, src1, src2 },
|
||||||
|
(0b101, 0b0000000) => Inst::SrlW { dest, src1, src2 },
|
||||||
|
(0b101, 0b0100000) => Inst::SraW { dest, src1, src2 },
|
||||||
|
|
||||||
|
(0b000, 0b0000001) => Inst::MulW { dest, src1, src2 },
|
||||||
|
(0b100, 0b0000001) => Inst::DivW { dest, src1, src2 },
|
||||||
|
(0b101, 0b0000001) => Inst::DivuW { dest, src1, src2 },
|
||||||
|
(0b110, 0b0000001) => Inst::RemW { dest, src1, src2 },
|
||||||
|
(0b111, 0b0000001) => Inst::RemuW { dest, src1, src2 },
|
||||||
|
_ => return Err(decode_error(code, "OP-32 funct3/funct7")),
|
||||||
|
}
|
||||||
|
}
|
||||||
// MISC-MEM
|
// MISC-MEM
|
||||||
0b0001111 => {
|
0b0001111 => {
|
||||||
let fm = code.extract(28..=31);
|
let fm = code.extract(28..=31);
|
||||||
|
|
@ -1557,6 +1632,8 @@ impl Inst {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
use core::sync::atomic::AtomicU32;
|
||||||
|
use core::sync::atomic::Ordering;
|
||||||
use std::prelude::rust_2024::*;
|
use std::prelude::rust_2024::*;
|
||||||
|
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
|
|
@ -1564,6 +1641,8 @@ mod tests {
|
||||||
|
|
||||||
use object::Object;
|
use object::Object;
|
||||||
use object::ObjectSection;
|
use object::ObjectSection;
|
||||||
|
use rayon::iter::IntoParallelRefIterator;
|
||||||
|
use rayon::iter::ParallelIterator;
|
||||||
|
|
||||||
use crate::Fence;
|
use crate::Fence;
|
||||||
use crate::FenceSet;
|
use crate::FenceSet;
|
||||||
|
|
@ -1574,18 +1653,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(not(slow_tests), ignore = "cfg(slow_tests) not enabled")]
|
#[cfg_attr(not(slow_tests), ignore = "cfg(slow_tests) not enabled")]
|
||||||
fn exhaustive_decode_no_panic() {
|
fn exhaustive_decode_no_panic_32() {
|
||||||
for i in 0..u32::MAX {
|
exhaustive_decode_no_panic(Xlen::Rv32);
|
||||||
if (i % (2 << 25)) == 0 {
|
|
||||||
let percent = i as f32 / (u32::MAX as f32);
|
|
||||||
let done = (100.0 * percent) as usize;
|
|
||||||
std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done));
|
|
||||||
std::io::stdout().flush().unwrap();
|
|
||||||
}
|
}
|
||||||
let _ = Inst::decode(i, Xlen::Rv32);
|
|
||||||
}
|
|
||||||
let _ = Inst::decode(u32::MAX, Xlen::Rv32);
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(not(slow_tests), ignore = "cfg(slow_tests) not enabled")]
|
||||||
|
fn exhaustive_decode_no_panic_64() {
|
||||||
|
exhaustive_decode_no_panic(Xlen::Rv64);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exhaustive_decode_no_panic(xlen: Xlen) {
|
||||||
for i in 0..u32::MAX {
|
for i in 0..u32::MAX {
|
||||||
if (i % (2 << 25)) == 0 {
|
if (i % (2 << 25)) == 0 {
|
||||||
let percent = i as f32 / (u32::MAX as f32);
|
let percent = i as f32 / (u32::MAX as f32);
|
||||||
|
|
@ -1593,14 +1671,18 @@ mod tests {
|
||||||
std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done));
|
std::print!("\r{}{}", "#".repeat(done), "-".repeat(100 - done));
|
||||||
std::io::stdout().flush().unwrap();
|
std::io::stdout().flush().unwrap();
|
||||||
}
|
}
|
||||||
let _ = Inst::decode(i, Xlen::Rv64);
|
let _ = Inst::decode(i, xlen);
|
||||||
}
|
}
|
||||||
let _ = Inst::decode(u32::MAX, Xlen::Rv64);
|
let _ = Inst::decode(u32::MAX, xlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn size_of_instruction() {
|
fn size_of_instruction() {
|
||||||
assert!(size_of::<Inst>() <= 12);
|
assert!(
|
||||||
|
size_of::<Inst>() <= 16,
|
||||||
|
"size of instruction is too large: {}",
|
||||||
|
size_of::<Inst>()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TEST_SECTION_NAME: &str = ".text.rvdctest";
|
const TEST_SECTION_NAME: &str = ".text.rvdctest";
|
||||||
|
|
@ -1699,14 +1781,14 @@ mod tests {
|
||||||
const CHUNKS: u32 = 128;
|
const CHUNKS: u32 = 128;
|
||||||
const CHUNK_SIZE: u32 = u32::MAX / CHUNKS;
|
const CHUNK_SIZE: u32 = u32::MAX / CHUNKS;
|
||||||
|
|
||||||
for (chunk_idx, start) in ((SKIP_CHUNKS * CHUNK_SIZE)..u32::MAX)
|
let chunks = ((SKIP_CHUNKS * CHUNK_SIZE)..u32::MAX)
|
||||||
.step_by(CHUNK_SIZE as usize)
|
.step_by(CHUNK_SIZE as usize)
|
||||||
.enumerate()
|
.collect::<Vec<_>>();
|
||||||
{
|
|
||||||
let chunk_idx = chunk_idx + SKIP_CHUNKS as usize;
|
|
||||||
|
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
|
let completed = AtomicU32::new(0);
|
||||||
|
|
||||||
|
chunks.par_iter().for_each(|&start| {
|
||||||
let insts = (start..=start.saturating_add(CHUNK_SIZE))
|
let insts = (start..=start.saturating_add(CHUNK_SIZE))
|
||||||
.filter_map(|code| Some((code, Inst::decode_normal(code, Xlen::Rv32).ok()?)))
|
.filter_map(|code| Some((code, Inst::decode_normal(code, Xlen::Rv32).ok()?)))
|
||||||
.filter(|(_, inst)| is_inst_supposed_to_roundtrip(inst))
|
.filter(|(_, inst)| is_inst_supposed_to_roundtrip(inst))
|
||||||
|
|
@ -1732,16 +1814,19 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = start_time.elapsed();
|
let already_completed = completed.fetch_add(1, Ordering::Relaxed);
|
||||||
let chunk_number = chunk_idx + 1;
|
let already_elapsed = start_time.elapsed();
|
||||||
let remaining = elapsed * (CHUNKS.saturating_sub(chunk_number as u32));
|
|
||||||
|
let remaining_chunks = CHUNKS.saturating_sub(already_completed);
|
||||||
|
let remaining =
|
||||||
|
already_elapsed / std::cmp::max(already_completed, 1) * remaining_chunks;
|
||||||
|
|
||||||
writeln!(
|
writeln!(
|
||||||
std::io::stdout(),
|
std::io::stdout(),
|
||||||
"Completed chunk {chunk_number}/{CHUNKS} (estimated {remaining:?} remaining)",
|
"Completed chunk {already_completed}/{CHUNKS} (estimated {remaining:?} remaining)",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
108
src/emu.rs
108
src/emu.rs
|
|
@ -386,8 +386,33 @@ impl<XLEN: XLen> Emulator<XLEN> {
|
||||||
Inst::Srli { imm, dest, src1 } => self[dest] = self[src1].unsigned_shr(imm.as_u32()),
|
Inst::Srli { imm, dest, src1 } => self[dest] = self[src1].unsigned_shr(imm.as_u32()),
|
||||||
Inst::Srai { imm, dest, src1 } => self[dest] = self[src1].signed_shr(imm.as_u32()),
|
Inst::Srai { imm, dest, src1 } => self[dest] = self[src1].signed_shr(imm.as_u32()),
|
||||||
Inst::Add { dest, src1, src2 } => self[dest] = self[src1].add(self[src2]),
|
Inst::Add { dest, src1, src2 } => self[dest] = self[src1].add(self[src2]),
|
||||||
|
Inst::AddW { dest, src1, src2 } => {
|
||||||
|
self[dest] = XLEN::switch2(
|
||||||
|
self[src1],
|
||||||
|
self[src2],
|
||||||
|
|_, _| unreachable!(),
|
||||||
|
|a, b| (a as u32).add(b as u32) as i32 as i64 as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
Inst::Sub { dest, src1, src2 } => self[dest] = self[src1].sub(self[src2]),
|
Inst::Sub { dest, src1, src2 } => self[dest] = self[src1].sub(self[src2]),
|
||||||
Inst::Sll { dest, src1, src2 } => self[dest] = self[src1].shl(self[src2].truncate32()),
|
Inst::SubW { dest, src1, src2 } => {
|
||||||
|
self[dest] = XLEN::switch2(
|
||||||
|
self[src1],
|
||||||
|
self[src2],
|
||||||
|
|_, _| unreachable!(),
|
||||||
|
|a, b| (a as u32).sub(b as u32) as i32 as i64 as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Inst::Sll { dest, src1, src2 } => {
|
||||||
|
self[dest] = self[src1].shl(self[src2].truncate32());
|
||||||
|
}
|
||||||
|
Inst::SllW { dest, src1, src2 } => {
|
||||||
|
self[dest] = XLEN::switch(
|
||||||
|
self[src1],
|
||||||
|
|_| unreachable!(),
|
||||||
|
|a| (a as u32).shl(self[src2].truncate32()) as i32 as i64 as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
Inst::Slt { dest, src1, src2 } => {
|
Inst::Slt { dest, src1, src2 } => {
|
||||||
self[dest] = XLEN::from_bool(self[src1].signed_lt(self[src2]));
|
self[dest] = XLEN::from_bool(self[src1].signed_lt(self[src2]));
|
||||||
}
|
}
|
||||||
|
|
@ -398,9 +423,23 @@ impl<XLEN: XLen> Emulator<XLEN> {
|
||||||
Inst::Srl { dest, src1, src2 } => {
|
Inst::Srl { dest, src1, src2 } => {
|
||||||
self[dest] = self[src1].unsigned_shr(self[src2].truncate32())
|
self[dest] = self[src1].unsigned_shr(self[src2].truncate32())
|
||||||
}
|
}
|
||||||
|
Inst::SrlW { dest, src1, src2 } => {
|
||||||
|
self[dest] = XLEN::switch(
|
||||||
|
self[src1],
|
||||||
|
|_| unreachable!(),
|
||||||
|
|a| (a as u32).unsigned_shr(self[src2].truncate32()) as i32 as i64 as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
Inst::Sra { dest, src1, src2 } => {
|
Inst::Sra { dest, src1, src2 } => {
|
||||||
self[dest] = self[src1].signed_shr(self[src2].truncate32())
|
self[dest] = self[src1].signed_shr(self[src2].truncate32())
|
||||||
}
|
}
|
||||||
|
Inst::SraW { dest, src1, src2 } => {
|
||||||
|
self[dest] = XLEN::switch(
|
||||||
|
self[src1],
|
||||||
|
|_| unreachable!(),
|
||||||
|
|a| (a as u32).unsigned_shr(self[src2].truncate32()) as i32 as i64 as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
Inst::Or { dest, src1, src2 } => self[dest] = self[src1].or(self[src2]),
|
Inst::Or { dest, src1, src2 } => self[dest] = self[src1].or(self[src2]),
|
||||||
Inst::And { dest, src1, src2 } => self[dest] = self[src1].and(self[src2]),
|
Inst::And { dest, src1, src2 } => self[dest] = self[src1].and(self[src2]),
|
||||||
Inst::Fence { fence: _ } => { /* dont care */ }
|
Inst::Fence { fence: _ } => { /* dont care */ }
|
||||||
|
|
@ -502,7 +541,40 @@ impl<XLEN: XLen> Emulator<XLEN> {
|
||||||
}
|
}
|
||||||
self.reservation_set = None;
|
self.reservation_set = None;
|
||||||
}
|
}
|
||||||
_ => return Err(Status::UnsupportedInst(inst)),
|
Inst::AddiW { imm, dest, src1 } => {
|
||||||
|
self[dest] = XLEN::switch(
|
||||||
|
self[src1],
|
||||||
|
|_| unreachable!(),
|
||||||
|
|src| ((src as u32).wrapping_add(imm.as_u32())) as i32 as i64 as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Inst::SlliW { imm, dest, src1 } => {
|
||||||
|
self[dest] = XLEN::switch(
|
||||||
|
self[src1],
|
||||||
|
|_| unreachable!(),
|
||||||
|
|src| ((src as u32).shl(imm.as_u32())) as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Inst::SrliW { imm, dest, src1 } => {
|
||||||
|
self[dest] = XLEN::switch(
|
||||||
|
self[src1],
|
||||||
|
|_| unreachable!(),
|
||||||
|
|src| ((src as u32).unsigned_shr(imm.as_u32())) as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Inst::SraiW { imm, dest, src1 } => {
|
||||||
|
self[dest] = XLEN::switch(
|
||||||
|
self[src1],
|
||||||
|
|_| unreachable!(),
|
||||||
|
|src| ((src as u32).signed_shr(imm.as_u32())) as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Inst::MulW { dest, src1, src2 } => todo!(),
|
||||||
|
Inst::DivW { dest, src1, src2 } => todo!(),
|
||||||
|
Inst::DivuW { dest, src1, src2 } => todo!(),
|
||||||
|
Inst::RemW { dest, src1, src2 } => todo!(),
|
||||||
|
Inst::RemuW { dest, src1, src2 } => todo!(),
|
||||||
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !jumped {
|
if !jumped {
|
||||||
|
|
@ -676,7 +748,7 @@ impl<XLEN: XLen> Emulator<XLEN> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait XLen: Copy + PartialEq + Eq {
|
pub trait XLen: Copy + PartialEq + Eq + Debug {
|
||||||
type Signed;
|
type Signed;
|
||||||
type NextUnsigned;
|
type NextUnsigned;
|
||||||
type NextSigned;
|
type NextSigned;
|
||||||
|
|
@ -687,7 +759,15 @@ pub trait XLen: Copy + PartialEq + Eq {
|
||||||
const SIGNED_MIN: Self;
|
const SIGNED_MIN: Self;
|
||||||
const MAX: Self;
|
const MAX: Self;
|
||||||
|
|
||||||
fn switch<R>(self, on_32: impl FnOnce(u32) -> R, on_64: impl FnOnce(u64) -> R) -> R;
|
const IS_64: bool = matches!(Self::XLEN, rvdc::Xlen::Rv64);
|
||||||
|
|
||||||
|
fn switch(self, on_32: impl FnOnce(u32) -> u32, on_64: impl FnOnce(u64) -> u64) -> Self;
|
||||||
|
fn switch2(
|
||||||
|
self,
|
||||||
|
other: Self,
|
||||||
|
on_32: impl FnOnce(u32, u32) -> u32,
|
||||||
|
on_64: impl FnOnce(u64, u64) -> u64,
|
||||||
|
) -> Self;
|
||||||
|
|
||||||
fn from_bool(v: bool) -> Self {
|
fn from_bool(v: bool) -> Self {
|
||||||
Self::from_32_z(v as u32)
|
Self::from_32_z(v as u32)
|
||||||
|
|
@ -839,9 +919,17 @@ impl XLen for u32 {
|
||||||
|
|
||||||
const XLEN: rvdc::Xlen = rvdc::Xlen::Rv32;
|
const XLEN: rvdc::Xlen = rvdc::Xlen::Rv32;
|
||||||
|
|
||||||
fn switch<R>(self, on_32: impl FnOnce(u32) -> R, _: impl FnOnce(u64) -> R) -> R {
|
fn switch(self, on_32: impl FnOnce(u32) -> u32, _: impl FnOnce(u64) -> u64) -> Self {
|
||||||
on_32(self)
|
on_32(self)
|
||||||
}
|
}
|
||||||
|
fn switch2(
|
||||||
|
self,
|
||||||
|
other: Self,
|
||||||
|
on_32: impl FnOnce(u32, u32) -> u32,
|
||||||
|
_: impl FnOnce(u64, u64) -> u64,
|
||||||
|
) -> Self {
|
||||||
|
on_32(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
fn from_32_s(v: u32) -> Self {
|
fn from_32_s(v: u32) -> Self {
|
||||||
v
|
v
|
||||||
|
|
@ -863,9 +951,17 @@ impl XLen for u64 {
|
||||||
|
|
||||||
const XLEN: rvdc::Xlen = rvdc::Xlen::Rv64;
|
const XLEN: rvdc::Xlen = rvdc::Xlen::Rv64;
|
||||||
|
|
||||||
fn switch<R>(self, _: impl FnOnce(u32) -> R, on_64: impl FnOnce(u64) -> R) -> R {
|
fn switch(self, _: impl FnOnce(u32) -> u32, on_64: impl FnOnce(u64) -> u64) -> Self {
|
||||||
on_64(self)
|
on_64(self)
|
||||||
}
|
}
|
||||||
|
fn switch2(
|
||||||
|
self,
|
||||||
|
other: Self,
|
||||||
|
_: impl FnOnce(u32, u32) -> u32,
|
||||||
|
on_64: impl FnOnce(u64, u64) -> u64,
|
||||||
|
) -> Self {
|
||||||
|
on_64(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
fn from_32_s(v: u32) -> Self {
|
fn from_32_s(v: u32) -> Self {
|
||||||
v as i32 as i64 as u64
|
v as i32 as i64 as u64
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ fn check() -> eyre::Result<()> {
|
||||||
test_case(tmpdir.path(), &file, name, 32, "rv32ima")?;
|
test_case(tmpdir.path(), &file, name, 32, "rv32ima")?;
|
||||||
test_case(tmpdir.path(), &file, name, 32, "rv32imac")?;
|
test_case(tmpdir.path(), &file, name, 32, "rv32imac")?;
|
||||||
|
|
||||||
// test_case(tmpdir.path(), &file, name, 64, "rv64ima")?;
|
test_case(tmpdir.path(), &file, name, 64, "rv64ima")?;
|
||||||
// test_case(tmpdir.path(), &file, name, 64, "rv64imac")?;
|
// test_case(tmpdir.path(), &file, name, 64, "rv64imac")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ fn test_case(
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Box::new(|_, xreg| {
|
Box::new(|_, xreg| {
|
||||||
if xreg[Reg::A7.0 as usize] == u32::MAX as u64 {
|
if xreg[Reg::A7.0 as usize] == u64::MAX {
|
||||||
if xreg[Reg::A0.0 as usize] == 1 {
|
if xreg[Reg::A0.0 as usize] == 1 {
|
||||||
Err(rustv32i::emu::Status::Exit { code: 0 })
|
Err(rustv32i::emu::Status::Exit { code: 0 })
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -101,6 +101,7 @@ fn build(tmpdir: &Path, src: &Path, size: u8, march: &str) -> eyre::Result<PathB
|
||||||
cmd.arg(src);
|
cmd.arg(src);
|
||||||
cmd.arg("-o");
|
cmd.arg("-o");
|
||||||
cmd.arg(&out_path);
|
cmd.arg(&out_path);
|
||||||
|
cmd.arg(format!("-DRV{size}"));
|
||||||
|
|
||||||
let output = cmd.output().wrap_err("failed to spawn clang")?;
|
let output = cmd.output().wrap_err("failed to spawn clang")?;
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
|
|
|
||||||
|
|
@ -83,25 +83,49 @@ START_TEST
|
||||||
CASE sll, 2, 1, 4
|
CASE sll, 2, 1, 4
|
||||||
CASE sll, 2, 20, 2097152
|
CASE sll, 2, 20, 2097152
|
||||||
CASE sll, 2, 30, 2147483648
|
CASE sll, 2, 30, 2147483648
|
||||||
CASE sll, 2, 31, 0
|
|
||||||
CASER sll, 2, 32, 2 # error for immediate
|
|
||||||
CASE sll, 0, 10, 0
|
CASE sll, 0, 10, 0
|
||||||
CASE sll, 10, 0, 10
|
CASE sll, 10, 0, 10
|
||||||
|
#ifdef RV32
|
||||||
|
CASE sll, 2, 31, 0
|
||||||
CASE sll, -1, 31, -2147483648
|
CASE sll, -1, 31, -2147483648
|
||||||
CASER sll, -1, 32, -1 # error for immediate
|
CASER sll, -1, 32, -1 # error for immediate
|
||||||
|
CASER sll, 2, 32, 2 # error for immediate
|
||||||
|
#elif RV64
|
||||||
|
#CASE_BOTH sllw, slliw, 2, 31, 0
|
||||||
|
#CASE_BOTH sllw, slliw, -1, 31, -2147483648
|
||||||
|
#CASER sllw, -1, 32, -1 # error for immediate
|
||||||
|
#CASER sllw, 2, 32, 2 # error for immediate
|
||||||
|
|
||||||
|
CASE sll, -1, 31, 18446744071562067968
|
||||||
|
CASER sll, 2, 63, 0 # error for immediate
|
||||||
|
CASE sll, -1, 32, 18446744069414584320 # test with immediate as well
|
||||||
|
CASER sll, -1, 63, 9223372036854775808 # error for immediate
|
||||||
|
CASER sll, -1, 64, -1 # error for immediate
|
||||||
|
CASER sll, 2, 64, 2 # error for immediate
|
||||||
|
#endif
|
||||||
|
|
||||||
CASE srl, 4, 1, 2
|
CASE srl, 4, 1, 2
|
||||||
CASE srl, 0, 10, 0
|
CASE srl, 0, 10, 0
|
||||||
CASE srl, 10, 0, 10
|
CASE srl, 10, 0, 10
|
||||||
CASE srl, -1, 1, 2147483647
|
|
||||||
CASE srl, 0b111, 2, 0b001
|
CASE srl, 0b111, 2, 0b001
|
||||||
|
#ifdef RV32
|
||||||
|
CASE srl, -1, 1, 2147483647
|
||||||
CASER srl, -1, 32, -1 # error for immediate
|
CASER srl, -1, 32, -1 # error for immediate
|
||||||
|
#elif RV64
|
||||||
|
CASE srl, -1, 1, 9223372036854775807
|
||||||
|
CASE srl, -1, 32, 4294967295
|
||||||
|
CASER srl, -1, 64, -1 # error for immediate
|
||||||
|
#endif
|
||||||
|
|
||||||
CASER sub, 10, 5, 5
|
CASER sub, 10, 5, 5
|
||||||
CASER sub, -1, 1, -2
|
CASER sub, -1, 1, -2
|
||||||
CASER sub, 1, 2, -1
|
CASER sub, 1, 2, -1
|
||||||
CASER sub, -1, -2, 1
|
CASER sub, -1, -2, 1
|
||||||
|
#ifdef RV32
|
||||||
CASER sub, 0, 4294967295, 1
|
CASER sub, 0, 4294967295, 1
|
||||||
|
#elif RV64
|
||||||
|
CASER sub, 0, 18446744073709551615, 1
|
||||||
|
#endif
|
||||||
|
|
||||||
.macro CASE_SUB a:req, b:req
|
.macro CASE_SUB a:req, b:req
|
||||||
CASER sub, \a, \b, \a - \b
|
CASER sub, \a, \b, \a - \b
|
||||||
|
|
@ -115,7 +139,12 @@ START_TEST
|
||||||
CASE sra, -1, 1, -1
|
CASE sra, -1, 1, -1
|
||||||
CASE sra, -1, 31, -1
|
CASE sra, -1, 31, -1
|
||||||
CASE sra, 0b111, 2, 0b001
|
CASE sra, 0b111, 2, 0b001
|
||||||
|
#ifdef RV32
|
||||||
CASER sra, 10, 32, 10 # error for immediate
|
CASER sra, 10, 32, 10 # error for immediate
|
||||||
|
#elif RV64
|
||||||
|
CASE sra, 10, 32, 0
|
||||||
|
CASER sra, 10, 64, 10 # error for immediate
|
||||||
|
#endif
|
||||||
|
|
||||||
# M extension
|
# M extension
|
||||||
|
|
||||||
|
|
@ -123,7 +152,11 @@ START_TEST
|
||||||
CASER mul, 10, 0, 0
|
CASER mul, 10, 0, 0
|
||||||
CASER mul, 10, 1, 10
|
CASER mul, 10, 1, 10
|
||||||
CASER mul, -1, -1, 1
|
CASER mul, -1, -1, 1
|
||||||
|
#ifdef RV32
|
||||||
CASER mul, 25252566, 5225225, 353909638
|
CASER mul, 25252566, 5225225, 353909638
|
||||||
|
#elif RV64
|
||||||
|
// TODO
|
||||||
|
#endif
|
||||||
|
|
||||||
.macro CASE_MUL a:req, b:req
|
.macro CASE_MUL a:req, b:req
|
||||||
CASER mul, \a, \b, \a * \b
|
CASER mul, \a, \b, \a * \b
|
||||||
|
|
@ -133,11 +166,19 @@ START_TEST
|
||||||
|
|
||||||
CASER mulh 4, 4, 0
|
CASER mulh 4, 4, 0
|
||||||
CASER mulh, -1, -1, 0
|
CASER mulh, -1, -1, 0
|
||||||
|
#ifdef RV32
|
||||||
CASER mulh, 25252566, 5225225, 30722
|
CASER mulh, 25252566, 5225225, 30722
|
||||||
|
#elif RV64
|
||||||
|
// TODO
|
||||||
|
#endif
|
||||||
|
|
||||||
CASER mulhu 4, 4, 0
|
CASER mulhu 4, 4, 0
|
||||||
|
#ifdef RV32
|
||||||
CASER mulhu, -1, -1, 4294967294
|
CASER mulhu, -1, -1, 4294967294
|
||||||
CASER mulhu, 25252566, 5225225, 30722
|
CASER mulhu, 25252566, 5225225, 30722
|
||||||
|
#elif RV64
|
||||||
|
// TODO
|
||||||
|
#endif
|
||||||
|
|
||||||
# mulhsu hasn't been implemented yet.
|
# mulhsu hasn't been implemented yet.
|
||||||
|
|
||||||
|
|
@ -148,21 +189,33 @@ START_TEST
|
||||||
CASER div, -10, 2, -5
|
CASER div, -10, 2, -5
|
||||||
CASER div, 5, 2, 2
|
CASER div, 5, 2, 2
|
||||||
CASER div, 5, -1, -5
|
CASER div, 5, -1, -5
|
||||||
|
#ifdef RV32
|
||||||
CASER div, -2147483648, -1, -1
|
CASER div, -2147483648, -1, -1
|
||||||
|
#elif RV64
|
||||||
|
// TODO
|
||||||
|
#endif
|
||||||
|
|
||||||
CASER divu, 4, 2, 2
|
CASER divu, 4, 2, 2
|
||||||
CASER divu, -1, 1, -1
|
CASER divu, -1, 1, -1
|
||||||
CASER divu, 1, 1, 1
|
CASER divu, 1, 1, 1
|
||||||
CASER divu, 1, 0, -1
|
CASER divu, 1, 0, -1
|
||||||
CASER divu, -10, 2, 2147483643
|
|
||||||
CASER divu, 5, 2, 2
|
CASER divu, 5, 2, 2
|
||||||
|
#ifdef RV32
|
||||||
|
CASER divu, -10, 2, 2147483643
|
||||||
|
#elif RV64
|
||||||
|
// TODO
|
||||||
|
#endif
|
||||||
|
|
||||||
CASER rem, 4, 2, 0
|
CASER rem, 4, 2, 0
|
||||||
CASER rem, 5, 2, 1
|
CASER rem, 5, 2, 1
|
||||||
CASER rem, 5, 0, 5
|
CASER rem, 5, 0, 5
|
||||||
CASER rem, -10, 3, -1
|
CASER rem, -10, 3, -1
|
||||||
CASER rem, 5, -1, 0
|
CASER rem, 5, -1, 0
|
||||||
|
#ifdef RV32
|
||||||
CASER rem, -2147483648, -1, 0
|
CASER rem, -2147483648, -1, 0
|
||||||
|
#elif RV64
|
||||||
|
// TODO
|
||||||
|
#endif
|
||||||
|
|
||||||
CASER remu, 4, 2, 0
|
CASER remu, 4, 2, 0
|
||||||
CASER remu, 5, 2, 1
|
CASER remu, 5, 2, 1
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue