use celox::{BigUint, Simulator};
macro_rules! cross_validate {
($code:expr, $top:expr, $setup_n:expr, $setup_c:expr, $signals:expr) => {{
let mut sim_n = Simulator::builder($code, $top).build().unwrap();
let mut sim_c = Simulator::builder($code, $top).build_cranelift().unwrap();
$setup_n(&mut sim_n);
$setup_c(&mut sim_c);
for &sig_name in $signals {
let sn = sim_n.signal(sig_name);
let sc = sim_c.signal(sig_name);
let vn: BigUint = sim_n.get(sn);
let vc: BigUint = sim_c.get(sc);
assert_eq!(
vn, vc,
"Native vs Cranelift mismatch on '{sig_name}': native={vn:#x}, cranelift={vc:#x}"
);
}
}};
}
#[test]
fn xv_wide_to_narrow_shift_keeps_the_narrow_result_width() {
let code = r#"
module Top (
wide: input logic<128>,
out: output logic<32>
) {
assign out = (((wide >> 127) as 1) as i32) << 31 >>> 31;
}
"#;
for value in [BigUint::from(0u8), BigUint::from(1u8) << 127usize] {
let mut native = Simulator::builder(code, "Top").build().unwrap();
let mut cranelift = Simulator::builder(code, "Top").build_cranelift().unwrap();
native.set_wide(native.signal("wide"), value.clone());
cranelift.set_wide(cranelift.signal("wide"), value.clone());
let native_out: BigUint = native.get(native.signal("out"));
let cranelift_out: BigUint = cranelift.get(cranelift.signal("out"));
assert_eq!(native_out, cranelift_out);
assert_eq!(
native_out,
if value == BigUint::ZERO {
BigUint::ZERO
} else {
BigUint::from(u32::MAX)
}
);
}
}
#[test]
fn xv_add_sub_mul() {
let code = r#"module Top(a: input logic<64>, b: input logic<64>,
o_add: output logic<64>, o_sub: output logic<64>, o_mul: output logic<64>) {
assign o_add = a + b;
assign o_sub = a - b;
assign o_mul = a * b;
}"#;
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set(sim.signal("a"), 0xDEAD_BEEF_u64);
sim.set(sim.signal("b"), 0xCAFE_BABE_u64);
},
|sim: &mut Simulator<_>| {
sim.set(sim.signal("a"), 0xDEAD_BEEF_u64);
sim.set(sim.signal("b"), 0xCAFE_BABE_u64);
},
&["o_add", "o_sub", "o_mul"]
);
}
#[test]
fn xv_wide_add_128() {
let code = r#"module Top(a: input logic<128>, b: input logic<128>, o: output logic<128>) {
assign o = a + b;
}"#;
let a_val: BigUint = BigUint::from(1u64) << 64 | BigUint::from(0x1234_5678u64);
let b_val: BigUint = BigUint::from(1u64);
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), a_val.clone());
sim.set_wide(sim.signal("b"), b_val.clone());
},
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), a_val.clone());
sim.set_wide(sim.signal("b"), b_val.clone());
},
&["o"]
);
}
#[test]
fn xv_wide_add_carry() {
let code = r#"module Top(a: input logic<128>, b: input logic<128>, o: output logic<128>) {
assign o = a + b;
}"#;
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(u64::MAX));
sim.set_wide(sim.signal("b"), BigUint::from(1u64));
},
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(u64::MAX));
sim.set_wide(sim.signal("b"), BigUint::from(1u64));
},
&["o"]
);
}
#[test]
fn xv_wide_sub_128() {
let code = r#"module Top(a: input logic<128>, b: input logic<128>, o: output logic<128>) {
assign o = a - b;
}"#;
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(1u64) << 64);
sim.set_wide(sim.signal("b"), BigUint::from(1u64));
},
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(1u64) << 64);
sim.set_wide(sim.signal("b"), BigUint::from(1u64));
},
&["o"]
);
}
#[test]
fn xv_wide_shl_256() {
let code = r#"module Top(a: input logic<256>, amt: input logic<9>, o: output logic<256>) {
assign o = a << amt;
}"#;
for &amt in &[0u16, 1, 4, 63, 64, 65, 128, 200, 255] {
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(0xDEAD_BEEF_CAFE_BABEu64));
sim.set(sim.signal("amt"), amt);
},
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(0xDEAD_BEEF_CAFE_BABEu64));
sim.set(sim.signal("amt"), amt);
},
&["o"]
);
}
}
#[test]
fn xv_wide_shr_256() {
let code = r#"module Top(a: input logic<256>, amt: input logic<9>, o: output logic<256>) {
assign o = a >> amt;
}"#;
let val: BigUint = BigUint::from(0xABCDu64) << 192 | BigUint::from(0x1234u64);
for &amt in &[0u16, 1, 4, 64, 128, 192, 255] {
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), val.clone());
sim.set(sim.signal("amt"), amt);
},
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), val.clone());
sim.set(sim.signal("amt"), amt);
},
&["o"]
);
}
}
#[test]
fn xv_wide_shl_512() {
let code = r#"module Top(a: input logic<512>, amt: input logic<10>, o: output logic<512>) {
assign o = a << amt;
}"#;
for &amt in &[0u16, 1, 64, 65, 200, 511] {
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(0xDEADu64));
sim.set(sim.signal("amt"), amt);
},
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(0xDEADu64));
sim.set(sim.signal("amt"), amt);
},
&["o"]
);
}
}
#[test]
fn xv_wide_shl_1024() {
let code = r#"module Top(a: input logic<1024>, amt: input logic<11>, o: output logic<1024>) {
assign o = a << amt;
}"#;
for &amt in &[0u16, 1, 64, 500, 1023] {
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(0xDEADu64));
sim.set(sim.signal("amt"), amt);
},
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(0xDEADu64));
sim.set(sim.signal("amt"), amt);
},
&["o"]
);
}
}
#[test]
fn xv_narrow_to_wide_shl() {
let code = r#"module Top(a: input logic<256>, amt: input logic<10>, o: output logic<512>) {
assign o = (a as 512) << amt;
}"#;
for &amt in &[0u16, 1, 256, 300] {
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(0xDEAD_BEEF_CAFE_BABEu64));
sim.set(sim.signal("amt"), amt);
},
|sim: &mut Simulator<_>| {
sim.set_wide(sim.signal("a"), BigUint::from(0xDEAD_BEEF_CAFE_BABEu64));
sim.set(sim.signal("amt"), amt);
},
&["o"]
);
}
}
#[test]
fn xv_wide_bitwise() {
let code = r#"module Top(a: input logic<256>, b: input logic<256>,
o_and: output logic<256>, o_or: output logic<256>, o_xor: output logic<256>) {
assign o_and = a & b;
assign o_or = a | b;
assign o_xor = a ^ b;
}"#;
cross_validate!(
code,
"Top",
|sim: &mut Simulator<_>| {
sim.set_wide(
sim.signal("a"),
BigUint::from(0xFF00u64) | (BigUint::from(0xAAu64) << 128),
);
sim.set_wide(
sim.signal("b"),
BigUint::from(0x0FF0u64) | (BigUint::from(0x55u64) << 128),
);
},
|sim: &mut Simulator<_>| {
sim.set_wide(
sim.signal("a"),
BigUint::from(0xFF00u64) | (BigUint::from(0xAAu64) << 128),
);
sim.set_wide(
sim.signal("b"),
BigUint::from(0x0FF0u64) | (BigUint::from(0x55u64) << 128),
);
},
&["o_and", "o_or", "o_xor"]
);
}