Full Subtractor Verilog Code: Half, Full & N-bit Borrow-Ripple Subtractor

A subtractor computes diff = a − b and produces a borrow when the minuend is smaller than the subtrahend. This page gives you working full subtractor verilog code, building up from a half subtractor to a full subtractor to a parameterized N-bit borrow-ripple chain — the structural mirror of the ripple-carry adder.

Both examples compile in strict Verilog-2005 and ship with self-checking testbenches: the first walks the half- and full-subtractor truth tables plus signed cases such as 3 − 5 (borrow out), and the second contrasts the dedicated borrow logic with the classic two’s-complement trick a − b = a + ¬b + 1, checked exhaustively over all 4-bit operands.

Half, full, and N-bit borrow-ripple subtractor

The half subtractor gives diff = a ⊕ b and borrow = ¬a & b. The full subtractor adds a borrow-in by cascading two half subtractors and OR-ing their borrows. The N-bit sub_n module chains full subtractors, threading the borrow from bit 0 upward exactly like a ripple-carry adder threads its carry.

// Half-subtractor, full-subtractor, and N-bit borrow-ripple subtractor
module half_sub(input a, input b, output diff, output borrow);
  assign diff   = a ^ b;        // difference
  assign borrow = (~a) & b;     // borrow when a<b
endmodule

module full_sub(input a, input b, input bin, output diff, output bout);
  wire d1, b1, b2;
  half_sub hs0(.a(a),  .b(b),   .diff(d1),   .borrow(b1));
  half_sub hs1(.a(d1), .b(bin), .diff(diff), .borrow(b2));
  assign bout = b1 | b2;        // borrow out
endmodule

// N-bit borrow-ripple subtractor: diff = a - b - bin
module sub_n #(parameter N=4)(
  input  [N-1:0] a,
  input  [N-1:0] b,
  input          bin,
  output [N-1:0] diff,
  output         bout);
  wire [N:0] br;                // borrow chain
  assign br[0] = bin;
  genvar i;
  generate
    for (i=0; i<N; i=i+1) begin : stage
      full_sub fs(.a(a[i]), .b(b[i]), .bin(br[i]),
                  .diff(diff[i]), .bout(br[i+1]));
    end
  endgenerate
  assign bout = br[N];
endmodule

module tb;
  reg a, b, bin;
  wire hd, hbo, fd, fbo;
  reg [3:0] av, bv;
  reg nbin;
  wire [3:0] nd;
  wire nbo;
  integer i, errors;

  half_sub  u_hs(.a(a), .b(b), .diff(hd), .borrow(hbo));
  full_sub  u_fs(.a(a), .b(b), .bin(bin), .diff(fd), .bout(fbo));
  sub_n #(.N(4)) u_sn(.a(av), .b(bv), .bin(nbin), .diff(nd), .bout(nbo));

  initial begin
    errors = 0;

    // Half-subtractor truth table
    for (i=0; i<4; i=i+1) begin
      {a,b} = i[1:0]; #1;
      if (hd !== (a^b) || hbo !== ((~a)&b)) errors = errors+1;
      $display("HALF a=%b b=%b -> diff=%b borrow=%b", a, b, hd, hbo);
    end

    // Full-subtractor truth table (8 rows)
    for (i=0; i<8; i=i+1) begin
      {a,b,bin} = i[2:0]; #1;
      if (({fbo,fd}) !== (a - b - bin)) errors = errors+1;
      $display("FULL a=%b b=%b bin=%b -> diff=%b bout=%b", a, b, bin, fd, fbo);
    end

    // N-bit cases including a<b (borrow out)
    av=4'd9; bv=4'd4; nbin=1'b0; #1;      // 9-4=5, no borrow
    $display("N 9-4 = %0d bout=%b", nd, nbo);
    if (nd !== 4'd5 || nbo !== 1'b0) errors=errors+1;

    av=4'd3; bv=4'd5; nbin=1'b0; #1;      // 3-5 = -2 -> 1110, borrow out
    $display("N 3-5 = %b (unsigned %0d) bout=%b", nd, nd, nbo);
    if (nd !== 4'b1110 || nbo !== 1'b1) errors=errors+1;

    av=4'd7; bv=4'd7; nbin=1'b1; #1;      // 7-7-1 = -1 -> 1111, borrow out
    $display("N 7-7-1 = %b bout=%b", nd, nbo);
    if (nd !== 4'b1111 || nbo !== 1'b1) errors=errors+1;

    if (errors==0) $display("ALL PASS");
    else           $display("FAIL errors=%0d", errors);
    $finish;
  end
