35. Guard Division by Zero

Build a combinational divider that avoids simulation x from divide-by-zero. If the divisor is zero, clamp outputs to zero and raise a flag.

Requirements

  • Module: safe_div8
  • Inputs: dividend[7:0], divisor[7:0]
  • Outputs:
    • q[7:0] — unsigned quotient
    • r[7:0] — unsigned remainder
    • div_by_zero1 if divisor==0, else 0
  • Behavior:
    • If divisor == 8'd0: q=8'd0, r=8'd0, div_by_zero=1'b1
    • Else: q = dividend / divisor, r = dividend % divisor, div_by_zero=1'b0
  • Pure combinational RTL (no delays).

Testbench Code

`timescale 1ns/1ps

module tb_safe_div8;
    // 1) Inputs
    reg  [7:0] dividend;
    reg  [7:0] divisor;

    // 2) DUT outputs
    wire [7:0] q;
    wire [7:0] r;
    wire       div_by_zero;

    // 3) Expected (prefixed expected_)
    reg  [7:0] expected_q;
    reg  [7:0] expected_r;
    reg        expected_div_by_zero;

    // 4) Mismatch (HIGH on fail)
    wire mismatch = (q !== expected_q) ||
                    (r !== expected_r) ||
                    (div_by_zero !== expected_div_by_zero);

    // Counters
    integer TOTAL_TEST_CASES        = 0;
    integer TOTAL_PASSED_TEST_CASES = 0;
    integer TOTAL_FAILED_TEST_CASES = 0;

    // VCD limit
    integer VCD_MAX_CASES = 32;

    // Randoms (Verilog-2005: declare at module scope)
    integer seed;
    integer i;
    reg [7:0] rand_din, rand_div;

    // DUT
    safe_div8 dut(
        .dividend(dividend), .divisor(divisor),
        .q(q), .r(r), .div_by_zero(div_by_zero)
    );

    // ---------- Golden model (mirror DUT’s ternary semantics) ----------
    task compute_expected;
        input  [7:0] din;
        input  [7:0] div;
        begin
            // Use the same ternary (+==) modeling so x/z propagate consistently
            expected_div_by_zero = (div == 8'd0) ? 1'b1 : 1'b0;
            expected_q           = (div == 8'd0) ? 8'd0  : (din / div);
            expected_r           = (div == 8'd0) ? 8'd0  : (din % div);
        end
    endtask

    // Apply + check (EXPECTED FIRST -> WAIT -> COMPARE)
    task apply_and_check;
        input [7:0] din;
        input [7:0] div;
        begin
            dividend = din;
            divisor  = div;

            compute_expected(din, div); // expected first
            #1;                         // let DUT settle

            TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
            if (!mismatch) TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
            else           TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;

            $display("   %3d      %3d  | %3d %3d  %1b |  %3d   %3d     %1b  |    %0d",
                     dividend, divisor, q, r, div_by_zero,
                     expected_q, expected_r, expected_div_by_zero, mismatch);

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

    // VCD dump (Inputs -> Outputs -> Expected -> Mismatch)
    initial begin
        $dumpfile("tb_safe_div8.vcd");
        $dumpvars(0,
            tb_safe_div8.dividend, tb_safe_div8.divisor,                 // Inputs
            tb_safe_div8.q, tb_safe_div8.r, tb_safe_div8.div_by_zero,    // Outputs
            tb_safe_div8.expected_q, tb_safe_div8.expected_r,            // Expected
            tb_safe_div8.expected_div_by_zero,                           // Expected
            tb_safe_div8.mismatch                                        // Mismatch
        );
        $dumpon; // start at #0
    end

    // Header + init + stimulus
    initial begin
        dividend = 8'd0; divisor = 8'd1;
        expected_q = 8'd0; expected_r = 8'd0; expected_div_by_zero = 1'b0;
        seed = 32'hA5A5_5AA5 ^ $time;

        $display(" dividend divisor |   q   r  dz | exp_q exp_r exp_dz | mismatch");
        $display("----------------------------------------------------------------");

        // Directed (≤ ~12 printed rows)
        apply_and_check(8'd0,   8'd1);   // 0/1
        apply_and_check(8'd9,   8'd3);   // exact
        apply_and_check(8'd10,  8'd3);   // remainder
        apply_and_check(8'd255, 8'd255); // =1, r=0
        apply_and_check(8'd200, 8'd250); // q=0, r=200
        apply_and_check(8'd37,  8'd0);   // divide-by-zero (guard)
        apply_and_check(8'd255, 8'd2);   // 127, r=1

        // X/Z showcase (explicit unknowns)
        apply_and_check(8'd127, 8'bzzzz_zzzz); // divisor all Z -> (==0) is x -> outputs x
        apply_and_check(8'd247, 8'b0000_10x0); // divisor has X -> outputs x

        // A few randoms (stay under 32 total)
        for (i = 0; i < 6; i = i + 1) begin
            rand_din = $random(seed);
            rand_div = $random(seed);
            if (rand_div == 8'd0) rand_div = 8'd1; // zero tested above
            apply_and_check(rand_din, rand_div);
        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