use celox::{BigUint, Simulator};
#[path = "test_utils/mod.rs"]
#[macro_use]
mod test_utils;
all_backends! {
fn test_wide_context_addition_carry(sim) {
@setup { let code = r#"
module Top (
a: input logic<64>,
b: input logic<64>,
o: output logic<65>
) {
assign o = a + b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let o = sim.signal("o");
let val_a = u64::MAX;
let val_b = 1u64;
let expected = BigUint::from(1u32) << 64;
sim.modify(|io| {
io.set(a, val_a);
io.set(b, val_b);
})
.unwrap();
assert_eq!(
sim.get(o),
expected,
"Carry bit should be preserved in 65-bit context"
);
}
fn test_wide_context_subtraction_underflow(sim) {
@setup { let code = r#"
module Top (
a: input logic<64>,
b: input logic<64>,
o: output logic<65>
) {
assign o = a - b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let o = sim.signal("o");
let val_a = 0u64;
let val_b = 1u64;
let expected = (BigUint::from(1u32) << 65) - 1u32;
sim.modify(|io| {
io.set(a, val_a);
io.set(b, val_b);
})
.unwrap();
assert_eq!(
sim.get(o),
expected,
"Underflow in 65-bit context should result in 65-bit all-ones"
);
}
fn test_wide_context_shift_left(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (
i: input logic<64>,
s: input logic<8>,
o: output logic<130>
) {
// i as 130 ensures the shift happens in 130-bit context
assign o = (i as 130) << s;
}
"#; }
@build Simulator::builder(code, "Top");
let i = sim.signal("i");
let s = sim.signal("s");
let o = sim.signal("o");
let val_i = 1u64;
let val_s = 65u8;
let expected = BigUint::from(1u32) << 65;
sim.modify(|io| {
io.set(i, val_i);
io.set(s, val_s);
})
.unwrap();
assert_eq!(sim.get(o), expected, "Shift left with wide cast failed");
}
fn test_wide_context_constant_folding(sim) {
@setup { let code = r#"
module Top (
o: output logic<65>
) {
always_comb {
o = 64'hffff_ffff_ffff_ffff + 64'h1;
}
}
"#; }
@build Simulator::builder(code, "Top");
let o = sim.signal("o");
let expected = BigUint::from(1u32) << 64;
assert_eq!(
sim.get(o),
expected,
"Constant folding in 65-bit context failed"
);
}
fn test_wide_runtime_shift_width_behavior(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (
i: input logic<64>,
s: input logic<8>,
o1: output logic<130>,
o2: output logic<130>
) {
always_comb {
// The shift result width is determined by lhs.
o1 = i << s;
// To keep bits, 'i' must be cast to the target width before shifting.
o2 = (i as 130) << s;
}
}
"#; }
@build Simulator::builder(code, "Top");
let i = sim.signal("i");
let s = sim.signal("s");
let o1 = sim.signal("o1");
let o2 = sim.signal("o2");
let val_i: BigUint = (BigUint::from(1u32) << 63) | (BigUint::from(1u32) << 62);
let val_s = 1u8;
let expected_o1 = val_i.clone() << val_s;
let expected_o2 = val_i.clone() << val_s;
sim.modify(|io| {
io.set_wide(i, val_i);
io.set(s, val_s);
})
.unwrap();
assert_eq!(sim.get(o1), expected_o1, "Upper bit should be preserved");
assert_eq!(
sim.get(o2),
expected_o2,
"Upper bit should be preserved after 130-bit cast"
);
let val_i: BigUint = BigUint::from(1u32) << 63;
let val_s = 2u8;
let expected_o1 = val_i.clone() << val_s;
let expected_o2 = val_i.clone() << val_s;
sim.modify(|io| {
io.set_wide(i, val_i);
io.set(s, val_s);
})
.unwrap();
assert_eq!(sim.get(o1), expected_o1);
assert_eq!(sim.get(o2), expected_o2);
}
fn test_wide_context_constant_folding_128bit(sim) {
@setup { let code = r#"
module Top (
o: output logic<128>
) {
always_comb {
o = 32'hffff_ffff + 1;
}
}
"#; }
@build Simulator::builder(code, "Top");
let o = sim.signal("o");
let expected = BigUint::from(1u32) << 32;
assert_eq!(
sim.get(o),
expected,
"Constant folding in 128-bit context failed: 32'hffff_ffff + 1 should be 32'h1_0000_0000"
);
}
fn test_wide_context_multiplication_boundary(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (
a: input logic<64>,
b: input logic<64>,
o: output logic<128>
) {
assign o = (a as 128) * (b as 128);
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let o = sim.signal("o");
let val_a = u64::MAX;
let val_b = u64::MAX;
let expected = (BigUint::from(1u32) << 128) - (BigUint::from(2u32) << 64) + 1u32;
sim.modify(|io| {
io.set(a, val_a);
io.set(b, val_b);
})
.unwrap();
assert_eq!(
sim.get(o),
expected,
"64-bit * 64-bit multiplication should not truncate in 128-bit context"
);
}
fn test_wide_context_addition_mixed_boundary(sim) {
@setup { let code = r#"
module Top (
a: input logic<64>,
b: input logic<1>,
o: output logic<65>
) {
assign o = a + b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let o = sim.signal("o");
let val_a = u64::MAX;
let val_b = 1u8;
let expected = BigUint::from(1u32) << 64;
sim.modify(|io| {
io.set(a, val_a);
io.set(b, val_b);
})
.unwrap();
assert_eq!(
sim.get(o),
expected,
"Mixed 64-bit + 1-bit addition should not truncate in 65-bit context"
);
}
}
#[test]
fn test_wide_context_nested_propagation() {
let code = r#"
module Top (
a: input logic<120>,
b: input logic<120>,
o: output logic<122>
) {
assign o = (a + b) * 2'd2;
}
"#;
let code_top = "Top";
let mut sim = celox::SimulatorBuilder::new(code, code_top)
.build()
.expect("Build should succeed");
let a = sim.signal("a");
let b = sim.signal("b");
let o = sim.signal("o");
let val_a = BigUint::from(1u32) << 119;
let val_b = BigUint::from(1u32) << 119;
let expected = BigUint::from(1u32) << 121;
sim.modify(|io| {
io.set_wide(a, val_a);
io.set_wide(b, val_b);
})
.unwrap();
assert_eq!(sim.get(o), expected, "Nested 122-bit context width failed");
}