75. 4-bit Register with Synchronous Reset

module reg4_sync_reset (
    input        CLK,
    input        RST,
    input  [3:0] D,
    output reg [3:0] Q
);
    always @(posedge CLK) begin
        if (RST)
            Q <= 4'b0000;
        else
            Q <= D;
    end
endmodule

💡Remember

  • Synchronous reset is sampled at the rising edge; changes between edges do not affect Q until the next edge.
  • Put higher-priority controls (reset) first, then data/enable.
  • Use nonblocking assignments in sequential logic.

Testbench Code

`timescale 1ns/1ps

module tb_reg4_sync_reset;
    // 1) Inputs
    reg        CLK, RST;
    reg  [3:0] D;
    // 2) Outputs
    wire [3:0] Q;
    // 3) Expected outputs (prefixed "expected_")
    reg  [3:0] expected_Q;
    // 4) Mismatch (HIGH when outputs != expected)
    wire mismatch;

    // DUT
    reg4_sync_reset dut(.CLK(CLK), .RST(RST), .D(D), .Q(Q));

    // Scoreboard: mirror DUT at posedge
    always @(posedge CLK) begin
        if (RST) expected_Q <= 4'b0000;
        else     expected_Q <= D;
    end

    // 4-state aware compare
    assign 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 i, j;

    // Free-running clock (10 time-unit period)
    initial begin
        CLK = 1'b0;
        forever #5 CLK = ~CLK;
    end

    // VCD — order: Inputs -> Outputs -> Expected -> mismatch; start at #0
    initial begin
        $dumpfile("tb_reg4_sync_reset.vcd");
        $dumpvars(0,
            tb_reg4_sync_reset.CLK,
            tb_reg4_sync_reset.RST,
            tb_reg4_sync_reset.D,
            tb_reg4_sync_reset.Q,
            tb_reg4_sync_reset.expected_Q,
            tb_reg4_sync_reset.mismatch
        );
        $dumpon;
    end

    // Drive RST/D on negedge; check after the next posedge
    task drive_and_check;
        input rst_v;
        input [3:0] d_v;
    begin
        @(negedge CLK);
        RST = rst_v;
        D   = d_v;
        @(posedge CLK); #1;

        TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
        if (!mismatch) begin
            TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
        end else begin
            TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;
            if (TOTAL_FAILED_TEST_CASES <= ERROR_MAX_CASES)
                $display("[FAIL] RST=%b D=0x%1h  Q=0x%1h expected_Q=0x%1h  t=%0t",
                         RST, D, Q, expected_Q, $time);
        end

        if (TOTAL_TEST_CASES == VCD_MAX_CASES) $dumpoff;
    end
    endtask

    initial begin
        // Start unknown like real silicon; first edge defines it
        RST = 1'b0; D = 4'h0; expected_Q = 4'hx;
        @(posedge CLK); #1;

        // Directed
        drive_and_check(1'b1, 4'hF); // reset dominates
        drive_and_check(1'b0, 4'h5); // capture D
        drive_and_check(1'b0, 4'hA); // capture D
        drive_and_check(1'b1, 4'h3); // reset again

        // Two-step sequences (trimmed sweep)
        for (i=0; i<4; i=i+1) begin
            drive_and_check(i[0], i[3:0]);
            for (j=0; j<4; j=j+1)
                drive_and_check(j[0], j[3:0]);
        end

        // Random stress
        for (i=0; i<10; i=i+1)
            drive_and_check($random, $random);

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