78. Serial-In Parallel-Out Register

module sipo4 (
    input        CLK,
    input        RST,
    input        serial_in,
    output reg [3:0] Q
);
    always @(posedge CLK or posedge RST) begin
        if (RST)
            Q <= 4'b0000;
        else
            Q <= {serial_in, Q[3:1]};
    end
endmodule

💡Remember

  • MSB-first means the newest serial bit lands in Q[3] each edge.
  • Asynchronous reset forces a known state independent of the clock.

Testbench Code

`timescale 1ns/1ps

module tb_sipo4;
    // Inputs
    reg CLK;
    reg RST;
    reg serial_in;
    // Output
    wire [3:0] Q;

    // DUT
    sipo4 dut(.CLK(CLK), .RST(RST), .serial_in(serial_in), .Q(Q));

    // Expected (golden model)
    reg [3:0] expected_Q;

    // Mismatch (4-state aware)
    wire mismatch = (Q !== expected_Q);

    // Counters / limits
    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;

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

    // Golden model mirrors DUT behavior
    always @(posedge CLK or posedge RST) begin
        if (RST)
            expected_Q <= 4'b0000;
        else
            expected_Q <= {serial_in, expected_Q[3:1]};
    end

    // VCD: inputs -> outputs -> expected -> mismatch (start at #0)
    initial begin
        $dumpfile("tb_sipo4.vcd");
        $dumpvars(0,
            tb_sipo4.CLK,
            tb_sipo4.RST,
            tb_sipo4.serial_in,
            tb_sipo4.Q,
            tb_sipo4.expected_Q,
            tb_sipo4.mismatch
        );
        $dumpon;
    end

    // Log one sampled row (after posedge)
    task log_row;
    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(" serial_in=%b | Q=0x%1h EXP=0x%1h | mismatch=%0d",
                         serial_in, Q, expected_Q, 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: serial_in=%b => Q=0x%1h (exp 0x%1h)",
                         serial_in, Q, expected_Q);
                printed_errs = printed_errs + 1;
            end
        end
        if (TOTAL_TEST_CASES == VCD_MAX_CASES) $dumpoff;
    end
    endtask

    // Drive serial bit on negedge; sample/log after next posedge
    task shift_bit;
        input bit_val;
    begin
        @(negedge CLK);
        serial_in = bit_val;
        @(posedge CLK); #1;  // allow scheduling to settle
        log_row();
    end
    endtask

    integer i;

    initial begin
        // Header
        $display(" serial_in | Q | EXP | mismatch");
        $display("--------------------------------");

        // Bring to known state via async reset
        RST = 1'b1; serial_in = 1'b0; expected_Q = 4'b0000;
        @(posedge CLK); #1; log_row();
        RST = 1'b0;
        @(posedge CLK); #1; log_row();

        // Directed sequence 1 (1,0,1,1)
        shift_bit(1'b1);
        shift_bit(1'b0);
        shift_bit(1'b1);
        shift_bit(1'b1);

        // Directed sequence 2 (0,1,0,0)
        shift_bit(1'b0);
        shift_bit(1'b1);
        shift_bit(1'b0);
        shift_bit(1'b0);

        // Random stress
        for (i = 0; i < 12; i = i + 1)
            shift_bit($random);

        // 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