最早接触 bind 关键字是在assertion 当中,将assertion 与 dut 进行绑定连接,如下例子:
bind cpu fpu_props fpu_rules_1(a,b,c);
//cpu 是module 名字
//fpu_props 是内部包含 property 以及断言的模块,可以是 module 或者 program 甚至 interface
//fpu_rules_1 是 fpu_props 的实例名
//括号中的信号 a b c 是 cpu 的端口信号,并且连接到 fpu_props 的对应端口
来看下面一个将 interface bind 到 module 的例子:
interface range (input clk,enable, input int minval,expr);
property crange_en;
@(posedge clk) enable |-> (minval <= expr);
endproperty
range_chk: assert property (crange_en);
endinteface
bind cr_unit range r1(c_clk,c_en,v_low,(in1&&in2));
可以看到,包含断言的 interface ,其端口信号的方向均为 input ,也就是说 property 中包含的信号都是从 interface 的外部给进来的;
实际上, bind 不仅仅 可以进行 断言 与 dut 之间的连接,两个 module 之间也能进行连接,如下面例子:
有 dut.v :
module dut(clk, rst_n, vld, rdy, data);
input clk;
input rst_n;
input vld;
output reg rdy;
output reg[31:0] data;
bit[31:0] count;
always @(posedge clk) begin
if(rst_n == 0)begin
end
else begin
count ++;
data <= count;
if(count == 150)begin
$finish;
end
end
end
endmodule
dut_assert.sv :
module dut_assert(clk_sva, rst_n_sva, vld_sva, rdy_sva, data_sva);//这里全都是 input
input clk_sva,rst_n_sva,vld_sva;
input rdy_sva;
input reg[31:0] data_sva;
always @(posedge clk_sva)begin
@(posedge clk_sva);
if(data_sva == 10)begin
$display("++++canli data_sva is %d at %0t",data_sva, $time);
end
if(data_sva == 13)begin
$display("****canli data_sva is %d at %0t",data_sva, $time);
end
end
endmodule
test.sv :
module test;
bit clk;
bit rst_n;
bit [31:0] data;
bit rdy;
initial begin
#10 rst_n = 1;
forever begin
#1 clk = ~clk;
end
end
initial begin
$timeformat(-9, 0, "ns", 20);
$fsdbDumpfile("top.fsdb");
$fsdbDumpvars(0, test);
$fsdbDumpflush;
$fsdbDumpSVA;
end
dut my_dut(.clk(clk),.rst_n(rst_n),.vld(1'd0),.rdy(rdy),.data(data));
endmodule
然后通过 bind 将 dut 与 dut_assert 连接起来,写在 assert.sv 中:
bind dut dut_assert my_assert(
.clk_sva(clk),
.rst_n_sva(rst_n),
.vld_sva(vld),
.rdy_sva(rdy),
.data_sva(data)
);
然后filelist 文件如下:
./dut.v
./dut_assert.sv
./assert.sv
./test.sv
运行结果如下:
从上面的例子可以看到:即使 dut_assert.sv 中不包含 断言,bind 依旧可以将 dut_assert 与 dut 连接起来;不过一般来说,在 dut_assert 中一般添加断言来检测dut,否则这样的 bind 看上去似乎没啥子意义;
也可以将 bind 语句加到 test.sv 中,运行结果是一样的,如下:
module test;
bit clk;
bit rst_n;
bit [31:0] data1;
bit [31:0] data2;
bit rdy1;
bit rdy2;
initial begin
#10 rst_n = 1;
forever begin
#1 clk = ~clk;
end
end
initial begin
$timeformat(-9, 0, "ns", 20);
$fsdbDumpfile("top.fsdb");
$fsdbDumpvars(0, test);
$fsdbDumpflush;
$fsdbDumpSVA;
end
dut my_dut(.clk(clk),.rst_n(rst_n),.vld(1'd0),.rdy(rdy1),.data(data1));
bind dut dut_assert my_assert(//这里的 dut 也可以是 my_dut
.clk_sva(clk),
.rst_n_sva(rst_n),
.vld_sva(vld),
.rdy_sva(rdy),
.data_sva(data)
);
endmodule
此时,如果将 dut 实例化2次,如下:
module test;
bit cl;//注意这里名字变了
bit rst_n;
bit [31:0] data1;
bit [31:0] data2;
bit rdy1;
bit rdy2;
initial begin
#10 rst_n = 1;
forever begin
#1 cl = ~cl;
end
end
initial begin
$timeformat(-9, 0, "ns", 20);
$fsdbDumpfile("top.fsdb");
$fsdbDumpvars(0, test);
$fsdbDumpflush;
$fsdbDumpSVA;
end
dut my_dut1(.clk(cl),.rst_n(rst_n),.vld(1'd0),.rdy(rdy1),.data(data1));
dut my_dut2(.clk(cl),.rst_n(rst_n),.vld(1'd0),.rdy(rdy2),.data(data2));
bind dut dut_assert my_assert(//注意这里 bind 的还是 dut
.clk_sva(clk), //这里绑定的还是 clk
.rst_n_sva(rst_n),
.vld_sva(vld),
.rdy_sva(rdy),
.data_sva(data)
);
endmodule
运行结果如下:
可以看到,此时 bind dut,相当于 bind 了2次,所以打印语句打印2次;如果将上面的 bind dut 修改:
module test;
bit cl;
bit rst_n;
bit [31:0] data1;
bit [31:0] data2;
bit rdy1;
bit rdy2;
initial begin
#10 rst_n = 1;
forever begin
#1 cl = ~cl;
end
end
initial begin
$timeformat(-9, 0, "ns", 20);
$fsdbDumpfile("top.fsdb");
$fsdbDumpvars(0, test);
$fsdbDumpflush;
$fsdbDumpSVA;
end
dut my_dut1(.clk(cl),.rst_n(rst_n),.vld(1'd0),.rdy(rdy1),.data(data1));
dut my_dut2(.clk(cl),.rst_n(rst_n),.vld(1'd0),.rdy(rdy2),.data(data2));
bind my_dut1 dut_assert my_assert(// 这里 bind 的修改为 my_dut1
.clk_sva(clk),
.rst_n_sva(rst_n),
.vld_sva(vld),
.rdy_sva(rdy),
.data_sva(data)
);
endmodule
运行结果如下:
也就是说,此时改为 dut 的实例名,相当于只 bind 了1次,所以打印1次;如果修改 bind 的端口信号名:
module test;
bit cl;
bit rst_n;
bit [31:0] data1;
bit [31:0] data2;
bit rdy1;
bit rdy2;
initial begin
#10 rst_n = 1;
forever begin
#1 cl = ~cl;
end
end
initial begin
$timeformat(-9, 0, "ns", 20);
$fsdbDumpfile("top.fsdb");
$fsdbDumpvars(0, test);
$fsdbDumpflush;
$fsdbDumpSVA;
end
dut my_dut1(.clk(cl),.rst_n(rst_n),.vld(1'd0),.rdy(rdy1),.data(data1));
dut my_dut2(.clk(cl),.rst_n(rst_n),.vld(1'd0),.rdy(rdy2),.data(data2));
bind my_dut1 dut_assert my_assert(
.clk_sva(cl), //这里 bind 的信号改为 cl
.rst_n_sva(rst_n),
.vld_sva(vld),
.rdy_sva(rdy),
.data_sva(data)
);
endmodule
此时运行会报错,如下:
由此,可以得出结论,bind 时,bind 的 dut 中的信号必须是 dut module中的信号名,而不能是 dut 实例的输入输出信号名!!
最近看到一种bind 方式来大批量 bind ,语法如下:
bind module_name: module_inst_name1, module_inst_name2...
checker_module[#(.Para(xx))] checker_module_inst
// checker_module 可以带 parameter
例子如下:
bind DUT: dut_inst1, dut_inst2 checker#(.a(1)) checker_inst(.*);