今回はややマニアックな仕様の解説で、デザインの階層名(LRMだとHierarchical namesと表現されています)になります。普段あまり深く考えずに設計していてもあまり問題になることはないですが、ふとしたきっかけで仕様を調査したところ、意外に知らない部分があったのでまとめることにしました。
なお今回はdesignに関係する部分のみで、Classについては別途まとめる予定です。
なお、本調査では$display("%m")で表示されるものを階層名としていますが、後述するように階層の作られ方には仕様として未定義の部分もあり、そのような場合はシミュレータ内で一貫して取り扱われていない場合もあるようです(%mで表示された階層内の信号をforceしようとしても、信号が無いと言われてしまうなど)。
今回はVCS 2019.06の出力結果を載せています。
仕様のリファレンスに使用しているのはIEEE STd. 1800-2017になります。適宜関連するセクション番号とタイトルを付記しています。
階層名の用途
デザインの階層名を使用する場面を思いつくままに列挙してみます。
テストベンチからのDUT内部信号のforceや参照
シミュレーション時にDUTの内部信号を直接操作、観測するのに使用します。
force top.dut.usub.s_sig = 1;
# 10ns force top.dut.usub.s_sig = 0;
# 10ns release top.dut.usub.s_sig;
シミュレーション時間短縮のためにDUT内部メモリのLoad/Dumpや、UVMのRAL経由アクセスのバックドアなど、使用シーンが多いです。
Bind
SVAやcovergroupなど、検証用のモジュールをデザインのモジュールに割り当てるのに使用します。
bindの使い方はbindでデザインにSVAを紐づけするを参照してください。
デバッグ時の情報表示
$display()のフォーマット指定子に%mを使用するとその位置の階層名を表示できます(LRM 21.2.1.6 "Hierarchical name format")。
DUT内部の状態に異常があった場合などにその場所を特定するのに使えます。
例えばDUT内部の信号に対してaseertでプロパティーチェックをかける場合、エラーメッセージに階層名を含めてデザインのどの部分のエラーかを表示させます。
q_check: assert(q_val < 10) else
$error("%m: q_val exceeded limit.");
Assertionの制御
例えば特定のテストでDUTへのassertionをoffにする場合、以下のようにassertへのパスを指定します。
$assertoff(0, test.dut.umod1.q_check);
Assertionやcoverのレポート
assertionやcoverなどのレポートのプロパティー名は階層付きでレポートされます。レポートの形式自体はツール依存です。
Disabled Finish Failed Assertion Name
0 2 0 test.dut.umod1.s_check
0 1 1 test.dut.umod1.q_check
0 0 1 test.dut.umod2.q_check
階層生成条件とその名前
ModuleとInterface
Moduleで作成される階層は、他のModuleで一度も呼び出されていないModuleを最上位階層として、インスタンシエーションに従ってツリー状の階層構造となります。
最上位階層の名前はModule名になります。最上位階層は複数あっても構いません。(LRM 23.3 "Module instances (hierarchy)")
`define DISPLAY_HIER $display("time = %02t, line = %03d, hierarchy = %m", $time, `__LINE__)
interface myif;
function void printHierarchy();
`DISPLAY_HIER;
endfunction
endinterface
module submd1(myif s_myif);
submd2 u_submd2();
initial begin
#30ns;
`DISPLAY_HIER;
#10ns;
s_myif.printHierarchy();
end
endmodule
module submd2();
initial begin
#50ns;
`DISPLAY_HIER;
end
endmodule
module topmdl();
myif s_myif();
initial begin
$timeformat(-9, 0, "ns", 3);
`DISPLAY_HIER;
end
submd1 u_submd1(.s_myif(s_myif));
submd2 u_submd2();
endmodule
# KERNEL: time = 0ns, line = 032, hierarchy = topmdl
# KERNEL: time = 30ns, line = 014, hierarchy = topmdl.u_submd1
# KERNEL: time = 40ns, line = 005, hierarchy = topmdl.s_myif.printHierarchy
# KERNEL: time = 50ns, line = 023, hierarchy = topmdl.u_submd1.u_submd2
# KERNEL: time = 50ns, line = 023, hierarchy = topmdl.u_submd2
Generate block
generateでモジュールを呼び出す場合、新たな階層名は以下のルールになります。
generateのブロック名に名前を付けた場合、階層名はblock_name[m]になります。mはgenerate loopの変数のそれぞれの値に対応します(LRM 27.4 "Loop generate constructs")。
generateのブロック名に名前を付けていない場合。階層名はgenblk_n_[m]で、nは1から始まり、ソースコードの上の行からgenerate文の度にブロック名の有る無しに関わらず連番で増えていきます。mについては上記generateのブロック名に名前を付けた場合と同様です(LRM 27.6 "External names for unnamed generate blocks")。
`define DISPLAY_HIER $display("time = %02t, line = %03d, hierarchy = %m", $time, `__LINE__)
module submd2();
initial begin
#50ns;
`DISPLAY_HIER;
end
endmodule
module topmdl();
genvar gi;
genvar gj;
initial begin
$timeformat(-9, 0, "ns", 3);
`DISPLAY_HIER; // topmd1
#100ns;
end
// Generate w/ block name
generate
for (gi = 0; gi < 2; gi+=1) begin: gb
submd2 u_submd2();
end
endgenerate
// Generate w/o block name
generate
for (gj = 0; gj < 2; gj+=1) begin
submd2 u_submd2();
end
endgenerate
endmodule
# KERNEL: time = 0ns, line = 016, hierarchy = topmdl
# KERNEL: time = 50ns, line = 006, hierarchy = topmdl.gb[0].u_submd2
# KERNEL: time = 50ns, line = 006, hierarchy = topmdl.gb[1].u_submd2
# KERNEL: time = 50ns, line = 006, hierarchy = topmdl.genblk2[0].u_submd2
# KERNEL: time = 50ns, line = 006, hierarchy = topmdl.genblk2[1].u_submd2
Function
LRMに階層を作る場所の説明が見つけられなかったのですが、そのfunctionが定義された場所に階層を作るようです。つまりパッケージの中で定義された場合そのパッケージの中、モジュールの中で定義された場合そのモジュールの中に、階層がファイル単位の空間で定義された場合はトップにシミュレータ依存の階層を作り、その中にfunction名の階層を作ります。
モジュールの中で定義された場合以外の階層名はシミュレータによって各様でした。
`define DISPLAY_HIER $display("time = %02t, line = %03d, hierarchy = %m", $time, `__LINE__)
function void printHierarchy2();
`DISPLAY_HIER;
endfunction
package myPkg;
function void printHierarchy();
`DISPLAY_HIER;
endfunction
endpackage
module topmdl();
function void printHierarchyModule();
`DISPLAY_HIER;
endfunction
initial begin
$timeformat(-9, 0, "ns", 3);
#10ns;
myPkg::printHierarchy(); // myPkg::printHierarchy
#10ns
printHierarchy2(); // $unit::printHierarchy2
#10ns;
printHierarchyModule(); // topmdl.printHierarchyModule
end
endmodule
time = 10ns, line = 009, hierarchy = myPkg::printHierarchy
time = 20ns, line = 004, hierarchy = $unit::printHierarchy2
time = 30ns, line = 015, hierarchy = topmdl.printHierarchyModule
Begin-end blocks
Named block
名前付きのbegin-endでは、その度に階層が生成されます。(LRM 23.6 "Hierarchical names")。
`define DISPLAY_HIER $display("time = %02t, line = %03d, hierarchy = %m", $time, `__LINE__)
module top();
initial begin: blk_a
begin : blk_b
`DISPLAY_HIER;
end
end
endmodule
time = 0, line = 007, hierarchy = top.blk_a.blk_b
Unnamed block
Unnamed blockでは基本は新たに階層が生成されることはありません。ただしそのブロックの中にローカル変数が宣言された場合そのための階層を作る必要があります。名前がないので、多くの場合シミュレータが独自に名前をつけます(つけないシミュレータもありました)。
`define DISPLAY_HIER $display("time = %02t, line = %03d, hierarchy = %m", $time, `__LINE__)
module top();
initial begin
begin
`DISPLAY_HIER;
end
end
endmodule
module top2();
initial begin
logic a;
#10ns;
begin
logic b;
`DISPLAY_HIER;
end
end
endmodule
time = 0, line = 006, hierarchy = top
time = 10, line = 017, hierarchy = top2.unnamed$$_0.unnamed$$_1
Forループでの階層生成
Forループでは暗黙的なbegin-end blockを生成することがあります。
以下のコードではiがローカル変数としてforの中で宣言されているので、そのために階層が生成されます。さらにforループの中でローカル変数を宣言するともう一階層増える可能性があります(シミュレータによる)。どのシミュレータでも確実に階層名を指定する場合は、サンプルプログラム一番下のforループのように、forの前と後ろ両方にブロック名を指定します。
`define DISPLAY_HIER $display("time = %02t, line = %03d, hierarchy = %m", $time, `__LINE__)
module top();
initial begin : b1
int v;
for (v = 0; v <= 0; v++) begin
`DISPLAY_HIER;
end
#10ns;
for (v = 0; v <= 0; v++) begin
int s;
`DISPLAY_HIER;
end
#10ns;
for (int i = 0; i <= 0; i++) begin
`DISPLAY_HIER;
end
#10ns;
for (int i = 0; i <= 0; i++) begin
int s;
`DISPLAY_HIER;
end
#10ns;
for (int i = 0; i <= 0; i++) begin : x1
int s;
`DISPLAY_HIER;
end
#10ns;
y1: for (int i = 0; i <= 0; i++) begin : x2
int s;
`DISPLAY_HIER;
end
end
endmodule
time = 0, line = 008, hierarchy = top.b1
time = 10, line = 014, hierarchy = top.b1.unnamed$$_0
time = 20, line = 019, hierarchy = top.b1.unnamed$$_1
time = 30, line = 025, hierarchy = top.b1.unnamed$$_2.unnamed$$_3
time = 40, line = 031, hierarchy = top.b1.unnamed$$_4.x1
time = 50, line = 037, hierarchy = top.b1.y1.x2
おまけ。論理合成で階層名が変わるもの
ゲートシミュレーションするのであれば注意が必要です。ここらへんは論理合成ツールと合成スクリプトで振る舞いが変わるかもしれませんので参考情報です。今思いだせるのは一例のみです。他にも思い出したら追記します。
レンジ指定付きのインスタンシエーション及びgenerate block
階層名に"[]"が含まれる場合です。例えば以下のように複数のモジュールをまとめてインスタンシエーションした場合
submod u[0:2] (...);
合成後のインスタンス名は展開されたものになります。[]が階層名にあるとバックエンドツールに都合が悪いためだと思われます。generateの場合も展開後のインスタンス名に[]が使用されるので同様の処理がされます。展開後のネーミングスタイルは合成スクリプトで制御できたと思います。下は一例になります。
submod u_0_ (...);
submod u_1_ (...);
submod u_2_ (...);