bindとは
アサーションやcovergroup等の検証用の記述はデザイン内に直接記述することもできますが、コードフリーズのタイミングの違いや記述者が異なるなどの理由で別ファイルに分けた方が管理がしやすいことが多いです。ファイルを分割した時にデザインと検証用の記述を紐づけるための機能がbindになります。
bindの機能自体はアサーション、covergroupに特化したものではありません。任意のモジュールを、対象モジュールのファイルを書き換えることなくインスタンシエーションする機能です。ただし試したことはありませんが論理合成には対応していないと思います。
例えばデザインdutの下にモジュールsubをインスタンシエーションする時は以下のように記述しますが、
module dut();
sub u_sub(.sig_a(sig_a), .sig_b(sig_b));
endmodule
以下のようにbindを使用して、dutファイルを書き換えることなく別ファイルでsubをインスタンシエーションできます。subの中にはアサーションやcovergroupを記述します。上記記述と下のbindの記述は等価になります。
bind dut sub u_sub(.sig_a(sig_a), .sig_b(sig_b));
文法
bindはほぼどこでも宣言でき、moduleの外にでも記述できるのですが、個人的には最上位階層にtop_bindというモジュールを作って、その中で全てのbindを行うようにしています。
デザインにbindする場合
モジュール、インタフェースに対してbindする場合は以下のようになります。対象が複数回インスタンシエーションされていれば、その全てのインスタンスにbindされます。
<target_module_name>はbind先のモジュール名です。
<common_module_instantiation>は通常のmoduleをインスタンシエーションする時と全く同じ書式です。
bind <target_module_name> <common_module_instantiation>
特定インスタンスにbindする場合
特定のインスタンスに対してbindする場合は以下のようになります。
<instance_path>が対象インスタンスのパス名です。パス名はbindを記述した場所から検索されます。
<common_module_instantiation>は通常のmoduleをインスタンシエーションする時と全く同じ書式です。
bind <instance_path> <common_module_instantiation>
verilogの階層を超えた名前の参照ルールは結構ややこしいので 1 、可能であれば$rootを使用してルートからのパスを指定するのがお勧めです。
サンプルプログラム
プログラム
モジュールsubに対してさまざまな方法でprop_modをbindしています。
- 全てのモジュールsubのインスタンス(top.u_dut.u_sub1, top.u_dut.u_sub2)に対し、prop_for_designという名前でbind
- 特定インスタンス(top.u_dut.u_sub2)に対し、prop_for_instanceという名前でbind
- 比較のために、bindを使用せずに直接prop_directという名前でsubの下にインスタンシエーション
本サンプルでは、parameterの指定を含めてみました。parameterもバインド先のスコープが見えるのでそのまま代入すれば良いだけです。
プロパティーチェック用のモジュール(prop_mod)は、本来はpropertyやcovergroupを記述するのですが、本例ではログに結果を残す目的で変数vとパラメータPARAMの値を表示させています。
module prop_mod
#(parameter PARAM = 8)
(input logic [3 : 0] v);
initial begin
#1ns;
$display("%m: v = 0x%x, PARAM = %d", v, PARAM);
end
endmodule
module sub();
parameter PARAM = 16;
logic [3 : 0] v = 'ha;
// Directly instantiate the property module
prop_mod #(.PARAM(PARAM)) prop_direct(.*);
endmodule
module dut();
sub #(.PARAM(1)) u_sub_1();
sub #(.PARAM(4)) u_sub_2();
endmodule
module top;
dut u_dut();
endmodule
module bind_module;
// Bind to a module
bind sub prop_mod #(.PARAM(PARAM)) prop_for_design(.*);
// Bind to a instance
bind $root.top.u_dut.u_sub_2 prop_mod #(.PARAM(PARAM)) prop_for_instance(.*);
endmodule
結果
以下がログです。
# KERNEL: ASDB file was created in location /home/runner/dataset.asdb
run -all;
# KERNEL: bind_module.\bind:top.u_dut.u_sub_2:prop_for_instance .prop_for_instance: v = 0xa, PARAM = 4
# KERNEL: top.u_dut.u_sub_1.prop_direct: v = 0xa, PARAM = 1
# KERNEL: top.u_dut.u_sub_1.prop_for_design: v = 0xa, PARAM = 1
# KERNEL: top.u_dut.u_sub_2.prop_direct: v = 0xa, PARAM = 4
# KERNEL: top.u_dut.u_sub_2.prop_for_design: v = 0xa, PARAM = 4
# KERNEL: Simulation has finished. There are no more test vectors to simulate.
EDA Playgroundにソースコードがありますので、WEB browserから各自実行して結果を確かめることができます。
[bind_sample] (https://www.edaplayground.com/x/2yx5)
Tips
property用モジュールの入力信号名
上の例でも採用していますが、デザイン側のモニタしたい信号名にプロパティー時に使用する信号名を一致させておくと、bind時のポート接続が.*だけで済むので楽です。
加えて対象デザイン内に無い信号を新たに作って入力することも可能です。
bind dut sub u_sub(.* .a_and_b(sig_a & sig_b));
他の階層のモジュールの信号も引っ張ってこれます。下の例はfooという下位階層の信号barを参照している例です。
bind dut sub u_sub(.* .sub_sig(foo.bar));
bind先のモジュール内にローカルなスコープの型変数の参照
デザインのステートマシンをモニタする時によく問題になるのですが、ステートマシンのステートは通常はenumでそのモジュール内でローカルに定義することが多いです。この型はモジュールの外からは見えないので、property用のモジュールでは直接参照することができません。
以下の例だと、st_tはモジュールdutの中にしかスコープが無いので、プロパティー用のモジュールpropの入力ポートの型としては使用できません。
module dut();
typdef enum logic [1:0] {IDLE, BUSY, WAIT} st_t;
st_t st;
always_ff @(posedge clk or posedge rst) begin
if (rst) begin
st <= IDLE;
end else begin
if (start) begin
st <= BUSY;
end
...
endmodule
module prop_mod(input st_t st); // Will get an error
...
endmodule
一番安全な対処法は、対象のtypedefを別のpackageに移動し、それをデザイン、プロパティー双方でimportすることです。デザイン内で閉じていたtypedefのスコープを広げてしまうことになりますしデザインファイルに修正が必要なので、場合によっては適用できないかもしれません。
他には、同じtypedefをプロパティー用モジュールの中で再定義する方法もあります。
以下のように入力は同じ幅を持つlogicで宣言します。さらにデザインと同じtypedefを宣言し、モジュール内で改めてこのtypeにキャスティングを行います。アサーションにはキャスティングされたcast_stを使用します。ただしこの方法はデザインのtypedefに変更が入った場合にプロパティー側のtypedefを変更し忘れて不一致が起こる危険性があります。
module prop_mod(input logic [1:0] st);
typdef enum logic [1:0] {IDLE, BUSY, WAIT} st_t;
st_t cast_st;
assign cast_st = st_t'(st);
endmodule
ちなみに対象のtypeをtype parameterを使ってモジュールのパラメータとして渡す実験も行ってみたのですが、どのシミュレータでもその状態でカバレッジを定義することはできませんでいた。以下のような感じです。LRM上では可能そうなのですが、実装が難しいのかもしれません。
module prop_mod #(parameter type T=logic[1:0])
(input T st);
endmodule
bind sub prop_mod #(.T(st_t)) prop_for_design(.*);
参考文献
"23.11 Binding auxiliary code to scopes or instances". 1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language. pp.738-741.
-
"23.6 Hierarchical names". 1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language. pp.721-725. ↩