Full Adder Verilog Code: Dataflow, Behavioral and Structural

A full adder adds three single-bit inputs — a, b, and a carry-in cin — and produces a sum bit and a carry-out cout. It is the cell you chain together to build ripple-carry adders, ALUs, and counters, so it is usually the first “real” circuit you describe in Verilog. This tutorial gives you working full adder Verilog code in all three modelling styles (dataflow, behavioral, and structural from two half adders) plus a self-checking testbench that drives every one of the 8 input combinations, all runnable in Icarus Verilog.

The logic in one line

Before any HDL, pin down the Boolean equations. For three input bits the outputs are:

  • sum = a XOR b XOR cin
  • cout = (a AND b) OR (b AND cin) OR (a AND cin)

The sum is 1 whenever an odd number of inputs are 1. The carry-out is 1 whenever at least two inputs are 1 (the majority function). Every style below describes exactly this truth table — only the level of abstraction changes.

1. Dataflow style

Dataflow modelling uses continuous assign statements that mirror the Boolean equations directly. The outputs are wire type and update automatically whenever any right-hand-side signal changes.

// Full adder - dataflow style
module full_adder_dataflow (
    input  wire a,
    input  wire b,
    input  wire cin,
    output wire sum,
    output wire cout
);
    assign sum  = a ^ b ^ cin;
    assign cout = (a & b) | (b & cin) | (a & cin);
endmodule

// Testbench - exercises all 8 input combinations
module tb_full_adder;
    reg  a, b, cin;
    wire sum, cout;
    integer i;

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

    initial begin
        $display(" a b cin | cout sum | expected");
        $display("---------+----------+---------");
        for (i = 0; i < 8; i = i + 1) begin
            {a, b, cin} = i[2:0];
            #1;
            $display(" %b %b  %b  |  %b   %b  |   %0d",
                     a, b, cin, cout, sum, (a + b + cin));
        end
        $display("All 8 input combinations verified.");
        $finish;
    end
endmodule
a b cin | cout sum | expected
---------+----------+---------
 0 0  0  |  0   0  |   0
 0 0  1  |  0   1  |   1
 0 1  0  |  0   1  |   1
 0 1  1  |  1   0  |   0
 1 0  0  |  0   1  |   1
 1 0  1  |  1   0  |   0
 1 1  0  |  1   0  |   0
 1 1  1  |  1   1  |   1
All 8 input combinations verified.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpdg_x13l8/full_adder_dataflow.v:34: $finish called at 8 (1s)

Reading it line by line:

  • The module header lists the three 1-bit inputs and two 1-bit outputs. Operators ^ (XOR), & (AND), and | (OR) are bit-wise.
  • assign sum = a ^ b ^ cin; is a pure combinational net — no clock, no latch.
  • In the testbench, {a, b, cin} = i[2:0]; packs the loop counter into the three inputs, so one for loop sweeps 000 through 111.
  • The expected column prints a + b + cin as a plain integer (0, 1, or 2); compare it against the {cout, sum} pair to confirm correctness.

2. Behavioral style

Behavioral modelling describes what the hardware does inside a procedural always block rather than wiring gates. Here a single arithmetic add captures both outputs at once, and this testbench is self-checking: it compares the result against an expected value and prints a mismatch if the logic is ever wrong.

// Full adder - behavioral style
module full_adder_behavioral (
    input  wire a,
    input  wire b,
    input  wire cin,
    output reg  sum,
    output reg  cout
);
    always @(*) begin
        {cout, sum} = a + b + cin;
    end
endmodule

// Testbench - checks all 8 cases and flags mismatches
module tb_full_adder;
    reg  a, b, cin;
    wire sum, cout;
    integer i;
    reg [1:0] expected;

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

    initial begin
        $display("Time  a b cin | cout sum");
        $monitor("%4t  %b %b  %b  |  %b   %b", $time, a, b, cin, cout, sum);
        for (i = 0; i < 8; i = i + 1) begin
            {a, b, cin} = i[2:0];
            #5;
            expected = a + b + cin;
            if ({cout, sum} !== expected)
                $display("  MISMATCH at a=%b b=%b cin=%b", a, b, cin);
        end
        $display("Done: behavioral full adder matched all 8 cases.");
        $finish;
    end
endmodule
Time  a b cin | cout sum
   0  0 0  0  |  0   0
   5  0 0  1  |  0   1
  10  0 1  0  |  0   1
  15  0 1  1  |  1   0
  20  1 0  0  |  0   1
  25  1 0  1  |  1   0
  30  1 1  0  |  1   0
  35  1 1  1  |  1   1
Done: behavioral full adder matched all 8 cases.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmp3jupy2os/full_adder_behavioral.v:37: $finish called at 40 (1s)

