mirror of
https://github.com/Noratrieb/rustv32i.git
synced 2026-01-14 13:25:01 +01:00
This commit is contained in:
parent
f0c04f1466
commit
8d01c9f1d7
4 changed files with 290 additions and 67 deletions
183
rvdc/src/lib.rs
183
rvdc/src/lib.rs
|
|
@ -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)])
|
||||||
|
|
@ -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,24 +1396,42 @@ impl Inst {
|
||||||
src1: code.rs1(),
|
src1: code.rs1(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0b101 => match code.funct7() {
|
0b101 => match xlen {
|
||||||
0b0000000 => Inst::Srli {
|
Xlen::Rv32 => match code.funct7() {
|
||||||
imm: Imm::new_u32(code.rs2_imm()),
|
0b0000000 => Inst::Srli {
|
||||||
dest: code.rd(),
|
imm: Imm::new_u32(code.rs2_imm()),
|
||||||
src1: code.rs1(),
|
dest: code.rd(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
},
|
||||||
|
0b0100000 => Inst::Srai {
|
||||||
|
imm: Imm::new_u32(code.rs2_imm()),
|
||||||
|
dest: code.rd(),
|
||||||
|
src1: code.rs1(),
|
||||||
|
},
|
||||||
|
_ => return Err(decode_error(code, "srli shift overflow")),
|
||||||
},
|
},
|
||||||
0b0100000 => Inst::Srai {
|
Xlen::Rv64 => {
|
||||||
imm: Imm::new_u32(code.rs2_imm()),
|
let upper = code.funct7() >> 1;
|
||||||
dest: code.rd(),
|
match upper {
|
||||||
src1: code.rs1(),
|
0b010000 => Inst::Srai {
|
||||||
},
|
imm: Imm::new_u32(code.rs2_imm_plus()),
|
||||||
_ => return Err(decode_error(code, "OP-IMM funct7")),
|
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);
|
||||||
|
|
@ -1713,47 +1788,45 @@ mod tests {
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
let completed = AtomicU32::new(0);
|
let completed = AtomicU32::new(0);
|
||||||
|
|
||||||
chunks
|
chunks.par_iter().for_each(|&start| {
|
||||||
.par_iter()
|
let insts = (start..=start.saturating_add(CHUNK_SIZE))
|
||||||
.for_each(|&start| {
|
.filter_map(|code| Some((code, Inst::decode_normal(code, Xlen::Rv32).ok()?)))
|
||||||
let insts = (start..=start.saturating_add(CHUNK_SIZE))
|
.filter(|(_, inst)| is_inst_supposed_to_roundtrip(inst))
|
||||||
.filter_map(|code| Some((code, Inst::decode_normal(code, Xlen::Rv32).ok()?)))
|
.collect::<Vec<_>>();
|
||||||
.filter(|(_, inst)| is_inst_supposed_to_roundtrip(inst))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut text =
|
let mut text = std::format!(".section {TEST_SECTION_NAME}\n.globl _start\n_start:\n");
|
||||||
std::format!(".section {TEST_SECTION_NAME}\n.globl _start\n_start:\n");
|
for (_, inst) in &insts {
|
||||||
for (_, inst) in &insts {
|
writeln!(text, " {inst}").unwrap();
|
||||||
writeln!(text, " {inst}").unwrap();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let data = clang_assemble(&text, "-march=rv32ima_zihintpause");
|
let data = clang_assemble(&text, "-march=rv32ima_zihintpause");
|
||||||
|
|
||||||
for (i, result_code) in data.chunks(4).enumerate() {
|
for (i, result_code) in data.chunks(4).enumerate() {
|
||||||
let result_code = u32::from_le_bytes(result_code.try_into().unwrap());
|
let result_code = u32::from_le_bytes(result_code.try_into().unwrap());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
insts[i].0, result_code,
|
insts[i].0, result_code,
|
||||||
"failed to rountrip!\n\
|
"failed to rountrip!\n\
|
||||||
instruction `{:0>32b}` failed to rountrip\n\
|
instruction `{:0>32b}` failed to rountrip\n\
|
||||||
resulted in `{:0>32b}` instead.\n\
|
resulted in `{:0>32b}` instead.\n\
|
||||||
disassembly of original instruction: `{}`",
|
disassembly of original instruction: `{}`",
|
||||||
insts[i].0, result_code, insts[i].1
|
insts[i].0, result_code, insts[i].1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let already_completed = completed.fetch_add(1, Ordering::Relaxed);
|
let already_completed = completed.fetch_add(1, Ordering::Relaxed);
|
||||||
let already_elapsed = start_time.elapsed();
|
let already_elapsed = start_time.elapsed();
|
||||||
|
|
||||||
let remaining_chunks = CHUNKS.saturating_sub(already_completed);
|
let remaining_chunks = CHUNKS.saturating_sub(already_completed);
|
||||||
let remaining = already_elapsed / std::cmp::max(already_completed, 1) * remaining_chunks;
|
let remaining =
|
||||||
|
already_elapsed / std::cmp::max(already_completed, 1) * remaining_chunks;
|
||||||
|
|
||||||
writeln!(
|
writeln!(
|
||||||
std::io::stdout(),
|
std::io::stdout(),
|
||||||
"Completed chunk {already_completed}/{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