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 PASSED

The 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 rca4 block 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
endmodule
PASS 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 PASSED

The 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]), where g[i] = a[i]&b[i] is the generate term and p[i] = a[i]^b[i] is the propagate term. The whole worst-case delay comes from a carry rippling through every bit, for example FFFF + 0001 where 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: when P_block is 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 k full-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 as k + N/k. Minimizing that gives an optimum near k ≈ 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_cout or forwarding a stale value silently breaks only on all-propagate inputs, which generic random tests may miss. Always test an all-propagate vector such as AAAA + 5555 + 1.
  • Forgetting the carry-in in the block propagate. P_block depends only on the p[i] bits, never on generate terms or the carry. Including a g[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 behavioral assign {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 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