Dominar bloques generate en Verilog: Instanciación dinámica y diseño condicional
Un bloque generate permite multiplicar instancias de módulos o realizar instanciación condicional de cualquier módulo. Proporciona capacidad de construir diseño basado en parámetros Verilog. Estas sentencias son convenientes cuando la misma operación o instancia debe repetirse múltiples veces o si cierto código debe incluirse condicionalmente según parámetros.
Un bloque generate no puede contener declaraciones de puerto, parámetro, specparam o bloques specify. Pero otros elementos de módulo y otros bloques generate sí. Todas las instancias generadas se codifican dentro de un module entre generate y endgenerate.
Instancias generadas pueden tener módulos, asignaciones continuas, bloques always o initial y primitivas definidas por usuario. Hay tres tipos de constructos generate:
- Generate for loop
- Generate if else
- Generate case
Generate for loop
Un medio sumador se instanciará N veces en módulo top-level my_design usando generate for loop. La variable de bucle debe declararse con genvar indicando que es para elaboración del generate.
// Diseño medio sumador
module ha ( input a, b, output sum, cout);
assign sum = a ^ b;
assign cout = a & b;
endmodule
// Top level con N instancias de medio sumador
module my_design #(parameter N=4) (
input [N-1:0] a, b,
output [N-1:0] sum, cout
);
genvar i;
generate
for (i = 0; i < N; i = i + 1) begin
ha u0 (a[i], b[i], sum[i], cout[i]);
end
endgenerate
endmodule
Testbench
Parámetro testbench controla número de instancias. Con N=2, my_design tiene dos instancias de medio sumador.
module tb;
parameter N = 2;
reg [N-1:0] a, b;
wire [N-1:0] sum, cout;
my_design #(.N(N)) md( .a(a), .b(b), .sum(sum), .cout(cout));
initial begin
a <= 0; b <= 0;
$monitor ("a=0x%0h b=0x%0h sum=0x%0h cout=0x%0h", a, b, sum, cout);
#10 a <= 'h2; b <= 'h3;
#20 b <= 'h4;
#10 a <= 'h5;
end
endmodule
a[0] y b[0] dan sum[0] y cout[0]; a[1] y b[1] dan sum[1] y cout[1].
Salida:
ncsim> run a=0x0 b=0x0 sum=0x0 cout=0x0 a=0x2 b=0x3 sum=0x1 cout=0x2 a=0x2 b=0x0 sum=0x2 cout=0x0 a=0x1 b=0x0 sum=0x1 cout=0x0 ncsim: *W,RNQUIE: Simulation is complete.
El RTL elaborado tiene dos instancias de medio sumador generadas por generate.

Generate if
Ejemplo usando if else dentro de generate para seleccionar entre dos implementaciones de multiplexor. Primera usa assign, segunda case. Parámetro USE_CASE en top-level selecciona.
// Diseño #1: Mux con assign
module mux_assign ( input a, b, sel, output out);
assign out = sel ? a : b;
initial $display ("mux_assign is instantiated");
endmodule
// Diseño #2: Mux con case
module mux_case (input a, b, sel, output reg out);
always @ (a or b or sel) begin
case (sel)
0 : out = a;
1 : out = b;
endcase
end
initial $display ("mux_case is instantiated");
endmodule
// Top Level: Parámetro elige uno
module my_design ( input a, b, sel, output out);
parameter USE_CASE = 0;
generate
if (USE_CASE)
mux_case mc (.a(a), .b(b), .sel(sel), .out(out));
else
mux_assign ma (.a(a), .b(b), .sel(sel), .out(out));
endgenerate
endmodule
Testbench
Instancia top-level y pone USE_CASE a 1 para instanciar diseño con case.
module tb;
reg a, b, sel;
wire out;
integer i;
my_design #(.USE_CASE(1)) u0 ( .a(a), .b(b), .sel(sel), .out(out));
initial begin
a <= 0; b <= 0; sel <= 0;
for (i = 0; i < 5; i = i + 1) begin
#10 a <= $random; b <= $random; sel <= $random;
$display ("i=%0d a=0x%0h b=0x%0h sel=0x%0h out=0x%0h", i, a, b, sel, out);
end
end
endmodule
Con USE_CASE = 1, simulación muestra "mux_case is instantiated". Con 0, "mux_assign is instantiated". Visible por $display en log.
Salida:
// USE_CASE = 1 ncsim> run mux_case is instantiated i=0 a=0x0 b=0x0 sel=0x0 out=0x0 i=1 a=0x0 b=0x1 sel=0x1 out=0x1 i=2 a=0x1 b=0x1 sel=0x1 out=0x1 i=3 a=0x1 b=0x0 sel=0x1 out=0x0 i=4 a=0x1 b=0x0 sel=0x1 out=0x0 ncsim: *W,RNQUIE: Simulation is complete. // USE_CASE = 0 ncsim> run mux_assign is instantiated i=0 a=0x0 b=0x0 sel=0x0 out=0x0 i=1 a=0x0 b=0x1 sel=0x1 out=0x0 i=2 a=0x1 b=0x1 sel=0x1 out=0x1 i=3 a=0x1 b=0x0 sel=0x1 out=0x1 i=4 a=0x1 b=0x0 sel=0x1 out=0x1 ncsim: *W,RNQUIE: Simulation is complete.
Generate Case
Un generate case permite instanciar módulos, bloques initial/always basado en expresión case para elegir entre varias opciones.
// Diseño #1: Medio sumador
module ha (input a, b, output reg sum, cout);
always @ (a or b) {cout, sum} = a + b;
initial $display ("Half adder instantiation");
endmodule
// Diseño #2: Sumador completo
module fa (input a, b, cin, output reg sum, cout);
always @ (a or b or cin) {cout, sum} = a + b + cin;
initial $display ("Full adder instantiation");
endmodule
// Top level: Elige entre medio y sumador completo
module my_adder (input a, b, cin, output sum, cout);
parameter ADDER_TYPE = 1;
generate
case(ADDER_TYPE)
0 : ha u0 (.a(a), .b(b), .sum(sum), .cout(cout));
1 : fa u1 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
endcase
endgenerate
endmodule
Testbench
module tb;
reg a, b, cin;
wire sum, cout;
my_adder #(.ADDER_TYPE(0)) u0 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
initial begin
a <= 0; b <= 0; cin <= 0;
$monitor("a=0x%0h b=0x%0h cin=0x%0h cout=0%0h sum=0x%0h", a, b, cin, cout, sum);
for (int i = 0; i < 5; i = i + 1) begin
#10 a <= $random; b <= $random; cin <= $random;
end
end
endmodule
Como se instancia medio sumador, cin no afecta salidas sum y cout.
Salida:
ncsim> run Half adder instantiation a=0x0 b=0x0 cin=0x0 cout=00 sum=0x0 a=0x0 b=0x1 cin=0x1 cout=00 sum=0x1 a=0x1 b=0x1 cin=0x1 cout=01 sum=0x0 a=0x1 b=0x0 cin=0x1 cout=00 sum=0x1 ncsim: *W,RNQUIE: Simulation is complete.
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
- 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
- Comprender e imprimir timescales de Verilog en cualquier alcance