endmodule
HALF a=0 b=0 -> diff=0 borrow=0
HALF a=0 b=1 -> diff=1 borrow=1
HALF a=1 b=0 -> diff=1 borrow=0
HALF a=1 b=1 -> diff=0 borrow=0
FULL a=0 b=0 bin=0 -> diff=0 bout=0
FULL a=0 b=0 bin=1 -> diff=1 bout=1
FULL a=0 b=1 bin=0 -> diff=1 bout=1
FULL a=0 b=1 bin=1 -> diff=0 bout=1
FULL a=1 b=0 bin=0 -> diff=1 bout=0
FULL a=1 b=0 bin=1 -> diff=0 bout=0
FULL a=1 b=1 bin=0 -> diff=0 bout=0
FULL a=1 b=1 bin=1 -> diff=1 bout=1
N 9-4 = 5 bout=0
N 3-5 = 1110 (unsigned 14) bout=1
N 7-7-1 = 1111 bout=1
ALL PASS

The testbench reconstructs the expected full-subtractor output as {bout,diff} == a − b − bin, so every row is checked against arithmetic rather than a hand-typed table. For 3 − 5 the result 1110 is −2 in two's complement with bout=1 flagging the borrow.

Variant: two's-complement subtractor (a + ¬b + 1)

Hardware rarely builds a separate borrow network. Instead an adder/subtractor reuses one ripple-carry adder: XOR b with the sub control to invert it, and feed sub into the carry-in to supply the +1. On subtraction the adder's carry-out is an active-low borrow, so borrow = ¬cout.

// Two's-complement subtractor: a - b == a + (~b) + 1
// Built from a ripple-carry adder; cross-checked against Verilog's own math.
module full_add(input a, input b, input cin, output sum, output cout);
  assign sum  = a ^ b ^ cin;
  assign cout = (a & b) | (a & cin) | (b & cin);
endmodule

// N-bit adder/subtractor. sub=1 -> diff = a - b (two's complement).
// For subtraction, borrow-out = ~cout (carry-out is active-low borrow).
module addsub_n #(parameter N=4)(
  input  [N-1:0] a,
  input  [N-1:0] b,
  input          sub,          // 0=add, 1=subtract
  output [N-1:0] y,
  output         cout);        // on subtract: 1 means NO borrow
  wire [N:0] c;
  wire [N-1:0] bx = b ^ {N{sub}};   // invert b when subtracting
  assign c[0] = sub;                 // +1 for two's complement
  genvar i;
  generate
    for (i=0; i<N; i=i+1) begin : bit
      full_add fa(.a(a[i]), .b(bx[i]), .cin(c[i]),
                  .sum(y[i]), .cout(c[i+1]));
    end
  endgenerate
  assign cout = c[N];
endmodule

