主にAssertion等の用途でmoduleやinterfaceを他のmoduleにbindする事がある。その際に必要な信号を参照する方法を紹介する。
bindのポート接続で参照
bindされるmoduleにinput portを定義し、bind時に参照する信号を接続する。その際に.*
による暗黙的ポート接続が使える。いちいち手で繋ぐのも面倒なので.*
で繋ぐことをお勧めする。
下位モジュールの信号はポート接続の所で階層参照で接続する。
bind tb.i_dut checker i_checker( .*, .signal_b(i_sub.signal_b) );
module checker ( input clk, rst_n, signal_a, signal_b );
a_AB : assert property (@(posedge clk) disable iff(!rst_n) signal_a |=> signal_b);
endmodule
絶対パスで指定
ポートで接続せずとも当然絶対パスで信号を参照する事ができる。しかし、絶対パスは階層が変わったりすると使えなくなり、再利用性が悪いのでお勧めしない。そもそも絶対パスで参照するならbindなんてする必要はない。
bind tb.i_dut checker i_checker( .*);
module checker ( input clk, rst_n);
// Absolute signal path referencing
wire aignal_a = $root.tb.i_dut.signal_a;
wire aignal_b = $root.tb.i_dut.i_sub.signal_b;
//wire aignal_a = tb.i_dut.signal_a;
//wire aignal_b = tb.i_dut.i_sub.signal_b;
a_AB : assert property (@(posedge clk) disable iff(!rst_n) signal_a |=> signal_b);
endmodule
相対パスで指定
あまり知られてはいない様だがbindしたmoduleからの相対パスで信号を参照できる。相対パスでの信号の参照方法にも2種類あり、bindしたmoduleのインスタンス名から含める方法と含めない方法がある。
bindしたmodule以下のサブモジュール以下の信号への参照であればどちらの方法でも良いが、bindしたmodule内の直下の信号を参照する場合には必ずそのmoduleのインスタンス名から指定する必要がある。
bind tb.i_dut checker i_checker( .*);
module checker ( input clk, rst_n);
//-- 1. Relative signal path referencing with bound module instance name
wire signal_a = i_dut.signal_a;
wire signal_b = i_dut.i_sub.signal_b;
//-- 2. Relative signal path referencing without bound module instance name
//wire signal_a2 = signal_a; // NG !
wire signal_b2 = i_sub.signal_b; // OK
a_AB : assert property (@(posedge clk) disable iff(!rst_n) signal_a |=> signal_b );
a_AB2 : assert property (@(posedge clk) disable iff(!rst_n) signal_a |=> signal_b2);
endmodule
interfaceのbind
interfaceもmoduleと同様なので全く同様にbindが可能。
bind tb.i_dut dut_if i_if( .*);
interface dut_if ( input clk, rst_n, signal_a);
wire signal_b = i_sub.signal_b;
a_AB : assert property (@(posedge clk) disable iff(!rst_n) signal_a |=> signal_b);
endinterface
interfaceをbindすると何が良いのかと言うとvirtual interfaceとしてそのinterfaceのハンドル(?)をcalssの様なダイナミックオブジェクトにも渡す事ができる。
module tb;
class aaa;
virtual dut_if v_if;
virtual task run();
wait(v_if.signal_a);
...
endtask
endclass
initial begin
aaa a = new();
a.v_if = i_dut.i_if; // <==== Passing the handle to a class
a.run();
...
end
dut i_dut(.*);
endmodule
これはmoduleではできず、interfaceにしかする事ができない。なので特に理由が無ければmoduleではなくinterfaceをbindする事をお勧めする。
moduleは完全にstaticな世界の住人なのだが、interfaceはvirtual interfaceによりstaticな世界をdynamicな世界に結ぶ事ができる存在なのである。
UVMではSequenceとかでDUTの内部信号を参照したい場合に、このinterfaceのbindを使う事でそれが可能となる。(あるいはbindせずともテストベンチでinterfaceをinstance化して階層参照で信号を参照してinterface内の信号にassignしてもできる。)
以下のWhitebox verificationをご参考
以下、Virtual Interfaceを使ってDUTのstaticな世界をUVMのdyamicな世界を繋げるQiita内の記事。ただしこれではbindは使っていない。
この記事では既存のBFMのfunction/taskにアクセスする方法としてVirtual Class(Abstract Class)を紹介しているが、必ずしもVirtual Classを使う必要はなくVirtual Interfaceでやる事も可能。その場合にはinterfaceをBFM moduleにbindしてinterfaceからBFM内にアクセス可能にするとか、あるいはテストベンチトップにbindしてBFM module instanceにアクセスするとか考えられる。もしくはbindせずとも絶対パス参照でBFMにアクセスするようにしても良い。
参考