文章目录
前言
断言(assertion)是验证工作中常用的自动检查手段,相比于checker或者reference model更加轻量化也更方便移植,同时也是收集功能覆盖率的重要一环。
一、断言分类
1、即时断言 Immediate Assertions
即时断言作用和 if 语句的效果类似,在仿真运行到这句代码时做出判断。
int a;
assert(a) else `uvm_error(get_type_name(), "assert fail");
if(a) else `uvm_error(get_type_name(), "assert fail");
如上两句代码,在验证环境中效果是一样的,都是运行到这行代码时,a非0就报错。
需要注意的是,断言如果违例,默认报错的信息是“ Offending xxx ”,加上 else `uvm_error() 语句会更加方便。
2、并发断言 Concurrent Assertions
并发断言是最常用的,一般和时序强相关的检查都可以用这种形式实现,本文内容也主要描述并发断言。
并发断言需要指定采样时钟,可以在module、interface里实现,在整个仿真周期内都起效。
ast_ack_when_req: assert property(@(posedge clk) disable iff(~rstn) ack |-> req)
else `uvm_error("xxx", "ast_ack_when_req fail");
如上就是一个简单的检查,在rstn为1时生效,每当clk的上升沿判断,如果当拍a为高,检查下一拍b为高,否则报错。
符号|->称为蕴含符,只有当蕴含符前面的条件满足时,才检查后面的时序是否正确。
二、断言的层次结构
SVA中可以存在内建的单元,这些单元可以是以下的几种:
1、Boolean expressions 布尔表达式
布尔表达式是组成断言的最小单元,断言可以由多个逻辑事件组成,这些逻辑事件可以是简单的布尔表达式.在SVA中,信号或事件可以使用常用的操作符,如:&&, ||, !, ^,&等;
2、Sequence序列
sequence是布尔表达式更高一层的单元,而且可以表达时序关系。一个sequence中可以包含若干个布尔表达式,同时在sequence中可以使用一些新的操作符,如 ## 、重复操作符、序列操作符等。
sequence具有如下特性:
可带参数;
可以在 property 中调用;
可以使用局部变量;
可以定义时钟周期;
可以使用序列方法,如 ended、matched、triggered,这些序列方法只能在sequence中使用;
sequence seq(a, b); //形参a,b方便复用
@(posedge clk) a ##2 b; //在时钟clk上升沿采样,a为1时,两拍后b也为1
endsequence
seq方法:ended
默认情况下,多重sequence的组合是以sequence的起始时间作为同步标志的,就是以序列的起始点作为同步点,来组合成时间上连续的检查。
SVA提供ended结构以sequence的结束时间作为序列同步点。关键字ended存储一 个反映在指定时钟处序列是否匹配成功的布尔值。
ended代表匹配的完成,是匹配的结束点,而不是匹配的起点。
3、Property 属性
property是比sequence更高一层的单元,也是构成断言最常用的模块,其中最重要的性质是可以在property中使用蕴含操作符(|-> |=>);property的定义格式如下:
property name_of_property(参数列表);
布尔表达式 或 sequence;
endproperty
property就是SVA中需要用来判定的部分,用来模拟过程中被验证的单元,它必须在模拟过程中被断言来发挥作用,SVA提供了关键字 assert 来检查属性,assert的基本语法是:
assertion_name: assert property(property_name) else $display("SVA error");
//else语句在断言失败时才会触发,可以不写
1、property 和 sequence
如果有两组信号的检查逻辑是类似的,可以使用property来复用,将两组信号作为参数传入。
property ack_when_req_p(logic ack, logic req);
@(posedge clk) disable iff(~rstn) ack |-> req; // 可以复用的检查逻辑
endproperty
ast_cmd_ack: assert property(ack_when_req_p(cmd_ack, cmd_req));
ast_data_ack: assert property(ack_when_req_p(data_ack, data_req));
如果有一段时序需要复用,可以使用sequence描述这段时序,然后在不同的property内复用。
sequence req_ack_success_s;
req && ack; // req和ack同时为高
endsequence
property no_consecutive_p(logic ack, logic req);
@(posedge clk) disable iff(~rstn)
req_ack_success_s |-> ##1 not req_ack_success_s; // 连续两次req和ack握手成功间隔必须大于1拍
endproperty
property与sequence的相同和不同之处:
- 任何在sequence中的表达式都可以放到property中;
- 任何在property中的表达式也可以搬到sequence中,但是只有在property中才能使用蕴含操作符;
- property中可以例化其他property和sequence,sequence中也可以调用其他的sequence,但是不能例化property;
- property需要用cover/assert 等关键字进行实例化,而sequence直接调用即可;
三、辅助用法
1、generate
断言和verilog语句类似,只是多了一些独特的用法和关键词,也支持用generate语句批量产生断言。如果DUT内有一个module例化多份,一个断言规则在这些实例中都适用,那么这个功能就很好用。
`define INST(num) tb_top.dut.inst``num // 宏定义路径,用法详见文章 [宏定义日常用法](https://blog.csdn.net/Zwwang97/article/details/136601241)
generate
for(genvar num=0; num<4; num++) begin: inst_gen
ast_ack_when_req: assert property(@(posedge clk) disable iff(~rstn)
`INST(num).ack |-> `INST(num).req) // 通过路径宏分别索引到各个inst,ack为高的同时req必定为高
else `uvm_error("xxx", "ack when req is 0");
end
endgenerate
2、if else
如果对于cmd_req/ack信号,写命令是当拍回ack,但是读命令是下一拍,可以分开写成两个,也可以使用if else语句写成一个。
ast_ack_when_req: assert property(@(posedge clk) disable iff(~rstn)
cmd_ack |->
if(cmd_type == WR)
cmd_req
else if(cmd_type == RD)
$past(cmd_req))
else `uvm_error("xxx", "ack when req is 0");
3、变量延迟
断言中只支持##1这种常数delay,但如果delay值是不确定的(如和寄存器配置值有关),可以用以下方法。
ast_ack_after_req: assert property(@(posedge clk) disable iff(~rstn)
int delay;
(req, delay = cfg_delay) ##0 (delay>0, delay=delay-1)[*0:$] ##0 (delay == 0) |-> ack) // req为高后cfg_delay拍,ack必为高
else `uvm_error("xxx", "ast_ack_after_req fail");
总结
以上就是功能覆盖率中写断言时会用到的一些用法。