数字验证--断言assert
断言是什么?
断言是对设计违例的一种严查,能够在违例时立刻报出错误。
为什么使用断言,断言的优势又有那些呢?
1.断言能够缩短你的开发时间,断言的代码是比较简单的,相比systemverilog能够很好的处理信号的电平和边沿变化的检测。如systemverilog要想实现时钟上升沿时如果FRAME_也是上升沿则2个时钟后LDP_是下降沿有需要开发下图的代码,但是使用断言则非常简单,
property ldpcheck; @(posedge clk) $rose(FRAME_) |-> ##2 $fell(LDP_) ; endproperty assert property(ldpcheck);
2.断言是可以观测的,能够通过断言直接看到是哪里的bug;
3.断言能够提供覆盖率收集;
还有很多优点如断言支持CDC(clock domain crossing)、可重用性好、断言在验证和设计中是一直处于检测状态的等。
断言需要添加在哪里?
1.rtl设计中,内部module,如非法状态转换、死锁、fifo等
2.module接口intrerace中,内部模型接口中,如在req是0时ack不能是1。
3.芯片功能断言
4.用来检测芯片接口违例的断言,如独立的PCIX和AXI接口等
5.用来性能的断言,如读响应的最大时延不能超过5个clk;
断言的类型
1.立即断言(不支持formal 验证)
用来检测待测设计的信号值,可以理解为设计中的if。
2.并发断言
存在edge采样的检测信号的断言
立即断言
立即断言是一个程序块中的简单信号状态检测,可以直接理解为条件判定if。下面是一个简单的状态检测,通过assert检测b或c,条件成立打印时间和assert passed
并发断言
一个简单的并发断言的例子,在clk上升沿时,cStart发生,则触发断言检测,req和cStat同时发生并且2个clk后gnt发生。
其中cStat为antecedent,|->为implication operator,str1为consequent
inplication operator分为overlapping operattor(|->)和non-overlapping operattor(|=>),分别就是overlapping operattor是consequent 要和antecedent同时发生,而non-overlapping operattor是consequent 要在antecedent下一个采样时钟发生,其实cStart |-> ##1 (req ##2 gnt) 等效于cStart |=> (req ##2 gnt)
并发断言中常用的表达式和语法
operator | description |
$rose | 上升沿检测,从0或x或z变为1 |
$fell | 下降沿检测,从1或x或z变为0 |
$stable | 检测当前边沿采样状态和上一边沿采样状态没有发生变化 |
$past | 追溯过去任意时钟 |
$onehot | 用于检测表达式中只有1bit为1其他bit为0或高阻或X态 |
$onehot0 | 检测表达式中所有bit都为0或只有1bit为1 |
$isunknown | 检测信号为高阻态或者X态 |
$countones | 统计表达式中bit为1的个数 |
$asserton | 开启断言 |
$assertoff | 关闭不处于活动状态下断言 |
$assertkill | 杀死所有断言 |
$rose
$rose,如下面使用在clk上升沿时intr存在从0或x态或高阻态变为1的过程,也就是在clk上一个上升沿时intr是0或x态或高阻态,在当前clk上升沿时是1,这时就满足$rose的边沿检测属性了。
$fell
$fell,$fell和$rose是相似的,只不过是用来检测下降沿的,在clk上升沿时req存在从1或x态或高阻态变为0的过程,也就是在clk上一个上升沿时req是1或x态或高阻态,在当前clk上升沿时是0,这时就满足$fell的边沿检测属性了。
正好截图中把$isunknown()带上了,这里解释下$isunknown(),是检测信号为高阻态或为X态,加上not,即判定的结果相反,$fell(we_) |-> not ($isunknown(wData))意思是,在we_出现下降沿时 wData为0或1,不能是高阻态或者X态的检测。
$stable
$stable,检测当前时钟边沿的状态和上一时钟边沿的状态是一致的不发生改变则通过。
$past
$past,它可以让你追溯过去任意时钟,然后您可以检查'表达式1'是否具有特定的值,在以前的时钟数(严格地说是在时间点之前)。请注意,时钟的数量是可选的。如果你没有指定它,默认情况下是在过去的一个时钟查找'表达式1'。
需要注意的另一个警告是当你在仿真的初始时间刻度中调用$past时,没有足够的时钟去'过去'。例如,您可以指定'a | - > $ past(b)',并且在时间'0'前提'a'为真。过去并没有时钟。在这种情况下,断言将使用'b'的'初始'值。 'b'的初始值是多少?它不是'初始'块中的那个,它是变量'b'被声明的值(如'logic b = 1'b1;')。在我们的例子中,如果'b'在其声明中未被初始化,断言将失败。如果声明初始值为1'b1,则断言将通过.
$onehot
$onehot用于检测表达式中只有1bit为1其他bit为0或高阻或X态不关心。
$onehot0用于检测表达式中所有bit都为0或只有1bit为1。
$isunknown
检测出高阻态Z或者是X态。
$countones
统计表达式中每bit是1的个数。
$asserton
开启断言
$assertoff
关闭断言,处于活动状态下的断言不关闭。
$assertkill
杀死所有的断言。
operator | description |
##m | 延迟m个采样时钟 |
##[m:n] | 延迟m到n个采样时钟 |
[*m] | 重复m次,不连续 |
[*m:n] | 重复m到n次,不连续 |
[=m] | 重复m次,连续 |
[=m:n] | 重复m到n次,连续 |
[->m] | 到达m次,不连续 |
[->m:n] | 到达m到n次,不连续 |
sig1 througnout seq1 | 在seq1整个中sig1要为真true |
seq1 within seq2 | seq1一定要包含在seq2中 |
seq1 intersect seq2 |
seq1和seq2要同时开始同时结束 |
seq1 and seq2 | seq1和seq2要同时开始, |
seq1 or seq2 | seq1或者seq2任意一个succeeds就succeeds |
first match | 第一次匹配 |
not | 表达式结果取反 |
if else |
##m
延迟m个采样时钟,m可以是0或0以上的正整数
##[m:n]
延迟m到n之间的任意一个采样时钟都可,m可为0或任意正整数,n可为0或任意正整数也可直接用$表示极大值。
[*m]
重复m次出现,a ##1 b[*2]等效于 啊 ##1 b ##1 b,m为0或0以上正整数但不能为极大值$
[*m:n]
重复m到n之间的任意数次均可
[=m]
b[=m] ##1 c 表示b进行m次,间隔不连续个采样时钟周期,c在b最后一次发生后最小间隔一个时钟后发生。
[=m:n]
b[=m:n] ##1 c 表示b进行m到n之间任意数次,间隔不连续个采样时钟周期,c在b最后一次发生后最小间隔一个时钟后发生。
在特殊情况下如果m=0,n=$,结果为下图,b可不发生,b也可和c同事发生
[->m]
b [->m] ##1 c 表示b进行m次,间隔不连续个采样时钟周期,c在b最后一次发生后间隔一个时钟后发生。
[->m:n]
b[=m:n] ##1 c 表示b进行m到n之间任意数次,间隔不连续个采样时钟周期,c在b最后一次发生后间隔一个时钟后发生。
[=m]和[->m]、[=m:n]和 [->m:n]之间的区别
区别在于在使用[=m]、[->m]、[=m:n]、 [->m:n]后如果接一个##1 c,则c是在最小间隔一个时钟还是正好间隔一个时钟周期上。
sig1 throughout seq1
在sig1匹配的时间内seq1一定是匹配的。
seq1 within seq2
seq1一定发生的比seq2晚,结束的seq2早。处于seq2满足的内部。
seq1 and seq2
seq1和seq2是同时发生的,在seq1和seq2全通过时断言才是通过。
seq1 or seq2
seq1和seq2同时发生,只要seq1和seq2中的一个通过,则断言通过。
seq1 intersect seq2
seq1和seq2一定是同时开始同时结束的,存在不匹配则断言失败。
first_match
第一次匹配事件。下图的断言为d发生后第一次发生bc事件的下一个时钟周期a是上升沿则断言通过。
not
对发生的事件结果取反。
if else
条件判定,用法和systemverilog相同。
验证人员如何对dut内部信号进行断言验证
1.可使用层级关系 . 的方法引用dut的内部信号,如要想使用if_stage_i内部的instr_rvalid_i信号可以通过dut_wrap.cv32e40p_wrapper_i.core_i.if_stage_i.instr_rvalid_i,(不推荐,编译存在层级关系而且一旦例化位置变化,验证平台代码也需作出调整)
2.定义一个断言module,将dut的模块和断言module绑定,关键字为bind,其中
cv32e40p_wrapper :是dut中的模块名
uvma_obi_assert :是验证平台中的断言模块名
obi_data_assert_i :是断言模块的例化名,
bind cv32e40p_wrapper uvma_obi_assert#( .ADDR_WIDTH(32), .DATA_WIDTH(32) ) obi_data_assert_i(.clk(clk_i), .reset_n(rst_ni), .req(data_req_o), .gnt(data_gnt_i), .addr(data_addr_o), .be(data_be_o), .we(data_we_o), .wdata(data_wdata_o), .rdata(data_rdata_i), .rvalid(data_rvalid_i), .rready(1'b1) );
断言模块使用范例
module uvma_obi_assert import uvm_pkg::*; #( parameter int ADDR_WIDTH=32, parameter int DATA_WIDTH=32 ) ( input clk, input reset_n, input req, input gnt, input [ADDR_WIDTH-1:0] addr, input we, input [DATA_WIDTH/8-1:0] be, input [DATA_WIDTH-1:0] wdata, input [DATA_WIDTH-1:0] rdata, input rvalid, input rready ); // --------------------------------------------------------------------------- // Local variables // --------------------------------------------------------------------------- string info_tag = "CV32E40P_OBI_ASSERT"; // --------------------------------------------------------------------------- // Clocking blocks // --------------------------------------------------------------------------- // Single clock, single reset design, use default clocking default clocking @(posedge clk); endclocking default disable iff !(reset_n); // --------------------------------------------------------------------------- // Begin module code // --------------------------------------------------------------------------- // R-3.1.1 : Address phase signals stable during address phase property p_addr_signal_stable(sig); req ##0 !gnt |=> $stable(sig); endproperty : p_addr_signal_stable a_addr_stable: assert property(p_addr_signal_stable(addr)) else `uvm_error(info_tag, "addr signal not stable in address phase") a_we_stable: assert property(p_addr_signal_stable(we)) else `uvm_error(info_tag, "we signal not stable in address phase") a_wdata_stable: assert property(p_addr_signal_stable(wdata)) else `uvm_error(info_tag, "wdata signal not stable in address phase") a_be_stable: assert property(p_addr_signal_stable(be)) else `uvm_error(info_tag, "be signal not stable in address phase") // R-3.1.2 : Req may not deassewrt until the gnt is asserted property p_req_until_gnt; req ##0 !gnt |=> req; endproperty : p_req_until_gnt a_req_until_gnt : assert property(p_req_until_gnt) else `uvm_error(info_tag, "req may not deassert until gnt asserted") // R-7 At least one byte enable must be set property p_be_not_zero; req |-> be != 0; endproperty : p_be_not_zero a_be_not_zero : assert property(p_be_not_zero) else `uvm_error(info_tag, "be was zero during an address cycle") // R-7 All ones must be contiguous in writes reg[3:0] contiguous_be[] = { 4'b0001, 4'b0011, 4'b0111, 4'b1111, 4'b0010, 4'b0110, 4'b1110, 4'b0100, 4'b1100, 4'b1000 }; bit be_inside_contiguous_be; always_comb begin be_inside_contiguous_be = be inside {contiguous_be}; end property p_be_contiguous; req |-> be_inside_contiguous_be; endproperty : p_be_contiguous a_be_contiguous : assert property(p_be_contiguous) else `uvm_error(info_tag, $sformatf("be of 0x%0x was not contiguous", $sampled(be))); // R-8 Data address LSBs must be consistent with byte enables on writes function bit [1:0] get_addr_lsb(bit[3:0] be); casex (be) 4'b???1: return 0; 4'b??10: return 1; 4'b?100: return 2; 4'b1000: return 3; endcase endfunction : get_addr_lsb property p_addr_be_consistent; req |-> addr[1:0] == get_addr_lsb(be); endproperty : p_addr_be_consistent a_addr_be_consistent: assert property(p_addr_be_consistent) else `uvm_error(info_tag, $sformatf("be of 0x%01x not consistent with addr 0x%08x", $sampled(be), $sampled(addr))); // Cover that grant is asserted when unrequested property p_unrequested_gnt; !req ##0 gnt; endproperty : p_unrequested_gnt c_unrequested_gnt: cover property(p_unrequested_gnt); endmodule : uvma_obi_assert