全面验证设计的唯一途径是采用受约束的随机测试方法(CRT),而功能覆盖率是用来衡量哪些设计特征已经被测试程序测试过的一个指标。
如何衡量验证的进展?
依靠完善的验证计划test plan和对应的功能覆盖率coverage。
用验证计划和对应功能覆盖率的结果来指导验证的进展,这样才能站在更高的抽象层次上看待验证。
根据功能覆盖率结果来思考如何提高功能覆盖率,并在功能覆盖率达标或者止步不前时思考验证计划本身。
(1)运行一个带多个种子的测试;
(2)检查运行通过与否;
(3)分析通过多次运行得到的覆盖率;
道法术
验证人员的最重要工作是制定完善的验证计划,这是验证之道。
搭建验证环境和编写对应验证计划的覆盖率代码,可以认为是法。
编写测试用例,就是术了。
9.1 覆盖率的类型
代码覆盖率
根据不同仿真工具有不同分类,比如S的line/condition/togggle/fsm,C的block/expression/toggle/fsm。后续处理工具会把数据库转换成可读格式。最终的结果用于衡量你执行了设计中的多少代码。关注点应该放在对设计代码的分析上,不是测试平台。
功能覆盖率
也被称为规范spec覆盖率。spec通常包括features,boundary/corner case,error injection。
漏洞率
漏洞率是另一个指标,指项目追踪期间新漏洞出现的概率。
断言覆盖率
断言是用于一次性地或一段时间内核对两个设计信号之间关系的声明性代码。
断言常用于查找错误,例如两个信号是否应该互斥或者请求是否被许可等。
cover property,会检查property是否至少成立一次,不同于assert要求property一直成立。
9.2 功能覆盖策略
收集信息而非数据。
收集覆盖率数据开销很大,要控制收集范围。只测量你将会分析并用来改进测试的那些内容。
保存给出高覆盖率的随机种子,以备回归测试用。
目标是取得高的代码和功能覆盖率。
要注意思考,漏洞是根据验证计划特意检查的,还是说完全出乎意料地随机撞见的?如果是后者,说明验证计划仍需完善。
9.3 功能覆盖率的简单例子
为测量功能覆盖率,先编写验证计划和相对应的可用于仿真的可执行版本。在System Verilog测试平台中对变量和表达式的数值进行采样。采样的地方就是覆盖点。同一时间点(比如当某一事务处理完成时)的多个覆盖点被一起放在一个覆盖组里。
例 9.2 的设计事务有八种不同的情形。测试程序随机产生port变量,验证计划要求把每一种情形都测试到。
例9.2 一个简单对象的功能覆盖率
program automatic test(busifc.TB ifc);
class Transaction ;
rand bit [31:0] data;
rand bit [2:0] port; //八种端口(port)数据
endclass
covergroup CovPort;
coverpoint tr.port; //测量覆盖率
endgroup
initial begin
Transaction tr;
CovPort ck;
ck=new(); //实例化组
tr=new(); //
repeat (32) begin //运行几个周期
assert (tr.randomize); //创建一个事务
ifc.cb.port<=tr.port; //并发送到接口上
ifc.cb.data<=tr.data;
ck.sample(); //收集覆盖率
@ifc.cb; //等待一个周期
end
end
endprogram
例9.2 创建了一个随机的事务并把它驱动到接口上。这个测试程序使用CovPort覆盖组对port字段的数值进行采样。八种可能的数值,32次随机事务——你的测试平台把所有情形都测试过了吗?下面是VCS给出的覆盖率报告的一部分。
例9.3 一个简单对象的覆盖率报告
Coverpoint Coverage report
Coveragegroup:CovPort
Coverpoint:tr.port
Summary
Coverage:87.50
Goal:100
Number of Expected atuo-bins:8
Number of User Defined Bins:0
Number of Automatically Generated Bins:7
Number of User Defined Transactions:0
Automatically Generated Bins
Bin #hits at least
auto[1] 7 1
auto[2] 7 1
auto[3] 1 1
auto[4] 5 1
auto[5] 4 1
auto[6] 2 1
auto[7] 6 1
可以看出,测试平台产生了1、2、3、4、5、6和7,但没有产生数值为0的port。at least一栏标出的是一个仓(bin)被认为已经覆盖所需要的最低命中(hit)次数。
改进功能覆盖率最简易的办法是仅仅增加仿真的时间或尝试新的随机种子。如果某个覆盖点没有命中或仅命中一次,可能需要改变策略,因为测试程序没有给出适当的激励。
例9.4 对例9.3 增加一个事务(数据#33),恰好给出了数值为0的port值,达到100%覆盖率。
Coverpoint Coverage report
Coveragegroup:CovPort
Coverpoint:tr.port
Summary
Coverage:100
Goal:100
Number of Expected atuo-bins:8
Number of User Defined Bins:0
Number of Automatically Generated Bins:8
Number of User Defined Transactions:0
Automatically Generated Bins
Bin #hits at least
auto[0] 1 1
auto[1] 7 1
auto[2] 7 1
auto[3] 1 1
auto[4] 5 1
auto[5] 4 1
auto[6] 2 1
auto[7] 6 1
9.4 覆盖组详解
覆盖组与类相似——一次定义后可多次例化。它包含有覆盖点、选项、形式参数和可选触发(trigger)。一个覆盖组包含了一个或多个数据点,全都在同一时间采集。
覆盖组可以在程序、模块或类里定义。在所有情况下,覆盖组都要进行明确的实例化才可以开始采样。
例9.5 类里的功能覆盖率
class Transaction;
Transaction tr;
mailbox mbx_in;
covergroup CovPort; //定义覆盖组CovPort
coverpoint tr.port; //测量覆盖率
endgroup;
function new(mailbox mbx_in);
CovPort=new(); //实例化覆盖组
this.mbx_in=mbx.in;
endfunction
task main;
foerver begin
tr=mbx_in.get; //获取下一个事务
ifc.cb.port<=tr.port; //发送到待测设计中
ifc.cb.data<=tr.data;
CovPort.sample(); //收集覆盖率
end
endtask
endclass
9.5 覆盖组的触发
功能覆盖率的两个主要部分是采样的数据和数据被采样的时刻。当这些新数据都被准备好了以后(比如一个事务结束),测试平台便会触发覆盖组。
覆盖组触发可以直接调用sample函数,也可以用阻塞表达式wait或者@。
1)使用回调函数进行采样
2)使用事件触发的覆盖组
例9.8 带触发的覆盖组
event trans_ready;
covergroup CovPort @(trans_ready);
coverpoint ifc.cb.port; //测量覆盖率
endgroup
3)使用System Verilog断言(SVA)进行触发
例9.9 带SVA的模块
module mem(simple_bus sb);
bit [7:0] data, addr;
event write_event;
cover property
(@(posedge sb.clock)sb.write_ena==1)
->write_write;
endmodule
9.6 数据采样
当你在覆盖点上指定一个变量或表达式时,SystemVerilog便会创建很多的“仓(bin)
”来记录每个数值被捕捉到的次数。这些仓bin是衡量功能覆盖率的基本单位。
为了计算一个点上的覆盖率,首先需确定所有可能数值的个数,这也被称为域。一个仓可能有一个或多个数值。覆盖率就是采样值的数目除以域中仓的数目。
对于一个位宽为N的表达式,有2的N次方个可能的值。例如一个3bit的变量覆盖点的域是0:7,共八个仓。
覆盖组选项auto_bin_max指明了自动创建仓的最大数目,缺省值是64。
通过设置仓的值域范围来设置仓的个数。
例9.11 使用auto_bin_max并把仓数设置为2
covergroup CovPort;
coverpoint tr.port
{options.auto_bin_max=2;} //分成2个仓
endgroup
例9.13 在所有覆盖点中使用auto_bin_max
covergroup CovPort;
options.auto_bin_max=2;//影响port和data
coverpoint tr.port;
coverpoint tr.data;
endgroup
对表达式进行采样,始终需要核对覆盖率报告以确保能够得到预期值。
对一个3比特头长度(0:7)加上4比特负载长度(0:15)的加法表达式取样,只能得到2的4次方即16个仓,而如果实际数据可以达到0:23个字节时,仓数可能不够。
自己命名覆盖点的仓
bin设置时用 $可以表示范围表达式的上边界或者下边界,用wildcard来通配。
例9.17 指定仓名
Covergroup CovKind;
coverpoint tr.kind{
bins zero={0}; //1个仓zero代表kind==0
bins lo={[1:3],5}; //1个仓lo代表1:3和5的值
bins hi[]={[8:$]}; //8个独立的仓:8...15
bins misc=default; //1个仓misc代表剩余所有的值
} //声明语句,没有分号
endgroup //CoverKind
条件覆盖率用iff或start/stop来控制。
例9.20 条件覆盖——复位期间禁止
covergroup CoverPort;
//当reset==1时不要收集覆盖率数据
coverpoint port iff(!bus_if.reset);
endgroup
例 9.21 使用start和stop函数
initial begin
CovPort ck=new(); //实例化覆盖组
//复位期间停止收集覆盖率数据
#1ns ck.stop();
bus_if.reset=1;
#100ns bus_if.reset=0; //复位结束
ck.start();
...
end
翻转覆盖率用=>表示。
ignore_bins表示忽略bins,illegal_bins表示不合法bins。
9.7 交叉覆盖率
交叉覆盖率可以同时测量两个或两个以上覆盖点的组合值。使用SystemVerilog中的cross结构可以实现,cross语句只允许带覆盖点或者简单的变量名。
cross(交叉覆盖率)里可以用binsof和intersect来选定bins,binsof指定覆盖点,intersect指定数值集。
例9.33 在交叉覆盖中排除部分bin
covergroup Covport;
port:coverpoint tr.port
{bins port[]={[0:$]};
}
kind:coverpoint tr.kind{
bins zero={0}; //1个仓zero代表kind==0
bins lo={[1:3],5}; //1个仓lo代表1:3和5的值
bins hi[]={[8: $]}; //8个独立的仓:8...15
bins misc=default; //1个仓misc代表剩余所有的值
} //声明语句,没有分号
cross kind,port{
ignore_bins hi=binsof(port) intersect{7};
ignore_bins md=binsof(port) intersect{0}&&
binsof(kind) intersect{[9:11]};
ignore_bins lo=binsof(kind.lo);
}
endgroup
9.9 覆盖选项option
统计覆盖率时,有类型选项和实例选项。因为覆盖率可能进行了多次实例化。对应type_option和option。
类型选项,用于所有的覆盖组实例,类似于类中的静态数据成员。实例选项,用于特定的覆盖组实例。
covergroup里可以有type_option,比如type_option.comment;也可以有option,比如option.per_instance/comment/auto_bin_max/cross_num_print_missing/goal。
coverpoint和cross里可以用option,比如option.weight/at_least。
9.11 在仿真过程中进行覆盖率统计
动态获取覆盖率结果并动态控制
仿真过程中可以进行覆盖率统计,动态控制测试。
$get_coverage得到整体覆盖率结果。
用法:Covergroup::get_covergroup()或cgInst.get_coverage()。
get_coverage(path)和get_inst_coverage(path)得到具体某个覆盖组或者某个覆盖组实例的覆盖率结果。
用法:cgInst.get_inst_coverage()