工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Industrial programming >> Verilog

Verilog generateブロックをマスター:動的モジュールインスタンス化と条件付き設計

generate ブロックを使用すると、モジュールインスタンスを複数生成したり、任意のモジュールを条件付きでインスタンス化したりできます。設計をVerilogパラメータに基づいて構築する機能を提供します。同じ操作やモジュールインスタンスを複数回繰り返す必要がある場合や、与えられたVerilogパラメータに基づいて特定のコードを条件付きで含める場合に特に便利です。

generate ブロックには、ポート、パラメータ、specparam 宣言や specify ブロックを含められません。しかし、他のモジュールアイテムや他のgenerateブロックは許可されます。すべての生成インスタンス化は、module 内で、キーワード generate と endgenerate の間に記述されます。

生成されるインスタンス化には、モジュール、連続代入、always または initial ブロック、ユーザー定義プリミティブが含まれます。generate構造には2種類あります:ループと条件分岐。

Generate for ループ

半加算器を、generate forループ構造を使って、別のトップレベル設計モジュール my_design 内で N 回インスタンス化します。ループ変数はキーワード genvar で宣言し、この変数がgenerateブロックのエラボレーション専用であることをツールに伝えます。

// 半加算器の設計
module ha (input a, b, output sum, cout);
assign sum = a ^ b;
assign cout = a & b;
endmodule

// N個の半加算器インスタンスを含むトップレベル設計
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

テストベンチ

テストベンチパラメータで設計内の半加算器インスタンス数を制御します。N=2なら、my_designは半加算器2インスタンスを持ちます。

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] と b[0] が sum[0] と cout[0] を、a[1] と b[1] が sum[1] と cout[1] を出力します。

シミュレーションログ

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.

エラボレートされたRTLには、generate ブロックによって生成された2つの半加算器インスタンスが確かに存在します。

Generate if

以下は、generate 構造内で if else を使って、2つの異なる多重化器実装を選択する例です。最初の設計は assign 文でMUXを実装し、2番目は case 文を使用。トップレベル設計モジュールで USE_CASE というパラメータを定義し、2つの選択肢から選びます。

// 設計#1: assign文でMUX実装
module mux_assign (input a, b, sel, output out);
assign out = sel ? a : b;
initial $display ("mux_assign is instantiated");
endmodule

// 設計#2: case文で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
initial $display ("mux_case is instantiated");
endmodule

// トップレベル設計: パラメータでどちらかを選択
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

テストベンチ

テストベンチはトップレベルモジュール my_design をインスタンス化し、パラメータ USE_CASE を1に設定して、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

USE_CASE が1ならシミュレーションログから case 文の設計が、0なら assign 文の設計がインスタンス化されたことがわかります。これはシミュレーションログに表示される $display 文で確認できます。

シミュレーションログ

// USE_CASE = 1の場合
ncsim> run
mux_case is instantiated
i=0 a=0x0 b=0x0 sel=0x0 out=0x0
...

// USE_CASE = 0の場合
ncsim> run
mux_assign is instantiated
i=0 a=0x0 b=0x0 sel=0x0 out=0x0
...

Generate case

Generate caseは、case 式に基づいて複数の選択肢から1つを選び、モジュール、initial/alwaysブロックをインスタンス化できます。

// 設計#1: 半加算器
module ha (input a, b, output reg sum, cout);
always @ (a or b) {cout, sum} = a + b;
initial $display ("Half adder instantiation");
endmodule

// 設計#2: 全加算器
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

// トップレベル設計: 半加算器と全加算器を選択
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

テストベンチ

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

半加算器がインスタンス化されるため、cin は出力 sum と 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

  1. Verilogマスター:デジタル回路設計のための完全チュートリアル
  2. Verilog連結マスター:実践ガイド
  3. Verilogエッセンシャル:簡潔な概要
  4. Verilog代入文の理解:種類、構文、ベストプラクティス
  5. Verilogブロッキング代入とノンブロッキング代入:違いを徹底解説
  6. Verilog関数のマスター:効率的なRTL設計のために
  7. Verilogタスクの理解:関数との違い、構文、使用法
  8. Verilogクロックジェネレータ:設計と実装ガイド
  9. Verilog数学関数のマスター:効率的なハードウェア設計のために
  10. Verilog $timeformatのマスター:精度と表示制御
  11. 任意のスコープでのVerilog timescale理解と表示