71. JK Flip-Flop with Enable

Loading...

Testbench Code

`timescale 1ns/1ps

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

    // DUT
    jk_ff_enable dut(.CLK(CLK), .EN(EN), .J(J), .K(K), .Q(Q));

    // Scoreboard: mirror DUT behavior at posedge CLK
    always @(posedge CLK) begin
        if (EN) begin
            case ({J, K})
                2'b00: expected_Q <= expected_Q;
                2'b01: expected_Q <= 1'b0;
                2'b10: expected_Q <= 1'b1;
                2'b11: expected_Q <= ~expected_Q;
            endcase
        end
        // EN=0 -> hold (no assignment)
    end

    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 — only Inputs -> Outputs -> Expected -> mismatch; start at #0
    initial begin
        $dumpfile("tb_jk_ff_enable.vcd");
        $dumpvars(0,
            tb_jk_ff_enable.CLK,
            tb_jk_ff_enable.EN,
            tb_jk_ff_enable.J,
            tb_jk_ff_enable.K,
            tb_jk_ff_enable.Q,
            tb_jk_ff_enable.expected_Q,
            tb_jk_ff_enable.mismatch
        );
        $dumpon;
    end

    // Drive inputs on negedge; check after next posedge
    task drive_and_check;
        input [127:0] name;
        input en_v, j_v, k_v;
    begin
        @(negedge CLK);
        EN = en_v; J = j_v; K = k_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] %0s  EN=%b J=%b K=%b  Q=%b expected_Q=%b  t=%0t",
                         name, EN, J, K, Q, expected_Q, $time);
        end
        if (TOTAL_TEST_CASES == VCD_MAX_CASES) $dumpoff;
    end
    endtask

    // Compact edge table from current state
    task print_edge_table;
        integer e, jv, kv;
        reg curQ, nxtQ;
    begin
        curQ = expected_Q;
        $display("Edge table (from current Q=%b):", curQ);
        $display(" EN J K | EXP_Q_after_↑CLK");
        $display("---------------------------");
        for (e=0; e<2; e=e+1)
            for (jv=0; jv<2; jv=jv+1)
                for (kv=0; kv<2; kv=kv+1) begin
                    if (e==0) nxtQ = curQ;
                    else begin
                        case ({jv[0], kv[0]})
                          2'b00: nxtQ = curQ;
                          2'b01: nxtQ = 1'b0;
                          2'b10: nxtQ = 1'b1;
                          2'b11: nxtQ = ~curQ;
                        endcase
                    end
                    $display("  %0d  %0d %0d |        %0d", e, jv, kv, nxtQ);
                end
    end
    endtask

    initial begin
        // Start unknown like real silicon; first edge defines it
        EN = 1'b0; J = 1'b0; K = 1'b0; expected_Q = 1'bx;
        @(posedge CLK); #1;

        // Directed
        drive_and_check("disabled_hold" , 1'b0, 1'b1, 1'b1);
        drive_and_check("reset"         , 1'b1, 1'b0, 1'b1);
        drive_and_check("set"           , 1'b1, 1'b1, 1'b0);
        drive_and_check("toggle"        , 1'b1, 1'b1, 1'b1);
        drive_and_check("hold_enabled"  , 1'b1, 1'b0, 1'b0);
        drive_and_check("toggle_ignored", 1'b0, 1'b1, 1'b1);

        // Exhaustive two-step sequences over {EN,J,K}
        for (i=0; i<8; i=i+1) begin
            drive_and_check("prev_vec", i[2], i[1], i[0]);
            for (j=0; j<8; j=j+1)
                drive_and_check("next_vec", j[2], j[1], j[0]);
        end

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

        // Edge table
        print_edge_table();

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