Every time a processor adds two numbers, a tiny circuit called the full adder is doing the work. It is one of the first circuits you learn in digital design, and it is the building block of the adders and ALUs inside modern CPUs.
In this post, I design a 1-bit full adder using structural modeling in Verilog, built from two half adders and one OR gate. I verify it with a testbench and timing diagram, then implement it on the Digilent Nexys A7 (Artix-7) FPGA board. If you are new to Verilog or FPGAs, you can follow along step by step and reproduce the result on your own board.
What you will learn:
- How a full adder works (truth table and Boolean equations)
- How a full adder is built from two half adders and an OR gate
- What structural modeling is and why it is useful
- How to write the design, testbench and constraint file
- How to read a timing diagram
- How to program the Nexys A7 and verify the result on real hardware
Tools used:
- Xilinx Vivado 2018.2
- Digilent Nexys A7 100T, Artix-7
- Verilog HDL
Source code: https://github.com/UmaimaKhan-13/full-adder-nexys-a7
1. What Is a Full Adder?
A full adder adds three 1-bit inputs and produces a sum and a carry-out.
The full adder has three inputs and two outputs. A and B are the two bits to be added, and Cin is the carry coming in from a previous stage. The circuit produces Sum, which is the result bit, and Cout, which is the carry going out to the next stage.
A half adder has no carry input, so it can only add two bits. The full adder's Cin lets it be chained to add multi-bit numbers.
A useful way to read the output: the result is a 2-bit number {Cout, Sum}. Since the largest possible sum is 1 + 1 + 1 = 3, the result always fits in two bits (00 to 11). For example, when A = 1, B = 1, Cin = 1, the output is 11 in binary, which is 3.
Truth Table
| A | B | Cin | Sum | Cout | Decimal result |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 0 | 1 |
| 0 | 1 | 0 | 1 | 0 | 1 |
| 0 | 1 | 1 | 0 | 1 | 2 |
| 1 | 0 | 0 | 1 | 0 | 1 |
| 1 | 0 | 1 | 0 | 1 | 2 |
| 1 | 1 | 0 | 0 | 1 | 2 |
| 1 | 1 | 1 | 1 | 1 | 3 |
Boolean Equations
Sum = A ⊕ B ⊕ Cin
Cout = (A · B) + (Cin · (A ⊕ B))
2. Building a Full Adder from Two Half Adders
A half adder adds two bits:
Sum = A ⊕ B
Carry = A · B
A full adder is made by connecting two half adders and an OR gate:
-
Half Adder 1 adds
AandB, givingS1 = A ⊕ BandC1 = A · B. -
Half Adder 2 adds
S1andCin, givingSum = S1 ⊕ CinandC2 = S1 · Cin. - An OR gate combines the two carries:
Cout = C1 + C2.
You can check this against the equation above: C1 + C2 = A·B + (A⊕B)·Cin, which is exactly the full adder's Cout.
3. What Is Structural Modeling?
In structural modeling, the code mirrors the schematic. In this design, the full adder is built by instantiating two half adders and combining their carries with an OR operation, with internal wires connecting everything together.
Why use structural modeling?
- The code reads like the circuit diagram, so it is easy to understand.
- It encourages modular, reusable design: the half adder is written once and used twice.
- Each module can be tested separately, which makes debugging easier.
- It teaches how bigger systems (adders, ALUs, processors) are assembled from small blocks.
4. Verilog Design Code
The design has two modules: half_adder, and full_adder, which instantiates the half adder twice and adds an OR gate.
half_adder.v
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.v
module full_adder (
input wire a,
input wire b,
input wire cin,
output wire sum,
output wire cout
);
wire s1, c1, c2;
half_adder HA1 (
.a(a),
.b(b),
.sum(s1),
.carry(c1)
);
half_adder HA2 (
.a(s1),
.b(cin),
.sum(sum),
.carry(c2)
);
assign cout = c1 | c2;
endmodule
Code walkthrough:
-
half_adder: takes two 1-bit inputs,aandb. The sum output is their XOR (a ^ b) and the carry output is their AND (a & b). -
full_adder: instantiateshalf_addertwice.HA1addsaandb, producing the partial sums1and the first carryc1.HA2addss1andcin, producing the finalsumoutput and the second carryc2. The internal wiress1,c1andc2carry these intermediate values. -
Carry-out: a carry is generated if either half adder produced one, so
cout = c1 | c2. This is the OR gate from the block diagram.
5. Testbench
A testbench is a Verilog module with no ports. It applies inputs to the design and lets us observe the outputs in simulation. Because the full adder has only 3 inputs, I test all 2³ = 8 combinations (exhaustive testing), so every row of the truth table is verified.
tb_full_adder.v
`timescale 1ns / 1ps
module tb_full_adder;
reg a;
reg b;
reg cin;
wire sum;
wire cout;
full_adder uut (
.a(a),
.b(b),
.cin(cin),
.sum(sum),
.cout(cout)
);
integer i;
initial begin
for (i = 0; i < 8; i = i + 1) begin
{a, b, cin} = i[2:0];
#10;
end
$finish;
end
endmodule
How it works:
- The
timescaledirective at the top sets the time unit to 1 ns and the precision to 1 ps. - The inputs
a,bandcinare declared asreg, because the testbench drives them. The outputssumandcoutarewire. - The design under test is instantiated as
uut(unit under test). - A
forloop countsifrom 0 to 7. Each pass assigns the 3 lowest bits ofito{a, b, cin}, so the inputs step through 000, 001, 010 ... 111, the same order as the truth table. -
#10holds each combination for 10 ns, so the whole test takes 80 ns. -
$finishends the simulation after the last combination.
6. Simulation Results and Timing Diagram
I ran the behavioral simulation in Vivado and got the following waveform.
Behavioral simulation of the full adder: all 8 input combinations, each held for 10 ns.
How to read it:
- The top three signals (
a,b,cin) are inputs. They change every 10 ns, and the simulation runs for 80 ns in total. -
sumandcoutrespond to each input change. - Each time step corresponds to one row of the truth table.
| Time | a | b | cin | sum | cout | Result |
|---|---|---|---|---|---|---|
| 0-10 ns | 0 | 0 | 0 | 0 | 0 | 0 |
| 10-20 ns | 0 | 0 | 1 | 1 | 0 | 1 |
| 20-30 ns | 0 | 1 | 0 | 1 | 0 | 1 |
| 30-40 ns | 0 | 1 | 1 | 0 | 1 | 2 |
| 40-50 ns | 1 | 0 | 0 | 1 | 0 | 1 |
| 50-60 ns | 1 | 0 | 1 | 0 | 1 | 2 |
| 60-70 ns | 1 | 1 | 0 | 0 | 1 | 2 |
| 70-80 ns | 1 | 1 | 1 | 1 | 1 | 3 |
Verification: I compared every time step with the truth table in Section 1, and all 8 combinations matched.
Note: Behavioral simulation assumes zero gate delay. In real hardware, signals take a few nanoseconds to propagate through the logic. That delay shows up in post-implementation timing simulation.
7. Mapping to the Nexys A7: Pin Assignment
To test the design on the board, I connected the inputs to slide switches and the outputs to LEDs.
| Port | Board Component | FPGA Pin |
|---|---|---|
| a | SW0 | J15 |
| b | SW1 | L16 |
| cin | SW2 | M13 |
| sum | LED0 | H17 |
| cout | LED1 | K15 |
These pins come from Digilent's master XDC for the Nexys A7.
8. Constraint File (XDC)
The constraint file tells Vivado which physical FPGA pin each port of your design connects to. Without it, Vivado does not know how to connect your design to the board's switches and LEDs.
full_adder.xdc
## Inputs (Switches: SW0, SW1, SW2)
set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports {a}]
set_property -dict { PACKAGE_PIN L16 IOSTANDARD LVCMOS33 } [get_ports {b}]
set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 } [get_ports {cin}]
## Outputs (LEDs: LED0, LED1)
set_property -dict { PACKAGE_PIN H17 IOSTANDARD LVCMOS33 } [get_ports {sum}]
set_property -dict { PACKAGE_PIN K15 IOSTANDARD LVCMOS33 } [get_ports {cout}]
Key points:
-
PACKAGE_PINmaps a port to a physical FPGA pin. -
IOSTANDARD LVCMOS33sets the 3.3 V I/O standard used on this board. - Port names in the XDC (
a,b,cin,sum,cout) must exactly match the port names of the top-level modulefull_adder, including capitalization.
9. Implementation Flow in Vivado
-
Create a project and select the board part:
xc7a100tcsg324-1for the Nexys A7-100T, orxc7a50tcsg324-1for the A7-50T. - Add the design sources (Verilog files) and the constraint file.
-
Set the top module to
full_adder. - Run simulation with the testbench to verify the logic.
- Run Synthesis, which converts the Verilog into logic elements.
- Run Implementation, which places and routes the design on the FPGA.
-
Generate the Bitstream, the
.bitfile that configures the FPGA. - Open Hardware Manager, connect the board, and click Program Device.
10. Hardware Demonstration
After programming the board, I set the switches and observed the LEDs.
| SW2 (Cin) | SW1 (B) | SW0 (A) | LED0 (Sum) | LED1 (Cout) |
|---|---|---|---|---|
| 0 | 0 | 0 | OFF | OFF |
| 0 | 0 | 1 | ON | OFF |
| 0 | 1 | 0 | ON | OFF |
| 0 | 1 | 1 | OFF | ON |
| 1 | 0 | 0 | ON | OFF |
| 1 | 0 | 1 | OFF | ON |
| 1 | 1 | 0 | OFF | ON |
| 1 | 1 | 1 | ON | ON |
The LED behavior matches both the truth table and the simulation, so the design works correctly on real hardware.
11. Common Mistakes and Troubleshooting
- LEDs not responding: Check that the port names in the XDC match your top module exactly.
- "Unconstrained ports" warning: Some ports are missing from the XDC.
-
Wrong top module: Make sure
full_adder, nothalf_adder, is set as the top module. - Board not detected: Check the USB cable and drivers, make sure the board is powered on, and check that the JP1 jumper is set for USB/JTAG programming.
-
Simulation shows
XorZ: An input was not initialized in the testbench, or a wire is left unconnected.
12. Applications
- Building block of ripple-carry and carry-lookahead adders
- Arithmetic Logic Units (ALUs) in processors
- Digital signal processing circuits
- Counters, address calculation and multipliers
Conclusion
In this project, I designed a full adder using structural modeling by connecting two half adders and an OR gate, verified it with an exhaustive testbench and timing diagram, and implemented it on the Nexys A7 FPGA. The hardware output matched the simulation for all 8 input combinations.
Even a circuit this small covers the entire FPGA design flow: design → simulate → constrain → synthesize → implement → program → verify. Once this flow is clear, scaling up to larger designs becomes much easier.
If this post helped you, feel free to leave a comment or question below.




Top comments (0)