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 type_cast_clears_unknown_bits_in_comb_and_ff(sim) {
        @omit_veryl;
        @ignore_on(sv);
        @build Simulator::builder(r#"
module Top (
    clk: input clock,
    narrow: input logic<8>,
    wide: input logic<130>,

    c_u8: output logic<8>,
    c_i8: output logic<8>,
    f_u8: output logic<8>,
    f_i8: output logic<8>,
    c_wide: output logic<130>,
    f_wide: output logic<130>,
) {
    type U130 = bit<130>;

    assign c_u8 = narrow as u8;
    assign c_i8 = narrow as i8;
    assign c_wide = wide as U130;

    always_ff (clk) {
        f_u8 = narrow as u8;
        f_i8 = narrow as i8;
        f_wide = wide as U130;
    }
}
"#, "Top").four_state(true);

        let narrow_value = BigUint::from(0xa9u8);
        let narrow_mask = BigUint::from(0x0cu8);

        let full = (BigUint::from(1u8) << 130usize) - BigUint::from(1u8);
        let wide_mask = (BigUint::from(1u8) << 5usize)
            | (BigUint::from(1u8) << 70usize)
            | (BigUint::from(1u8) << 129usize);
        let wide_value = (BigUint::from(1u8) << 129usize)
            | (BigUint::from(1u8) << 64usize)
            | (BigUint::from(1u8) << 5usize)
            | BigUint::from(0x35u8);
        let narrow = sim.signal("narrow");
        let wide = sim.signal("wide");
        let clk = sim.event("clk");

        sim.modify(|io| {
            io.set_four_state(narrow, narrow_value.clone(), narrow_mask.clone());
            io.set_four_state(wide, wide_value.clone(), wide_mask.clone());
        })
        .unwrap();
        assert_eq!(
            sim.get_four_state(wide),
            (wide_value.clone(), wide_mask.clone()),
            "wide test input"
        );
        let expected_wide = &wide_value & (&full ^ &wide_mask);
        assert_eq!(
            sim.get_four_state(sim.signal("c_wide")),
            (expected_wide.clone(), BigUint::from(0u8)),
            "c_wide before clock"
        );
        sim.tick(clk).unwrap();

        let expected_narrow = &narrow_value & (&BigUint::from(0xffu8) ^ &narrow_mask);
        for name in ["c_u8", "c_i8", "f_u8", "f_i8"] {
            assert_eq!(
                sim.get_four_state(sim.signal(name)),
                (expected_narrow.clone(), BigUint::from(0u8)),
                "{name}"
            );
        }

        for name in ["c_wide", "f_wide"] {
            assert_eq!(
                sim.get_four_state(sim.signal(name)),
                (expected_wide.clone(), BigUint::from(0u8)),
                "{name}"
            );
        }
    }

    fn constant_type_cast_clears_unknown_bits(sim) {
        @omit_veryl;
        @ignore_on(sv);
        @build Simulator::builder(r#"
module Top (
    clk: input clock,
    c_compound: output logic<8>,
    c_variable: output logic<8>,
    f_compound: output logic<8>,
    f_variable: output logic<8>,
) {
    const X: logic<8> = 8'b10xz_01zx;
    const U: u8 = X as u8;

    assign c_compound = X as u8;
    assign c_variable = U;
    always_ff (clk) {
        f_compound = X as u8;
        f_variable = U;
    }
}
"#, "Top").four_state(true);

        let clk = sim.event("clk");
        sim.tick(clk).unwrap();
        for name in ["c_compound", "c_variable", "f_compound", "f_variable"] {
            assert_eq!(
                sim.get_four_state(sim.signal(name)),
                (BigUint::from(0x84u8), BigUint::from(0u8)),
                "{name}"
            );
        }
    }

    fn function_formal_type_clears_unknown_bits(sim) {
        @omit_veryl;
        @ignore_on(sv);
        @build Simulator::builder(r#"
module Top (
    clk: input clock,
    a: input logic<8>,
    c: output logic<8>,
    f: output logic<8>,
) {
    function through (x: input bit<8>) -> logic<8> {
        return x;
    }
    assign c = through(a);
    always_ff (clk) {
        f = through(a);
    }
}
"#, "Top").four_state(true);

        let clk = sim.event("clk");
        let a = sim.signal("a");
        sim.modify(|io| {
            io.set_four_state(
                a,
                BigUint::from(0xa9u8),
                BigUint::from(0x0cu8),
            );
        })
        .unwrap();
        sim.tick(clk).unwrap();
        for name in ["c", "f"] {
            assert_eq!(
                sim.get_four_state(sim.signal(name)),
                (BigUint::from(0xa1u8), BigUint::from(0u8)),
                "{name}"
            );
        }
    }

    fn signed_four_state_function_formal_preserves_unknown_bits(sim) {
        @ignore_on(sv);
        @build Simulator::builder(r#"
module Top (
    clk: input clock,
    a: input logic<8>,
    c: output logic<8>,
    f: output logic<8>,
) {
    function through (x: input signed logic<8>) -> logic<8> {
        return x;
    }
    assign c = through(a);
    always_ff (clk) {
        f = through(a);
    }
}
"#, "Top").four_state(true);

        let clk = sim.event("clk");
        let a = sim.signal("a");
        sim.modify(|io| {
            io.set_four_state(
                a,
                BigUint::from(0xa9u8),
                BigUint::from(0x0cu8),
            );
        })
        .unwrap();
        sim.tick(clk).unwrap();
        for name in ["c", "f"] {
            let signal = sim.signal(name);
            assert_eq!(
                sim.get_four_state(signal),
                (BigUint::from(0xa9u8), BigUint::from(0x0cu8)),
                "{name}"
            );
        }
    }

    fn implicit_assignment_to_bit_clears_unknowns_without_mutating_source(sim) {
        // Veryl 0.20.3 retains the X/Z mask when assigning logic to bit.
        @ignore_on(veryl, sv);
        @build Simulator::builder(r#"
module Top (
    clk: input clock,
    narrow: input logic<8>,
    wide: input logic<130>,
    c_narrow_bit: output bit<8>,
    c_narrow_after: output logic<8>,
    c_wide_bit: output bit<130>,
    c_wide_after: output logic<130>,
    f_narrow_bit: output bit<8>,
    f_narrow_after: output logic<8>,
    f_wide_bit: output bit<130>,
    f_wide_after: output logic<130>,
) {
    var narrow_source: logic<8>;
    var wide_source: logic<130>;
    always_comb {
        narrow_source = narrow;
        wide_source = wide;
        c_narrow_bit = narrow_source;
        c_wide_bit = wide_source;
        c_narrow_after = narrow_source;
        c_wide_after = wide_source;
    }
    always_ff (clk) {
        f_narrow_bit = narrow;
        f_wide_bit = wide;
        f_narrow_after = narrow;
        f_wide_after = wide;
    }
}
"#, "Top").four_state(true);

        let narrow_value = BigUint::from(0xa9u8);
        let narrow_mask = BigUint::from(0x0cu8);
        let wide_mask = (BigUint::from(1u8) << 5usize)
            | (BigUint::from(1u8) << 70usize)
            | (BigUint::from(1u8) << 129usize);
        let wide_value = (BigUint::from(1u8) << 129usize)
            | (BigUint::from(1u8) << 64usize)
            | (BigUint::from(1u8) << 5usize)
            | BigUint::from(0x35u8);
        let full_wide = (BigUint::from(1u8) << 130usize) - BigUint::from(1u8);
        let expected_narrow = &narrow_value & (BigUint::from(0xffu8) ^ &narrow_mask);
        let expected_wide = &wide_value & (&full_wide ^ &wide_mask);

        let narrow = sim.signal("narrow");
        let wide = sim.signal("wide");
        let clk = sim.event("clk");
        sim.modify(|io| {
            io.set_four_state(narrow, narrow_value.clone(), narrow_mask.clone());
            io.set_four_state(wide, wide_value.clone(), wide_mask.clone());
        })
        .unwrap();
        sim.tick(clk).unwrap();

        for name in ["c_narrow_bit", "f_narrow_bit"] {
            let signal = sim.signal(name);
            assert_eq!(
                sim.get_four_state(signal),
                (expected_narrow.clone(), BigUint::from(0u8)),
                "{name}"
            );
        }
        for name in ["c_wide_bit", "f_wide_bit"] {
            let signal = sim.signal(name);
            assert_eq!(
                sim.get_four_state(signal),
                (expected_wide.clone(), BigUint::from(0u8)),
                "{name}"
            );
        }
        for name in ["c_narrow_after", "f_narrow_after"] {
            let signal = sim.signal(name);
            assert_eq!(
                sim.get_four_state(signal),
                (narrow_value.clone(), narrow_mask.clone()),
                "{name}"
            );
        }
        for name in ["c_wide_after", "f_wide_after"] {
            let signal = sim.signal(name);
            assert_eq!(
                sim.get_four_state(signal),
                (wide_value.clone(), wide_mask.clone()),
                "{name}"
            );
        }
    }
}

#[test]
fn state_cast_is_explicit_in_sir() {
    let result = Simulator::builder(
        r#"
module Top (
    a: input logic<130>,
    y: output logic<130>,
) {
    type U130 = bit<130>;
    assign y = a as U130;
}
"#,
        "Top",
    )
    .four_state(true)
    .trace_post_optimized_sir()
    .build_with_trace();
    let sir = result
        .trace
        .format_post_optimized_sir()
        .expect("post-optimized SIR");
    assert!(
        sir.contains("ToTwoState"),
        "the state-conversion boundary disappeared from SIR:\n{sir}"
    );
}