Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Industrial programming >> Verilog

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
endmodule

Testbench

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
endmodule

a[0] en b[0] geven sum[0] en cout[0], terwijl a[1] en b[1] sum[1] en cout[1] geven.

Simulation Log
 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
endmodule

Testbench

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
endmodule

Wanneer 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.

Simulation 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
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

Omdat een half-adder geïnstantieerd is, heeft cin geen effect op de uitgangen sum en cout.

Simulation Log
 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. Verilog beheersen: Complete tutorial voor digitale schakelingontwerp
  2. Verilog concatenatie beheersen: Een praktische gids
  3. Verilog Essentials: Een beknopt overzicht
  4. Verilog-toewijzingen begrijpen: types, syntax en best practices
  5. Verilog Blocking vs. Non-Blocking: belangrijkste verschillen uitgelegd
  6. Verilog-functies beheersen voor efficiënt RTL-ontwerp
  7. Verilog Tasks begrijpen: Functions vs Tasks, Syntax en Gebruik
  8. Verilog Clock Generator: Ontwerp & Implementatie Gids
  9. Verilog Math Functions beheersen voor efficiënt Hardware-ontwerp
  10. Verilog $timeformat beheersen: Precisie en Weergave-controle
  11. Verilog Timescales begrijpen en printen in elke Scope