Hierarchy: How Not to Drown in Wires
The Half Adder is elegant, but it has a limitation: only two inputs. Real addition chains through multiple bits, and each column needs to accept a carry from the column before it. That means we need a component with three inputs: bit A, bit B, and carry-in. This is called a Full Adder.
Let's derive its truth table. With three bits, there are 8 combinations:
- 0 + 0 + 0 = sum 0, carry 0
- 0 + 0 + 1 = sum 1, carry 0
- 0 + 1 + 0 = sum 1, carry 0
- 0 + 1 + 1 = sum 0, carry 1
- 1 + 0 + 0 = sum 1, carry 0
- 1 + 0 + 1 = sum 0, carry 1
- 1 + 1 + 0 = sum 0, carry 1
- 1 + 1 + 1 = sum 1, carry 1
You could build this from scratch with raw gates. But there's a more elegant approach: reuse. A Full Adder can be built from two Half Adders and a single OR gate. This is where module instantiation comes in — one of the most important concepts in hardware design.
In software, you'd call a function: result = halfAdder(a, b). In Verilog, you instantiate a module — you place a physical copy of the Half Adder circuit inside your Full Adder and wire it up:
module FullAdder(
output sum,
output c_out,
input a,
input b,
input c_in
);
wire w1, w2, w3;
HalfAdder ha1(.sum(w1), .carry(w2), .a(a), .b(b));
HalfAdder ha2(.sum(sum), .carry(w3), .a(w1), .b(c_in));
or g1(c_out, w2, w3);
endmodule
Let's trace the data flow. The first Half Adder (ha1) adds a and b. Its sum goes to w1, its carry goes to w2. The second Half Adder (ha2) adds w1 (the partial sum) and c_in (the incoming carry). Its sum becomes the final sum. Its carry goes to w3. Now, a carry-out should happen if either the first Half Adder produced a carry (a and b both 1) or the second Half Adder produced a carry (the partial sum and the incoming carry both 1). The OR gate at the end combines w2 and w3 into c_out.
Notice the syntax. When instantiating HalfAdder ha1, we use named port connections: .sum(w1) means "connect the sum port of the Half Adder to the wire called w1 in this module." The dot prefix names the port; the parentheses contain the local wire. This is explicit, readable, and order-independent — you can list the ports in any order.
(Alternatively, Verilog supports positional connections: HalfAdder ha1(w1, w2, a, b); This works but is fragile — if the port order changes, your wiring silently breaks. Named connections are safer and are the convention in professional designs.)
The beauty of module instantiation is that it mirrors how real engineers work. You build small, testable blocks (Half Adder), verify them independently, and then compose them into larger structures. The Half Adder's internal gates are hidden inside a black box; you only see its ports. This hierarchical abstraction is the hardware equivalent of functions in software — except each "function call" creates a physical copy of the circuitry on the silicon.
Level 34 ("FullAdder in Verilog") lets you build this exact circuit. When you instantiate two Half Adders and wire the OR gate, you're not just solving a puzzle — you're practicing the skill that all digital design is built on: hierarchy.