2. モジュール構造
2. モジュール構造
Velirog-HDLコードは以下の特徴を持ちます
- テキストファイルで記述される
- 拡張子は通常.vを使用
- 一つ以上のモジュールで構成される
1つのテキストファイルに複数モジュールを記述する事が出来ますが、1モジュールを1ファイルで記述する事が多いです。
モジュール構造
moduleから始まりendmoduleまでが一つのモジュール。この間に回路を記述する。
module モジュール名 (ポートリスト); ポート宣言:モジュールに入出力される信号の入出力を定義 パラメータ宣言: 内部信号の属性:レジスタ出力か否か 回路記述 下位モジュールの呼び出し assign文、always文など endmodule
2入力XORを記述したXOR2モジュールの例を挙げます。
/* モジュール例として2入力XORを書きました この行もコメントになります。 */ module XOR2( IN_A , // in : Input IN_B , // in : Input O // out : Output ); // ----- input/ output ----- input IN_A ; input IN_B ; output O ; //------------------------------------------------------------------------------ // XOR //------------------------------------------------------------------------------ wire O ; // 組み合わせ回路の出力はwireで定義する assign O = (IN_A & ~IN_B) | (~IN_A & IN_B); endmodule
上から順に説明します。
コメント
先ずはコメントについて説明します。
コメントを入れる事が出来ます。コメントを表す表現は下の2種類あります。
- /*から*/まで。複数行にわたる事が出来ます。
- //から行末まで
- 日本語について
- コメント以外の場所に全角スペースなどの全角文字を入れる事はツール誤動作の原因になりますので注意して下さい。コメント内では日本語も使えますが、以前はコメントに日本語を入れると開発ツールの動作が異常になる事がありました。どう考えても記述と異なる動作をするので日本語コメントを外したら正常に動作したという経験があります。注意して使ってください。
モジュール名とポートリスト
module XOR2( IN_A , // in : Input IN_B , // in : Input O // out : Output );
moduleがモジュールの始まりです。スペースを空けてXOR2がモジュール名です。その隣の(から);までがポートリストです。
ポートリストとは入出力信号のリストです。このカッコで挟まれる間に、このモジュールXOR2に入出力される信号名を記述します。信号名は設計者が自由につける事が出来ますが識別子規則に従う必要があります。このモジュールは3つの信号:IN_A, IN_B, Oを持っている事が分かります。各信号はカンマで仕切ります。各信号の右にある//はコメントの開始を意味しています。//から行末まではコメントと解釈されます。コメントはこの他に/*から*/で囲む事でも表す事が出来ます。これらの信号の向き(入出力)はこの時点では分かりません。IN_A, IN_B, Oの3つの信号がある事だけが定義されています。
Verilog-HDLはセミコロンで分かれている単位で解釈します。従って、一文の最後には必ずセミコロンをつけなければいけません。セミコロンまでを一文と認識するので途中にタブやスペース改行を任意に入れる事が出来ます。この例の様にコメントも入れる事が出来ます。自分が見やすいように記述して下さい。
例えば、下の様に書くこともできます。
module XOR2(IN_A, IN_B, O);
ポート宣言
// ----- input/ output ----- input IN_A ; input IN_B ; output O ;
ポートリストの信号の入出力を定義します。この例ではIN_A, IN_Bが入力、Oが出力と定義されています。
入出力定義は以下の3種類あります。
キーワード | 意味 |
input | 入力 |
output | 出力 |
inout | 入出力(双方向) |
- FPGA設計時の注意
- FPGA内部で入出力信号は使用できません。仮に使用した場合は開発ツールが自動的に入力と出力の独立した信号へ変換します。もちろんFPGA外部とのI/F信号では使用する事が出来ます。
パラメータ宣言
パラメータは便利な書き方の一つですのでここでは解説しません。
信号の属性
wire O ; // 組み合わせ回路の出力はwireで定義する
信号の属性を定義します。
下2つの属性があります。
属性 | 意味 |
wire | 組み合わせ回路出力の信号 |
reg | フリッププロップ(記憶素子)出力の信号 |
最初は記憶素子出力の信号はreg、そうでない信号はwireで宣言すると覚えてください。
属性の宣言は信号を初めて使用するより前(上の行)で行う必要があります。例えば、下は正しい例です。Bの宣言位置に注目してください。
wire B; wire C; assign C = B; assign B = A;
下の記述はエラーになります。
wire C; assign C = B; wire B; assign B = A;
上の例の場合、
assign C = B;
でBを使用していますが、Bの宣言が後(下の行)にあるためにエラーになります。信号を初めて使用するより前(上の行)で宣言してください。
内部信号については全て宣言しなければいけません。内部信号とはポートリストに表れない信号の事です。ポートリストに表れる入力信号については省略可能です。出力信号についてはwireタイプのみ省略可能で、regタイプの場合は宣言が必要です。
- 補足
- シミュレーションを行う時にテストベンチを用いますが、ベンチから与える信号はregで定義します。これは、記憶素子に直接値を代入していると考えてください。通常はテストする回路へ信号を与える為にある信号の変化点のみ時間と値を設定するので記憶素子として定義しないと指定した瞬間しか値を指定できません。
回路記述
ここからendmoduleまでの部分に回路を記述します。
回路の記述法を後の章で説明します。この文書ではassign文とalways文の2種類を説明します。シミュレーション記述ではinitial文を説明します。
他の表現方法もありますが、便利な書き方、作業効率を上げる書き方、エレガントな恰好が良い書き方に属する記述法とみなし説明しません。先の3つの文を学習した後に各自書籍等で習得してください。
先ずは先にあげた3つを習得しましょう。ちまみに、私はコードの9割以上をこの3つの文を使って書いています。
assign O = (IN_A & ~IN_B) | (~IN_A & IN_B); endmodule
下位モジュールの組み込み
モジュールは階層構造と取る事が出来ます。
例えば、ここで使用したXOR2を親モジュールTOPに組み入れ3入力XORにする例を見てみます。
/* XOR2を2つ組み込んだ例です */ module TOP( IN_A , // in : Input IN_B , // in : Input IN_C , // in : Input O // out : Output ); // ----- input/ output ----- input IN_A ; input IN_B ; input IN_C ; output O ; wire O ; wire Z ; XOR2 U1(IN_A(IN_A), IN_B(IN_B), O(Z)); XOR2 U2(IN_A(IN_C), IN_B(Z) , O(O)); endmodule
endmoduleの上の部分でXOR2を組んでいます。
XOR2 U1(IN_A(IN_A), IN_B(IN_B), O(Z));
XOR2はモジュール名(回路の名前)、U1がインスタンス名(モジュール番号)です。その後ろにあるのがポートリストでTOPモジュールの信号とXOR2モジュールの信号との接続関係を定義します。この例では、XOR2モジュールのIN_AをTOPモジュールのIN_Aに、XOR2モジュールのIN_BをTOPモジュールのIN_Bに、XOR2モジュールのOをTOPモジュールのZに接続しています。下位モジュールと上位モジュールの信号名は異なって構いません。階層が異なれば別信号と認識します。
XOR2 U2(IN_A(IN_C), IN_B(Z) , O(O));
同様に次をみると、U1の出力がIN_Bに接続され、出力がTOPモジュールの出力Oに接続されています。
- インスタンス名
- 同一モジュール(階層)内で同じインスタンス名を付ける事はできません。モジュール(階層)が異なれば使用できます。