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
endmoduleHALF 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 PASSThe 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. For3 − 5the result1110is −2 in two's complement withbout=1flagging 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
bwith thesubcontrol to invert it, and feedsubinto the carry-in to supply the+1. On subtraction the adder's carry-out is an active-low borrow, soborrow = ¬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 endmodule9 - 4 = 5 (no-borrow=1)
3 - 5 = 1110 = -2 (no-borrow=0)
ALL PASS (256 exhaustive + 2 spot checks)Because
addsub_nand the borrow-ripplesub_nmust agree, the exhaustive 256-case sweep cross-checks the two's-complement datapath against Verilog's built-ina − b, confirming both the result bits and the active-low borrow interpretation of carry-out.How borrow propagates
In a half subtractor,
diff = a ⊕ band the borrow fires only whena=0, b=1, givingborrow = ¬a & b. The full subtractor folds in a borrow-in: it subtractsbfromawith the first half subtractor, subtractsbinfrom that difference with the second, and assertsboutif either stage needed to borrow. Chaining N full subtractors with the borrow rippling from LSB to MSB yields a true multi-bit subtractor whoseboutis the final borrow — set exactly whena < b + binas 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 asubcontrol bit. The bit-invert comes fromb ⊕ suband the+1comes from forcing carry-in tosub. A subtle but important detail: on subtraction the adder's carry-out is the complement of the borrow —cout=1means no borrow,cout=0means 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 roughlyO(log N). Note that modern synthesis tools recognizea − band 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 − 5yields1110, 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 toN+1bits 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 thea + ¬b + 1identity, 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 LabRun it in the LabOpen this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.
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 Hi! Ask me about courses, fees, batches or discounts. ×ChipXpert AssistantAsk about courses, fees, batches and centres