1 Assertion介绍
- 什么是断言:断言是用来与设计功能和时序做比较的属性描述;
- 断言可以用来完成
- 检查设计的内容;
- 提高设计可视度和调试能力;
- 检查设计特性是否在验证中被覆盖;
- 可以自由打开和关闭;
- 一小部分可以用来综合或者移植到emulation中,用来完成跨平台的移植;
- 类型划分
- 非时序;
- 执行过程如同过程语句;
- 可以在initial/always活成块或者task/function中使用;
- 时序性的;
- 关键词property用来区分立即断言和并行断言;
- 之所以称之为并行,是因为它们与设计模块异同并行执行;
- 并行断言只在时钟边沿激活,变量的值是采样到的值;
- 在property内部、外部关闭property;
2 sequence定义
- sequence是用来表示一个或多个时钟周期内时序描述;
- 是property的基本构建模块,并经过组合来描述复杂功能属性;
- sequence用来提供下列的场景描述:
3.1. 第一个时钟周期,第一个表达式成立;
3.2. 接下来若各个时钟周期,第二个表达式也成立;
3.3. 以此类推,在接下来的若干时钟周期,后续表达式也成立;
3.4. sequence可以在module、interface、program、clocking块和package中声明,不可以在类中声明;
3.4. sequence可以提供形式参数,用来提高复用性;
3.5. 蕴含操作符:如果property中左边的先行算子成立,那么property右边的后续算子才会被计算;
3.6. 如果先行算子不成功,那么整个属性就默认为成功,这叫做“空成功”;
3.7. 蕴含结构只能用在属性定义中,不能在序列中使用; - 基本操作符
##
用来表示周期延迟符号,例如##n
表示在n个周期后,##0
表示在当前周期,即交叠周期;a ##0 b
可以表示为a && b
;##[min:max]
表示在一个范围内的时钟周期延迟,min、max必须是非负数,序列从min到max时间窗口中最早的时间来匹配;- 事件也可以通过
[*n]
操作符号来表示重复,n为非负数; - 类似,也可以使用
[*m:n]
来表示一定范围内的重复事件; [=3]
用来表示一个事件的连续重复,需要重复发生m次,但并不需要在连续周期内发生;- 类似的,
[=m:n]
用来表示从最小m到最大n的重复发生的非连续周期次数; a[*0]
用来表示没有在任何正数时钟周期内有效;- 例如
a[*0:3] ##1 b ##1 c
与那些清醒匹配呢?
- (b ##1 c)
- (a ##1 b ##1 c)
- (a ##1 a ##1 b ##1 c)
- (a ##1 a ##1 a ##1 b ##1 c)
and
用来表示两个序列需要保持匹配;- 用法:SEQ1 and SEQ2
- 下列情形将满足此操作符:
3.1. 在同一个起始点开始,seq1和seq2均满足;
3.2. 满足的时刻发生在两个序列都满足的周期,即稍晚时刻序列的满足时刻;
3.3. 两个序列的满足时间可以不同; - 如果操作符两边的序列都是用来衡量采样信号而非事件序列,那么要求在相同周期内,and左右两边的序列都应该满足条件;
- 用法:与
and
操作符相似,要求操作符两边的事件序列在同一时刻开始,同一时刻结束;
- 用法:用来表示两个序列至少有一个满足;
- 下列情形将满足此操作符:
2.1. seq1和seq2都从同一时刻被触发;
2.2. 最终满足seq1或者满足seq2;
2.3. 每个序列的结束时间可以不同,结束时间移序列满足最后一个序列时间为准;
- 用法:
first_match
用来从多次满足的序列中选择第一次满足时刻,从而放弃其他满足时刻; - first_match(SEQ),只会选择所有seq中最短的序列;
- 可以用来检查一个信号或者表达式在贯穿一个序列时是否满足要求;
- 用法:Sig/Exp1 throughout Seq
- 例如,在burst信号拉低2拍后,irdy/trdy也应该在连续7拍内拉低,并且burst信号也应在这一连续周期内保持为低;
sequence burst_rul1;
@(posedge clk)
$fell(burst) ##0
(!burst) throughout (##2 (!irdy && !trdy) [*7]);
endsequence
within
操作符- 用来检查一个序列与弄一个序列在部分周期长度上的重叠;
- 局部变量
- 局部变量可以在sequence或者property中使用;
- 这些变量会伴随着sequence、property动态创建;
- 每个sequence实例都会有它自己的变量拷贝;
- 在记录变量时,局部变量与sequence采用“,”分隔开来;
- 例如,在cache rdDone拉高后,读出的rdData会在2个周期后,在其基础上加1,并作为wrData写入;
sequence check_reg_wr_data;
int local_data;
(rdDone && local_data = cache_rd_data) ##2 (reg_wr_data == local_data + 1);
endsequence
- 例如,如果read拉高,伴随readId,则在下一次read必须在这一次readAck返回之后,再发起;
property checkReadIdAck;
int local_id;
($rose(read), local_id = readId) |=> (not ($rose(read) && readId)) && ($rose(read) && readAckId == local_id);
endproperty
- 调用方法
- 在序列匹配时,可以调用task, void function和系统函数;
- 例如,可以在s1序列末尾,分别打印出e和f变量被采样时的数值;
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
3 断言中常见的函数
- 访问采样的方法
$stable(expression, clocking_event)
,用来表示在连续的两个采样周期内,表达式的值保持不变,如果满足,返回1,否则返回0;$past(expr, [num_cycle])
用来访问在过去若干采样周期前的数值,num_cycle默认为1;$rose、$fell、$stable
也可以在过程块和连续赋值语句中使用;- 在sequence/propety/assertion中使用:
1.1. $countbits 计算向量位宽
1.2. $onehot 检查表达式中是否只有1位为1
1.3. $isunknown 检查表达式中是否有x或者z
1.4. $countones 计算表达式中为1的位数 - 这些系统函数也可以一般在过程语句块和赋值语句块中;
- 在assertion或者property中,可以通过“disable iff”来给assertion做局部的条件控制;
- 在全局控制方面,可以通过系统函数对property模块或者实例作出控制:
a. $asserton
,默认控制,用来打开所有assertion;
b. $assertoff
, 暂时停止assertion运行;
c. $assertkill
,终止所有运行的assertion; - 例如,在reset阶段停止所有assertion,并且在其复位之后,使能所有assertion;
module assert_control;
initial begin
@(negedge rst_n)
$assertoff(0, top_tb.cpu_inst1);
@(posedge rst_n)
$asserton(0, top_tb.cpu_inst1);
end
endmodule