11. 階層構造
11.1 SCOPE
x.y.a とか、ドットでつながった表現を見たことがあると思います。なにを単位としているかというと次の4つのいずかです。
(Verilog2001 generate を後で説明しますが、Named blockの扱いになります。)
Modules Tasks Functions Named blocks |
階層アクセスするには、名前がついていないといけません。これらは、言語仕様で皆、名前がついています。(名前のないモジュールや、タスク、ファンクションはありませんね。) begin
end も次のようにすれば、名前付になります。これをNamed Blockといいます。
これを、VeritakのSCOPE TREE VIEWで見ると下のようになります。
今回は、モジュールの中の階層アクセスでしたが、モジュール階層をまたいでも、同様です。
ところで、識別子の探索は、どのように行われるでしょうか?本例の場合、WIDTHと、bは、複数の階層にあります。
アルゴリズムは、
という具合になっています。実は、シミュレータの中の管理単位は、SCOPEになっています。SCOPEは、インスタンス化された後のUnique表現(ただひとつしか存在しない)です。インスタンス化されるとモジュールは、複数の実体を持ちえます。それは、複数のSCOPEになっているということです。ハードウェアは、究極のオブジェクト指向になっているということなんですね。
11.2 generate
generateは、Altera,Xilinx共に合成でのサポートはされていないようです。FPGA関係もいずれ合成サポートはされると思います。
genvarは、genarate用の特別インデックス変数です。0以上の整数として使い、ZX、負数はエラーです。generateの外、内にあってもいいのですが、module内にないといけません。また、SCOPEの概念を持たない(SCOPE生成前なので)で階層表現は、無効です。
実行結果です。
上記は、generate forの例です。genvar宣言 iは、for loopのインデックスとしての意味を持ちます。
このときSCOPEが生成されることに注意してください。SCOPEが生成されるためには、名前が必要です。なので、begin:名前 が必ずつきます。
一番目のLOOPは、
assign bin[0]=^gray[11:0]; assign bin[1]=^gray[11:1]; assign bin[2]=^gray[11:2]; assign bin[3]=^gray[11:3]; ....... assign bin[11]=^gray[11:11]; |
と等価です。こう書いてもいいのですが、同じようなことをずらずらと書きたくない!という人はgenerateを使うといいでしょう。
2番めも同じですね
always @* bin[0]=^gray[11:0]; always @* bin[1]=^gray[11:1]; always @* bin[2]=^gray[11:2]; always @* bin[3]=^gray[11:3]; ........ always @* bin[11]=^gray[11:11]; |
実際、シミュレータのなかでは、上記のように展開しています。このとき、genvarは、消えています。genvarは、展開するためのインデックスに過ぎないということです。
次の例は、リップルアダーです。任意ビットの加算器がコンパクトに書ける、という例です。ただし、こういうのを書くのは、ライブラリ担当者だけでしょう。設計資産として保存するには、パラメタライズできることが必要です。そういう面では重宝するかもしれません。
実行結果です。
もし、dut1のfor loopが
だと、t1,t2,t3は、内側ループですから、bit SCOPEの下に信号が配置され
bit[0]、bit[1]、、bit[7] SCOPEの下に信号t1,t2,t3が来ます。
次は、最初の例のdut1をさらに for loopでインスタンス化した例です。
このSCOPEは、
になります。
次の例は、for loopのNestingです。
このSCOPEは、
になります。
次は、generate if else の例です。Verilog2001LRMでは、generate if は、のスコープの取り扱いに不明確な点があります。Veritakでは、3.23Eから、IEEE1800-2005仕様に変更しています。
次例で見ていきましょう。generate if 文の最初の方はラベルが書いてありません。一方else文の方にはラベルが書いてあります。IEEE1800-2005の仕様では、generate
構文でラベルがないものは、コンパイラが生成する内部ラベルを使うことになっています。当然、Vendor間では内部ラベルは変わります。(また、Xilinxのツールでは、ラベルがないと単純にエラーとしているようです。)
if文を生かすインスタンス化では、
select #(.WIDTH(WIDTH),.inv_type(0)) dut(.d0(d0),.d1(d1),.dout(dout));
次のように内部ラベル(Implicit Scope0 これはVeritakUniqueな名前です)が生成されます。
一方else文を生かすインスタンス化では、
select #(.WIDTH(WIDTH),.inv_type(1)) dut(.d0(d0),.d1(d1),.dout(dout));
このときのSCOPEは、
となります。このような仕様の変更があったので、regで書くところをwireで置き換える、あるいは、別なtaskやfunctionを呼ぶ、みたいなことは出来なくなりました。
また、Verilog2001では、generate if文のラベルはあってもなくてもよいのですが、for文ではLRM上必須です。
しかしIEEE1800-2005では、これもなくてもよいことになっています。ラベルがない場合は、必ず暗黙ラベルがついてしまう(VCD出力とか、Vendor依存になってしまう)ので、ラベルを明示しておいた方がよいでしょう。
次の例は、generate if文とfor文を駆使したパイプラインレジスタの例です。幅と深さをインスタンス化の際に自由に指定できますので、強力にライブラリ化を促進します。
generate は、知的な「条件コンパイル」です。色々書いてあっても、実行時もしくは論理合成時に残るのは、アクティブにされる記述だけです。
下のScope Viewで、一番目のレジスタのスコープがif_label, 2番目以降のスコープがelse_labelになっているのが分かります。
task や、functionもgenerate下に置けますが、for loopの下には置けません。
それからgenerate case というのもあります。これも想像される通りですが、ここで、注意するべきは、generate
が評価されるときに定数でなくてはいけないということです。parameter はオーバライドがありますが、オーバライド後の値で評価されます。
verilog のgenerateは、VHDLのそれより強力になってしまいました。使い方は難しいと思いますし、使わないと書けないというものでもありません。どちらかというとライブラリ向きだと思います。
11.3 ANSI スタイルポートリスト
2001になって、ANSI−Cスタイルのリストが可能になりました。
下は、色々なスタイルで、書いてみたものです。
パラメータの名前による結合が可能になった他、ポートリスト中にポートタイプが入れられるようになりました。
これによって、短く書けますのでお勧めです。ただし、1995スタイル、2001スタイルは統一して使うようにしましょう。
テストベンチ用途では、初期化付きも可能なので、さらに短く書けます。勿論これは、reg等のvariable typeのみに限定されます。
ANSI Cスタイルと呼ぶのなぜ?
昔C言語が出始めの頃(K&Rの時代)、C言語のポートリストスタイルは、Verilog1995みたいに別々に書く感じでした。その後ANSIでC言語の標準化の際に、今のC言語スタイルになったのです。Verilog言語が設計された当時は、まだK&Rの時代だったのです。