35. Guard Division by Zero

module safe_div8 (
    input  [7:0] dividend,
    input  [7:0] divisor,
    output [7:0] q,
    output [7:0] r,
    output       div_by_zero
);
    assign div_by_zero = (divisor == 8'd0) ? 1'b1 : 1'b0;
    assign q           = (divisor == 8'd0) ? 8'd0  : (dividend / divisor);
    assign r           = (divisor == 8'd0) ? 8'd0  : (dividend % divisor);
endmodule

💡Remember

  • The conditional (?:) operator is an inline if–else in an expression: y = sel ? a : b;
    • sel=1 → use the then value; sel=0 → use the else value.
  • Crucial with sel = x/z: Verilog evaluates both branches and merges bit-by-bit:
    • If branch values are identical → result is that value.
    • If they differ → result is x.
    • Examples:
      • (1’bx) ? 4’b1111 : 4’b11111111
      • (1’bx) ? 4’b1111 : 4’b0000xxxx
  • In this divider:
    • (divisor == 0) is x if divisor contains x/z, so div_by_zero, q, and r typically become x. That’s good—x-propagation exposes unknown/floating signals instead of hiding them.
  • We guard zero only; we do not sanitize x/z (intentional for learning).

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