Verilogモジュールインスタンス化のベストプラクティス:階層設計のために
前回の記事で見たように、より大規模で複雑な設計は、複数のモジュールを階層的に統合することで構築されます。モジュールは他のモジュール内で インスタンス化 でき、これらの インスタンス のポートを親モジュール内の他の信号と接続できます。
これらのポート接続は、順序付きリストまたは名前による方法で行えます。
順序付きリストによるポート接続
モジュールインスタンス化時のポート式リストと、親モジュール内の信号を接続する方法の1つは 順序付きリスト です。
mydesign は module で、別のモジュール tb_top 内で d0 という名前でインスタンス化されます。ポートは、モジュール宣言のポートリストでの位置によって決まる順序で接続されます。例えば、テストベンチの b が設計の y に接続されるのは、両方ともポートリストの第2位置にあるからです。
module mydesign (
input x, y, z, // xは位置1, yは2, zは3
output o // oは位置4
);
endmodule
module tb_top;
wire [1:0] a;
wire b, c;
mydesign d0 (a[0], b, a[1], c); // a[0]→x, b→y, a[1]→z, c→o
endmodule
これは非常に不便です。ポート順序が新しいポート追加で変わったり、ポート数が非常に多い場合に対応が困難になるからです。
名前によるポート接続
より良い方法は、両側のポートを ポート名 で明示的にリンクすることです。
ドット . は、その後に続くポート名が設計側のものであることを示します。設計ポートを接続する信号名を、続いて括弧 ( ) 内で指定します。
module design_top;
wire [1:0] a;
wire b, c;
mydesign d0 (
.x (a[0]), // mydesignの"x"をこのモジュール(design_top)の"a[0]"に接続
.y (b), // mydesignの"y"をこのモジュールの"b"に接続
.z (a[1]),
.o (c)
);
endmodule
各ポート接続を別行に記述することを推奨します。そうすれば、コンパイルエラーメッセージがエラー発生行を正しく指し示し、デバッグと解決が容易になります。同じ行にすべて書いていた場合、どのポートがエラーの原因か特定しにくいためです。
これらの接続は名前で行われるため、記述順序は無関係です。複数のモジュールインスタンスポート接続は許可されません。
module design_top;
mydesign d0 (
.x (a[0]),
.z (a[1]), // zが2番目でもOK(明示的接続のため)
.y (a[1]),
.x (b), // 違法 - xは既にa[0]に接続済み
.o (c)
);
endmodule
未接続/フローティングポート
インスタンス化モジュール内でどのワイヤにも接続されていないポートは、ハイインピーダンス値を持ちます。
module design_top;
mydesign d0 (
// xは入力だが未接続 → a[0]はZ
.y (a[1]),
.z (a[1]),
.o () // oはmydesign内で有効な値だが、design_topの"c"に未接続 → cはZ
);
endmodule
例:シフトレジスタでの未接続ポート
以前のシフトレジスタ例で、一部のポートを未接続にしてみます。
module shift_reg (
input d, input clk, input rstn, output q
);
wire [2:0] q_net;
dff u0 (.d(d), .clk(clk), .rstn(rstn), .q(q_net[0]));
dff u1 (.d(q_net[0]), .clk(clk), .rstn(rstn), .q()); // 出力qをフローティング
dff u2 (.d(q_net[1]), .clk(clk), .rstn(rstn), .q()); // 出力qをフローティング
dff u3 (.d(q_net[2]), .clk(clk), .rstn(rstn), .q(q));
endmodule
合成後のRTL回路図では、インスタンス u1 と u2 の出力が未接続であることがわかります。インスタンス u2 と u3 の入力 d が何にも駆動されていないネットに接続されているため、グラウンドされます。
シミュレーションでは、このような未接続ポートはハイインピーダンス('hZ)で表され、波形では通常オレンジ色の垂直線として中央に表示されます。
すべてのポート宣言は暗黙的に wire と宣言されるため、ポート方向の指定だけで十分な場合があります。しかし、値を保持する必要がある output ポートは reg データ型で宣言し、always や initial などの手続きブロックでのみ使用できます。
input または inout タイプのポートを reg で宣言することはできません。これらは外部から継続的に駆動されるため値を保持せず、外部信号の変化をできるだけ早く反映すべきだからです。ベクトルサイズが異なる2つのポートを接続することは合法ですが、ベクトルサイズが小さい方が優先され、大きい方の上位ビットは無視されます。
// Case #1: 入力はデフォルトで暗黙的に"wire"型
module des0_1 (input wire clk ...); // wireはここで指定不要
module des0_2 (input clk, ...); // デフォルトでclkはwireタイプ
// Case #2: 入力はreg型にできない
module des1 (input reg clk, ...); // 違法: 入力はreg型不可
// Case #3: 異なるポート幅の2モジュール
module des2 (output [3:0] data, ...); // 4ビットベクトル出力
module des3 (input [7:0] data, ...); // 8ビットベクトル入力
module top (...);
wire [7:0] net;
des2 u0 (.data(net) ...); // netの上位4ビットは未駆動
des3 u1 (.data(net) ...);
endmodule
// Case #4: 出力を親モジュールのregに接続不可
module top_0 (...);
reg [3:0] data_reg;
des2 (.data(data) ...); // 違法: data出力ポートがreg型信号"data_reg"に接続
endmodule
Verilog
- Verilogマスター:デジタル回路設計のための完全チュートリアル
- Verilog連結マスター:実践ガイド
- Verilogエッセンシャル:簡潔な概要
- Verilog代入文の理解:種類、構文、ベストプラクティス
- Verilogブロッキング代入とノンブロッキング代入:違いを徹底解説
- Verilog関数のマスター:効率的なRTL設計のために
- Verilogタスクの理解:関数との違い、構文、使用法
- Verilog階層参照スコープのマスター:モジュールとブロックでの名前衝突回避
- Verilogクロックジェネレータ:設計と実装ガイド
- Verilog数学関数のマスター:効率的なハードウェア設計のために
- Verilog $timeformatのマスター:精度と表示制御