Modules, ports, and hierarchy
Declare an interface, choose between wire and reg, then connect several modules cleanly.
The role of a module
A module is the basic building block of Verilog. It can represent a small function, such as a comparator, or the top level of a complete FPGA.
module channel_guard (
input wire i_valid,
input wire i_ready,
output wire o_fault
);
assign o_fault = i_valid & ~i_ready;
endmoduleThe declaration inside the parentheses gives the name, direction, and type of each port. This ANSI-style form is available in Verilog-2001.
Inputs, outputs, and bidirectional ports
A port can have three directions:
| Direction | Role |
|---|---|
input | value received by the module |
output | value produced by the module |
inout | bidirectional connection |
inout is mainly useful at physical pins, for example for a truly tri-stated bus. Inside an FPGA, it is usually better to separate the input, output, and enable paths.
Understanding wire and reg
A wire represents a net driven by a continuous assignment, a primitive, or another module's output. It does not store a value by itself.
A reg is a variable that can be assigned in an always or initial procedural block. Despite its name, reg does not automatically mean a physical register. A reg assigned in a complete combinational block becomes combinational logic.
module threshold_flag (
input wire [7:0] i_level,
output reg o_high
);
always @* begin
o_high = 1'b0;
if (i_level >= 8'd200)
o_high = 1'b1;
end
endmoduleHere, o_high must be declared as reg because it is assigned in always. The block assigns a value in every case, so it does not require storage.
Instantiating a module
A module definition describes a type of block. An instance is one concrete use of that block inside another module.
module dual_channel_status (
input wire [1:0] i_valid,
input wire [1:0] i_ready,
output wire [1:0] o_fault,
output wire o_any_fault
);
wire w_fault_0;
wire w_fault_1;
channel_guard u_guard_0 (
.i_valid(i_valid[0]),
.i_ready(i_ready[0]),
.o_fault(w_fault_0)
);
channel_guard u_guard_1 (
.i_valid(i_valid[1]),
.i_ready(i_ready[1]),
.o_fault(w_fault_1)
);
assign o_fault =
u_guard_0 and u_guard_1 are two distinct circuits built from the same module. Each one has its own connections.
Connecting ports by name
The .port_name(signal) notation connects a port by name. It stays readable if the port order changes and reduces mistakes in modules that have several signals with the same width.
A positional connection is also possible:
channel_guard u_guard_0 (i_valid[0], i_ready[0], w_fault_0);It is shorter, but swapping two positions can go unnoticed. Named connections are preferred for the RTL on this site.
Drivers and conflicts
An RTL signal should usually have a single driver. Two assign statements driving the same wire can create a conflict and an unknown value in simulation.
Tri-state buses are a special case. Inside an FPGA, internal tri-states are often replaced by logic. It is therefore simpler to select the source explicitly with a multiplexer.
Key points
- A module defines an interface and a circuit.
- A
wireis driven continuously; aregis assigned in a procedural block. - The word
regdoes not guarantee that a flip-flop will be inferred. - An instance is a hardware copy of a module.
- Named connections make a hierarchy easier to review.
📝 Test your knowledge - Chapter quiz