30. Bus Error Checker

module error_checker_xz (
    input  [7:0] bus,
    output       all_known,
    output       has_unknown,
    output [7:0] bus_if_known
);
    // Propagate unknowns: (bus ^ bus) -> 0 on known bits, X on X/Z bits.
    // Reduction XOR of that is X iff any bit was X/Z.
    wire any_xz = (^(bus ^ bus)) === 1'bx;

    assign has_unknown  = any_xz;
    assign all_known    = ~any_xz;
    assign bus_if_known = {8{all_known}} & bus;  // pass when known, else 00
endmodule

💡 Remember

  • (bus ^ bus) trick: gives 0 on known bits but X on unknown (x/z) bits.
  • ^(...) (reduction XOR) → yields X if any bit is unknown.
  • Using === 1'bx detects presence of x/z reliably in simulation.
  • bus_if_known uses {8{all_known}} & buspasses bus only when fully known, else zeroes.

Testbench Code

`timescale 1ns/1ps

module tb_error_checker_xz;
    // Input
    reg  [7:0] bus;

    // DUT outputs
    wire       all_known;
    wire       has_unknown;
    wire [7:0] bus_if_known;

    // Expected (golden)
    reg        expected_all_known;
    reg        expected_has_unknown;
    reg  [7:0] expected_bus_if_known;

    // Mismatch flags + consolidated one for waveform
    wire mm_known   = (all_known    !== expected_all_known);
    wire mm_has     = (has_unknown  !== expected_has_unknown);
    wire mm_bus     = (bus_if_known !== expected_bus_if_known);
    wire mismatch   = mm_known | mm_has | mm_bus;

    integer TOTAL, PASS, FAIL;

    // DUT
    error_checker_xz dut (
        .bus(bus),
        .all_known(all_known),
        .has_unknown(has_unknown),
        .bus_if_known(bus_if_known)
    );

    // VCD: only input, outputs, expected, mismatch
    initial begin
        $dumpfile("tb_error_checker_xz.vcd");
        $dumpvars(0,
            tb_error_checker_xz.bus,
            tb_error_checker_xz.all_known, tb_error_checker_xz.has_unknown, tb_error_checker_xz.bus_if_known,
            tb_error_checker_xz.expected_all_known, tb_error_checker_xz.expected_has_unknown, tb_error_checker_xz.expected_bus_if_known,
            tb_error_checker_xz.mismatch
        );
    end

    // Golden model (same X/Z detection as DUT; NOT a self-compare)
    task compute_expected;
        input [7:0] t_bus;
        reg   any_xz_g;
        begin
            any_xz_g              = (^(t_bus ^ t_bus)) === 1'bx;
            expected_has_unknown  = any_xz_g;
            expected_all_known    = ~any_xz_g;
            expected_bus_if_known = expected_all_known ? t_bus : 8'h00;
        end
    endtask

    task run_test;
        input [7:0] t_bus;
        begin
            bus = t_bus;
            compute_expected(t_bus);
            #1;
            TOTAL = TOTAL + 1;
            if (!mismatch) PASS = PASS + 1; else begin
                FAIL = FAIL + 1;
                $display("FAILED: bus=%b | known=%b has=%b out=%h | exp_known=%b exp_has=%b exp_out=%h",
                         bus, all_known, has_unknown, bus_if_known,
                         expected_all_known, expected_has_unknown, expected_bus_if_known);
            end
        end
    endtask

    integer i;
    initial begin
        TOTAL=0; PASS=0; FAIL=0;

        // Directed 8-bit cases (with X/Z)
        run_test(8'h00);              // known
        run_test(8'hFF);              // known
        run_test(8'b0000_010x);       // one X
        run_test(8'b0000_z001);       // one Z
        run_test(8'bzzzz_zzzz);       // all Z
        run_test(8'bxxxx_1010);       // leading Xs
        run_test(8'hA5);              // known
        run_test(8'h5A);              // known

        // Random known values (truncate $random to 8 bits -> still 0/1 only)
        for (i=0; i<6; i=i+1)
            run_test($random);

        // Summary
        $display("======================================");
        $display("TOTAL_TEST_CASES=%0d", TOTAL);
        $display("TOTAL_PASSED_TEST_CASES=%0d", PASS);
        $display("TOTAL_FAILED_TEST_CASES=%0d", FAIL);
        $display("ALL_TEST_CASES_PASSED=%s", (FAIL==0) ? "true" : "false");
        $display("======================================");
        $finish;
    end
endmodule