64. D Latch

Design a level-sensitive D latch. When EN=1, the output must follow D. When EN=0, the latch must hold its previous value.

Requirements

  • Module: d_latch
  • Ports:
    • Inputs:
      • EN
      • D
    • Outputs:
      • Q
  • Functional behavior
    • EN=1Q tracks D
    • EN=0Q holds previous value
    • No internal reset; any initialization is driven by stimulus
  • Modeling constraints
    • Infer a level-sensitive latch using always @(EN or D) (or always @(*)).
    • Use nonblocking assignments (<=) for storage.
    • Ensure no assignments occur in the hold path so the latch retains state.

Behaviour / Worked Example

Starting from Q=0:

  1. EN=1, D=0Q=0
  2. EN=1, D=1Q=1
  3. EN=0, D=0Q=1 (hold)
  4. EN=0, D=1Q=1 (hold)
  5. EN=1, D=0Q=0

Testbench Code

`timescale 1ns/1ps

module tb_d_latch;
    // 1) Inputs
    reg EN, D;
    // 2) Outputs
    wire Q;
    // 3) Expected outputs (prefixed "expected_")
    reg  expected_Q;
    // 4) Mismatch (HIGH when outputs != expected)
    wire mismatch;

    // DUT
    d_latch dut(.EN(EN), .D(D), .Q(Q));

    // 4-state 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;

    // Loop indices
    integer i, j;

    // VCD — start at #0; dump only requested signals in order
    initial begin
        $dumpfile("tb_d_latch.vcd");
        $dumpvars(0,
            tb_d_latch.EN,
            tb_d_latch.D,
            tb_d_latch.Q,
            tb_d_latch.expected_Q,
            tb_d_latch.mismatch
        );
        $dumpon; // begin dumping at time 0
    end

    // Golden next-state function
    function next_q;
        input en_val, d_val, cur_q;
    begin
        if (en_val) next_q = d_val;
        else        next_q = cur_q; // hold
    end
    endfunction

    // Named testcase runner
    task apply_and_check;
        input [127:0] tc_name;
        input en_val, d_val;
        reg   nxt;
    begin
        EN = en_val;
        D  = d_val;

        nxt = next_q(en_val, d_val, expected_Q);
        expected_Q = nxt;

        #1; // allow settle

        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) begin
                $display("[FAIL] %0s  EN=%b D=%b  Q=%b  expected_Q=%b  t=%0t",
                         tc_name, EN, D, Q, expected_Q, $time);
            end
        end

        if (TOTAL_TEST_CASES == VCD_MAX_CASES)
            $dumpoff; // limit VCD to first 32 cases
    end
    endtask

    // Compact truth table from current state
    task print_truth_table;
        reg curQ, nxt;
        integer e, d;
    begin
        curQ = expected_Q;
        $display("Truth table (from current state Q=%b):", curQ);
        $display(" EN D | EXP_Q");
        $display("--------------");
        for (e=0; e<2; e=e+1) begin
            for (d=0; d<2; d=d+1) begin
                nxt = next_q(e[0], d[0], curQ);
                $display("  %0d %0d |   %0d", e, d, nxt);
            end
        end
    end
    endtask

    initial begin
        // Establish a known value at t=0 by enabling and driving D=0
        EN = 1'b1; D = 1'b0; expected_Q = 1'b0; #1;

        // Directed tests (meaningful names)
        apply_and_check("track_low"         , 1'b1, 1'b0); // Q=0
        apply_and_check("track_high"        , 1'b1, 1'b1); // Q=1
        apply_and_check("hold_ignore_low"   , 1'b0, 1'b0); // hold 1
        apply_and_check("hold_ignore_high"  , 1'b0, 1'b1); // hold 1
        apply_and_check("re_enable_to_low"  , 1'b1, 1'b0); // Q=0
        apply_and_check("hold_again"        , 1'b0, 1'b1); // hold 0
        apply_and_check("glitch_filter_demo", 1'b0, 1'b0); // still hold 0

        // Exhaustive two-step transitions over {EN,D} (4 -> 4)
        for (i=0; i<4; i=i+1) begin
            apply_and_check("prev_vec", i[1], i[0]);
            for (j=0; j<4; j=j+1)
                apply_and_check("next_vec", j[1], j[0]);
        end

        // Random stress
        for (i=0; i<100; i=i+1)
            apply_and_check("rand", $random, $random);

        // Truth table (small input space)
        print_truth_table();

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