[name:] assert (expression) [pass_statement] [else fail_statement]
time t;
always @(posedge clk)
if(staet == REQ)
assert (req1 || req2)
else begin
t = $time;
#5 $error("assert failed at time %0t", t);
end
//如果状态为REQ,但req1或req2均不为1时,断言将失败
立即断言可以结合$fatal/$error/$warning/$info
给出不同严重级别的消息提示。
always @(state)
assert (state == $onehot) else $fatal;
base_rule1: assert property (cont_prop(rst,in1,in2)) pass_stat else fail_stat;
property req_grant_prop
@(posedge clk) req ##2 gnt ##1 !req ## !gnt;
endproperty
assert property req_grant_prop else $error("Req-Gnt Protocol violoation");
//Request-Grant协议描述:request拉高,两个周期后grant拉高
//一个周期后request拉低,一个周期后grant拉低
并行断言只会在时钟边沿激活,变量的值是采样到的值。
并行断言的执行:
assert property(@(posedge clk)
disable iff (!rset)
a |=> b ##1 c);
assert property(my_prop);
property my_prop;
@(posedge clk)
disable iff(!reset)
a |=> b ##1 c;
endproperty
sequence s1;
@(posedge clk) a ##1 b ##1 c;
endsequence
sequence s2;
@(posedge clk) a ##1 c;
endsequence
property p1;
@(posedge clk) disable iff (!reset)
s1 |=> s2;
endproperty
sequence用来提供下列的场景描述:
sequence可以在module、interface、program、clocking块和package中声明。
sequence s20_1(data,en);
(!frame && (data==data_bus)) ##1 (c_be[0:3] == en);
endsequence
交叠蕴含(|->):
非交叠蕴含(|=>):
##
表示周期延迟符号,如##n表示在n个时钟周期后,##0表示在当前周期,即交叠周期。##[min:max]
表示在一个范围内的时钟周期延迟。min和max必须是非负数,序列会在从min到max时间窗口中最早的时间来匹配。$
用来表示无穷大的周期(在仿真结束前),但一般不建议这么做,这会增大仿真评估序列的负担。[*n]
操作符来表示重复,n须为非负数且不能为$;类似的,也可使用[*m:n]
表示一定范围内的重复事件。[=m]
用来表示一个事件的连续性,需要重复发生m次,但并不需要在连续周期内发生。类似的,也可使用[=m:n]
表示从最小m到最大n的重复发生的非连续周期次数。a[*0]
用来表示没有在任何正数时钟周期内有效。SEQ1 and SEQ2
下列情形将满足此操作符:
SEQ1 or SEQ2
下列情形将满足此操作符:
sequence checkBusIdle;
(##[2:$] (frame && irdy));
endsequence
property first_match_idle
@(posedge clk) first_match(checkBusIdle) |-> (state==busidle);
endproperty
sequence burst_rule1;
@(posedge clk)
$fell(burst_mode) ##0
(!burst_mode) throughout (##2 ((trdy==0)&&(irdy==0)) [*7]);
endsequence
SEQ1 within SEQ2
!trdy[*7] within (($fell irdy) ##1 !irdy[*8])
if...else
property master_child_reqs;
@(posedge clk) master_req ##1 (req1 || req2)
if(req1)
(##1 ack1);
else
(##1 ack2);
endproperty
property cache_hit_check
@(posedge clk) (state==CACHE_LOOKUP) ##1 (CHit||CMiss) |->
if(CHit)
##1 (state == CACHE_READ);
else
##1 (state == REQ_OUT);
endproperty
assert property(cache_hit_check) else $error;
SEQ.ended
sequence e1;
@(posedge sysclk) $rose(ready) ##1 proc1 ##1 proc2;
endsequence
sequence rule;
@(posedge sysclk) $reset ##1 inst ##1 e1.ended ##1 branch_back;
endsequence
sequence aRbseq (aFell,bRose);
@(posedge clk) $fell(aFell) ##1 $rose(bRose);
endsequence
property endCycle;
@(posedge clk) $rose(c) |=> aRbseq(a,b).ended;
endproperty
//等价于
$rose(c)&&$fell(a)|=>$rose(b)
例如,在rdDone拉高后,读出的cache_rd_data会在2个周期后,在其基础上加1,并作reg_wr_data写入。
sequence rd_cache_done;
##[1:5] rdDone;
endsequence
sequence check_reg_wr_data;
int local_data;
(rd_cache_done,local_data=cache_rd_data) ##2 (reg_wr_data==(local_data+1));
endsequence
例如:如果read拉高,伴随着readId,则下一次read必须在这一次read对应的readAck返回之后才可以发起。
需要先记录上一次read的readId,继而在接下来的周期,检查没有相同readId的read发起,直到对应readId的上一次read的readAck拉起,并且readAckId与之相同。
property checkReadIdAck;
int loc_id;
($rose(read),loc_id=readId) |=>
not(($rose(read)&&readId==loc_id) [*1:$]) ##0 //##0相当于&&
($rose(readAck)&&readAckId==loc_id);
endproperty
sequence s1;
logic v, w;
(a, v=e) ##1
(b[->1],w=f,$display("b after a with v = %h, w = %h\n",v,w));
endsequence
一些系统函数可以用来访问指定类型的变量采样:
$rose(expression[, clocking_event])
和$fell(expression)[, clocking_event])
用来表示与上一个采样周期相比,变量最低位是否跳变为1或0,满足条件返回1,否则返回0。
clocking_event
在sequence中不需要单独提供,因为sequence中一般会指定采样时钟。
例如,在req拉起的2个周期以后,ack也应该拉高:
例如,在write_en拉低好(表征发起写访问),write_data的数据内容不应该为X:
$stable(expression[, clocking_event])
,用来表示在连续两个采样周期内,表达式的值保持不变,如果满足返回1,否则返回0。property reg_rd_data_stable;
@(posedge clk) rd_en |-> $stable(reg_data);
endproperty
$pase(expr[, num_cycles][, gating_expr][, clocking_event])
用来访问在过去若干采样周期前的数值。num_cycles=1
,即采样1个周期前的数值。property ReqCauseAck;
@(posedge clk) $rose(ack) |-> $past(req,2);
endproperty
property cache_read_chk;
@(posedge clk) (state==CACHE_READ) |-> ($past(state) != CACHE_MISS);
endproperty
$rose/$fell/$stable
也可以在过程块语句和连续赋值中使用。always @(posedge clk)
TestDone <= stimulus_over & $rose(unit_done);
always @(posedge clk) begin
if($stable(my_sig)) begin
$display($time, "my_sig is stable from previous clk");
end
end
assign intr_cleared = $fell(intr, @(posedge clk);
assign intr_set = $rose(intr, @(posedge clk);
$countbits(expression, control_bit)
用来计算expression中匹配control_bit数值的位数。$onehot(expression)
与$countbits(expression, '1)
一致,即计算expression中为1的位数。$isunknown(expression)
与$countbits(expression, '1)==1
一致,即检查expression中是否有且只有1位为1。$countones(expression)
与$countbits(expression, 'x, 'z)!=0
一致,即检查expression中是否有x或z。disable iff
来给assertion做局部的条件控制。$asserton
,默认控制,用来打开所有的assertion;$assertoff
,暂时停止assertion运行;$assertkill
,终止所有执行的assertion。$assertoff(level, [list of module, instance or assertion_identifier]);
$assertkill(level, [list of module, instance or assertion_identifier]);
$asserton(level, [list of module, instance or assertion_identifier]);
level=0
,表示当前模块或层次下的所有assertion;level=n
,表示当前模块或层次下n层范围中的assertion;assertion_identifier
表示property的名字或assertion的label。例如,在reset阶段停止所有的assertion,并在其复位之后,使能所有的assertion:
module assert_control();
initial begin: disable_assert_during_reset
@(negedge top_tb.reset_n) //active low reset
$display("Disabling assertion during reset");
$assertoff(0, top_tb.cpu_inst1);
@(posedge top_tb.reset_n)
$display("enabling assertions after reset");
$assertion(0, top_tb.cpu_inst1);
endmodule
property可以在验证中用来做assumption、checker或coverage:
assert
关键词时,可以用作checker
来检查设计是否遵循property的描述;assume
关键词时,可以作为环境的假设条件,对于仿真环境和形式验证均起到对激励进行假设的作用;cover
关键词时,可以将property是否真正通过作为断言覆盖率来衡量。property可以在module、interface、clocking块或package中
声明;也可以同sequence一样具备形式参数:sequence、negation、disjunction、conjunction、if...else、implication、instantiation
。
sequence
类型的property,只有当出现满足该sequence条件时,property才可以通过。negation
类型,即not_property_expr
。如果property_expr不满足,那么negation类型的property可以通过。property rule2;
@(clkev) disable iff (foo) a |-> not(b ##1 c ##1 d);
endproperty
disjunction(OR)
用法即property_expr1 or property_expr2
,当至少一个property_expr满足时,property即通过。property rule3;
@(posedge clk) a[*2] |-> ((##[1:3] c) or (d |=> e));
endproperty
conjunction(AND)
,用法property_expr1 and property_expr2
,当两个property_expr均满足时,property通过。property rule3;
@(posedge clk) a[*2] |-> ((##[1:3] c) and (d |=> e));
endproperty
if(expression) property_expr1 else property_expr2
sequence_expr {|->, |=>} property_expr
property rule6(x,y);
##1 x |-> y;
endproperty
property rule5a;
@(posedge clk)
a ##1 (b || c) [->1] |->
if(b)
rule6(d, e)
else // c
f;
endproperty
@(posedge clk0) sig0 ##1 @(posedge clk1) sig1 // ##1的时钟对应的clk0
##1
结合第二个时钟沿采样,表示与第一个时钟(clk0)沿紧密相连的下个时钟(clk1)沿。sequence
操作符号如and、or、intersect等无法被使用在多时钟sequence。@(posedge clk1) s1 ##0 @(posedge clk2) s2
@(posedge clk1) s1 ##2 @(posedge clk2) s2
@(posedge clk1) s1 intersect @(posedge clk2) s2
property
中的and、or、not
则可以用在多时钟的property声明中,因为它们代表逻辑运算,并不参与sequence之间的时序关系。sequence
中独立指定时钟:sequence s2;
@(posedge clk) a ##2 b;
endsequence
property p2;
not s2;
endproperty
assert property(p2);
property
中独立指定时钟:property p3;
@(posedge clk) a ##2 b;
endproperty
assert property(p3);
always @(posedge clk) assert property (not (a ##2 b));
clocking master_clk @(posedge clk);
property p3;
not (a ##2 b);
endproperty
endclocking
assert property (master_clk.p3);
由此,断言的时钟由以下条件的优先级逐级判定:
对于并行断言,其必须具备时钟,即满足上述时钟条件之一。
对于多时钟的断言,必须显式声明时钟,无法继承或使用默认时钟;也无法嵌套入时钟块,同样也无法嵌套入由时钟驱动的过程块语句:
always @(clk) assert property (mult_clock_prop); // illegal
initial @(clk) assert property (mult_clock_prop); // illegal
bind design_block_or_instance_name block_with_assertions
interface range (input clk, enable, input int minval, expr);
property crange_en;
@(posedge clk) enable |-> (minval <= expr);
endproperty
range_chk: assert property (crange_en);
endinterface
bind cr_unit range r1(c_clk, c_en, v_low, (in1&&in2));
expect_property_statement ::= expect(property_spec) action block
initial begin
#200ms;
expect(@(posedge clk) a ##1 b ##1 c) else $error("expect failed");
ABC:...
end
integer data;
...
task automatic wait_for(integer value, output bit success);
expect (@(posedge clk) ##[1:10] data == value) success = 1;
else success = 0;
endtask
initial begin
bit ok;
wait_for(23,ok);
...
end