Conditional-Sum Adder in Verilog: Speculate-Both-Carries with Code

A conditional-sum adder speeds up addition by refusing to wait for the carry. Every block computes its sum twice in parallel, once assuming an incoming carry of 0 and once assuming 1, then a multiplexer picks the correct answer the instant the real carry arrives from the block below. Because both candidates are already sitting at the mux inputs, the critical path through the high bits collapses to a chain of 2:1 selects instead of a long ripple.

This page gives you working, simulator-verified conditional-sum adder Verilog code: a clear 8-bit design and a parameterized 16-bit hierarchy, each self-checked against a golden a + b + cin reference so you can trust every reported result. It is the direct conceptual ancestor of the carry-select adder.

Two 4-bit blocks each produce a (sum, carry-out) pair for carry-in 0 and for carry-in 1. The real cin selects the low block; the low block’s resolved carry selects the high block. The testbench sweeps all 131,072 operand/carry combinations against a golden model.

// 4-bit conditional-sum building block:
// precompute the sum for carry-in = 0 and carry-in = 1 in parallel.
module csum_block4(a, b, sum0, cout0, sum1, cout1);
  input  [3:0] a, b;
  output [3:0] sum0, sum1;
  output       cout0, cout1;
  assign {cout0, sum0} = a + b + 1'b0;   // speculate cin = 0
  assign {cout1, sum1} = a + b + 1'b1;   // speculate cin = 1
endmodule

// 8-bit conditional-sum adder: two 4-bit blocks, each speculating both
// carries. The real cin selects the low block; the low block's resolved
// carry selects the high block.
module csum_add8(a, b, cin, sum, cout);
  input  [7:0] a, b;
  input        cin;
  output [7:0] sum;
  output       cout;

  wire [3:0] s0l, s1l, s0h, s1h;
  wire       c0l, c1l, c0h, c1h;

  csum_block4 lo(.a(a[3:0]), .b(b[3:0]),
                 .sum0(s0l), .cout0(c0l), .sum1(s1l), .cout1(c1l));
  csum_block4 hi(.a(a[7:4]), .b(b[7:4]),
                 .sum0(s0h), .cout0(c0h), .sum1(s1h), .cout1(c1h));

  wire [3:0] suml = cin ? s1l : s0l;   // select low block by real cin
  wire       cl   = cin ? c1l : c0l;   // low block's resolved carry-out
  wire [3:0] sumh = cl  ? s1h : s0h;   // select high block by that carry
  wire       ch   = cl  ? c1h : c0h;

  assign sum  = {sumh, suml};
  assign cout = ch;
endmodule

