Menguasai Blok Generate Verilog: Instanisasi Modul Dinamis & Desain Bersyarat
Sebuah blok generate memungkinkan untuk memperbanyak instance modul atau melakukan instantiasi bersyarat dari modul apa pun. Ini memberikan kemampuan untuk desain dibangun berdasarkan parameter Verilog. Pernyataan ini sangat nyaman ketika operasi atau instance modul yang sama perlu diulang beberapa kali atau jika kode tertentu harus disertakan secara bersyarat berdasarkan parameter Verilog yang diberikan.
Sebuah blok generate tidak dapat berisi deklarasi port, parameter, specparam atau blok specify. Namun, item modul lain dan blok generate lain diizinkan. Semua instantiasi generate dikodekan di dalam sebuah module dan di antara kata kunci generate dan endgenerate.
Instantiasi yang dihasilkan dapat berupa modul, penugasan berkelanjutan, blok always atau initial dan primitive yang ditentukan pengguna. Terdapat dua jenis konstruksi generate - loop dan kondisional.
Generate for loop
Generate if else
Generate case
Generate for loop
Sebuah half adder akan diinstanisasi N kali dalam modul desain tingkat atas lain yang disebut my_design menggunakan konstruksi generate for loop. Variabel loop harus dideklarasikan menggunakan kata kunci genvar yang memberitahu alat bahwa variabel ini akan digunakan khusus selama elaborasi blok generate.
// Desain untuk half-adder module ha ( input a, b, output sum, cout); assign sum = a ^ b; assign cout = a & b; endmodule // Desain tingkat atas yang berisi N instance half adder module my_design #(parameter N=4) ( input [N-1:0] a, b, output [N-1:0] sum, cout); // Deklarasikan variabel loop sementara yang akan digunakan selama // generasi dan tidak akan tersedia selama simulasi genvar i; // Generate for loop untuk menginstanisasi N kali generate for (i = 0; i < N; i = i + 1) begin ha u0 (a[i], b[i], sum[i], cout[i]); end endgenerate endmodule
Testbench
Parameter testbench digunakan untuk mengontrol jumlah instance half adder dalam desain. Ketika N adalah 2, my_design akan memiliki dua instance half adder.
module tb; parameter N = 2; reg [N-1:0] a, b; wire [N-1:0] sum, cout; // Instanisasi desain tingkat atas dengan N=2 sehingga akan memiliki 2 // instance half adder terpisah dan keduanya diberikan dua input terpisah 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] dan b[0] memberikan output sum[0] dan cout[0] sementara a[1] dan b[1] memberikan output sum[1] dan cout[1].
Lihat bahwa RTL yang dihasilkan memang memiliki dua instance half adder yang dihasilkan oleh blok generate.
Generate if
Ditunjukkan di bawah ini adalah contoh menggunakan if else di dalam konstruksi generate untuk memilih antara dua implementasi multiplexer yang berbeda. Desain pertama menggunakan pernyataan assign untuk mengimplementasikan mux sementara desain kedua menggunakan pernyataan case. Sebuah parameter yang disebut USE_CASE didefinisikan dalam modul desain tingkat atas untuk memilih antara dua pilihan.
// Desain #1: Desain multiplexer menggunakan pernyataan "assign" untuk menugaskan // sinyal out module mux_assign ( input a, b, sel, output out); assign out = sel ? a : b; // Pernyataan display awal digunakan sehingga // kita tahu desain mana yang diinstanisasi dari log // simulasi initial $display ("mux_assign is instantiated"); endmodule // Desain #2: Desain multiplexer menggunakan pernyataan "case" untuk menggerakkan // sinyal out 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 // Pernyataan display awal digunakan sehingga // kita tahu desain mana yang diinstanisasi dari log // simulasi initial $display ("mux_case is instantiated"); endmodule // Desain Tingkat Atas: Gunakan parameter untuk memilih salah satu module my_design ( input a, b, sel, output out); parameter USE_CASE = 0; // Gunakan blok "generate" untuk menginstanisasi mux_case // atau mux_assign menggunakan konstruk if else dengan generate 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 menginstanisasi modul tingkat atas my_design dan mengatur parameter USE_CASE ke 1 sehingga menginstanisasi desain menggunakan pernyataan case.
module tb; // Deklarasi variabel testbench reg a, b, sel; wire out; integer i; // Instanisasi desain tingkat atas dan atur parameter USE_CASE ke 1 sehingga // desain menggunakan pernyataan case diinstanisasi my_design #(.USE_CASE(1)) u0 ( .a(a), .b(b), .sel(sel), .out(out)); initial begin // Inisialisasi variabel testbench a <= 0; b <= 0; sel <= 0; // Berikan nilai acak ke input DUT dengan beberapa keterlambatan 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
Ketika parameter USE_CASE adalah 1, dapat terlihat dari log simulasi bahwa desain multiplexer menggunakan pernyataan case diinstanisasi. Dan ketika USE_CASE adalah nol, desain multiplexer menggunakan pernyataan assign diinstanisasi. Ini terlihat dari pernyataan display yang dicetak dalam log simulasi.
Sebuah generate case memungkinkan modul, blok initial dan always untuk diinstanisasi dalam modul lain berdasarkan ekspresi case untuk memilih salah satu dari banyak pilihan.
// Desain #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 // Desain #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 // Desain tingkat atas: Pilih antara half adder dan 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
Perhatikan bahwa karena half adder diinstanisasi, cin tidak memiliki efek pada output sum dan cout.