Verilog generate-blokken beheersen: Dynamische module-instantiatie & conditioneel ontwerp
Een generate blok stelt in staat om module-instanties te vermenigvuldigen of conditionele instantiatie van elke module uit te voeren. Het biedt de mogelijkheid om het ontwerp te bouwen op basis van Verilog-parameters. Deze statements zijn bijzonder handig wanneer dezelfde operatie of module-instantie meerdere keren herhaald moet worden of als bepaalde code conditioneel opgenomen moet worden op basis van gegeven Verilog-parameters.
Een generate blok mag geen poort, parameter, specparam declaraties of specify blokken bevatten. Wel mogen andere module-items en andere generate-blokken. Alle generate-instantiaties worden gecodeerd binnen een module en tussen de sleutelwoorden generate en endgenerate.
Gegenereerde instantiaties kunnen modules, continue toewijzingen, always of initial blokken en gebruiker-gedefinieerde primitieven bevatten. Er zijn twee typen generate-constructs: lussen en conditioneel.
- Generate for-lus
- Generate if-else
- Generate case
Generate for-lus
Een half-adder wordt N keer geïnstantieerd in een top-level ontwerpmodule my_design met een generate for-lus construct. De lusvariabele moet gedeclareerd worden met het sleutelwoord genvar wat de tool vertelt dat deze variabele specifiek gebruikt wordt tijdens elaboratie van het generate-blok.
// Ontwerp voor half-adder
module ha ( input a, b, output sum, cout);
assign sum = a ^ b; assign cout = a & b;
endmodule
// Top-level ontwerp met N instanties van half-adder
module my_design #(parameter N=4) ( input [N-1:0] a, b, output [N-1:0] sum, cout);
genvar i; // Tijdelijke lusvariabele voor generatie, niet beschikbaar tijdens simulatie
generate
for (i = 0; i < N; i = i + 1) begin
ha u0 (a[i], b[i], sum[i], cout[i]);
end
endgenerate
endmoduleTestbench
De testbench-parameter controleert het aantal half-adder instanties in het ontwerp. Als N=2, heeft my_design twee instanties van half-adder.
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
endmodulea[0] en b[0] geven sum[0] en cout[0], terwijl a[1] en b[1] sum[1] en cout[1] geven.
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. ncsim> exit
Het geëlaboreerde RTL heeft inderdaad twee half-adder instanties gegenereerd door het generate blok.
Generate if
Hieronder een voorbeeld met if else in een generate construct om te kiezen tussen twee verschillende multiplexer-implementaties. Het eerste ontwerp gebruikt een assign statement, het tweede een case statement. Een parameter USE_CASE in de top-level module selecteert tussen de twee keuzes.
// Ontwerp #1: Multiplexer met assign
module mux_assign ( input a, b, sel, output out);
assign out = sel ? a : b;
initial $display ("mux_assign is instantiated");
endmodule
// Ontwerp #2: Multiplexer met 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: Kies met parameter
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
endmoduleTestbench
Testbench instantieert top-level module my_design en zet parameter USE_CASE op 1 zodat het ontwerp met case statement geïnstantieerd wordt.
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
endmoduleWanneer USE_CASE=1, toont de simulatie-log dat de multiplexer met case statement geïnstantieerd is. Bij USE_CASE=0 is het de assign versie. Dit is zichtbaar door de display-statement in de simulatie-log.
// When 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. // When 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
Een generate case stelt modules, initial en always blokken in een andere module te instantiëren op basis van een case expressie om een van de veelvoudige keuzes te selecteren.
// Ontwerp #1: Half-adder
module ha (input a, b, output reg sum, cout);
always @ (a or b) {cout, sum} = a + b;
initial $display ("Half adder instantiation");
endmodule
// Ontwerp #2: Full-adder
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: Kies tussen half en full adder
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
endmoduleTestbench
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
endmoduleOmdat een half-adder geïnstantieerd is, heeft cin geen effect op de uitgangen sum en cout.
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
- Verilog beheersen: Complete tutorial voor digitale schakelingontwerp
- Verilog concatenatie beheersen: Een praktische gids
- Verilog Essentials: Een beknopt overzicht
- Verilog-toewijzingen begrijpen: types, syntax en best practices
- Verilog Blocking vs. Non-Blocking: belangrijkste verschillen uitgelegd
- Verilog-functies beheersen voor efficiënt RTL-ontwerp
- Verilog Tasks begrijpen: Functions vs Tasks, Syntax en Gebruik
- Verilog Clock Generator: Ontwerp & Implementatie Gids
- Verilog Math Functions beheersen voor efficiënt Hardware-ontwerp
- Verilog $timeformat beheersen: Precisie en Weergave-controle
- Verilog Timescales begrijpen en printen in elke Scope