celox 0.4.1

Celox HDL Simulator
Documentation
use celox::{BigUint, Simulator};

#[path = "test_utils/mod.rs"]
#[macro_use]
mod test_utils;

all_backends! {

    fn test_wide_int_memory_access(sim) {
        @setup { let code = r#"
module Top (
i: input logic<256>,
o: output logic<256>
) {
var mem: logic<256>;
always_comb {
mem = i;
o = mem;
}
}
"#; }
        @build Simulator::builder(code, "Top");
    let i = sim.signal("i");
    let o = sim.signal("o");

    let input_val: BigUint =
        (BigUint::from(1u32) << 200) | (BigUint::from(1u32) << 100) | BigUint::from(1u32);

    sim.modify(|io| io.set_wide(i, input_val.clone())).unwrap();
    assert_eq!(
        sim.get(o),
        input_val,
        "256-bit value should be preserved through memory"
    );

    }

    fn test_wide_concatenation(sim) {
        @setup { use num_bigint::ToBigUint;
let code = r#"
module Top (
a: input logic<64>,
b: input logic<64>,
c: input logic<64>,
o: output logic<192>
) {
assign o = {a, b, c};
}
"#; }
        @build Simulator::builder(code, "Top");
    let a = sim.signal("a");
    let b = sim.signal("b");
    let c = sim.signal("c");
    let o = sim.signal("o");

    let val_a = 0xAAAA_AAAA_AAAA_AAAAu64;
    let val_b = 0xBBBB_BBBB_BBBB_BBBBu64;
    let val_c = 0xCCCC_CCCC_CCCC_CCCCu64;

    sim.modify(|io| {
        io.set(a, val_a);
        io.set(b, val_b);
        io.set(c, val_c);
    })
    .unwrap();

    let expected = (val_a.to_biguint().unwrap() << 128)
        | (val_b.to_biguint().unwrap() << 64)
        | val_c.to_biguint().unwrap();

    assert_eq!(sim.get(o), expected);

    }

    fn test_nested_wide_concatenation(sim) {
        @setup { use num_bigint::ToBigUint;
let code = r#"
module Top (
a: input logic<65>,
b: input logic<63>,
c: input logic<1>,
d: input logic<70>,
o_nested: output logic<199>,
o_flat: output logic<199>
) {
var ab: logic<128>;
assign ab = {a, b};
assign o_nested = {ab, c, d};
assign o_flat = {a, b, c, d};
}
"#; }
        @build Simulator::builder(code, "Top");
    let a = sim.signal("a");
    let b = sim.signal("b");
    let c = sim.signal("c");
    let d = sim.signal("d");
    let o_nested = sim.signal("o_nested");
    let o_flat = sim.signal("o_flat");

    let val_a = (1u128 << 64) | 0x0123_4567_89AB_CDEFu128;
    let val_b = 0x2345_6789_ABCD_EF01u64 & ((1u64 << 63) - 1);
    let val_c = 1u8;
    let val_d: BigUint =
        (BigUint::from(1u32) << 69) | BigUint::from(0x3456_789A_BCDE_F012u64);

    sim.modify(|io| {
        io.set_wide(a, val_a.to_biguint().unwrap());
        io.set(b, val_b);
        io.set(c, val_c);
        io.set_wide(d, val_d.clone());
    })
    .unwrap();

    let expected = (val_a.to_biguint().unwrap() << (63 + 1 + 70))
        | (val_b.to_biguint().unwrap() << (1 + 70))
        | (val_c.to_biguint().unwrap() << 70)
        | val_d;

    assert_eq!(sim.get(o_nested), expected);
    assert_eq!(sim.get(o_flat), expected);
    assert_eq!(sim.get(o_nested), sim.get(o_flat));

    }

    fn test_wide_partial_write(sim) {
        @setup { use num_bigint::ToBigUint;
let code = r#"
module Top (
val: input logic<64>,
o: output logic<256>
) {
var wide: logic<256>;
always_comb {
wide = 0;
wide[127:64] = val;
o = wide;
}
}
"#; }
        @build Simulator::builder(code, "Top");
    let val = sim.signal("val");
    let o = sim.signal("o");

    let input_val = 0xDEAD_BEEF_CAFE_BABE_u64;
    sim.modify(|io| io.set(val, input_val)).unwrap();

    let expected = input_val.to_biguint().unwrap() << 64;
    assert_eq!(
        sim.get(o),
        expected,
        "Partial write across 64-bit boundaries should work"
    );

    }

    fn test_wide_cross_boundary_unaligned_write(sim) {
        @setup { let code = r#"
module Top (
val: input logic<125>,
o: output logic<256>
) {
var wide: logic<256>;
always_comb {
wide = 0;
wide[184:60] = val;
o = wide;
}
}
"#; }
        @build Simulator::builder(code, "Top");
    let val = sim.signal("val");
    let o = sim.signal("o");

    let input_val: BigUint = (BigUint::from(1u32) << 125) - 1u32;
    sim.modify(|io| io.set_wide(val, input_val.clone()))
        .unwrap();

    let expected = input_val << 60;
    assert_eq!(
        sim.get(o),
        expected,
        "Unaligned write crossing 3 chunks failed"
    );

    }

    fn test_wide_rmw_preserve_neighboring_bits(sim) {
        @setup { let code = r#"
module Top (
val: input logic<16>,
o: output logic<128>
) {
var wide: logic<128>;
always_comb {
wide = 128'hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;
wide[71:56] = val;
o = wide;
}
}
"#; }
        @build Simulator::builder(code, "Top");
    let val = sim.signal("val");
    let o = sim.signal("o");

    sim.modify(|io| io.set(val, 0xFFFFu16)).unwrap();

    let base_val = BigUint::parse_bytes(b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 16).unwrap();
    let full_mask = (BigUint::from(1u32) << 128) - 1u32;
    let target_mask = ((BigUint::from(1u32) << 16) - 1u32) << 56;
    let inv_target_mask = &full_mask ^ &target_mask;
    let expected = (base_val & inv_target_mask) | (BigUint::from(0xFFFFu32) << 56);

    assert_eq!(
        sim.get(o),
        expected,
        "Neighbors were corrupted during Read-Modify-Write"
    );

    }
}