Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Industrial programming >> Verilog

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

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.

Dominar bloques generate en Verilog: Instanciación dinámica y diseño condicional

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

  1. Dominar Verilog: Tutorial completo para diseño de circuitos digitales
  2. Dominar la concatenación en Verilog: Guía práctica
  3. Verilog Esencial: Resumen conciso
  4. Comprender las asignaciones en Verilog: tipos, sintaxis y mejores prácticas
  5. Bloqueante vs. No bloqueante en Verilog: Diferencias clave explicadas
  6. Dominar las funciones en Verilog para diseño RTL eficiente
  7. Comprender las Tasks en Verilog: Funciones vs Tasks, sintaxis y uso
  8. Generador de reloj en Verilog: Guía de diseño e implementación
  9. Dominar las funciones matemáticas en Verilog para diseño de hardware eficiente
  10. Dominar $timeformat en Verilog: Control de precisión y visualización
  11. Comprender e imprimir timescales de Verilog en cualquier alcance