use celox::{BigUint, Simulator};
#[path = "test_utils/mod.rs"]
#[macro_use]
mod test_utils;
all_backends! {
fn test_ternary_operator(sim) {
@setup { let code = r#"
module Top (sel: input logic, a: input logic<8>, b: input logic<8>, o: output logic<8>) {
assign o = if sel ? a : b ;
}
"#; }
@build Simulator::builder(code, "Top");
let sel = sim.signal("sel");
let a = sim.signal("a");
let b = sim.signal("b");
let o = sim.signal("o");
sim.modify(|io| {
io.set(a, 0xAAu8);
io.set(b, 0xBBu8);
})
.unwrap();
sim.modify(|io| io.set(sel, 1u8)).unwrap();
assert_eq!(sim.get(o), 0xAAu64.into());
sim.modify(|io| io.set(sel, 0u8)).unwrap();
assert_eq!(sim.get(o), 0xBBu64.into());
}
fn test_nested_ternary(sim) {
@setup { let code = r#"
module Top (s1: input logic, s2: input logic, a: input logic<8>, b: input logic<8>, c: input logic<8>, o: output logic<8>) {
assign o = if s1 ? (if s2 ? a : b) : c;
}
"#; }
@build Simulator::builder(code, "Top");
let s1 = sim.signal("s1");
let s2 = sim.signal("s2");
let a = sim.signal("a");
let b = sim.signal("b");
let o = sim.signal("o");
sim.modify(|io| {
io.set(a, 0xAAu8);
io.set(b, 0xBBu8);
})
.unwrap();
sim.modify(|io| {
io.set(s1, 1u8);
io.set(s2, 0u8);
})
.unwrap();
assert_eq!(sim.get(o), 0xBBu64.into());
}
fn test_bitwise_operations(sim) {
@setup { let code = r#"
module Top (a: input logic<8>, b: input logic<8>, o_and: output logic<8>, o_or: output logic<8>, o_xor: output logic<8>) {
always_comb {
o_and = a & b;
o_or = a | b;
o_xor = a ^ b;
}
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let o_and = sim.signal("o_and");
let o_or = sim.signal("o_or");
let o_xor = sim.signal("o_xor");
sim.modify(|io| {
io.set(a, 0xA5u8);
io.set(b, 0x5Au8);
})
.unwrap();
assert_eq!(sim.get(o_and), 0x00u8.into());
assert_eq!(sim.get(o_or), 0xFFu8.into());
assert_eq!(sim.get(o_xor), 0xFFu8.into());
}
fn test_shift_logical_vs_arithmetic(sim) {
@setup { let code = r#"
module Top (a: input logic<8>, o_shr: output logic<8>, o_sar: output logic<8>) {
always_comb {
o_shr = a >> 2;
o_sar = a >>> 2;
}
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let o_shr = sim.signal("o_shr");
let o_sar = sim.signal("o_sar");
sim.modify(|io| io.set(a, 0x80u8)).unwrap();
assert_eq!(sim.get(o_shr), 0x20u8.into());
assert_eq!(sim.get(o_sar), 0x20u8.into());
}
fn test_signed_arithmetic_shift_right(sim) {
@setup { let code = r#"
module Top (a: input i8, o_sar: output i8) {
always_comb {
o_sar = a >>> 2;
}
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let o_sar = sim.signal("o_sar");
sim.modify(|io| io.set(a, 0x80u8)).unwrap(); assert_eq!(sim.get(o_sar), 0xE0u8.into());
}
fn test_subtraction_underflow(sim) {
@setup { let code = r#"
module Top (a: input logic<8>, b: input logic<8>, o: output logic<8>) {
assign o = a - b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let o = sim.signal("o");
sim.modify(|io| {
io.set(a, 0x05u8);
io.set(b, 0x0Au8);
})
.unwrap();
assert_eq!(sim.get(o), 0xFBu8.into());
}
fn test_unary_operations(sim) {
@setup { let code = r#"
module Top (a: input logic<8>, o_bitnot: output logic<8>) {
always_comb {
o_bitnot = ~a;
}
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let o = sim.signal("o_bitnot");
sim.modify(|io| io.set(a, 0x55u8)).unwrap();
assert_eq!(sim.get(o), 0xAAu8.into());
}
fn test_unary_plus_operator(sim) {
@setup { let code = r#"
module Top (a: input logic<8>, o: output logic<8>) {
always_comb {
o = +a;
}
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let o = sim.signal("o");
sim.modify(|io| io.set(a, 0xA5u8)).unwrap();
assert_eq!(sim.get(o), 0xA5u8.into());
}
fn test_comparisons(sim) {
@setup { let code = r#"
module Top (a: input logic<8>, b: input logic<8>, o_lt: output logic<1>, o_ge: output logic<1>) {
always_comb {
o_lt = a <: b;
o_ge = a >= b;
}
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let o_lt = sim.signal("o_lt");
let o_ge = sim.signal("o_ge");
sim.modify(|io| {
io.set(a, 10u8);
io.set(b, 20u8);
})
.unwrap();
assert_eq!(sim.get(o_lt), 1u8.into());
assert_eq!(sim.get(o_ge), 0u8.into());
}
fn test_signed_comparison_and_extension(sim) {
@setup { let code = r#"
module Top (a: input i8, b: input i8, o_lt: output logic) {
assign o_lt = a <: b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let o_lt = sim.signal("o_lt");
sim.modify(|io| {
io.set(a, 0xFBu8); io.set(b, 0x02u8); })
.unwrap();
assert_eq!(sim.get(o_lt), 1u8.into());
}
fn test_logical_operators_execution(sim) {
@setup { let code = r#"
module Top (
a: input logic<8>,
b: input logic<8>,
o_and: output logic,
o_or: output logic
) {
assign o_and = (|a) && (|b);
assign o_or = (|a) || (|b);
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let o_and = sim.signal("o_and");
let o_or = sim.signal("o_or");
sim.modify(|io| {
io.set(a, 0x55u8);
io.set(b, 0x00u8);
})
.unwrap();
assert_eq!(sim.get(o_and), 0u8.into());
assert_eq!(sim.get(o_or), 1u8.into());
}
fn test_reduction_operators_execution(sim) {
@setup { let code = r#"
module Top (
a: input logic<4>,
o_and: output logic,
o_or: output logic
) {
assign o_and = &a;
assign o_or = |a;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let o_and = sim.signal("o_and");
let o_or = sim.signal("o_or");
sim.modify(|io| io.set(a, 0xEu8)).unwrap();
assert_eq!(sim.get(o_and), 0u8.into());
assert_eq!(sim.get(o_or), 1u8.into());
}
fn test_pow_operator_constant_exponent(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (a: input logic<8>, o: output logic<8>) {
assign o = a ** 3;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let o = sim.signal("o");
sim.modify(|io| io.set(a, 2u8)).unwrap();
assert_eq!(sim.get(o), 8u8.into());
sim.modify(|io| io.set(a, 3u8)).unwrap();
assert_eq!(sim.get(o), 27u8.into());
}
fn test_as_operator_passthrough(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (a: input logic<8>, o: output logic<8>) {
assign o = a as u8;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let o = sim.signal("o");
sim.modify(|io| io.set(a, 0xA5u8)).unwrap();
assert_eq!(sim.get(o), 0xA5u8.into());
}
fn test_pow_operator_constant_exponent_ff(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (clk: input clock, a: input logic<8>, o: output logic<8>) {
var r: logic<8>;
always_ff {
r = a ** 2;
}
assign o = r;
}
"#; }
@build Simulator::builder(code, "Top");
let clk = sim.event("clk");
let a = sim.signal("a");
let o = sim.signal("o");
sim.modify(|io| io.set(a, 5u8)).unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(o), 25u8.into());
}
fn test_pow_operator_runtime_exponent_comb_and_ff(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (
clk: input clock,
base: input logic<8>,
exponent: input logic<4>,
comb_o: output logic<8>,
ff_o: output logic<8>,
comb_seen: output logic<4>,
ff_seen: output logic<4>,
) {
function observe_exponent (
value: input logic<4>,
observed: output logic<4>,
) -> logic<4> {
observed = value;
return value;
}
assign comb_o = base ** observe_exponent(exponent, comb_seen);
always_ff (clk) {
ff_o = base ** observe_exponent(exponent, ff_seen);
}
}
"#; }
@build Simulator::builder(code, "Top");
let clk = sim.event("clk");
let base = sim.signal("base");
let exponent = sim.signal("exponent");
let comb_o = sim.signal("comb_o");
let ff_o = sim.signal("ff_o");
let comb_seen = sim.signal("comb_seen");
let ff_seen = sim.signal("ff_seen");
for (base_value, exponent_value, expected) in [
(3u8, 4u8, 81u8),
(7u8, 0u8, 1u8),
(2u8, 8u8, 0u8),
] {
sim.modify(|io| {
io.set(base, base_value);
io.set(exponent, exponent_value);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(comb_o), expected.into());
assert_eq!(sim.get(ff_o), expected.into());
assert_eq!(sim.get(comb_seen), exponent_value.into());
assert_eq!(sim.get(ff_seen), exponent_value.into());
}
}
fn test_pow_operator_runtime_signed_and_unknown_operands(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (
clk: input clock,
base: input signed logic<8>,
exponent: input signed logic<4>,
comb_o: output signed logic<8>,
ff_o: output signed logic<8>,
) {
assign comb_o = base ** exponent;
always_ff (clk) {
ff_o = base ** exponent;
}
}
"#; }
@build Simulator::builder(code, "Top").four_state(true);
let clk = sim.event("clk");
let base = sim.signal("base");
let exponent = sim.signal("exponent");
let comb_o = sim.signal("comb_o");
let ff_o = sim.signal("ff_o");
for (base_value, exponent_value, expected) in [
(2u8, 0x0fu8, 0u8), (1u8, 0x0du8, 1u8), (0xffu8, 0x0du8, 0xffu8), (0xffu8, 0x0eu8, 1u8), ] {
sim.modify(|io| {
io.set(base, base_value);
io.set(exponent, exponent_value);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(comb_o), expected.into());
assert_eq!(sim.get(ff_o), expected.into());
}
let full_unknown = BigUint::from(0xffu8);
sim.modify(|io| {
io.set_four_state(base, BigUint::from(0u8), BigUint::from(0u8));
io.set_four_state(exponent, BigUint::from(2u8), BigUint::from(1u8));
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(
sim.get_four_state(comb_o),
(full_unknown.clone(), full_unknown.clone())
);
assert_eq!(
sim.get_four_state(ff_o),
(full_unknown.clone(), full_unknown)
);
}
fn test_signed_comparison_after_as_cast(sim) {
@ignore_on(veryl, sv);
@setup { let code = r#"
module Top (a: input logic<8>, b: input logic<8>, y: output logic) {
assign y = (a as i8) <: (b as i8);
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let y = sim.signal("y");
sim.modify(|io| {
io.set(a, 0xFFu8);
io.set(b, 0x01u8);
})
.unwrap();
assert_eq!(sim.get(y), 1u8.into());
}
fn test_cast_signed_to_unsigned_affects_comparison(sim) {
@ignore_on(veryl, sv);
@setup { let code = r#"
module Top (a: input i8, b: input i8, y: output logic) {
assign y = (a as u8) <: (b as u8);
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let y = sim.signal("y");
sim.modify(|io| {
io.set(a, 0xFFu8);
io.set(b, 0x01u8);
})
.unwrap();
assert_eq!(sim.get(y), 0u8.into());
}
fn test_symbolic_store_preserves_declared_state_signedness(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (
sel: input logic,
signed_value: input signed logic<8>,
signed_lhs: input signed logic<8>,
unsigned_cmp: output logic,
unsigned_wide: output logic<16>,
signed_cmp: output logic
) {
var unsigned_state: logic<8>;
var signed_state: signed logic<8>;
always_comb {
unsigned_state = 8'd0;
signed_state = signed_value;
if sel {
unsigned_state = signed_value;
}
unsigned_cmp = signed_lhs >: unsigned_state;
unsigned_wide = unsigned_state;
signed_cmp = signed_lhs >: signed_state;
}
}
"#; }
@build Simulator::builder(code, "Top");
let sel = sim.signal("sel");
let signed_value = sim.signal("signed_value");
let signed_lhs = sim.signal("signed_lhs");
let unsigned_cmp = sim.signal("unsigned_cmp");
let unsigned_wide = sim.signal("unsigned_wide");
let signed_cmp = sim.signal("signed_cmp");
sim.modify(|io| {
io.set(sel, 1u8);
io.set(signed_value, 0xffu8); io.set(signed_lhs, 1u8);
})
.unwrap();
assert_eq!(sim.get(unsigned_cmp), 0u8.into()); assert_eq!(sim.get(unsigned_wide), 0x00ffu16.into());
assert_eq!(sim.get(signed_cmp), 1u8.into());
}
fn test_unsigned_type_cast_does_not_inherit_source_signedness(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (
sel: input logic,
signed_in: input signed logic<5>,
lhs: input signed logic<8>,
cmp: output logic,
wide: output logic<12>
) {
var state: logic<8>;
always_comb {
state = 8'd0;
if sel {
state = signed_in as u8;
}
cmp = lhs >: state;
wide = state;
}
}
"#; }
@build Simulator::builder(code, "Top");
let sel = sim.signal("sel");
let signed_in = sim.signal("signed_in");
let lhs = sim.signal("lhs");
let cmp = sim.signal("cmp");
let wide = sim.signal("wide");
sim.modify(|io| {
io.set(sel, 1u8);
io.set(signed_in, 0x1fu8);
io.set(lhs, 0x20u8);
})
.unwrap();
assert_eq!(sim.get(cmp), 0u8.into()); assert_eq!(sim.get(wide), 0x00ffu16.into());
}
fn test_comb_div(sim) {
@setup { let code = r#"
module Top (
a: input logic<16>,
b: input logic<16>,
q: output logic<16>
) {
assign q = a / b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let q = sim.signal("q");
sim.modify(|io| {
io.set(a, 100u16);
io.set(b, 7u16);
})
.unwrap();
assert_eq!(sim.get(q), 14u16.into());
sim.modify(|io| {
io.set(a, 255u16);
io.set(b, 16u16);
})
.unwrap();
assert_eq!(sim.get(q), 15u16.into());
}
fn test_comb_rem(sim) {
@setup { let code = r#"
module Top (
a: input logic<16>,
b: input logic<16>,
r: output logic<16>
) {
assign r = a % b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let r = sim.signal("r");
sim.modify(|io| {
io.set(a, 100u16);
io.set(b, 7u16);
})
.unwrap();
assert_eq!(sim.get(r), 2u16.into());
sim.modify(|io| {
io.set(a, 255u16);
io.set(b, 16u16);
})
.unwrap();
assert_eq!(sim.get(r), 15u16.into());
}
fn test_ternary_div_zero_branch_is_lazy(sim) {
@setup { let code = r#"
module Top (
a: input logic<16>,
b: input logic<16>,
q: output logic<16>
) {
assign q = if b == 16'd0 ? 16'hFFFF : a / b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let q = sim.signal("q");
sim.modify(|io| {
io.set(a, 100u16);
io.set(b, 0u16);
})
.unwrap();
assert_eq!(sim.get(q), 0xFFFFu16.into());
sim.modify(|io| {
io.set(a, 100u16);
io.set(b, 7u16);
})
.unwrap();
assert_eq!(sim.get(q), 14u16.into());
}
fn test_ternary_rem_zero_branch_is_lazy(sim) {
@setup { let code = r#"
module Top (
a: input logic<16>,
b: input logic<16>,
r: output logic<16>
) {
assign r = if b == 16'd0 ? a : a % b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let r = sim.signal("r");
sim.modify(|io| {
io.set(a, 100u16);
io.set(b, 0u16);
})
.unwrap();
assert_eq!(sim.get(r), 100u16.into());
sim.modify(|io| {
io.set(a, 100u16);
io.set(b, 7u16);
})
.unwrap();
assert_eq!(sim.get(r), 2u16.into());
}
fn test_ff_div(sim) {
@setup { let code = r#"
module Top (
clk: input clock,
rst: input reset,
a: input logic<16>,
b: input logic<16>,
q: output logic<16>
) {
var r: logic<16>;
always_ff (clk, rst) {
if_reset {
r = 16'd0;
} else {
r = a / b;
}
}
assign q = r;
}
"#; }
@build Simulator::builder(code, "Top");
let clk = sim.event("clk");
let rst = sim.signal("rst");
let a = sim.signal("a");
let b = sim.signal("b");
let q = sim.signal("q");
sim.modify(|io| {
io.set(rst, 0u8);
io.set(a, 0u16);
io.set(b, 1u16);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(q), 0u16.into());
sim.modify(|io| {
io.set(rst, 1u8);
io.set(a, 42u16);
io.set(b, 5u16);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(q), 8u16.into());
}
fn test_ff_rem(sim) {
@setup { let code = r#"
module Top (
clk: input clock,
rst: input reset,
a: input logic<16>,
b: input logic<16>,
q: output logic<16>
) {
var r: logic<16>;
always_ff (clk, rst) {
if_reset {
r = 16'd0;
} else {
r = a % b;
}
}
assign q = r;
}
"#; }
@build Simulator::builder(code, "Top");
let clk = sim.event("clk");
let rst = sim.signal("rst");
let a = sim.signal("a");
let b = sim.signal("b");
let q = sim.signal("q");
sim.modify(|io| {
io.set(rst, 0u8);
io.set(a, 0u16);
io.set(b, 1u16);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(q), 0u16.into());
sim.modify(|io| {
io.set(rst, 1u8);
io.set(a, 42u16);
io.set(b, 5u16);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(q), 2u16.into());
}
fn test_comb_bitxnor(sim) {
@ignore_on(sv);
@setup { let code = r#"
module Top (
a: input logic<8>,
b: input logic<8>,
y: output logic<8>
) {
assign y = a ~^ b;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let b = sim.signal("b");
let y = sim.signal("y");
sim.modify(|io| {
io.set(a, 0xF0u8);
io.set(b, 0xFFu8);
})
.unwrap();
assert_eq!(sim.get(y), 0xF0u8.into());
sim.modify(|io| {
io.set(a, 0xAAu8);
io.set(b, 0x55u8);
})
.unwrap();
assert_eq!(sim.get(y), 0x00u8.into());
}
fn test_ff_bitxnor(sim) {
@ignore_on(veryl, sv);
@setup { let code = r#"
module Top (
clk: input clock,
rst: input reset,
a: input logic<8>,
b: input logic<8>,
y: output logic<8>
) {
var r: logic<8>;
always_ff (clk, rst) {
if_reset {
r = 8'd0;
} else {
r = a ~^ b;
}
}
assign y = r;
}
"#; }
@build Simulator::builder(code, "Top");
let clk = sim.event("clk");
let rst = sim.signal("rst");
let a = sim.signal("a");
let b = sim.signal("b");
let y = sim.signal("y");
sim.modify(|io| {
io.set(rst, 0u8);
io.set(a, 0u8);
io.set(b, 0u8);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(y), 0u8.into());
sim.modify(|io| {
io.set(rst, 1u8);
io.set(a, 0xF0u8);
io.set(b, 0xFFu8);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(y), 0xF0u8.into());
}
fn test_comb_reduction_nand(sim) {
@setup { let code = r#"
module Top (
a: input logic<8>,
y: output logic
) {
assign y = ~&a;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let y = sim.signal("y");
sim.modify(|io| io.set(a, 0xFFu8)).unwrap();
assert_eq!(sim.get(y), 0u8.into());
sim.modify(|io| io.set(a, 0xFEu8)).unwrap();
assert_eq!(sim.get(y), 1u8.into());
sim.modify(|io| io.set(a, 0x00u8)).unwrap();
assert_eq!(sim.get(y), 1u8.into());
}
fn test_comb_reduction_nor(sim) {
@setup { let code = r#"
module Top (
a: input logic<8>,
y: output logic
) {
assign y = ~|a;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let y = sim.signal("y");
sim.modify(|io| io.set(a, 0x00u8)).unwrap();
assert_eq!(sim.get(y), 1u8.into());
sim.modify(|io| io.set(a, 0x01u8)).unwrap();
assert_eq!(sim.get(y), 0u8.into());
sim.modify(|io| io.set(a, 0xFFu8)).unwrap();
assert_eq!(sim.get(y), 0u8.into());
}
fn test_comb_reduction_xnor(sim) {
@setup { let code = r#"
module Top (
a: input logic<8>,
y: output logic
) {
assign y = ~^a;
}
"#; }
@build Simulator::builder(code, "Top");
let a = sim.signal("a");
let y = sim.signal("y");
sim.modify(|io| io.set(a, 0x00u8)).unwrap();
assert_eq!(sim.get(y), 1u8.into());
sim.modify(|io| io.set(a, 0x01u8)).unwrap();
assert_eq!(sim.get(y), 0u8.into());
sim.modify(|io| io.set(a, 0x03u8)).unwrap();
assert_eq!(sim.get(y), 1u8.into());
}
fn test_ff_reduction_nand(sim) {
@ignore_on(veryl);
@setup { let code = r#"
module Top (
clk: input clock,
rst: input reset,
a: input logic<8>,
y: output logic
) {
var r: logic;
always_ff (clk, rst) {
if_reset {
r = 1'b0;
} else {
r = ~&a;
}
}
assign y = r;
}
"#; }
@build Simulator::builder(code, "Top");
let clk = sim.event("clk");
let rst = sim.signal("rst");
let a = sim.signal("a");
let y = sim.signal("y");
sim.modify(|io| {
io.set(rst, 0u8);
io.set(a, 0u8);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(y), 0u8.into());
sim.modify(|io| {
io.set(rst, 1u8);
io.set(a, 0xFFu8);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(y), 0u8.into());
sim.modify(|io| io.set(a, 0xFEu8)).unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(y), 1u8.into());
}
fn test_ff_comb_constant_folding_consistency(sim) {
@setup { let code = r#"
module Top (
clk: input clock,
o_ff: output logic<128>,
o_comb: output logic<128>
) {
always_ff (clk) {
o_ff = 32'hffff_ffff + 1;
}
always_comb {
o_comb = 32'hffff_ffff + 1;
}
}
"#; }
@build Simulator::builder(code, "Top");
let clk = sim.event("clk");
let o_ff = sim.signal("o_ff");
let o_comb = sim.signal("o_comb");
let expected = BigUint::from(1u32) << 32;
assert_eq!(
sim.get(o_comb),
expected,
"always_comb constant folding failed"
);
assert_eq!(sim.get(o_ff), BigUint::from(0u8));
sim.tick(clk).unwrap();
assert_eq!(sim.get(o_ff), expected, "always_ff constant folding failed");
assert_eq!(sim.get(o_comb), expected);
}
fn test_ff_reduction_nor(sim) {
@ignore_on(veryl);
@setup { let code = r#"
module Top (
clk: input clock,
rst: input reset,
a: input logic<8>,
y: output logic
) {
var r: logic;
always_ff (clk, rst) {
if_reset {
r = 1'b0;
} else {
r = ~|a;
}
}
assign y = r;
}
"#; }
@build Simulator::builder(code, "Top");
let clk = sim.event("clk");
let rst = sim.signal("rst");
let a = sim.signal("a");
let y = sim.signal("y");
sim.modify(|io| {
io.set(rst, 0u8);
io.set(a, 0u8);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(y), 0u8.into());
sim.modify(|io| {
io.set(rst, 1u8);
io.set(a, 0x00u8);
})
.unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(y), 1u8.into());
sim.modify(|io| io.set(a, 0x01u8)).unwrap();
sim.tick(clk).unwrap();
assert_eq!(sim.get(y), 0u8.into());
}
fn test_mixed_signed_unsigned_comparison(sim) {
@ignore_on(veryl, sv);
@setup { let code = r#"
module Top (
o_const: output logic,
o_var: output logic,
o_signed_op: output logic
) {
// -8'sd1 (255) >: 8'd1 (1) -> true (1)
assign o_const = -8'sd1 >: 8'd1;
var a: i8;
var b: u8;
always_comb {
a = -8'sd1;
b = 8'd1;
// Treated as unsigned: 255 > 1 -> true (1)
o_var = a >: b;
// To force signed comparison (if desired), both sides must be signed
// but Veryl doesn't have a direct "signed comparison" operator that
// overrides 11.8.1 other than casting both to signed.
o_signed_op = a >: (b as i8);
}
}
"#; }
@build Simulator::builder(code, "Top");
let o_const = sim.signal("o_const");
let o_var = sim.signal("o_var");
let o_signed_op = sim.signal("o_signed_op");
assert_eq!(
sim.get(o_const),
1u8.into(),
"Mixed signed/unsigned constant comparison failed"
);
assert_eq!(
sim.get(o_var),
1u8.into(),
"Mixed signed/unsigned variable comparison failed"
);
assert_eq!(
sim.get(o_signed_op),
0u8.into(),
"Mixed signed/unsigned (cast to signed) comparison failed"
);
}
}
#[test]
fn test_nested_ternary_concat_hybrid() {
let code = r#"
module Top (sel: input logic, a: input logic<4>, b: input logic<4>, c: input logic<8>, o: output logic<8>) {
always_comb {
o = if sel ? {a, b} : (if (a == b) ? c : 8'hEE);
}
}
"#;
let result = Simulator::builder(code, "Top").build();
assert!(
result.is_ok(),
"Should handle deeply nested expression structures"
);
}