58. Binary Subtractor

A. Dataflow

module sub4_2c (
    input  [3:0] a,
    input  [3:0] b,
    output [3:0] diff,
    output       bout
);
    // Width-safe 5-bit add to capture carry-out
    wire [4:0] sum5 = {1'b0, a} + {1'b0, ~b} + 5'b00001;
    assign diff = sum5[3:0];
    assign bout = ~sum5[4]; // borrow = NOT carry-out
endmodule

B. Structural via ripple adder

module full_adder_1bit(
    input  a, b, cin,
    output sum, cout
);
    assign sum  = a ^ b ^ cin;
    assign cout = (a & b) | (b & cin) | (a & cin);
endmodule

module rca4_add (
    input  [3:0] x, y,
    input        cin,
    output [3:0] sum,
    output       cout
);
    wire c1, c2, c3;
    full_adder_1bit fa0(.a(x[0]), .b(y[0]), .cin(cin), .sum(sum[0]), .cout(c1));
    full_adder_1bit fa1(.a(x[1]), .b(y[1]), .cin(c1 ), .sum(sum[1]), .cout(c2));
    full_adder_1bit fa2(.a(x[2]), .b(y[2]), .cin(c2 ), .sum(sum[2]), .cout(c3));
    full_adder_1bit fa3(.a(x[3]), .b(y[3]), .cin(c3 ), .sum(sum[3]), .cout(cout));
endmodule

module sub4_2c (
    input  [3:0] a,
    input  [3:0] b,
    output [3:0] diff,
    output       bout
);
    wire [3:0] b_inv = ~b;
    wire       cout;
    rca4_add adder(.x(a), .y(b_inv), .cin(1'b1), .sum(diff), .cout(cout));
    assign bout = ~cout;
endmodule

💡Remember

  • Two’s complement subtraction = invert b, add 1, then add to a.
  • For unsigned subtraction, borrow-out = NOT carry-out from the addition.
  • For signed 4-bit subtraction, the carry is irrelevant; overflow is detected with sign bits (not required here).
  • Keep everything combinational (use assign or always @* with proper defaults).

Testbench Code

`timescale 1ns/1ps

module tb_sub4_2c;
    // 1) Inputs
    reg  [3:0] a, b;

    // 2) DUT outputs
    wire [3:0] diff;
    wire       bout;

    // 3) Expected (purely combinational, width-safe)
    wire [4:0] golden   = {1'b0, a} + {1'b0, ~b} + 5'b00001; // a + (~b) + 1
    wire [3:0] exp_diff = golden[3:0];
    wire       exp_bout = ~golden[4]; // borrow = NOT carry

    // 4) Single mismatch flag (treat X/Z as mismatches)
    wire mismatch = ({diff, bout} !== {exp_diff, exp_bout});

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

    // Limits
    integer VCD_MAX_CASES   = 32; // normal printed rows
    integer ERROR_MAX_CASES = 32; // error printed rows
    integer printed_rows    = 0;
    integer printed_errors  = 0;

    // DUT (pick dataflow or structural)
    sub4_2c dut(.a(a), .b(b), .diff(diff), .bout(bout));
    // sub4_2c_struct dut(.a(a), .b(b), .diff(diff), .bout(bout));

    // VCD dump
    initial begin
        $dumpfile("tb_sub4_2c.vcd");
        $dumpvars(0,
            tb_sub4_2c.a, tb_sub4_2c.b,
            tb_sub4_2c.diff, tb_sub4_2c.bout,
            tb_sub4_2c.exp_diff, tb_sub4_2c.exp_bout,
            tb_sub4_2c.mismatch
        );
        $dumpon;
    end

    // Header + init
    initial begin
        a = 0; b = 0;
        $display("  a   b  | diff bout | exp_diff exp_bout | mismatch");
        $display("-----------------------------------------------------");
    end

    // Apply + check (drive -> wait -> read combinational mismatch)
    task apply_and_check;
        input [3:0] ta, tb_;
        begin
            a = ta; b = tb_;
            #1; // settle

            TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
            if (!mismatch) begin
                TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
                if (printed_rows < VCD_MAX_CASES) begin
                    $display("0x%1h 0x%1h | 0x%1h   %0d  |   0x%1h       %0d     |    %0d",
                             a, b, diff, bout, exp_diff, exp_bout, mismatch);
                    printed_rows = printed_rows + 1;
                end
            end else begin
                TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;
                if (printed_errors < ERROR_MAX_CASES) begin
                    $display("ERR: a=0x%1h b=0x%1h => diff=0x%1h bout=%0d (exp diff=0x%1h bout=%0d)",
                             a, b, diff, bout, exp_diff, exp_bout);
                    printed_errors = printed_errors + 1;
                end
            end

            // Optional: stop VCD after cap of total vectors
            if (TOTAL_TEST_CASES == VCD_MAX_CASES) $dumpoff;
        end
    endtask

    integer ia, ib;
    initial begin
        // Directed: equal, underflow/borrow, boundaries
        apply_and_check(4'h0, 4'h0); // 0 - 0
        apply_and_check(4'hF, 4'h0); // no borrow
        apply_and_check(4'h0, 4'hF); // borrow
        apply_and_check(4'h8, 4'h7); // near MSB boundary
        apply_and_check(4'h7, 4'h8); // borrow near MSB boundary
        apply_and_check(4'hA, 4'hA); // equal

        // Compact sweep (keeps prints/VCD readable)
        for (ia = 0; ia < 16; ia = ia + 1)
            for (ib = 0; ib < 16; ib = ib + 1)
                apply_and_check(ia[3:0], ib[3:0]);

        // A few randoms
        repeat (16) apply_and_check($random, $random);

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