Half Adder Verilog Code: Dataflow and Gate-Level

A half adder is the smallest building block of binary arithmetic: it takes two single-bit inputs, a and b, and produces a sum and a carry. This tutorial gives you working half adder Verilog code in two styles you will actually be asked to write in labs and interviews — dataflow (continuous assignments) and gate-level (built-in primitives) — each with a self-checking testbench that sweeps all four input combinations. Everything below was compiled and simulated with Icarus Verilog, so you can copy, run, and see the exact output.

The logic behind a half adder

Adding two bits has only four possibilities. Write them out and two familiar gates fall straight out of the table:

absumcarry
0000
0110
1010
1101

The sum column is exactly a XOR of the inputs, and the carry column is exactly an AND. So the whole device collapses to two equations: sum = a ^ b and carry = a & b. The only thing that changes between the versions below is how you tell Verilog to build those two gates.

Half adder Verilog code — dataflow style

Dataflow modelling uses the assign keyword to describe the circuit as Boolean expressions on the wires. It is the most compact and readable way to capture a half adder.

// half_adder_dataflow.v
// Half adder described with continuous (dataflow) assignments.
module half_adder_dataflow (
    input  wire a,      // first input bit
    input  wire b,      // second input bit
    output wire sum,    // sum output  = a XOR b
    output wire carry   // carry output = a AND b
);
    assign sum   = a ^ b;   // XOR gives the sum bit
    assign carry = a & b;   // AND gives the carry bit
endmodule

// Testbench: drives all 4 input combinations and prints a truth table.
module half_adder_dataflow_tb;
    reg  a, b;              // stimulus (reg: assigned in procedural block)
    wire sum, carry;        // observed outputs (wire: driven by the DUT)
    integer i;              // loop counter

    // Instantiate the Device Under Test (DUT) by named port connection.
    half_adder_dataflow dut (
        .a(a), .b(b), .sum(sum), .carry(carry)
    );

    initial begin
        $display("A B | SUM CARRY");
        $display("----+----------");
        for (i = 0; i < 4; i = i + 1) begin
            {a, b} = i[1:0];        // unpack the 2 input bits
            #10;                    // let combinational logic settle
            $display("%b %b |  %b    %b", a, b, sum, carry);
        end
        $display("Done. SUM=A^B, CARRY=A&B verified for all 4 cases.");
        $finish;
    end
endmodule
A B | SUM CARRY
----+----------
0 0 |  0    0
0 1 |  1    0
1 0 |  1    0
1 1 |  0    1
Done. SUM=A^B, CARRY=A&B verified for all 4 cases.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpj7u5klbo/half_adder_dataflow.v:33: $finish called at 40 (1s)

How the dataflow code works

  • module half_adder_dataflow (...) declares the design and its ports. Inputs and outputs are wire because nothing procedural drives them — they are pure combinational nets.
  • assign sum = a ^ b; is a continuous assignment. The right-hand side is re-evaluated whenever a or b changes, so sum always tracks the XOR instantly. ^ is the bitwise-XOR operator.
  • assign carry = a & b; does the same for the AND carry using the & operator.
  • In the testbench, a and b are reg because they are assigned inside an initial block, while sum and carry stay wire because the DUT drives them.
  • {a, b} = i[1:0]; uses concatenation to load both input bits from the loop counter, so i = 0,1,2,3 walks through every row of the truth table.
  • #10; waits 10 time units before sampling, giving the combinational output a moment to settle before $display prints the result.

Half adder Verilog code — gate-level style

Gate-level (structural) modelling wires up Verilog’s built-in primitives by hand. It maps one-to-one onto a schematic, which makes it the clearest way to see the two gates in the design.

// half_adder_gate.v
// Half adder built from Verilog built-in gate primitives (structural / gate-level).
module half_adder_gate (
    input  wire a,      // first input bit
    input  wire b,      // second input bit
    output wire sum,    // sum output
    output wire carry   // carry output
);
    // gate_primitive instance_name (output, input1, input2);
    xor g1 (sum,   a, b);   // sum   = a XOR b
    and g2 (carry, a, b);   // carry = a AND b
endmodule

// Testbench: $monitor logs every change across the 4 input combinations.
module half_adder_gate_tb;
    reg  a, b;
    wire sum, carry;

    half_adder_gate dut (.a(a), .b(b), .sum(sum), .carry(carry));

    initial begin
        // $monitor prints one line whenever any listed signal changes.
        $monitor("t=%0t | a=%b b=%b -> sum=%b carry=%b",
                 $time, a, b, sum, carry);
        a = 0; b = 0; #10;
        a = 0; b = 1; #10;
        a = 1; b = 0; #10;
        a = 1; b = 1; #10;
        $display("All 4 combinations applied to the gate-level half adder.");
        $finish;
    end
endmodule
t=0 | a=0 b=0 -> sum=0 carry=0
t=10 | a=0 b=1 -> sum=1 carry=0
t=20 | a=1 b=0 -> sum=1 carry=0
t=30 | a=1 b=1 -> sum=0 carry=1
All 4 combinations applied to the gate-level half adder.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpjdls8i71/half_adder_gate.v:30: $finish called at 40 (1s)

How the gate-level code works

  • xor g1 (sum, a, b); instantiates a built-in XOR gate. The syntax is fixed: the first port in the list is the output, every port after it is an input. g1 is just the instance name.
  • and g2 (carry, a, b); instantiates the AND gate that forms the carry, following the same output-first convention.
  • Because primitives are inherently combinational, the outputs stay wire and no assign is needed — the gates themselves drive the nets.
  • The testbench swaps the for loop for four explicit stimulus lines and uses $monitor, which automatically reprints whenever any watched signal changes, giving you a time-stamped trace instead of a static table.
  • $time with %0t prints the current simulation time so you can see each input change land at t = 0, 10, 20 and 30.

Both modules are logically identical; the dataflow version is what you would normally write for synthesis, while the gate-level version documents the exact structure.

Common mistakes to avoid

  • Declaring testbench inputs as wire. Signals you assign inside initial/always must be reg. Leaving a and b as wire is one of the most frequent compile errors beginners hit.
  • Confusing bitwise and logical operators. Use ^ and & (bitwise), not && (logical AND). For single bits && may look right, but it returns a 1-bit truth value, not a gate — a habit that breaks the moment your signals become vectors.
  • Getting primitive port order wrong. In xor g1 (sum, a, b); the output is always listed first. Writing xor g1 (a, b, sum); silently builds the wrong circuit.
  • Sampling outputs with no delay. If you print immediately after changing a or b without a # delay, you read stale values. Always let combinational logic settle before $display.
  • Forgetting $finish. A testbench that uses $monitor but never calls $finish can hang; always terminate the run.
  • Treating the half adder as a full adder. A half adder has no carry-in. If a problem gives you three inputs, you need a full adder instead.

Quick check

Which two gates fully describe a half adder?

An XOR gate produces the sum (a ^ b) and an AND gate produces the carry (a & b). No other gates are required.

Why are a and b declared as reg in the testbench but wire in the module?

Inside the testbench they are assigned procedurally in an initial block, which requires reg. Inside the DUT they are inputs driven from outside, so they are nets (wire).

In xor g1 (sum, a, b);, which terminal is the output?

The first one, sum. For all Verilog gate primitives the output port is listed first, followed by the inputs.

What is the key difference between a half adder and a full adder?

A half adder adds only two bits and has no carry-in. A full adder adds three bits (a, b, and a carry-in), so it needs extra logic to combine the two partial carries.

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