Dominar la instanciación de módulos en Verilog: Mejores prácticas para diseño jerárquico
Como vimos, diseños grandes y complejos se construyen integrando múltiples módulos jerárquicamente. Los módulos pueden instanciarse dentro de otros y los puertos de estas instancias conectarse con otras señales dentro del módulo padre.
Estas conexiones de puertos pueden hacerse vía lista ordenada o por nombre.
Conexión de puertos por lista ordenada
Un método es la lista ordenada. mydesign es un module instanciado con nombre d0 en otro módulo tb_top. Los puertos se conectan en orden determinado por su posición en la lista de puertos de la declaración del módulo. Por ejemplo, b en testbench se conecta a y del diseño porque ambos están en segunda posición.
module mydesign (
input x, y, z, // x pos 1, y pos 2, z pos 3
output o // o pos 4
);
endmodule
module tb_top;
wire [1:0] a;
wire b, c;
mydesign d0 (a[0], b, a[1], c);
// a[0] pos 1 -> conectado a x
// b pos 2 -> conectado a y
// a[1] pos 3 -> conectado a z
// c pos 4 -> conectado a o
endmodule
Es muy inconveniente porque el orden puede cambiar si se añade un puerto o si hay muchos puertos.
Conexión de puertos por nombre
Mejor forma: vincular explícitamente puertos en ambos lados usando su nombre de puerto.
El punto . indica que el nombre de puerto siguiente pertenece al diseño. El nombre de señal a conectar va entre paréntesis ( ).
module design_top;
wire [1:0] a;
wire b, c;
mydesign d0 (
.x (a[0]), // señal "x" en mydesign conectada a "a[0]" en design_top
.y (b), // señal "y" en mydesign conectada a "b" en design_top
.z (a[1]),
.o (c)
);
endmodule
Se recomienda codificar cada conexión en línea separada para que errores de compilación apunten a la línea correcta. Es más fácil depurar que si todos están en la misma línea.
Al conectarse por nombre, el orden es irrelevante. No se permiten múltiples conexiones de puertos de instancia.
module design_top;
mydesign d0 (
.x (a[0]),
.z (a[1]), // z en segunda posición OK por conexión explícita
.y (a[1]),
.x (b), // ilegal - x ya conectado a a[0]
.o (c)
);
endmodule
Puertos sin conectar / flotantes
Puertos no conectados a ningún wire en el módulo instanciador tendrán valor de alta impedancia.
module design_top;
mydesign d0 (
// x es input y no conectado, luego a[0] será Z
.y (a[1]),
.z (a[1]),
.o () // o tiene valor válido en mydesign pero no conectado a "c" en design_top, c será Z
);
endmodule
Ejemplo
Tomemos el registro de desplazamiento visto antes y dejemos algunos puertos sin conectar.
module shift_reg (
input d, input clk, input rstn, output q
);
wire [2:0] q_net;
dff u0 (.d(d), .clk(clk), .rstn(rstn), .q(q_net[0]));
dff u1 (.d(q_net[0]), .clk(clk), .rstn(rstn), .q()); // salida q flotante
dff u2 (.d(q_net[1]), .clk(clk), .rstn(rstn), .q()); // salida q flotante
dff u3 (.d(q_net[2]), .clk(clk), .rstn(rstn), .q(q));
endmodule
Las salidas de instancias u1 y u2 quedan sin conectar en el esquema RTL tras síntesis. Como las entradas d de u2 y u3 están conectadas a nets no impulsadas, se ponen a tierra.

En simulación, puertos sin conectar se denotan como alta impedancia ('hZ) típicamente mostrado en formas de onda como línea naranja vertical al centro.

Todas las declaraciones de puerto son implícitamente wire, así que la dirección basta en ese caso. Pero puertos output que necesiten almacenar valores deben declararse reg y usarse en bloques procedimentales como always e initial.
Puertos input o inout no pueden ser reg porque son impulsados desde fuera continuamente y no deben almacenar valores, sino reflejar cambios externos cuanto antes. Es legal conectar puertos con tamaños vectoriales distintos, pero prevalecerá el de menor tamaño y los bits restantes del otro se ignorarán.
// Caso #1: Inputs por defecto wire implícito
module des0_1 (input wire clk ...); // wire no necesario
module des0_2 (input clk, ...); // por defecto clk es wire
// Caso #2: Inputs no pueden ser reg
module des1 (input reg clk, ...); // Ilegal: inputs no pueden ser reg
// Caso #3: Módulos con anchos de puerto variables
module des2 (output [3:0] data, ...); // declaración con vector 4-bit output
module des3 (input [7:0] data, ...); // declaración con vector 8-bit input
module top ( ... );
wire [7:0] net;
des2 u0 ( .data(net) ... ); // 4 bits superiores de net sin impulsar
des3 u1 ( .data(net) ... );
endmodule
// Caso #4: Outputs no pueden conectarse a reg en módulo padre
module top_0 ( ... );
reg [3:0] data_reg;
des2 ( .data(data) ...); // Ilegal: puerto output conectado a reg "data_reg"
endmodule
Verilog
- Dominar Verilog: Tutorial completo para diseño de circuitos digitales
- Dominar la concatenación en Verilog: Guía práctica
- Verilog Esencial: Resumen conciso
- Comprender las asignaciones en Verilog: tipos, sintaxis y mejores prácticas
- Bloqueante vs. No bloqueante en Verilog: Diferencias clave explicadas
- Dominar las funciones en Verilog para diseño RTL eficiente
- Comprender las Tasks en Verilog: Funciones vs Tasks, sintaxis y uso
- Dominar el alcance de referencia jerárquica en Verilog: Evitar colisiones de nombres en módulos y bloques
- Generador de reloj en Verilog: Guía de diseño e implementación
- Dominar las funciones matemáticas en Verilog para diseño de hardware eficiente
- Dominar $timeformat en Verilog: Control de precisión y visualización