Key points:

  • Outputs are declared reg because they are assigned inside an always block. This does not make them flip-flops — with always @(*) (combinational sensitivity) they stay pure logic.
  • {cout, sum} = a + b + cin; concatenates the two outputs into a 2-bit bus. Adding three 1-bit values gives a result from 0 to 2, which fits in 2 bits: the top bit is the carry, the lower bit is the sum.
  • $monitor prints a line automatically whenever a watched signal changes, so you see the waveform as text.
  • The !== check uses the case-equality operator, which also catches x/z values that a plain != would miss.

3. Structural style (two half adders)

Structural modelling instantiates smaller modules and wires them together, exactly like a schematic. A full adder is classically built from two half adders and one OR gate. A half adder computes sum = a XOR b and carry = a AND b.

// Half adder building block
module half_adder (
    input  wire a,
    input  wire b,
    output wire sum,
    output wire carry
);
    assign sum   = a ^ b;
    assign carry = a & b;
endmodule

// Full adder - structural style (two half adders + OR gate)
module full_adder_structural (
    input  wire a,
    input  wire b,
    input  wire cin,
    output wire sum,
    output wire cout
);
    wire ha1_sum, ha1_carry, ha2_carry;

    half_adder ha1 (.a(a),       .b(b),   .sum(ha1_sum), .carry(ha1_carry));
    half_adder ha2 (.a(ha1_sum), .b(cin), .sum(sum),     .carry(ha2_carry));

    assign cout = ha1_carry | ha2_carry;
endmodule

// Testbench - drives all 8 combinations
module tb_full_adder;
    reg  a, b, cin;
    wire sum, cout;
    integer i;

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

    initial begin
        $display(" a b cin | cout sum");
        $display("---------+---------");
        for (i = 0; i < 8; i = i + 1) begin
            {a, b, cin} = i[2:0];
            #2;
            $display(" %b %b  %b  |  %b   %b", a, b, cin, cout, sum);
        end
        $display("Structural (2x half adder) full adder: 8/8 combinations done.");
        $finish;
    end
endmodule
a b cin | cout sum
---------+---------
 0 0  0  |  0   0
 0 0  1  |  0   1
 0 1  0  |  0   1
 0 1  1  |  1   0
 1 0  0  |  0   1
 1 0  1  |  1   0
 1 1  0  |  1   0
 1 1  1  |  1   1
Structural (2x half adder) full adder: 8/8 combinations done.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpp6ro7vhc/full_adder_structural.v:48: $finish called at 16 (1s)

How the wiring works:

  • ha1 adds a and b, giving a partial sum (ha1_sum) and a carry.
  • ha2 adds that partial sum to cin, producing the final sum and a second carry.
  • A full adder carries out if either half adder carried, so cout = ha1_carry | ha2_carry.
  • The .port(signal) form is named port connection — always prefer it over positional order, which is easy to get wrong.

How to run it

Save any one block to a .v file and run it with the free Icarus Verilog toolchain:

iverilog -g2005 -o sim full_adder_dataflow.v
vvp sim

Comparing the three styles

StyleHow it is writtenBest when
Dataflowassign with Boolean operatorsYou know the logic equations and want a compact, synthesizable description
Behavioralalways block with arithmetic/if-elseLogic is complex or arithmetic; readability matters most
StructuralInstantiate & wire sub-modulesBuilding hierarchy or reusing proven blocks like a half adder

All three synthesize to the same gate-level circuit. The choice is about clarity and reuse, not the final hardware.

Common mistakes

  • Using reg for dataflow outputs. Continuous assign targets must be wire. Only signals written inside an always/initial block are reg.
  • Confusing reg with a flip-flop. reg is just a variable. With always @(*) it stays combinational; a flip-flop appears only with an edge-sensitive list like always @(posedge clk).
  • Mixing up XOR and OR for the sum. The sum uses XOR (^), not OR. OR would wrongly output 1 for the 1+1 case where the sum bit is 0.
  • Forgetting a delay in the testbench loop. Without #1 (or similar) between stimulus and $display, you print before the combinational outputs settle.
  • Positional port mismatches in structural code. If you instantiate half_adder ha1 (a, b, sum, carry); and later reorder the module ports, the connections silently break. Named ports avoid this.
  • Dropping the carry-out. sum = a + b + cin into a 1-bit sum truncates the carry. Capture both with a 2-bit target: {cout, sum}.

Quick check

What is sum when a=1, b=1, cin=1?

sum = 1 ^ 1 ^ 1 = 1 and cout = 1, so the outputs are cout=1, sum=1 (decimal 3). Three 1s add to 3, which is binary 11.

Why are the dataflow outputs declared wire but the behavioral ones reg?

Continuous assign statements drive wire nets. Signals assigned inside a procedural always block must be variables, which in Verilog-2005 means reg. Neither choice changes whether the logic is combinational.

How many half adders and gates make a full adder?

Two half adders plus one OR gate. The first half adder adds a and b; the second adds that partial sum to cin; the OR combines the two carries into cout.

Why does the testbench loop run exactly 8 times?

Three 1-bit inputs give 2^3 = 8 possible combinations. Sweeping the counter from 0 to 7 and assigning {a, b, cin} covers the entire truth table, which is exhaustive verification for this circuit.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.

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