moduleのインターフェース
moduleのポートリストはその インターフェース であり、外の世界が見て触れるすべてです。module内部では、bodyが入力から出力を計算します。ポートは境界を渡る配線です。
モダンなANSIスタイルの宣言は方向、型、幅をインラインで配置します:
module my_module(
input wire clk,
input wire reset,
input wire [7:0] data_in,
output reg [7:0] data_out,
output wire valid
);
// 本体
endmodule
これが全体の形です。各ポートは以下を持ちます:
- 方向:
input、output、またはinout。 - 型:
wireまたはreg(SystemVerilogではlogic)。 - 幅:
[7:0]のような範囲。省略すれば1ビット。 - 名前: body内で使う識別子。
3つの方向
input - 外から駆動
inputは常にwireです。ドライバはmoduleの外側にあり、上位moduleのsignalかtestbenchのregです。このmoduleの内側では、inputを式の中で読めますが、代入することはできません:
module reader(input wire [7:0] data);
initial $display("data = %h", data);
endmodule
module内でdata = ...と書くとエラーになります。
output - 内から駆動
outputはこのmodule内の何かによって駆動されます。wire(assignまたはサブmoduleの出力で駆動)かreg(always/initialから駆動)のどちらかになります。
module driver(
input wire a,
input wire b,
input wire clk,
output wire y, // wire - assignで駆動
output reg q // reg - alwaysで駆動
);
assign y = a & b; // yがwireなのでOK
always @(posedge clk)
q <= a; // qがregなのでOK
endmodule
alwaysブロックの内側でyに代入したり、qをassignで駆動しようとするとコンパイルエラーになります。キーワードの選択はドライバと一致しなければなりません。
inout - 双方向
inoutポートはmoduleか外側から接続されているものかが交互に駆動できるwireです。チップ境界(I²CのSDA線、双方向GPIOピン、共有データバス)に登場します。module内側では、tri-stateパターンで方向を制御します:
module bidir_pin(
input wire data_out,
input wire output_enable,
output wire data_in,
inout wire pin
);
assign pin = output_enable ? data_out : 1'bz;
assign data_in = pin;
endmodule
pinが共有wireです。output_enableが1のとき、moduleはpinをdata_outに駆動します。0のときは、外部ドライバが使えるようにハイインピーダンス(z)に解放します。data_inは常にピン上の現在の値を観察します。
inoutは純粋に内部のロジックでは稀です。メモリコントローラ、CPUコア、画像処理パイプラインを作るなら、まったく必要にならないかもしれません。これはチップ境界のI/Oリング用の機能です。
シングルビット vs マルチビットポート
幅の範囲はオプションです。シングルビットなら省略します:
input wire valid, // 1ビット
input wire [7:0] data, // 8ビット
input wire [31:0] addr, // 32ビット
幅にparameterを使えます。これがparameter化されたmoduleの動作です:
module bus #(
parameter WIDTH = 32
)(
input wire [WIDTH-1:0] in,
output wire [WIDTH-1:0] out
);
assign out = in;
endmodule
ANSI vs Verilog-1995スタイル
ポートリストと宣言を分けた古いコードを時々目にします:
// 古いVerilog-1995スタイル - 新しいコードではやらない
module foo(clk, data_in, data_out);
input clk;
input [7:0] data_in;
output reg [7:0] data_out;
// ...
endmodule
ポートリストには名前だけを含み、各ポートは本体内で個別に宣言されます。冗長で、「ポートリストに名前があるが宣言されていない」エラーが起きやすく、タイピング量が2倍です。ANSI-2001スタイル(このドキュメントで使っているもの)は両方を置き換えます:
module foo(
input wire clk,
input wire [7:0] data_in,
output reg [7:0] data_out
);
// ...
endmodule
ANSIスタイルを使いましょう。ツールは2001年からサポートしています。
完全な例
この単一のmoduleは:
- parameter(
WIDTH)。 - クロック、reset、ロードイネーブル、データ、シフトイネーブルの入力。
alwaysブロック内で駆動されるregの出力(data_out)。- 連続代入で駆動される
wireの出力(msb_out)。 - 各ポートの方向、型、幅をインラインで記したANSIスタイルの完全な宣言。
これがあなたが書くことになるほぼすべての合成可能なmoduleの形です。
次に読むもの
次のドキュメントModuleインスタンス化では、このmoduleを取り出して大きな設計の中で使う方法を示します。今書いたshifterは単独では役に立ちません。何かがそれをインスタンス化してシステムに組み込んで初めて働きます。
よくある質問
Verilogのポート方向は3つありますか?
はい。input、output、inoutです。inputはmoduleの外から駆動されます。outputはmoduleの内側から駆動されます。inoutは双方向で、moduleが同じピンを駆動と読み取りの両方で使うtri-stateバスに役立ちます。内部ポートのほとんどはinputまたはoutputで、inoutはチップピンにしか現れません。
output wireとoutput regの違いは?
output wireは出力が連続代入またはサブmoduleの出力で駆動されることを意味します。alwaysブロックの外側からのすべてです。output regは手続きブロック(initialまたはalways)から駆動されることを意味します。このキーワードは合成されるハードウェアがflip-flopを含むかどうかではなく、alwaysの内側からsignalをターゲットにできるかどうかを制御します。
VerilogのANSIスタイルとは?
ANSIスタイルは各ポートの方向と型をポートリスト内にインラインで宣言します:module foo(input wire [7:0] data, output reg [7:0] result);。古いVerilog-1995スタイルはポートリストには名前だけを並べ、moduleの内側で再宣言していました。新しいコードでは常にANSIスタイルを使ってください。短く、エラーが起きにくく、モダンな環境では標準です。
Verilog moduleに複数のinputとoutputを持てますか?
はい。moduleは任意の方向の組み合わせで、必要なだけポートを持てます。ポートリスト内ではカンマで区切ります。ポートリストの順序はインスタンス化時の位置接続順を決めますが、ほぼ常に名前付き接続(.port(signal))を使って順序に依存しないようにします。