Carry-Skip Adder in Verilog: Block Skip Logic with Code
A carry-skip adder (also called a carry-bypass adder) is a ripple-carry adder sped up by letting the carry “jump over” whole blocks of bits when every bit in that block propagates a carry. It splits the operands into fixed-size blocks of full adders; each block adds a small block-propagate AND gate plus a 2:1 multiplexer that forwards the incoming carry directly to the next block when all bit-propagates are 1, instead of waiting for the slow ripple chain to resolve.
This page gives you clean, simulator-verified carry-skip adder Verilog code: first a fully parameterized N-bit design built with generate loops, then an explicit 8-bit version that exposes the block-propagate and skip-mux structure gate by gate. Both come with self-checking testbenches that compare {cout,sum} against a golden a + b + cin.
This parameterized design builds NBLOCKS = WIDTH/BLOCK_SIZE ripple-carry blocks with nested generate loops. Each block computes its bit-propagates p[i] = a[i]^b[i], ripples an internal carry, forms the block-propagate as &p, and drives a skip mux that bypasses the block when it fully propagates. The testbench sweeps eight fixed vectors (including all-propagate and full-ripple cases) at a 16-bit width with 4-bit blocks.
// Parameterized carry-skip (carry-bypass) adder
module carry_skip_adder #(
parameter WIDTH = 16,
parameter BLOCK_SIZE = 4
)(
input wire [WIDTH-1:0] a,
input wire [WIDTH-1:0] b,
input wire cin,
output wire [WIDTH-1:0] sum,
output wire cout
);
localparam NBLOCKS = WIDTH / BLOCK_SIZE;
wire [NBLOCKS:0] blk_cin;
assign blk_cin[0] = cin;
genvar g;
generate
for (g = 0; g < NBLOCKS; g = g + 1) begin : BLK
wire [BLOCK_SIZE:0] c;
wire [BLOCK_SIZE-1:0] p;
wire blk_prop;
wire rca_cout;
assign c[0] = blk_cin[g];
genvar i;
for (i = 0; i < BLOCK_SIZE; i = i + 1) begin : FA
wire ai = a[g*BLOCK_SIZE + i];
wire bi = b[g*BLOCK_SIZE + i];
assign p[i] = ai ^ bi;
assign sum[g*BLOCK_SIZE + i] = p[i] ^ c[i];
assign c[i+1] = (ai & bi) | (p[i] & c[i]);
end
assign rca_cout = c[BLOCK_SIZE];
assign blk_prop = &p;
assign blk_cin[g+1] = blk_prop ? blk_cin[g] : rca_cout;
end
endgenerate
assign cout = blk_cin[NBLOCKS];
endmodule
module tb;
localparam WIDTH = 16;
reg [WIDTH-1:0] a, b;
reg cin;
wire [WIDTH-1:0] sum;
wire cout;
reg [WIDTH:0] golden;
integer k, errors;
carry_skip_adder #(.WIDTH(WIDTH), .BLOCK_SIZE(4)) dut (
.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout)
);
reg [WIDTH-1:0] va [0:7];
reg [WIDTH-1:0] vb [0:7];
reg vc [0:7];
initial begin
va[0]=16'h0000; vb[0]=16'h0000; vc[0]=1'b0;
va[1]=16'hFFFF; vb[1]=16'h0001; vc[1]=1'b0;
va[2]=16'h1234; vb[2]=16'h5678; vc[2]=1'b0;
va[3]=16'hFFFF; vb[3]=16'hFFFF; vc[3]=1'b1;
va[4]=16'hAAAA; vb[4]=16'h5555; vc[4]=1'b1;
va[5]=16'h8000; vb[5]=16'h8000; vc[5]=1'b0;
va[6]=16'h0FFF; vb[6]=16'h0001; vc[6]=1'b0;
va[7]=16'hDEAD; vb[7]=16'hBEEF; vc[7]=1'b1;
errors = 0;
for (k = 0; k < 8; k = k + 1) begin
a = va[k]; b = vb[k]; cin = vc[k];
#1;
golden = a + b + cin;
if ({cout, sum} !== golden) begin
$display("FAIL vec %0d: a=%h b=%h cin=%b => {cout,sum}=%h expected=%h",
k, a, b, cin, {cout, sum}, golden);
errors = errors + 1;
end else begin
$display("PASS vec %0d: %h + %h + %b = %h (cout=%b)",
k, a, b, cin, sum, cout);
end
end
if (errors == 0) $display("ALL TESTS PASSED");
else $display("TESTS FAILED: %0d", errors);
$finish;
end
endmodule
PASS vec 0: 0000 + 0000 + 0 = 0000 (cout=0) PASS vec 1: ffff + 0001 + 0 = 0000 (cout=1) PASS vec 2: 1234 + 5678 + 0 = 68ac (cout=0) PASS vec 3: ffff + ffff + 1 = ffff (cout=1) PASS vec 4: aaaa + 5555 + 1 = 0000 (cout=1) PASS vec 5: 8000 + 8000 + 0 = 0000 (cout=1) PASS vec 6: 0fff + 0001 + 0 = 1000 (cout=0) PASS vec 7: dead + beef + 1 = 9d9d (cout=1) ALL TESTS PASSEDThe log shows all eight vectors passing, each checked against the golden
a + b + cin. Vector 1 (FFFF + 0001) exercises the full carry chain; vector 4 (AAAA + 5555 + 1) makes every block fully propagate so the skip muxes forward the carry-in end to end. Matching{cout,sum}on every case proves the block-propagate logic and skip muxes compute the same result as a plain adder.This 8-bit version makes the structure explicit: a reusable
rca4block exposes its ripple carry-out and its block-propagate (p0 & p1 & p2 & p3), and the top module wires two blocks together with a visible 2:1 skip mux on each. The testbench runs a wide self-checking sweep (640 vectors) plus targeted corner cases.// 8-bit carry-skip adder: explicit blocks with block-propagate AND + skip mux module rca4 ( input wire [3:0] a, input wire [3:0] b, input wire cin, output wire [3:0] sum, output wire cout, // ripple carry-out of the block output wire blk_prop // AND of all 4 bit-propagates ); wire [4:0] c; wire [3:0] p; assign c[0] = cin; assign p[0] = a[0] ^ b[0]; assign p[1] = a[1] ^ b[1]; assign p[2] = a[2] ^ b[2]; assign p[3] = a[3] ^ b[3]; assign sum[0] = p[0] ^ c[0]; assign sum[1] = p[1] ^ c[1]; assign sum[2] = p[2] ^ c[2]; assign sum[3] = p[3] ^ c[3]; assign c[1] = (a[0] & b[0]) | (p[0] & c[0]); assign c[2] = (a[1] & b[1]) | (p[1] & c[1]); assign c[3] = (a[2] & b[2]) | (p[2] & c[2]); assign c[4] = (a[3] & b[3]) | (p[3] & c[3]); assign cout = c[4]; assign blk_prop = p[0] & p[1] & p[2] & p[3]; endmodule module carry_skip_adder8 ( input wire [7:0] a, input wire [7:0] b, input wire cin, output wire [7:0] sum, output wire cout ); wire blk0_cout, blk0_prop; wire blk1_cout, blk1_prop; wire c_blk1_in; // carry into block 1 after skip mux of block 0 rca4 blk0 (.a(a[3:0]), .b(b[3:0]), .cin(cin), .sum(sum[3:0]), .cout(blk0_cout), .blk_prop(blk0_prop)); // skip mux block 0: bypass cin straight through when block fully propagates assign c_blk1_in = blk0_prop ? cin : blk0_cout; rca4 blk1 (.a(a[7:4]), .b(b[7:4]), .cin(c_blk1_in), .sum(sum[7:4]), .cout(blk1_cout), .blk_prop(blk1_prop)); // skip mux block 1 assign cout = blk1_prop ? c_blk1_in : blk1_cout; endmodule module tb; reg [7:0] a, b; reg cin; wire [7:0] sum; wire cout; reg [8:0] golden; integer ai, bi, ci, errors, tests; carry_skip_adder8 dut (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout)); initial begin errors = 0; tests = 0; // wide self-checking sweep: step a and b, both cin values for (ai = 0; ai < 256; ai = ai + 17) begin for (bi = 0; bi < 256; bi = bi + 13) begin for (ci = 0; ci < 2; ci = ci + 1) begin a = ai[7:0]; b = bi[7:0]; cin = ci[0]; #1; golden = a + b + cin; tests = tests + 1; if ({cout, sum} !== golden) begin $display("FAIL: a=%h b=%h cin=%b => {cout,sum}=%h exp=%h", a, b, cin, {cout, sum}, golden); errors = errors + 1; end end end end // targeted corner cases with visible PASS lines a=8'hFF; b=8'h01; cin=1'b0; #1; golden=a+b+cin; if ({cout,sum}===golden) $display("PASS carry-ripple: FF+01 = %h cout=%b", sum, cout); else begin $display("FAIL FF+01"); errors=errors+1; end a=8'h0F; b=8'h01; cin=1'b0; #1; golden=a+b+cin; if ({cout,sum}===golden) $display("PASS block-boundary carry: 0F+01 = %h cout=%b", sum, cout); else begin $display("FAIL 0F+01"); errors=errors+1; end a=8'hFF; b=8'hFF; cin=1'b1; #1; golden=a+b+cin; if ({cout,sum}===golden) $display("PASS all-propagate+cin: FF+FF+1 = %h cout=%b", sum, cout); else begin $display("FAIL FF+FF+1"); errors=errors+1; end $display("Swept %0d vectors", tests); if (errors == 0) $display("ALL TESTS PASSED"); else $display("TESTS FAILED: %0d", errors); $finish; end endmodulePASS carry-ripple: FF+01 = 00 cout=1
PASS block-boundary carry: 0F+01 = 10 cout=0
PASS all-propagate+cin: FF+FF+1 = ff cout=1
Swept 640 vectors
ALL TESTS PASSEDThe log reports the swept vector count and three named corner cases: a carry that ripples the whole width (
FF+01), a carry crossing the 4-bit block boundary (0F+01), and an all-propagate-plus-carry-in case (FF+FF+1). Every swept and targeted vector matches the golden sum, so the explicit block-propagate AND gates and skip muxes are functionally correct.How the carry-skip adder works
Start from a ripple-carry adder: the carry out of bit i is
g[i] | (p[i] & c[i]), whereg[i] = a[i]&b[i]is the generate term andp[i] = a[i]^b[i]is the propagate term. The whole worst-case delay comes from a carry rippling through every bit, for exampleFFFF + 0001where a single carry walks the entire word.The carry-skip adder breaks the word into blocks and adds one observation: if every bit in a block has
p[i] = 1, then whatever carry enters the block is guaranteed to leave it unchanged. That condition is the block propagate,P_block = p[0] & p[1] & ... & p[k-1]. A 2:1 mux then selects the block carry-out: whenP_blockis 1 it forwards the block's carry-in directly (the skip path); otherwise it uses the slow rippled carry-out. The incoming carry can thus skip a block in one gate delay instead of k ripple stages.Timing and the optimal block size
Within a block the carry still ripples, so each block costs roughly
kfull-adder carry delays. The skip path across a block is a single mux delay. For a uniform block size k over N bits there are N/k blocks, and the critical path is about one ripple through the first block, a chain of skip muxes across the middle, and one ripple through the last block: delay grows roughly ask + N/k. Minimizing that gives an optimum neark ≈ sqrt(N/2), which is why a 16-bit adder is often cut into 4-bit blocks. Production designs frequently use variable block sizes (smaller blocks at the ends, larger in the middle) to flatten the worst-case path further.Common mistakes
- Skipping with the wrong carry. The skip mux must forward the block carry-in, not the rippled carry-out. Selecting
P_block ? rca_cout : rca_coutor forwarding a stale value silently breaks only on all-propagate inputs, which generic random tests may miss. Always test an all-propagate vector such asAAAA + 5555 + 1.- Forgetting the carry-in in the block propagate.
P_blockdepends only on thep[i]bits, never on generate terms or the carry. Including ag[i]term is wrong.- WIDTH not divisible by BLOCK_SIZE. The parameterized design assumes
WIDTH % BLOCK_SIZE == 0. A leftover partial block needs its own handling.- A zero-delay combinational race in simulation. Drive inputs, then let the logic settle (the testbenches use
#1) before sampling{cout,sum}.Synthesis notes
Everything here is purely combinational: continuous assignments and generate loops, no clock. Synthesis tools will map the full adders and the skip muxes to the target library. Note that most modern synthesizers already recognize an addition (
a + b + cin) and pick an optimized adder architecture from their own datapath generators, so hand-written carry-skip RTL is mainly valuable for teaching, for understanding the delay/area trade-off, or when you must instantiate a specific structure. If you let the tool choose, write the behavioralassign {cout,sum} = a + b + cin;and constrain timing instead. The block-propagate AND can be shared logic, and the skip mux adds a small area overhead over a plain ripple-carry adder in exchange for shorter worst-case carry delay.FAQ
How is a carry-skip adder different from a carry-lookahead adder?
A carry-lookahead adder (CLA) computes carries in parallel from generate/propagate terms across the whole group, giving logarithmic-style depth but more gates. A carry-skip adder keeps the simple ripple inside each block and only adds a bypass path between blocks, so it is cheaper and simpler than a CLA while still beating a plain ripple-carry adder on worst-case delay.
Why is it also called a carry-bypass adder?
Because the skip mux lets the incoming carry bypass an entire block of full adders when that block fully propagates. "Carry-skip" and "carry-bypass" refer to the same architecture.
What block size should I use?
For a uniform design the delay is minimized near
k ≈ sqrt(N/2); 4-bit blocks for a 16-bit adder is a common, well-balanced choice. Variable block sizing can do better still.Does the skip path change the result?
No. The skip mux only shortens the delay of the carry; the final
{cout,sum}is identical to a ripple-carry adder, which is exactly what the golden-model testbenches verify.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