最早接触 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 的效果实际上就是在 被 bind 的dut 下实例化了一下 bind 的内容,如上就是在 dut 下面实例化了一个 dut_assert ;

也可以将 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(.*);

最近尝试使用 paramteter 来定义 sva module,然后bind 到 dut 上,语法如下:

bind dut_module_name  sva_module_name#(.parameter_name(parameter_name))
  sva_inst_name();

如果 sva_module_name 中的 parameter 名 与 dut_module_name 中的 parameter 名一致,则所有 dut_module_name 的实例都会被bind,即使此时 dut的实例中真正的 parameter 值不同!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