module tb;
  reg  [7:0] a, b;
  reg        cin;
  wire [7:0] sum;
  wire       cout;
  integer    i, j, k;
  reg  [8:0] ref9;
  integer    fails;

  csum_add8 dut(.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));

  initial begin
    fails = 0;
    // Exhaustive sweep over all 8-bit operands and both carry-in values.
    for (i = 0; i < 256; i = i + 1) begin
      for (j = 0; j < 256; j = j + 1) begin
        for (k = 0; k < 2; k = k + 1) begin
          a = i[7:0]; b = j[7:0]; cin = k[0];
          #1;
          ref9 = a + b + cin;                 // golden model
          if ({cout, sum} !== ref9) begin
            fails = fails + 1;
            if (fails <= 5)
              $display("FAIL a=%0d b=%0d cin=%0d got {cout,sum}=%0d exp=%0d",
                       a, b, cin, {cout, sum}, ref9);
          end
        end
      end
    end

    // A few named spot checks the reader can map to the output.
    a = 8'd0;   b = 8'd0;   cin = 1'b0; #1;
    if ({cout,sum} === 9'd0)   $display("PASS 0+0+0 = %0d", {cout,sum});
    else $display("FAIL 0+0+0");
    a = 8'd255; b = 8'd1;   cin = 1'b0; #1;
    if ({cout,sum} === 9'd256) $display("PASS 255+1+0 = %0d (carry out)", {cout,sum});
    else $display("FAIL 255+1+0");
    a = 8'd200; b = 8'd99;  cin = 1'b1; #1;
    if ({cout,sum} === 9'd300) $display("PASS 200+99+1 = %0d", {cout,sum});
    else $display("FAIL 200+99+1");

    if (fails == 0)
      $display("ALL TESTS PASSED (131072 exhaustive vectors)");
    else
      $display("TOTAL FAILS = %0d", fails);
    $finish;
  end
endmodule
PASS 0+0+0 = 0
PASS 255+1+0 = 256 (carry out)
PASS 200+99+1 = 300
ALL TESTS PASSED (131072 exhaustive vectors)

The log shows the exhaustive sweep passing: every one of the 2^8 x 2^8 x 2 input combinations matched a + b + cin, and the three named spot checks (0+0+0, the 255+1 carry-out case, and 200+99+1) print their decimal results. A single mismatch anywhere would have printed a FAIL line, so ALL TESTS PASSED proves the speculate-and-select logic is functionally identical to a plain adder.

This variant uses a parameterized csum_block instantiated four times with a generate loop, forming a 16-bit adder. The resolved carry ripples through a chain of select muxes from block 0 up to block 3, which is exactly how a conditional-sum hierarchy propagates its decision.

// Parameterized conditional-sum block of WIDTH bits.
// Produces both candidate results: (sum0,cout0) for cin=0 and
// (sum1,cout1) for cin=1. This is the pair that gets doubled at
// every level of a conditional-sum hierarchy.
module csum_block #(parameter WIDTH = 4)
  (a, b, sum0, cout0, sum1, cout1);
  input  [WIDTH-1:0] a, b;
  output [WIDTH-1:0] sum0, sum1;
  output             cout0, cout1;
  assign {cout0, sum0} = a + b + 1'b0;
  assign {cout1, sum1} = a + b + 1'b1;
endmodule

// 16-bit conditional-sum adder built as four 4-bit blocks.
// Each block speculates both carries; a mux chain selects the right
// candidate as the carry resolves from block 0 up to block 3.
module csum_add16(a, b, cin, sum, cout);
  input  [15:0] a, b;
  input         cin;
  output [15:0] sum;
  output        cout;

  wire [3:0] s0 [0:3];   // sum assuming cin=0 for each block
  wire [3:0] s1 [0:3];   // sum assuming cin=1 for each block
  wire       c0 [0:3];   // cout assuming cin=0
  wire       c1 [0:3];   // cout assuming cin=1

  genvar g;
  generate
    for (g = 0; g < 4; g = g + 1) begin : blk
      csum_block #(.WIDTH(4)) u (
        .a(a[g*4 +: 4]), .b(b[g*4 +: 4]),
        .sum0(s0[g]), .cout0(c0[g]),
        .sum1(s1[g]), .cout1(c1[g]));
    end
  endgenerate

  // Ripple the resolved carry through the select muxes.
  wire [3:0] y0 = cin ? s1[0] : s0[0];
  wire       k0 = cin ? c1[0] : c0[0];
  wire [3:0] y1 = k0  ? s1[1] : s0[1];
  wire       k1 = k0  ? c1[1] : c0[1];
  wire [3:0] y2 = k1  ? s1[2] : s0[2];
  wire       k2 = k1  ? c1[2] : c0[2];
  wire [3:0] y3 = k2  ? s1[3] : s0[3];
  wire       k3 = k2  ? c1[3] : c0[3];

  assign sum  = {y3, y2, y1, y0};
  assign cout = k3;
endmodule

module tb;
  reg  [15:0] a, b;
  reg         cin;
  wire [15:0] sum;
  wire        cout;
  integer     i, j, k;
  reg  [16:0] ref17;
  integer     fails;

  csum_add16 dut(.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));

  initial begin
    fails = 0;
    // Wide pseudo-sweep with FIXED (deterministic) stride vectors.
    // Steps of 777/1103 are coprime-ish to 65536 so they visit a broad,
    // reproducible spread of operands including boundary values.
    for (i = 0; i < 65536; i = i + 777) begin
      for (j = 0; j < 65536; j = j + 1103) begin
        for (k = 0; k < 2; k = k + 1) begin
          a = i[15:0]; b = j[15:0]; cin = k[0];
          #1;
          ref17 = a + b + cin;
          if ({cout, sum} !== ref17) begin
            fails = fails + 1;
            if (fails <= 5)
              $display("FAIL a=%0d b=%0d cin=%0d got=%0d exp=%0d",
                       a, b, cin, {cout, sum}, ref17);
          end
        end
      end
    end

    // Explicit boundary spot checks.
    a = 16'h0000; b = 16'h0000; cin = 1'b0; #1;
    if ({cout,sum} === 17'd0) $display("PASS 0+0+0 = %0d", {cout,sum});
    else $display("FAIL 0+0+0");
    a = 16'hFFFF; b = 16'h0001; cin = 1'b0; #1;
    if ({cout,sum} === 17'd65536) $display("PASS 65535+1+0 = %0d (carry out)", {cout,sum});
    else $display("FAIL 65535+1+0");
    a = 16'hFFFF; b = 16'hFFFF; cin = 1'b1; #1;
    if ({cout,sum} === 17'd131071) $display("PASS 65535+65535+1 = %0d", {cout,sum});
    else $display("FAIL max+max+1");
    a = 16'h0F0F; b = 16'h00F1; cin = 1'b0; #1;    // carry crosses block boundaries
    if ({cout,sum} === (16'h0F0F + 16'h00F1)) $display("PASS 0x0F0F+0x00F1 = %0d", {cout,sum});
    else $display("FAIL cross-block");

    if (fails == 0)
      $display("ALL TESTS PASSED (wide deterministic sweep + boundary checks)");
    else
      $display("TOTAL FAILS = %0d", fails);
    $finish;
  end
endmodule
PASS 0+0+0 = 0
PASS 65535+1+0 = 65536 (carry out)
PASS 65535+65535+1 = 131071
PASS 0x0F0F+0x00F1 = 4096
ALL TESTS PASSED (wide deterministic sweep + boundary checks)

The log confirms a wide, deterministic stride sweep (fixed steps, no random stimulus) plus explicit boundary cases: all-zero, the 65535+1 carry-out, the maximum 65535+65535+1, and a vector whose carry crosses every 4-bit block boundary. Each prints a PASS with its decimal sum, and the final ALL TESTS PASSED line only appears because zero vectors disagreed with the a + b + cin golden model.

How the conditional-sum adder works

The core idea is speculation. Addition is slow because bit i cannot finish until it knows the carry coming into it, and that carry depends on every bit below it. A conditional-sum adder breaks the dependency by computing both possible answers up front. For each block it evaluates:

  • {cout0, sum0} = a + b + 0 — the result if the incoming carry turns out to be 0;
  • {cout1, sum1} = a + b + 1 — the result if the incoming carry turns out to be 1.

Both computations run in parallel and do not depend on anything outside the block. When the true carry finally arrives from the block below, a 2:1 multiplexer selects the matching sum and the matching cout in one gate delay. That selected cout is then the true carry into the next block up, so the carry decision ripples through a short chain of muxes rather than through full adder logic.

Timing intuition

In a ripple-carry adder the worst-case path runs through every full adder in series. In the conditional-sum structure, once the block sums are ready, the remaining delay is one mux per block boundary. Trading a longer carry path for parallel speculation plus a mux chain is the whole point: you spend area to buy time.

Relationship to the carry-select adder

The conditional-sum adder is the direct precursor of the carry-select adder, and the two are easy to confuse. Both compute each block twice (for carry-in 0 and 1) and select with a mux. The distinction is how far the idea is pushed:

  • Carry-select adder: the block adders themselves are ordinary ripple-carry (or other) adders. Only the final carry-out of each block and its sum are muxed. It is a practical, widely used structure.
  • Conditional-sum adder: the speculate-both-carries idea is applied recursively, down to very small blocks (classically single bits), so the entire adder is a tree of muxes selecting precomputed candidate sums.

The doubling of candidate sums

Every level of a fully recursive conditional-sum adder keeps two candidate results per sub-block — one for each assumed carry-in. When two sub-blocks merge into a larger block, each of the parent's two required outputs (for carry-in 0 and 1) is formed by selecting among the children's candidates. This is why the structure is sometimes described as having its candidate count double as you go: at each bit position you must keep the carry-in-0 and carry-in-1 versions alive until the real carry resolves them. The cost is extra muxes and duplicated adder logic (area roughly doubles for the speculation), bought in exchange for a logarithmic-style carry-resolution path instead of a linear ripple. The parameterized 16-bit example above shows the small-block version of exactly this: four blocks, each holding both candidates, selected by a rippling carry.

Common mistakes

  • Dropping the block carry-out from speculation. You must precompute both cout0 and cout1. The selected carry-out is what drives the next block's mux; using only one of them breaks the chain for half the inputs.
  • Selecting sum and carry with different controls. Within a block, the same resolved carry must select the sum bits and the carry-out. Mixing c0/c1 selection for the sum but forgetting it for the carry produces results that pass on some vectors and fail on others — exactly the kind of bug an exhaustive self-check catches.
  • Width overflow on the concatenation. Each block's {cout, sum} must be one bit wider than the operands, or the carry is silently lost. In the code the left-hand side is WIDTH+1 bits via concatenation.
  • Assuming it is automatically faster in synthesis. For small widths a synthesis tool may map a behavioral + to something just as good or better. The conditional-sum structure earns its keep at wide widths where ripple delay dominates.

Synthesis notes

The Verilog here is fully synthesizable: every block is combinational, built from assign statements and continuous-assignment muxes, with no latches and no clock. A few practical points:

  • The a + b + 1'b0 and a + b + 1'b1 expressions synthesize to two adders per block. That duplication is intentional — it is the area cost of speculation.
  • The ternary selects (cl ? s1 : s0) map to 2:1 multiplexers. The resolved-carry chain (k0, k1, k2, k3 in the 16-bit version) is the structure's carry path; keeping blocks small shortens the sum computation but lengthens this mux chain, so block size is a real design knob.
  • Use generate (as in the 16-bit example) to scale the design to arbitrary widths without copy-paste, parameterizing both block width and block count.
  • Because the design is purely combinational, timing closure depends on the surrounding register-to-register path; pipeline the adder if the combined speculate-plus-select delay exceeds your clock period.

FAQ

Is a conditional-sum adder the same as a carry-select adder?

No, but they are closely related. Carry-select is the practical, single-level application of the speculate-both-carries idea; conditional-sum applies it recursively down to tiny blocks. The examples on this page use small fixed blocks, which sit between the two in spirit.

Why compute the sum twice instead of waiting for the carry?

Waiting serializes the whole adder. Computing both candidate sums in parallel means the only post-carry delay is a multiplexer, which is what makes wide conditional-sum adders fast.

Does the self-check really prove correctness?

The 8-bit example is exhaustive — it checks all 131,072 input combinations against a + b + cin, so it is a complete functional proof for that width. The 16-bit example cannot be fully exhaustive in reasonable time, so it uses a wide deterministic stride sweep plus explicit boundary vectors; it is strong evidence, though not a full proof, for the parameterized structure.

How do I make it wider than 16 bits?

Increase the block count in the generate loop and extend the resolved-carry mux chain, or restructure the chain as a balanced tree to shorten the carry-resolution path at very large widths.

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