verilog-HDL 入門
1.始めに
本文書では verilog-HDL を用いてハードウエアを記述するために必要な 基礎を説明する。 とりあえず、なにがしかのハードウエアを記述し論理合成や テストを行えるようになることが目標である。verilog-HDL の詳細な文法については規格書や各種参考書を見て欲しい。 2. verilog-HDL の成り立ち
verilog-HDL は旧 Gateway Design Automation 社のシミュレータ専用言語であった。 元々はシミュレーション言語であるから、 ハードウエアを効率的に記述することは主目的では無かった。 しかし、これに論理合成ツールが加わることによって、HDL を用いて ハードウエアを記述し設計することが現実的になった。 このため大いに広まり、現在では IEEE の規格の一つになっている。 しかし、シミュレーション言語である verilog-HDL を 無理矢理ハードウエア記述に使っているため、 現実のハードウエアとの間に一部不整合が有る。 この点をふまえて、ハードウエア記述源として用いる場合には、 非効率なハードウエアを記述しないように注意する必要がある。
なお、より効率的な記述を取り入れると共に、 このような不整合を解消する試みとして Systemverilog が規格化されている。
3. verilog-HDL によるハードウエア記述の構造
図1.に verilog-HDL によってハードウエアを記述した場合の構造を示す。 verilog-HDL ではモジュール(module)と呼ばれる単位によって構造を記述する。 各部品をモジュールとして記述し、 このモジュールを繋ぎ合わせることによって全体を構成する。 繋ぎ合わせる時に実際に呼び出される実態をインスタンス(instance)と言う。 このように、モジュールはハードウエアの記述を行うだけで、実態としては働かない。 例えば、同一の構造をもった演算器を複数設置する場合、 その演算器の機能を記述するのがモジュールで、演算器1、演算器2と 名前を付けて設置されるものがインスタンスである。 インスタンス中でさらに別のインスタンスを呼び出す事もできる。 (再帰的呼び出しはできない)。 注意すべきは、ハードウエア記述言語は、一般的な 手続き型プログラミングとは全く違うということである。 古典的な手続き型プログラミングでは、 関数インスタンスが多少の内部状態を持つことが有っても、 各々の関数が呼び出されない限りその働きを気にする必要は無い。 これに対し、ハードウエアでは各々の構成要素は常に動いている。 全てが並列に動いていると言い換えても良い。 どちらかと言えば、イベント駆動型プログラミングにおいて、 そこらじゅうでイベントが起こっている状態に近い。
したがって、ハードウエア記述言語では常に各々の部品の動作に気を使う必要がある。 この点においては物理的なハードウエアの性質と何ら変わることはない。 結局ハードウエア記述言語というのは、 別々に動作する部品を設計しそれらを繋ぎ合わせる作業を、 文字を用いて行うための手段である。
4. モジュール
ここでは verilog-HDL の基本であるモジュールについて説明する。 まず、モジュールの構造の概要について述べ、 単純な 4 bit カウンタを例にモジュール内の構造を大まかに説明する。 細かい記述法については後続の節を参照して欲しい。 4.1 モジュールの基本構造
モジュールの構造の概要をリスト1に示す。 モジュールは大まかにいって、ポート記述、 変数記述、組み合わせ回路記述、順序回路記述から成る。 厳密にはこれは正しくないが、 ディジタル LSI を記述するという目的から見たときは、 このように考えて問題無い。 リスト1 モジュールの構造の概要 module counter4(ポート名, ポート名,,, ); ポート記述 変数記述 組み合わせ回路記述 always 文 begin 順序回路記述 end endmodule
4 bit カウンタを記述したモジュールをリスト2 に示す。"//"以降行末まではコメントである。 ここでは、細かい記述はともかく、モジュール内の構造に注目して欲しい。
リスト2 モジュール記述の例 // 4 bit simple counter module counter4( //*************** port name list ************** clk, // clock rst, // reset count // counter output //********************************************* ); //************** port definition ************** input clk, rst; // clock, reset output [3:0] count; // counter output //********************************************* //*********** parameter declaration *********** reg [3:0] counter; // count register wire [3:0] count_tmp; // count up value //********************************************* //********* combination logic circuit ********* assign count = counter; // connect to output assign count_tmp = counter + 1'b1; // count up //********************************************* //*************** state machine *************** always @(posedge clk or negedge rst) begin if (~rst) // reset begin counter
4.2 モジュール文
モジュールは module 文で始まり、endmodule 文で終わる。 module 文ではモジュール名を示し、つづく括弧内で引数として入出力ポート名を示す。 4.3 ポート属性記述部
module 文の直後にポートの属性とビット幅を記述する。 通常使うポートの属性は、input, output, inout の 3 つである。 ビット幅は通常 [ビット幅-1 :0] のように上位下位の順で指定する。 ビット幅が同じ場合は、"input clk, rst;" のように続けて記述できる。 4.4 変数定義部
言語仕様上は変数定義はモジュール内のどこでも記述できる。 モジュールが小さい場合はポート属性記述の次に変数定義を行うことが多い。 モジュールが大きい場合は、変数定義とその変数の使用場所があまり離れないように記述した方が良い。 4.5 組み合わせ回路記述部
変数定義部の後に組み合わせ回路を記述する事が多い。 リスト2 中では assign 文を用いて簡単な組み合わせ回路を表現している。 この assign 文を用いるか、function を用いて組み合わせ回路を記述する。 assign 文と function 文については後述する。 always 文を用いて組み合わせ回路を記述することもできるが、 記述が繁雑になる上に 論理合成で思わぬ結果が出ることもある。 always 文で組み合わせ回路を記述するのは避けた方が賢明である。
4.6 順序回路記述部
順序回路を含まないモジュールの場合は記述しない。順序回路を含む場合は、 多くの場合モジュールの最後に always 文を用いて順序回路を記述する。 always @( に続く信号名はセンシティビティリストと呼ばれる。 always 文はこのセンシティビティリストに記述された信号が変化したときのみ動作する。 posedge, negedge は各信号の立ち上がりで反応するか立ち下がりで反応するかを指定する。
論理合成をを用いて大規模なディジタル LSI を設計する事を考えると、 このセンシティビティリストにクロックとリセット 以外の信号を混ぜるのは避けた方が良い。
要するに普通にディジタル LSI を設計している場合には、 リセットする順序回路部分は always @(posedge clk or negedge rst) で始め、 リセットしない順序回路は always @(posedge clk) で始めると憶えれば良い。
(注)リセット信号は負論理であることが多い。
なお、一つのモジュール内にいくつでも always 文を記述することができるが、 繁雑さを防ぐために、できるだけ一モジュール内の always 文は 一つにまとめた方が良い。
4.7 リセット
リセット信号は実に厄介なしろものである。 現在では reset 信号は clock と同様に特別な配慮をする必要があるため、 論理合成時には理想的な信号線として扱い、 レイアウト時にリセットツリーを挿入することが一般的である。 このため、適切に遅延時間を算出するのが難しく、 リセット信号を論理に用いると静的タイミング解析時に問題が生じることがある。 例えば、リセット時に定数をレジスタ変数に代入する場合は良いが、 リスト3 のようにリセット時に変数の値を代入するような場合に問題となる。 これはチップの外から初期値を取り込むような場合に生じる。
!doctype>