SystemVerilog functional coverage 学习
前言
基于《IEEE Standard for SystemVerilog — Unified Hardware Design, Specification, and Verification Language》19章的学习和自己的理解。有不对的地方希望大家补充。 编译工具 Cadence的Xcelium, coverage收集工具是IMc
Overview
标准上对Func_coverage的定义是:
Functional coverage is a user-defined metric that measures how much of the design specification, as enumerated by features in the test plan, has been exercised.
个人理解:
衡量验证激励完备性及场景完备性的一个标准,Functional_coverage可以检查
1. 你关心的场景是否覆盖
2. 构造的随机激励的随机程度
3. 对应的feature点是否有覆盖
Functional_coverage与code coverage不同. Code coverage 由工具自动产生, Functional coverage是验证工程师基于对DUT的认识来对验证feature进行分解后,将需要覆盖的点进行分析后写出。 因此对于DUT的认识非常重要。
Covergroup定义与收集
Covergroup定义与例化
Covergroup是承载coverage的容器。 coverage只能收集integral Data types,对于real等类型的数据是不能收集, 附:integral data type。
Covergroup只能定义在package/class/module/program/checker/interface中。
方式有两种:
1. 常规方式;
covergroup demo_cg; ... endgroup
demo_cg test_cg_inst = new;
这种方式可以例化多份,但注意covergroup 不能例化为数组:
demo_cg test_cg_inst[4]; //Error: Arrays of covergroup instances are not supported.
demo_cg a_inst, b_inst; a_inst = new; b_inst = new; // OK
- Class中, 支持embedding covergroup 可以简洁的方式快速采集Class中的变量
Class A;
covergroup demo_cg; ... endgroup
demo_cg = new;
endclass
Covergroup可以传入参数,端口类型的话只可以的input/ref, Output与inout是不支持的。定义为input的变量在例化时便定义好了,后面该变量变化,Covergroup内的值不会随着变化,如果需要采集变化的变量,需要定义为ref类型。
class test;
int port_a,port_b;
covergroup demo_cg(ref int cnt, input int mid) @(posedge clk);
counter_cg: 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
============================================================
NOTICE: 如果使用Xcelium作为编译工具:covergroup只能定义在uvm_component中,不能定义在uvm_object中, 否则无法采集(IMc 上看不到该coverage),貌似是工具bug, VCS并没有这种问题。
Coverage的sample
采集coverage的方式有很多,可以使用自带的sample函数,也可以使用 @来指定。
- Sample的方式
sample是Covergroup自带函数,可以调用sample来指定的采样时刻,Sample可以被override, 传入参数,传入的参数可以作为采集的一部分。
Class test;
logic[3:0] port_a;
covergroup demo_cg with function(bit[3:0] i);
coverpoint i;
endgroup
...
port_a = 1;
demo_cg.sample(port_a);
endclass
但注意 Sample传入的参数只能作为coverpoint、cross中需要采集的一部分, 其他的使用方式会报错:
covergroup demo_cg with function(boolean a, int b, int c);
coverpoint c;
option.per_instance = a; //Error
option.wegiht = b; //Error
endgroup
- 使用clocking event进行触发, @(expression), 这里的expression通常为 event,posedge clk, singal等,在这里expression值发生变化便会触发采样(如果是@(singal), signal的值发生变化及采样)。
int port_a;
covergroup demo1_cg@(port_a == 1); endgroup
covergroup demo2_cg@(port_a, iff port_a == 1 ); endgroup
...
@(posedge clk); port_a = 1; //(port_a == 1)0->1, port_a 0->1, demo1_cg, demo2_cg都发生采样
@(posedge clk); port_a = 1; // (port_a == 1) 1->1无变化 port_a 1->1变化, demo1_cg, demo2_cg都不采样
@(posedge clk); port_a = 0; //(port_a == 1) 1->0发生变化, demo1_cg发生变化, port_a != 1 , demo2_cg 不采样
因此注意实际采样时,不要用这中条件表达式的形式来进行采样, 得到的结果往往超出你的预期。
绿皮书中有采用 assert_porperty的方式进行采样,这里省略。
推荐采用指定event的方式,或者直接调用sample函数,这样清晰明确。
Coverpoint
Coverpoint的定义
Coverpoint定义了Covergroup具体需要cover的点,这个coverpoint的点可以是变量,或者变量的表达式,但数据类型注意依然必须是integral data types。另外coverpoint收集的变量需要在定义coverpoint前声明
定义方式:
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;
定义的两种方式, 指定label和不指定label,不指定label的话,系统自动将变量名作为label. 推荐指定label,加入_cp表明是coverpoint 清晰明了。
标准中可以在coverpoint前定义数据类型,该coverpoint中的变量的数据类型会被强制转换成定义的数据类型。但Xcelium不支持,VCS可以尝试一下。
Bins for values
Coverpoint中使用bins来收集每个coverpoint的变量具体值.
bins可以用户自己指定,也可以系统自动产生。
当用户自定义bins后,不在用户自定义的bins的范围内数值被自动归为default bins,default bins的值将不会建仓,default bins通常由系统自动产生,用户也可以自定义。bins可以cover 一个值/多个值,bins还可以指定为数组。
如果bins[fixed_number] = {x,y,…,z}; 如果fixed_number < {}中的数量,则每个子bins中的数量是他们的整除, 如果有余数,则放入最后一个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被分为10个子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
IEEE SystemVerilog-2012 中新增特性:
- with(with_cover_expression) 可以指定expression,对满足expression的值进行建仓,通过expression来构造出复杂的建仓场景,with_cover_expression中关键字item表示candidate value(不好翻译),用例子来表示:
coverpoint a {
bins test6[] = {[1:10]} with(item%3 == 0); //只对1~10范围内满足%3==0的数进行建仓,<3>,<6>,<9>
}
- 可以支持对自定义的数组进行建仓,这种常用再onehot、复杂算法场景中。
eg: select信号为onehot信号,cover在select信号所有有效的场景情况下,读写都有发生过。
logic[5:0] encoding[6] ;
logic[5:0] select;
logic rw;
covergroup onehot_rw_cg;
SEL: covergroup select{
bins onehot[6] = encoding; //这里onehot的每个子bins中都是有效的场景<6'b1><6'b10><6'b100> ... <6'b10_0000>
}
RW: coverpoint rw{
bins WR = {1};
bins RD = {0};
}
sel_rw_crs: cross SEL, RW;
endgroup
foreach(encoding[i]) encoding[i] = 1<<i; //encoding构造onehot数组 //需要在covergroup new之前定义好数组场景
onhot_rw_cg = new;
bins for sequence
coverpoint还可以收集变量的变化场景。直接上例子
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]); //收集...=>1...=>1...=>1 (...)表示任意数量的除1之外的任何值
bins test8 = (1[=3]) ; //收集...=>1...=>1...=>1 (...)表示任意数量的除1之外的任何值, 这个要求最后一个1结束后不会再出现任意1
bins test9 = (1=>2[->3]=>3); //收集 1...=>2...=>2...=>2=>3
bins test10 = (1=>2[->3]=>3); //收集 1...=>2...=>2...=>2...=>3
}
除了bins还有 illegal_bins和ignore_bins。illegal_bins 表示当采集的值再illegal_bins内,属于非法值报错,ignore_bins为不采集某些值。
优先级为: illegal_bins > ignore_bins> bins
coverpoint a {
illegal_bins zero0 = {0};
ignore_bins zero1 = {0};
bins zero2 = {0};
bins one = {1};
}
结果: 当a=0时, 首先报错,然后在工具上不能看到bins zero2(被ignore掉)只能看到one
Coverpoint自动划分bins
如果没有指定bins,那么coverpoint会自动划分N个bins,规则是:
1. 对enum: N= enum定义的数量
2. 对与其他类型: N 根据 2^M 以及auto_bin_max决定, M表示多少bit可以cover 这个coverpoint。 如果N<2^M, 则2^M个取值自动分配到N个bins中,如果无法整除,则自动分配到最后一个。
自动建仓,只能采集2-state的值,x,z无法采集。
每一个自动建的仓都已auto[value]命名,value为这个bins中覆盖的值
Wildcard
定义了wildcard后, x,z以及?都将被视为通配符,表示0/1.
wildcard bins g12[] = {4'b11??}; //表示1100,1101,1110,1111
bins gz[] = {}
Eg: wildcard bins g12_15[] = {4’b11??}; 表示: 1100,1101,1110, 1111.