Modules Inside Modules
A Verilog design is a tree of modules. The top-level module (your testbench, or a chip's top-level wrapper) instantiates lower-level modules, which instantiate even lower-level modules, all the way down to vendor-supplied gate primitives. Instantiation is the syntax for that nesting.
You've already seen the shape - we used it in Your First Module:
and_gate dut(.a(a), .b(b), .y(y));
That line stamps out a single instance of and_gate, names it dut, and connects its ports to local signals. Let's unpack each piece.
The Shape of an Instantiation
module_name instance_name (port_connections);
module_namemust match the name from amoduledeclaration somewhere in your project. Verilog is case-sensitive.instance_nameis a label you choose - usually descriptive of the role this instance plays. You'll use it in hierarchical paths and waveform views.port_connectionswire the instance's ports to local signals. There are two ways to spell this.
Named Port Connections (Use These)
The named form looks like:
my_module instance_name(
.clk (clk),
.reset (reset_n),
.data_in(in_bus),
.data_out(out_bus),
.valid (out_valid)
);
Each .port(signal) pair says "connect this instance's port called port to the local signal called signal". Order doesn't matter. If you add a new port to the module's declaration, existing instantiations don't break as long as you give the new port a default or update each site.
Two practical notes:
- The port name (left of the parens) must exactly match the module's declaration.
- The signal name (inside the parens) is local to wherever the instantiation lives - usually the parent module.
If a port is unconnected, leave the inside empty: .optional_port(). The signal floats (z) inside the instance. A few synthesis tools warn; most accept it.
Positional Port Connections (Avoid These)
The terser form lists signals in port-list order:
my_module instance_name(clk, reset_n, in_bus, out_bus, out_valid);
This is shorter but fragile. Reorder the module's port list (a real refactor that happens) and every positional instantiation silently miswires. Don't reach for positional unless the port list has exactly one or two members and is unlikely to ever change.
Where it's still acceptable: tiny utility modules where the port order is part of the API. A two-input gate is fine positional. A 30-port memory controller is asking for trouble.
A Complete Hierarchical Example
That's an actual three-level hierarchy: test → full_adder → two half_adder instances. Each instance has its own copy of the gates inside half_adder; the synthesis tool will emit one circuit per instantiation.
Multiple Instances of the Same Module
When you instantiate the same module several times, each instance is independent hardware. They don't share state. They don't share gates. Picture each instance as a fresh copy stamped out of the design.
adder add0(.a(a0), .b(b0), .sum(s0));
adder add1(.a(a1), .b(b1), .sum(s1));
adder add2(.a(a2), .b(b2), .sum(s2));
adder add3(.a(a3), .b(b3), .sum(s3));
That's four separate adders running in parallel. If adder contained a register, each instance would have its own copy of that register, with its own state.
generate Loops: Stamping Out Repeated Hardware
Writing four instances by hand is fine. Writing 64 is tedious. The generate block lets the elaborator do the typing for you:
Three pieces of new syntax:
genvar ideclares a loop variable usable ingenerate. It's not a runtime signal - it only exists at elaboration time.generate ... endgeneratewraps the loop. Some tools accept generate loops without the explicitgeneratekeyword, but writing it makes intent obvious.begin : invert_looplabels the generate scope. The label becomes part of the hierarchical name of each generated instance (dut.invert_loop[0].u_inv,dut.invert_loop[1].u_inv, etc.).
The synthesizer unrolls the loop and produces WIDTH copies of bit_inverter. Each copy is independent hardware.
Parameter Overrides at Instantiation
If the module has parameters, you can override them with #(.PARAM(value)) between the module name and the instance name:
counter #(.WIDTH(16)) c16 (.clk(clk), .count(out16));
counter #(.WIDTH(32)) c32 (.clk(clk), .count(out32));
Both instances use the same counter source but have different widths. We covered the syntax in Parameters; it slots cleanly into instantiation.
Hierarchical Names
Once you have a hierarchy, every signal has a hierarchical path:
test.dut.ha0.sum
That reads as: in the test module, inside the dut instance, inside the ha0 instance, the signal named sum. You'll see these paths in waveform viewers, error messages, and the occasional $display call that pokes deep into a sub-module from a testbench:
$display("internal carry1 = %b", dut.carry1);
Hierarchical references like that are only for testbenches and debug - synthesizable RTL doesn't reach into other modules.
Common Mistakes
Port name mismatch. .clk_in(clk) connects local clk to a port called clk_in. If the module's port is actually clk, the parser will tell you (some tools more clearly than others).
Width mismatch on a port. Connecting a 4-bit signal to an 8-bit port silently zero-extends; the reverse silently truncates. Most tools warn; if you don't see warnings, look harder.
Forgetting the parameter #. counter (.WIDTH(8)) c(.clk(clk)) looks like an override but it's not - the parser tries to treat (.WIDTH(8)) as a port connection and fails. Correct: counter #(.WIDTH(8)) c(.clk(clk)).
Reusing an instance name. Two instances can't have the same name in the same scope. The error message is usually clear; the temptation to copy-paste is what gets you.
What Comes Next
You can now wire modules together into a real hierarchy. The next doc rounds out the structural side of Verilog - Continuous Assignment - and goes deeper on the assign statement we've been using freely since the first chapter.
Frequently Asked Questions
How do you instantiate a module in Verilog?
Write the module name, then an instance name, then a parenthesized list of port connections: my_module instance_name(.port(signal), ...);. The most common style uses named connections (.port(signal)), which match by port name regardless of order. The terser positional style (my_module instance(signal1, signal2)) depends on the port list order and is dangerous to maintain.
What is the difference between named and positional port connections?
Positional connections list signals in the same order as the module's port list - the first signal connects to the first port, second to second, and so on. Named connections use .port_name(signal_name), matching by name. Named is verbose but immune to port reordering and self-documents at the call site. Use named for anything past two or three ports.
Can you instantiate the same Verilog module multiple times?
Yes - that's the whole point. Each instance is independent hardware with its own state. If you have an adder module, you can instantiate it 64 times in a SIMD unit, each with different inputs. The generate loop is the canonical syntax when the instances are similar and indexed.
What is a generate block in Verilog?
generate ... endgenerate is a compile-time construct that stamps out repeated hardware. A for loop inside generate creates N instances of whatever's in the body. generate runs at elaboration time, before simulation starts - it's not a runtime loop, it's a code generator for the synthesizer.