77. Parallel-In Serial-Out Register

Loading...

Testbench Code

`timescale 1ns/1ps

module tb_piso4;
    // Inputs
    reg        CLK, RST, LOAD;
    reg  [3:0] D;
    // Output
    wire       serial_out;

    // DUT
    piso4 dut(.CLK(CLK), .RST(RST), .LOAD(LOAD), .D(D), .serial_out(serial_out));

    // Expected outputs (for VCD only expected_* signals)
    reg        expected_serial_out;
    reg  [3:0] expected_SR;

    // Mismatch
    wire mismatch = (serial_out !== expected_serial_out);

    // Counters / caps
    integer TOTAL_TEST_CASES        = 0;
    integer TOTAL_PASSED_TEST_CASES = 0;
    integer TOTAL_FAILED_TEST_CASES = 0;
    integer VCD_MAX_CASES           = 32;
    integer ERROR_MAX_CASES         = 32;

    integer printed_rows  = 0;
    integer printed_errs  = 0;

    integer i;

    // 50% duty-cycle clock (10 TU period)
    initial begin
        CLK = 1'b0;
        forever #5 CLK = ~CLK;
    end

    // Scoreboard: mirror the "no-repeat LSB" DUT behavior
    always @(posedge CLK or posedge RST) begin
        if (RST) begin
            expected_SR         <= 4'b0000;
            expected_serial_out <= 1'b0;
        end else if (LOAD) begin
            expected_SR         <= D;                 // hold serial_out on load
            expected_serial_out <= expected_serial_out;
        end else begin
            expected_serial_out <= expected_SR[0];    // emit LSB
            expected_SR         <= {1'b0, expected_SR[3:1]};
        end
    end

    // VCD: Inputs -> Outputs -> Expected -> mismatch
    initial begin
        $dumpfile("tb_piso4.vcd");
        $dumpvars(0,
            tb_piso4.CLK,
            tb_piso4.RST,
            tb_piso4.LOAD,
            tb_piso4.D,
            tb_piso4.serial_out,
            tb_piso4.expected_serial_out,
            tb_piso4.mismatch
        );
        $dumpon;
    end

    task log_case;
    begin
        TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
        if (!mismatch) begin
            TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
            if (printed_rows < VCD_MAX_CASES) begin
                $display(" LOAD=%b D=0x%1h | SO=%b EXP=%b | mismatch=%0d",
                         LOAD, D, serial_out, expected_serial_out, mismatch);
                printed_rows = printed_rows + 1;
            end
        end else begin
            TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;
            if (printed_errs < ERROR_MAX_CASES) begin
                $display("ERR: LOAD=%b D=0x%1h => SO=%b (exp %b)",
                         LOAD, D, serial_out, expected_serial_out);
                printed_errs = printed_errs + 1;
            end
        end
        if (TOTAL_TEST_CASES == VCD_MAX_CASES) $dumpoff;
    end
    endtask

    // Apply LOAD/D on negedge; log after the following posedge(s)
    task load_word;
        input [3:0] word;
    begin
        @(negedge CLK);
        LOAD = 1'b1; D = word;
        @(posedge CLK); #1; log_case();   // load edge (SO holds)
        @(negedge CLK);
        LOAD = 1'b0;                      // begin shifting next edge
    end
    endtask

    task shift_n;
        input integer n;
        integer k;
    begin
        for (k=0; k<n; k=k+1) begin
            @(posedge CLK); #1; log_case();
        end
    end
    endtask

    initial begin
        // Init & reset
        RST = 1'b1; LOAD = 1'b0; D = 4'h0;
        expected_SR = 4'h0; expected_serial_out = 1'b0;

        @(posedge CLK); #1; log_case();   // during reset
        RST = 1'b0;
        @(posedge CLK); #1; log_case();   // after reset release

        // Directed cases
        load_word(4'b1011);  // expect stream: 1,1,0,1 on next 4 edges
        shift_n(4);

        load_word(4'b0100);  // expect: 0,0,1,0
        shift_n(4);

        // Randomized cases
        for (i=0; i<6; i=i+1) begin
            load_word($random);
            shift_n(4);
        end

        // Summary
        $display("------------------------------------");
        $display("TOTAL_TEST_CASES=%0d", TOTAL_TEST_CASES);
        $display("TOTAL_PASSED_TEST_CASES=%0d", TOTAL_PASSED_TEST_CASES);
        $display("TOTAL_FAILED_TEST_CASES=%0d", TOTAL_FAILED_TEST_CASES);
        $display("ALL_TEST_CASES_PASSED=%s", (TOTAL_FAILED_TEST_CASES==0) ? "true" : "false");

        #2 $finish;
    end
endmodule