摘要
本文介绍System Verilog提供的功能覆盖率分析方法,用于评估测试集对设计功能的覆盖情况,检验测试集的有效性和完整性。
- 功能覆盖率的定义和作用
- 基于IEEE标准介绍covergroup、coverpoint等使用语法
- 一些注意事项
1 前言
1.1 什么是功能覆盖率
随着芯片设计越来越复杂,覆盖率驱动的验证(CDV,coverage driven verification)成为必要的验证手段,需要使用覆盖率来保证验证的完备性。设计代码实现了若干个功能点(features),而我们一般不可能为每一个功能点设计对应的定向用例,而是通过随机用例来实现测试,如何保证我们的随机用例能够将这些功能点都覆盖到呢?就需要使用功能覆盖率。
首先和代码覆盖率的概念做一个区别。代码覆盖率(code coverage)是对代码执行情况的统计,它可以由仿真工具针对代码自动生成。而功能覆盖率则需要验证人员自己根据设计规范进行定义,因此它独立于实际的设计代码或其结构。
1.2 如何在验证中使用功能覆盖率
首先需要根据功能设计文档指定验证计划和编写功能覆盖率,然后编写受约束的随机用例,并使用不同的随机种子多次执行,然后收集功能覆盖率并进行分析,确认设计有缺陷的,进行修改;当覆盖率不再增长时,需要考虑编写定向用例,或者修改随机用例的约束。最终需要实现功能覆盖率实现100%. 此处仅介绍整体流程,具体的方法和技巧将在其他文章中介绍。
1.3 一个简单的例子
下面通过一个简单的例子来介绍功能覆盖率的实现。
program automatic test(busifc.TB ifc);
class Transaction;
rand bit [31:0] data;
rand bit [ 2:0] port;
endclass
Transaction tr;
//功能覆盖组的定义
covergroup CovPort;
coverpoint tr.port;
endgroup
initial begin
//实例化功能覆盖组
CovPort ck;
ck = new();
repeat (32) begin
@ifc.cb;
tr = new();
`SV_RAND_CHECK(tr.randomize);
ifc.cb.port <= tr.port;
ifc.cb.data <= tr.data;
ck.sample(); //覆盖率采样
end
end
endprogram
2 covergroup
2.1 covergroup的定义
covergroup
是覆盖率模型的载体,每个covergroup
可以包括以下组件:
- 同步覆盖点采样的时钟事件
- 一组采样点(coverage points)
- 覆盖点之间的交叉覆盖
- 可选的形式参数
- 覆盖选项
covergroup
是用户定义的类型。类型定义只需编写一次,就可以在不同的上下文中创建该类型的多个实例。与类类似,一旦定义了covergroup
实例,就可以通过new()
运算符创建它。covergroup
可以在package
、module
、program
、interface
、checker
或class
中定义。其语法格式如下:
covergroup covergroup_identifier [ ( [tf_port_list] ) ] [coverage_event];
{coverage_spec_or_option}
endgroup [:covergroup_identifier]
2.2 covergroup参数
tf_port_list
: 可选参数列表。端口类型只能为input
或者ref
,output
和inout
类型是非法的。定义为input
类型的参数在使用new
操作符例化时传入实际值,后面该变量变化时,covergroup
内的值不会随着变化。如果需要采集实时变化的输入参数,需要定义为ref
类型。
class test;
int port_a,port_b;
covergroup demo_cg(ref int cnt, input int mid) @(posedge clk);
counter_cp: coverpoint cnt{
bins part1[] = {[0:mid]};
bins part2[] = {[mid+1:10]};}
endgroup
port_b = 5;
demo_cg = new(port_a, port_b); //这里例化demo_cg, mid为5
....
port_a =1;
@(posdge clk); //sample, port_a=1
port_a =2;
port_b =4; //mid不再变化
@(posdge clk); //sample, port_a=2
...
endclass
2.3 covergroup采样
coverage_event
: 覆盖率事件,即定义何时对覆盖点进行采样。主要有两种方式:
sample()
函数采样。sample
是covergroup
的内建函数,可以调用sample
在指定的时刻进行采样。如果定义covergroup
时省略了coverage_event
,那么则使用默认的sample
函数。sample()
方法可以被用户重载,并传入参数,传入的参数可以作为被采样的对象。
class test;
logic[9:0] reg_addr;
covergroup address_cg with function sample(bit[9:0] addr);
coverpoint addr;
endgroup
...
address_cg.sample(reg_addr);
endclass
注意smaple
传入的参数只能作为coverpoint
、cross
中需要采集的一部分, 其他的使用方式会报错(covergroup
的参数既可以作为采样对象,也可以作为其他用途,如为option
赋值。):
covergroup demo_cg with function sample(boolean a, int b, int c);
coverpoint c;
option.per_instance = a; //Error
option.wegiht = b; //Error
endgroup
- 定义时钟事件采样。
@(event_expression)
,这里的表达式通常为event,posedge clk,signal等。当括号内的事件发生时便触发一次采样,无需显式调用sample函数。参见2.2中的示例。
3 coverpoint
3.1 coverpoint的定义
一个covergroup可以包含一个或多个coverpoint. 一个coverpoint可以覆盖一个变量或者表达式。coverpoint定义格式如下:
[cover_point_identifier:] coverpoint expression [iff(expression)]
bins_or_empty
可以在定义时指明label,这样在查看覆盖率时比较清晰,如果不指定的话,系统自动将变量名作为label。
real tim;
covergroup demo_cg;
coverpoint a; //方式1,不指定coverpoint的label
b_cp: coverpoint b; //方式2, 指定label
cb_cp: coverpoint c+b;
tim_cp: coverpoint tim; // Error,不可以是non-integral data type
d_cp: coverpoint d; // Error, 应先定义 int d;
endgroup
.....
int d;
可以使用关键词iff
给coverpoint
添加条件,这种做法常用于在复位期间关闭覆盖以忽略不合理的条件触发。
3.2 bins for value
当在coverpoint
中指定采样一个变量或者表达式时,SV会创建很多“仓(bins)”来记录每个数值被捕捉到的次数。这些bin
是衡量功能覆盖率的基本单位。
为了计算一个coverpoint
上的覆盖率,首先需要确定被采样队形可能数值的个数,也被称为域。覆盖率就是被击中的bin的数目除以总的bin的数目。例如一个2bit变量的域是0:7,正常情况下被自动分配8个bin,如果仿真过程中有7个值被采样到,那么该coverpoint的
覆盖率就是7/8,所有的coverpoint
的覆盖率构成一个covergroup
的覆盖率,所有的covergroup
的覆盖率构成整体的功能覆盖率。
coverpoint
中使用bins
来收集每个coverpoint
的变量的具体值,bins
可以用户自己指定,也可以由系统自动产生。- 如果采样变量的域范围过大且没有指定bin,那么系统会默认分配64个bin,将值域范围平均分配给这64个bin.
- 用户可以通过
covergroup
的选项auto_bin_max
来指定自动创建bin的最大数目。
-bins
可以指定为数组,bins demo_bin[num] = {x,y,...,z}
. 如果num
大于{x,y,...,z}
中实际元素的数量,则按实际元素数量创建bins
. 如果num
小于实际元素的数量,则进行平均分配,如果不能整除,则多余的放入最后一个bins
中。 - 在
bins
中可以使用iff(expression)
来限定采集的条件。
bit[3:0] a,b,c ;
covergroup demo_cg(input bit[3:0] mid);
coverpoint a {
bins zero = {0};
bins mult = {0,[2:3]}; //当a为 0/2/3便可以cover该bins
bins test1[13] = {[1:10],1,4,7}; //test1被分为13个子bins,分别为<1> ... <10> <1> <4> <7>
bins test2[20] = {[1:10],1,4,7}; //test2虽然定义了20个子bins,但实际上被分成了13个子bins,<1> ...<10> <1> <4> <7>
bins test3[4] = {[1:10],1,4,7}; //test3分成了4个子bins,<1,2,3>, <4,5,6>, <7,8,9>, <10,1,4,7>,这里是个trick
bins test4[] = {[1:10],1,4,7}; //test4被自动分成了10个子bins,<1> ... <10>, 重复的被merge在一起了
//这里default_bins 为<11> ...<15>,这些便不再建仓
}
coverpoint b {
bins sml[] = {[1:8]} iff(c==1); //在c==1时,才开始收集
bins big[] = default; //其他的<0>,<9>...<15>
}
c_cp: coverpoint c {
bins sml[] = {[0:mid]}; //mid的值在covergroup new的时候给出来。
}
endgroup
...
demo_cg = new(5); //指定mid=5
3.3 bins for sequence
coverpoint
也可以记录变量从A值到B值的跳转情况。
Coverpoint a {
bins test1 = (1=>2=>3); //收集变量a 从1变化到2再变化到3的场景
bins test2 = (1=>2), (2=>3); //收集变量a 从1变化到2或者2变化到3的场景
bins test3 = (1,2=>6,7); //表示收集1=>6/2=>6/1=>7/2=>7 四种场景之一
bins test4[] = (1,2=>6,7); //表示收集1=>6,2=>6,1=>7,2=>7 四种场景
bins test5 = (1=>[*3]2=>3); //收集1=>2=>2=>2=>3
bins test6 = (1=>[*3:5]2); //收集1=>3~5个2
bins test7 = (1[->3]=>5); //收集...=>1...=>1...=>1=>5,跳转到1三次(不一定要连续)后立即跳转到5 (...)表示任意数量的除1之外的任何值
bins test8 = (1[=3]=>5) ; //收集...=>1...=>1...=>1...=>5 ,跳转到1三次(不一定要连续)后跳转到到5(不一定马上)
}
3.4 wildcard、ignore_bins、illegal_bins
- 使用关键字
wildcard
,在表达式中,任何x, z或者?都会被当作0或1的通配符。
bit [2:0] port;
covergroup Coverport;
coverpoint port{
wildcard bins even = {3'b??0};
wildcard bins odd = {3'b??1};
}
endgroup
- 在某些
coverpoint
可能始终无法得到全部的域值,对于那些不计算功能覆盖率的域值可以使用ignore_bins
来排除,最终它们并不会计入coverpoint的覆盖率。
bit [2:0] low_port_0_5;
covergroup Coverport;
coverpoint low_port_0_5{
ignore_bins hi = {[6:7]}; //忽略数值6,7
}
endgroup
- 有些采样值不期望出现,出现时应该报错。可以使用
illegal_bins
进行标识。
bit [2:0] low_port_0_5;
covergroup Coverport;
coverpoint low_port_0_5{
illegal_bins hi = {[6:7]}; //如果出现6,7便报错
}
endgroup
4 交叉覆盖率
4.1 cross定义
coverpoint
记录的是单个变量或者表达式的观测值,有时候我们希望知道两个或以上覆盖点的组合取值情况,这个时候可以使用交叉覆盖率。如果一个变量有M种取值,另一个变量有N种取值,那么需要M*N个交叉仓来存储所有的组合。
使用cross
来记录两个或多个coverpoint
的组合值,如下是一个简单的例子:
class Transaction;
rand bit[3:0] kind;
rand bit[2:0] port;
endclass
Transaction tr;
covergroup CovPort;
kind : coverpoint tr.kind; //覆盖点kind,共16个取值,自动生成16个bins
port : coverpoint tr.port; //覆盖点port,共8个取值,自动生成8个bins
cross kind, port; //交叉覆盖,共16*8个组合,自动生成16*8个bins
endgroup
4.2 排除部分cross bin
自动生成的cross bin种可能包含一些我们并不关系的组合,这时可以通过ignore_bins bin_tag = binsof(cp_tag) intersect(cp_value)
来排除一些不关心的corss bin. binsof
用来指定coverpoint
,intersect
来指定值域。
covergroup Covport;
port : coverpoint tr.port{
bins port[] = {[0:$]};
}
kind : coverpoint tr.kind {
bins zero = {0};
bins lo = {[1:3]};
bins hi[] = {[8:$]};
bins misc = default;
}
cross kind, port {
ignore_bins hi = binsof(port) intersect {7}; //忽略port取值为7的组合
ignore_bins md = binsof(port) intersect {0} && binsof(kind) intersect {[9:11]};
ignore_bins lo = binsof(kind.lo); //忽略kind取值为lo的组合,等同于ignore_bins lo = binsof(kind) intersect {[1:3]};
}
endgroup
4.3 精细的交叉覆盖率指定
如果关心的交叉组合较少,而自动生成的cross bin太多,需要排除的情况太多,这是可以声明自己感兴趣的cross bin.
假如两个随机变量a和b,我们只关心三种组合状态:{a==0, b==0}
、{a==1, b==0}
和{b==1}
,那么可以按下述方式定义:
class Transaction;
rand bit a, b;
endclass
covergroup CrossBinNames;
a : coverpoint tr.a {
bins a0 = {0};
bins a1 = {1};
option.weight = 0; //不计算覆盖率
}
b : coverpoint tr.b {
bins b0 = {0};
bins b1 = {1};
option.weight = 0; //不计算覆盖率
}
ab : cross a, b {
bins a0b0 = binsof(a.a0) && binsof(b.b0); //等价于 bins a0b0 = binsof(a) intersect {0} && binsof(b) intersect {0};
bins a1b0 = binsof(a.a1) && binsof(b.b0);
bins b1 = binsof(b.b1);
}
endgroup
5 覆盖率选项
SV提供一些选项来对covergroup或者coverpoint进行设置,下表列出了一些常用选项option的说明。
选项名 | 默认值 | 说明 | 适用范围 |
---|---|---|---|
option.comment=string | “” | 注释,若一个covergroup例化多份,可在例化时通过参数传入不同的comment | covergroup |
option.per_instance=boolean | 0 | 为1表示例化多份时,每一个实例一个覆盖率;为0时覆盖率合并 | covergroup |
option.weight=number | 1 | 计算覆盖率时所在covergroup或coverpoint或cross的权重。常用于在不关心的coverpoint种的weight设置为0 | all |
option.at_least=number | 1 | 表示每个bin最少被采样多少次才算入命中 | all |
option.goal=number | 100 | 覆盖率目标 | all |
option.auto_bin_max=number | 64 | 自动产生bins的最大数量 | all |
除了option选项,还有type_option,type_option常用选项包括type_option.weight
和type_option.goal
。
type_option
作用于某类型,而option
作用于某个实例。
option.weight
和type_option.weight
的区别如下:
type_option
:顾名思义为类型选项,如果设置type_option.weight=0,则covergroup的类型在整体覆盖率计算时的其权重为0,不计入总体覆盖率,但是在单个实例覆盖率中仍然计算。
option
: 为实例选项,如果设置某coverpoint的option.weight=0,则计算单个实例时,该coverpoint的权重为0,但是在计算整个类型的覆盖率时,依然考虑其权重。
6 覆盖率系统方法
SV提供了一些内建的方法,如下表:
function | Can be called on | Despription |
---|---|---|
void sample() | covergroup | 触发采样 |
real get_coverage() | covergroup coverpoint cross | 计算该类型的覆盖率,返回0-100 |
real get_inst_coverage() | covergroup coverpoint cross | 计算该实例的覆盖率,返回0-100 |
void set_inst_name(string) | covergroup | 设置实例名称 |
void start() | covergroup coverpoint cross | 开启覆盖率信息收集 |
void stop() | covergroup coverpoint cross | 停止覆盖率信息收集 |
此外使用$get_coverage()
可以获得整体的覆盖率。
这些函数最实际的作用是在一个测试中监测覆盖率的变化,当覆盖率达到目标值时便可停止发送激励。如果覆盖率水平在一段事件之后不再提高,那么这个测试也应该停止,需要更换新的随机种子或者调整激励的约束。如果测试可以基于功能覆盖率采取一些深入的行动,例如重新限定随机的约束,那将极大提高测试效率,但是编写这种测试用例需要很精巧的设计。
7 覆盖率计算
覆盖率的计算层次由低到高为:coverpoint/cross --> covergroup实例(instance) --> covergroup类型 (type)–> 整体覆盖率
7.1 coverpoint 覆盖率的计算
一个coverpoint的覆盖率计算公式如下:
如果一个bin里没有任何有效的值(value)或翻转(tansition),那么它将不会对覆盖率计算有贡献,也就是说将会从分子和分母中都排除掉。如下面的例子:
bit [2:0] a, b;
covergroup ct;
coverpoint b {
option.auto_bin_max = 4;
ignore_bins ig = {[0:1}, [5:6]};
}
endgroup
在这个例子中,coverpoint
会自动生成4个bins,auto[0,1]
,auto[2,3]
, auto[4,5]
, auto[6,7]
. 但是ignore_bins
指定了对0, 1, 5, 6
这几个值的采样将会被忽略,那么实际上四个bins变成了auto[]
,auto[2,3]
, auto[4]
, auto[7]
,auto[]
不会对覆盖率计算有贡献。
7.2 cross 覆盖率计算
7.3 instance 覆盖率计算
7.3 type 覆盖率计算
类型覆盖率的计算有两种方式,由type_option.merge_instance
选项决定。当该选项为0时,类型覆盖率为各实例覆盖率的加权平均值。当该选项为1时,将各实例的覆盖率进行合并。
默认type_option.merge_instance
为0,即按照加权平均计算类型覆盖率,计算公式如下:
而如果type_option.merge_instance
为1,则在计算覆盖率时将各个实例的bins进行合并,然后统一计算覆盖率。
如下面的例子:
covergroup gt (int l, h);
coverpoint a {
bins b[] = { [l:h] };
}
endgroup
gt gv1 = new(0,1);
gt gv2 = new(1,2);
上述例子中,b[1]
这个bin在在gv1
和gv2
中有重复,covergroup gt
有b[0] b[1] b[2]
这3个bin,如果gv1
采样到了a=0
,而gv1
和gv2
都采样到了a=1
,那么对于gt::get_coverage()
结果为66.67(2/3)
,而gv1.get_inst_coverage()=100
,gv1.get_inst_coverage()=50
.
参考资料:
[1] https://blog.csdn.net/qq_40123322/article/details/115046090
[2] https://stackoverflow.com/questions/28968091/option-type-option-in-system-verilog
[3] https://www.chipverify.com/systemverilog/systemverilog-covergroup-coverpoint