一、简介
断言(assertion)一般可以放到RTL设计代码中,比较方便我们在仿真出现异常时查看它所报警的东西。通常意义上的断言具有:1)检查特定条件或事件序列的出现情况,提供功能覆盖。
- 根据spec提取逻辑关系或者时序关系
- 建立sequence得表达式
- 建立property,调用对应得sequence
- 断言属性:a1:assert propert()
- cover assert a1
二、立即断言(immediate assertions)
立即断言会检查当前仿真时间的条件,需要放到过程块中。语法是labels:assert(expression) action_block;
- action_block会在断言表达式的求值后立即执行;
- action_block指定在expression成功或者失败采取什么操作;
actioin_block:pass_statement;else fail_statement
asser(expression)$display("express evaluates to ture");
else $fatal("expression evaluates to false");
- 其中expression中所断言的条件必须为真,如果发送断言失败,我们需要用相应不同程度的去描述,由上往下严重程度递减;
- $fatal:a run-time fatal
- $error: a run-time error;
- $warning:a run-time warning
- $info:information
module top_tb;
bit clk,a,b;
always #5 clk = !clk;
initial begin
a = 1;
b = 1;
#15 b = 0;
#10 b = 1;
a = 0;
#20 a = 1;
#20;
$finish;
end
always@(posedge clk)
check_a_and_b:assert(a&&b);//2:$display("a&&b is ture");else $fatal("a&&b is false");
//3.:$display("a&&b is ture");else $error("a&&b is false");
endmodule
第2种(带有action_block)
第3种(使用error)
三、并发断言(concurrent assertion)
并发断言可以检查多个时钟周期的时间序列,可以将并发断言看成是一个连续运行的模块,为整个仿真过程检查信号
- 并发断言仅在有时钟周期的情况下出现;
- 测试表达式是基于所涉及变量的采样值在时钟边沿进行计算的;
- 可以在过程块、module、interface和program块内进行定义并发断言;
- 区别并发断言和立即断言的关键字是property
always@(posedge clk)begin//ime
check_a_and_b:assert(a&&b)$display("a&&b is ture");
else $error("a&&b is false");
end
check_a_and_b:assert property@(posedge clk)(a&&b))
3.1 SV assertion的层次结构
SVA可以存在内建的单元,
- 布尔表达式:断言的最小组成单元,断言可以由多个逻辑事件组成,这些逻辑事件可以是简单的布尔表达式;
- sequence序列:一个sequence中可以包含若干个布尔表达式,同时在sequence中可以使用一些新的操作符:`##、重复操作符、序列操作符;
- property:断言最常见的模块,其中可以包含(|-> |=>);
3.2 sequece
在任何设计中,功能总是由多个逻辑事件的组合来表示,这些事件可以是简单的同一个时钟沿被求值得布尔表达式,也可以是经过几个时钟周期的求值事件,SVA用关键字 sequence来表示这些事件,sequence可以让断言易读,复用性高;
sequence name_of_sequence(参数);
...
endsequence
特点:
- 可带参数
- 可以在property中调用;
- 可以使用局部变量;
- 可以定义时钟周期;
check_a_and_b:assert property@(posedge clk)(a&&b)$display("a&&b is ture");
else $error("a&&b is false");
sequence seq_a_and_b;
@(posedge clk)a&&b;
endsequence
//property中调用sequence
check_a_and_b:assert property(seq_a_and_b);
check_a_and_b:assert property(seq_a_and_b)$display("a&&b is ture");
else $error("a&&b is false);
1、sequence还可以通过参数去给其他的调用
//带参数的sequence
sequence seq1(sig1,sig2);
@(posedge clk)sig1&sig2;
endsequence
//在断言property中调用sequence
check_a_and_b:assert property(seq1(a,b));
check_a_and_b:assert property(seq1(a,b))$display("a&&b is ture");
else $error("a&&b is false");
2、sequence内可以调用其他sequence;
sequence seq1;
@(posedge clk)a&&b;
endsequence
sequence seq2;
seq1;
endsequence
3、带时序关系的sequence :在SVA中时钟延时用符号"##“来表示,如”##2"表示延时两个时钟周期;
sequence seq2;
@(posedge clk)a ##2 b;//a成立后两个时钟后b也成立,如果a没有成立则这个不算成立
endsequence
check_a_and_b:assert property(seq2);
3.3 property
property和sequence的异同
- 任何sequence可以放到property中;
- 任何property中的表达式也可以搬到sequence中,但是只有在property中才能使用蕴含操作符;
- property中可以例化其他property和sequence,sequence中也可以调用其他的sequence,但是不能例化property
- property需要用cover /assert/assume 等关键字进行实例化,而sequence直接调用即可;
property name_of_property(var);
测试表达式或是复杂的sequence;
endproperty
3.4 蕴含操作符
- 蕴含操作符的含义:如果property中左边的先运算子成立,那么property右边的后续运算子才会被计算。如果先行云算子不成功,那么整个属性就会被默认地认为成功,这叫做“空成功”;
- 蕴含只能用在属性定义中,不能在sequence中使用。
交叠蕴含
运算操作符|->如果满足条件,则评估后续算子序列;如果条件不满足,则表示为空成功,不执行后续算子。
property p_req_ack;
@(posedge clk) mem_en |-> (req ##2 ack);
//当men_en为高时,此时req为1 ,过两拍,ack要为拉高
endproperty
/*
【注意】
*断言成功:左边算子成功,右边算子成功
*断言失败:左边算子成功,右边算子失败
*/
非交叠蕴含
运算操作符 |=>如果满足条件,则在下一个周期评估后续算子序列;如果条件不满足,则表示为空成功,不执行后续算子。
property p_req_ack;
@(posedge clk) mem_en |=> (req ##2 ack);
//当men_en为高时,下一拍的req为1 ,过两拍,ack要为拉高
endproperty
四、触发判断
operator | description | example |
---|---|---|
##n | n cycle delay | req ##2 grant |
##[m:n] | m~n cycle delay | req##[1:2]grant |
##[*] | ##[0:$] | req ##[*] ack //when req, gnt should come eventually |
##[+] | ##[1:$] | req ##[+] ack //after req (next cycle), ack should come eventually |
a[*3] | a ##1 a ##1 a | Consecutive Repetition,a连续3个cycle都成立 |
a[->2] b | a ##[+] a ##1 b | a成立2次(不要求在连续的2个cycle内),1个cycle后b成立 |
a[=2] b | a ##[+] a ##[+] b | a成立2次(不要求在连续的2个cycle内),b不必匹配连续的时钟周期 |
判断多个事件/信号得行为关系
operator | description |
---|---|
intersect(a,b) | 断定a和b两个事件同时产生,且同时结束 |
a within b | 断定b事件发生得时间段里包含a事件发生得时间段 |
a ##2 b | 断定a事件发生后2个时间内b事件一定会发生 |
c throughout(a ##b) | 断定a事件成立到b事件成立的过程中,c事件“一直“成立 |
@(posedge clk)a /->b | 断定clk上升沿后,a事件”开始发生”,同时,b事件发生 |
@(posedge clk)a[*3] | 断定在3个时钟周期内都成立 |