module tb;
  reg  [3:0] a, b;
  wire [3:0] y;
  wire       cout;
  integer i, j, errors;
  reg  [3:0] expect_y;

  addsub_n #(.N(4)) dut(.a(a), .b(b), .sub(1'b1), .y(y), .cout(cout));

  initial begin
    errors = 0;
    // Exhaustively check a-b over all 4-bit operands (256 cases)
    for (i=0; i<16; i=i+1)
      for (j=0; j<16; j=j+1) begin
        a = i[3:0]; b = j[3:0]; #1;
        expect_y = a - b;              // reference 4-bit result (wraps mod 16)
        if (y !== expect_y) errors = errors + 1;
        // borrow occurs when a<b; here cout==1 means no borrow
        if ((a < b) && (cout !== 1'b0)) errors = errors + 1;
        if ((a >= b) && (cout !== 1'b1)) errors = errors + 1;
      end

    // A few explicit, readable checks
    a=4'd9; b=4'd4; #1;
    $display("9 - 4 = %0d  (no-borrow=%b)", y, cout);
    if (y!==4'd5  || cout!==1'b1) errors=errors+1;

    a=4'd3; b=4'd5; #1;
    $display("3 - 5 = %b = -%0d  (no-borrow=%b)", y, (~y)+1'b1, cout);
    if (y!==4'b1110 || cout!==1'b0) errors=errors+1;

    if (errors==0) $display("ALL PASS (256 exhaustive + 2 spot checks)");
    else           $display("FAIL errors=%0d", errors);
    $finish;
  end
endmodule
9 - 4 = 5  (no-borrow=1)
3 - 5 = 1110 = -2 (no-borrow=0)
ALL PASS (256 exhaustive + 2 spot checks)

Because addsub_n and the borrow-ripple sub_n must agree, the exhaustive 256-case sweep cross-checks the two's-complement datapath against Verilog's built-in a − b, confirming both the result bits and the active-low borrow interpretation of carry-out.

How borrow propagates

In a half subtractor, diff = a ⊕ b and the borrow fires only when a=0, b=1, giving borrow = ¬a & b. The full subtractor folds in a borrow-in: it subtracts b from a with the first half subtractor, subtracts bin from that difference with the second, and asserts bout if either stage needed to borrow. Chaining N full subtractors with the borrow rippling from LSB to MSB yields a true multi-bit subtractor whose bout is the final borrow — set exactly when a < b + bin as unsigned numbers.

Borrow-ripple vs. two's complement

The dedicated borrow network above is pedagogically clean but wasteful: it builds subtract-only hardware. Real datapaths use the identity a − b = a + ¬b + 1, which lets a single ripple-carry adder do both operations under a sub control bit. The bit-invert comes from b ⊕ sub and the +1 comes from forcing carry-in to sub. A subtle but important detail: on subtraction the adder's carry-out is the complement of the borrow — cout=1 means no borrow, cout=0 means borrow. This is why processors derive their borrow/carry flag conventions carefully, and why x86 and ARM disagree on the sense of the carry flag after a subtract.

Synthesis and timing notes

Both designs are purely combinational, so they synthesize to logic with no registers. The critical path is the borrow (or carry) chain: in an N-bit ripple structure, delay grows linearly with N because bit k cannot settle until bit k−1's borrow is valid. For wide operands this dominates the cycle time, which is why production adders use carry-lookahead, carry-select, or prefix (Kogge-Stone) schemes to cut the O(N) dependency to roughly O(log N). Note that modern synthesis tools recognize a − b and will infer an optimized subtractor automatically — you rarely hand-instantiate ripple chains outside teaching or area-starved corners.

Common pitfalls

Three traps catch students. First, sign interpretation: a 4-bit 3 − 5 yields 1110, which is 14 unsigned but −2 as a two's-complement value — the bits are identical, only your reading differs. Second, borrow polarity: do not assume carry-out equals borrow-out; in the adder-based subtractor they are inverses. Third, bit-width: if you widen operands, remember to widen the borrow/carry chain to N+1 bits or you will drop the final borrow. Keeping the testbench's reconstruction ({bout,diff} == a − b − bin) as your oracle catches all three immediately.

Interview angle

Expect to be asked to derive the full-subtractor equations from its truth table (diff = a ⊕ b ⊕ bin, bout = ¬a&b | ¬a&bin | b&bin) and to explain why one adder can serve as both adder and subtractor. A strong answer names the a + ¬b + 1 identity, shows the XOR-invert plus carry-in-of-1 trick, and flags the active-low borrow on carry-out. Bonus points for noting that ripple delay is linear and that lookahead or prefix adders fix it — the same reasoning that makes subtraction a solved problem in any real ALU.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.
▶ Try it in the ChipXpert Lab

Open this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.

Run it in the Lab

Learn Verilog with mentors, labs and placement support

This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.

Explore VLSI Courses
Next batch starts 18th November 2026
Start your VLSI career with ChipXpert

Live online and classroom batches in Hyderabad & Bengaluru. Fill in your details and a counsellor will call you back.

  • Real EDA tools in your browser: industry-standard EDAReal EDA tools in your browser
  • Recorded sessions on the elearn portalRecorded sessions
  • Placement assistance: resume, mock interviewsPlacement assistance
  • Merit scholarship up to 60% · EMI optionsScholarship up to 60%
Hi! Ask me about courses, fees, batches or discounts. ×
BENGALURU
HYDERABAD