1. The full adder is the building block
A full adder adds three bits, a, b and a carry in cin, and produces a sum bit and a carry out.
The two outputs together are the two-bit count of ones among the inputs. Operate the circuit until
you can predict both outputs for any row.
Concept experiment · full-adder
A browser model of the circuit, not a compiled design. The real toolchain runs in the examples.
Inputs
Outputs
- sum
- 0
- cout
- 0
| a | b | cin | sum | cout | current inputs |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | ◂ current inputs |
| 0 | 0 | 1 | 1 | 0 | |
| 0 | 1 | 0 | 1 | 0 | |
| 0 | 1 | 1 | 0 | 1 | |
| 1 | 0 | 0 | 1 | 0 | |
| 1 | 0 | 1 | 0 | 1 | |
| 1 | 1 | 0 | 0 | 1 | |
| 1 | 1 | 1 | 1 | 1 |
Check
a, b and cin are all 1. What are sum and cout?
FullAdder.v writes the same circuit with two assign statements. Its inputs are a, b, and
cin; its outputs are sum and cout. Four of these, chained through their carries, make a
four-bit adder, and chaining modules is what this chapter is about.
// One-bit full adder written directly with continuous assignments.
module full_adder (
input a,
input b,
input cin,
output sum,
output cout
);
assign sum = a ^ b ^ cin;
assign cout = (a & b) | (cin & (a ^ b));
endmodule2. Instances and port connections
Hierarchy is settled during elaboration. iverilog reads every module definition, builds the
instance tree from the top module down, fixes the value of every parameter, and checks every port
connection. Only then does vvp simulate. A misspelled port name is therefore reported before any
simulation time passes.
2.1 Creating an instance
A module definition is a template. Writing the template's name inside another module, followed by
a new name and a connection list, creates an instance: a copy with its own name, its own
internal signals, and its own connections. Two instances of the same definition share no state. You
saw one in every testbench so far, named dut.
Syntax
module_name instance_name (connection list);wire s1, c1, s2, c2;
half_adder ha0 (.a(x), .b(y), .sum(s1), .carry(c1));
half_adder ha1 (.a(s1), .b(z), .sum(s2), .carry(c2));| Part | Meaning |
|---|---|
half_adder | The name of the definition being instantiated |
ha0, ha1 | Instance names. They must be unique within the module |
.a(x) | Connects the child's port a to the parent's signal x |
s1 | An internal net: ha0's output drives it and ha1's input reads it |
2.2 Named port connections
The .port(signal) form matches each connection to a port by name. The order you write them in
does not matter.
Syntax
module_name instance_name (.port_name(signal), .port_name(signal));half_adder u (.carry(c), .sum(s), .b(y), .a(x)); // same wiring, any orderAn instance connected by name stays correct even if someone later reorders the ports in the definition. A name that the definition does not have is an elaboration error; section 5 shows the message.
2.3 Positional connections
If you list only signals, without port names, they are connected in the order the definition declares its ports.
Syntax
module_name instance_name (signal, signal, signal);// half_adder declares its ports as a, b, sum, carry
half_adder u (x, y, s, c);Note. A positional list in the wrong order produces no error and no warning as long as the widths fit. Use named connections for anything with more than two ports; this course connects by name throughout.
2.4 Unconnected ports
A port left out of the connection list, or written with empty parentheses as .port(), is
unconnected. Nothing drives an unconnected input, so it sits at z.
full_adder u0 (.a(a), .b(b), .cin(), .sum(s), .cout(co)); // cin is zA z input makes arithmetic and logic results undecidable, so sum, which depends on cin,
becomes x. Leaving an output unconnected is harmless; it just means you do not use that output.
3. Parameters
3.1 Declaring a parameter
A parameter gives a name to a width or constant inside a module definition. Because each instance
can override it, one definition can produce circuits of several widths.
Syntax
module module_name #(parameter NAME = default) (port list);module pass #(parameter W = 8) (
input [W-1:0] d,
output [W-1:0] q
);
localparam TOP = W - 1; // a constant used only inside the definition
assign q = d;
endmodule| Part | Meaning |
|---|---|
#(parameter W = 8) | Declares a parameter W with default value 8 |
[W-1:0] | Port widths written in terms of W. With W = 4 this is [3:0] |
localparam TOP | A constant derived from other values. An instance cannot override it |
3.2 Overriding a parameter per instance
Write #(...) between the module name and the instance name to change a parameter. The override
applies to that one instance only.
Syntax
module_name #(.NAME(value)) instance_name (connection list);
module_name #(value) instance_name (connection list);pass #(.W(4)) p4 (.d(nibble), .q(q4)); // by name: 4 bits
pass #(16) p16 (.d(word), .q(q16)); // by position: 16 bits
pass p8 (.d(data8), .q(q8)); // default: 8 bitsParameter values are fixed during elaboration, and each distinct value yields a separately sized
circuit. Average.v, one definition instantiated as average #(.W(8)) wide and
average #(.W(4)) narrow, runs in
Literals, signed values and expression width,
where the two widths decide whether a carry survives.
Note. If you try to override a localparam, as in #(.TOP(...)), Icarus does not stop with
an error. It prints the warning parameter TOP not found in ... and leaves the value unchanged.
Miss that warning and you will believe the override took effect.
4. Building hierarchy from instances
4.1 Internal nets
A parent module joins its child instances with internal nets: wires declared inside the
parent that no port of its own uses, driven by one instance's output and read by another instance's
input.
Syntax
wire internal_net;
module_a inst1 (.out_port(internal_net), ...);
module_b inst2 (.in_port(internal_net), ...);A ripple-carry adder is the classic case. Each one-bit full adder produces a carry, and that carry is the next stage's carry input.
4.2 Example: a 4-bit ripple-carry adder
Ripple4.v declares c1, c2, c3 and places four full-adder instances, bit0 to bit3.
Follow these connections as you read it; the bit selects from
Vectors, bit selects and operators pick one bit of
each 4-bit port for each stage.
| Instance | a, b inputs | cin input | cout output |
|---|---|---|---|
bit0 | a[0], b[0] | The module input cin | c1 |
bit1 | a[1], b[1] | c1 | c2 |
bit2 | a[2], b[2] | c2 | c3 |
bit3 | a[3], b[3] | c3 | The module output cout |
// Four full adders chained through their carries.
module ripple4 (
input [3:0] a,
input [3:0] b,
input cin,
output [3:0] sum,
output cout
);
// Internal nets carry each stage's carry into the next stage.
wire c1, c2, c3;
full_adder bit0 (.a(a[0]), .b(b[0]), .cin(cin), .sum(sum[0]), .cout(c1));
full_adder bit1 (.a(a[1]), .b(b[1]), .cin(c1), .sum(sum[1]), .cout(c2));
full_adder bit2 (.a(a[2]), .b(b[2]), .cin(c2), .sum(sum[2]), .cout(c3));
full_adder bit3 (.a(a[3]), .b(b[3]), .cin(c3), .sum(sum[3]), .cout(cout));
endmoduleCheck
Which net is connected to the cin port of bit2?
Check
If .cin(c2) is deleted from bit2's list and a[2] differs from b[2], what happens to bit2's cin and outputs?
Tb.v prints two directed cases, then compares all 512 combinations of the two 4-bit inputs and
the carry input against Verilog's own +. It counts disagreements instead of printing each one.
module tb;
reg [3:0] a, b;
reg cin;
wire [3:0] sum;
wire cout;
integer x, y, errors;
ripple4 dut (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
initial begin
a = 4'd9; b = 4'd5; cin = 1'b0;
#1 $display("RIPPLE 9+5+0 -> cout=%b sum=%0d", cout, sum);
a = 4'd15; b = 4'd0; cin = 1'b1;
#1 $display("RIPPLE 15+0+1 -> cout=%b sum=%0d", cout, sum);
// Compare all 512 input combinations with the + operator.
errors = 0;
for (x = 0; x < 32; x = x + 1)
for (y = 0; y < 16; y = y + 1) begin
{cin, a} = x; b = y; #1;
if ({cout, sum} !== a + b + cin) errors = errors + 1;
end
$display("EXHAUSTIVE errors=%0d", errors);
$finish;
end
endmodulePractice this concept
Use this concept in a short Verilog problem as your next step.
Predict before you run.
- What are
coutandsumfor 9 + 5 + 0? Trace the four stages:bit0adds 1 and 1,bit1adds 0, 0 and the carry, and so on. - For 15 + 0 + 1, through how many stages does the carry travel?
- How many errors does the exhaustive comparison report?
Check
What does the RIPPLE 9+5+0 line show?
Check the result. Press Run.
| Output line | What it shows |
|---|---|
RIPPLE 9+5+0 | 14 fits in four bits: cout=0, sum=14 |
RIPPLE 15+0+1 | bit0 adds 1, 0, and 1, writing sum 0 and carry 1; the other three stages do the same. sum=0, cout=1, so {cout, sum} is 16 |
EXHAUSTIVE | Zero errors: all 512 combinations agree with + |
In the comparison {cout, sum} !== a + b + cin, the left side is 5 bits, so the right-hand addition
is evaluated at 5 bits and keeps its carry. The width rules from
Literals, signed values and expression width are
what make the check sound.
4.3 Change one thing: a miswired net
In Ripple4.v, change the connection of bit2 from .cin(c2) to .cin(c1) and run again. The
design still compiles: c1 is a legal net of the right width, and c2 is now driven by bit1 but
read by nobody. Decide what 9 + 5 becomes before you look.
Check
With bit2 reading c1 instead of c2, what does RIPPLE 9+5+0 show?
A miswired internal net is the kind of defect no compiler reports: it compiles cleanly and fails on a precise subset of inputs. The exhaustive comparison finds it in one run; a handful of directed cases might not.
5. Connecting a port that does not exist
Named connections are checked during elaboration. A port name that the child's definition does not have stops the build.
The next example fails during elaboration on purpose. carry_chain connects .cn(c) to a
full_adder whose input is called cin.
module full_adder (
input a,
input b,
input cin,
output sum,
output cout
);
assign sum = a ^ b ^ cin;
assign cout = (a & b) | (cin & (a ^ b));
endmodule
module carry_chain (
input a,
input b,
input c,
output s,
output co
);
// The port is named cin, not cn.
full_adder u0 (.a(a), .b(b), .cn(c), .sum(s), .cout(co));
endmodulePredict before you run. Besides the wrong port name, what does the error message name: the
module full_adder, or the instance u0?
Check
The connection list of instance u0 names a port cn that full_adder does not have. Which name does the error message give?
Check the result.
| Item | Content |
|---|---|
| Faulty code | .cn(c) in the connection list of u0 |
| Failing stage | Elaboration in iverilog. Nothing is simulated |
| Error message | port ``cn'' is not a port of u0. |
| Fix | Use the definition's port name: .cin(c) |
The message names the instance u0, not the module. Even with many instances of the same module,
you can go straight to the connection list that is wrong.
Note. If .cn(c) is deleted instead of renamed, the design compiles without a word. The
unconnected cin floats at z, sum is always x, and cout is x whenever a and b
differ. A named connection catches typos; it does not check that every port is connected.
6. Common mistakes
6.1 Positional connections in the wrong order
// Wrong: the definition order is a, b, sum, carry
half_adder u (a, b, carry, sum);
// Right: connect by name
half_adder u (.a(a), .b(b), .sum(sum), .carry(carry));Positional connections follow the definition's port order. If the order is wrong but the widths match, there is no error or warning; two outputs simply trade values.
6.2 Leaving an input unconnected
// Wrong: cin is missing and floats at z
full_adder u0 (.a(a), .b(b), .sum(s), .cout(co));
// Right
full_adder u0 (.a(a), .b(b), .cin(c), .sum(s), .cout(co));A missing cin becomes z without any error. Here sum is always x, but cout is x only
when a and b differ; equal data bits determine the carry without cin. If an output remains
x after you set the inputs, look for a missing .port(...) in the connection list.
6.3 Connecting a name that was never declared
reg [7:0] din;
// Wrong: dinn was never declared
pass8 u (.data(dinn), .q(q));
// Right
pass8 u (.data(din), .q(q));An undeclared name in a connection list makes Verilog create a 1-bit wire of that name on the
spot. Instead of an error you get only the warning
Port 1 (data) of pass8 expects 8 bits, got 1., and q reads as seven 0 bits on top of a z in
bit 0. Putting `default_nettype none at the top of the file turns that automatic creation
off, and the same typo becomes the error Unable to bind wire/reg/memory.
6.4 Connecting a signal of the wrong width
// pass8's data port is 8 bits
reg [3:0] nib;
reg [11:0] wide12;
// Wrong: mismatched widths only produce warnings
pass8 u1 (.data(nib), .q(q1)); // upper 4 bits filled with 0
pass8 u2 (.data(wide12), .q(q2)); // upper 4 bits dropped
// Right: connect a signal as wide as the port
reg [7:0] byte_in;
pass8 u3 (.data(byte_in), .q(q3));A width mismatch on a port is not a compile error. Icarus prints a warning such as
Port 1 (data) of pass8 expects 8 bits, got 4. followed by a Padding or Pruning line, then
extends or truncates by the same rules as an assignment: an unsigned signal is extended with zeros,
a signed one with its sign bit. When the compile output mentions port widths, check the
declaration of the connected signal first.
7. Practice: two half adders into a full adder
The checkpoint is the Practice problem full-adder-wiring. A full adder is built from two instances
of a correct half_adder, but several rows of its table are wrong. Draw the two instances as boxes,
write each .port(net) on the matching pin, and follow a failing row through the drawing; one input
never reaches the second half adder. Change one connection and compare all eight rows with the count
of ones among the inputs.
8. Summary
| Element | Meaning |
|---|---|
name inst (.port(signal), …); | an instance, connected by port name; instances share no state |
| named connection | matches by name, order irrelevant; an unknown port name is an elaboration error |
| positional connection | follows the definition's order; a wrong order goes undetected |
wire c1, c2, c3; | internal nets carry one instance's output to another instance's input |
| unconnected input | floats at z; an output becomes x if the other inputs do not determine it |
parameter | declared with #(parameter W = 8), overridden per instance with #(.W(4)); a localparam cannot be overridden |
{cout, sum} !== a + b + cin | a 5-bit comparison that keeps the carry |
9. Check your understanding
-
A
half_adderdeclares its ports asa, b, sum, carry, and an instance was switched to the positional list(a, b, carry, sum). Why did it still compile?Show answer
A positional list just connects signals in the definition's order
a, b, sum, carry; it never compares names.carryandsumare both 1-bit nets, so swapping them still fits, and nothing gives the compiler a reason to complain. -
Which nets in
ripple4are internal, and which instance drives each one?Show answer
The internal nets are
c1,c2, andc3, driven by thecoutofbit0,bit1, andbit2respectively. Thecoutofbit3drives the module outputcout, not an internal net. -
What do you observe if
.cn(c)is deleted instead of corrected to.cin(c)?Show answer
The design compiles without any message. The unconnected
cinfloats atz, sosumis alwaysx, andcoutisxfor the inputs where it depends oncin.