SV绿皮书笔记(九)暂时完结

您所在的位置:网站首页 绿皮书的内容 SV绿皮书笔记(九)暂时完结

SV绿皮书笔记(九)暂时完结

2023-08-27 16:59| 来源: 网络整理| 查看: 265

第九章. 功能覆盖率 9.1覆盖率类型

功能覆盖率:功能覆盖率和设计意图是紧密相连的。用来衡量哪些设计特性已经被测试程序测试过的一个指标。

代码覆盖率:包括行覆盖率,路径覆盖率,翻转覆盖率,状态机覆盖率,衡量验证进展最简易的方式,仿真器自带代码覆盖率工具进行统计。

断言覆盖率:断言是用于一次性地或在某一段时间内核对两个设计信号之间关系的声明性代码。

9.2功能覆盖率策略

收集信息而非数据

只测量工你将会使用到的内容

测量的完备性 在这里插入图片描述

9.3 覆盖组

覆盖组与类相似,一次定义后便可以进行多次实例化。覆盖组包含覆盖点,选项,形式参数和可选触发。一个覆盖组必须被实例化后才可以用来收集数据。

9.3.1 在类中定义覆盖组

覆盖组可以在程序,模块或类里定义。如果覆盖组定义在类里,实例化时使用最初的名字即可,不用另起名字。

class Transactor; Transaction tr; mainbox mbx_in; covergroup CovPort; coverpoint tr.port; endgroup function new(mailbox mbx_in); CovPort = new(); this.mbx_in = mbx_in; endfunction task main; forever begin tr = mbx_in.get; ifc.cb.port tr.hdr_len + tr.payload_len}; len32: coverpoint {tr.hdr_len + tr.payload_len + 5'b0}; endgroup

因其中的表达式进行了加法运算,所以值的可能范围为[0,22],因此再定义最大仓数量为2^3就不合适了,所以在len32中使用了5‘b0将位宽拓展到了5bit,SV会自动创建2^5(32)个仓来收集数据。

9.5.5 命名覆盖点的仓

可以对仓名指定明确的仓名,以增加覆盖率报告的准确度。SV会自动为枚举类型的仓命名。直接看例子。

covergroup CovKind; coverpoint tr.kind { bins zero = {0}; //一个名为zero的仓代表kind==0 bins lo = {[1:3], 5}; //一个名为lo的仓代表1:3和5 bins hi[] = {[8:$]}; //8个独立的仓:8...15,[]会为其内的值各建立一个仓 bins misc = default; //1个仓代表剩余的所有的值 } //注意这里不带分号 endgroup

当你定义仓时,实际上是把用来计算覆盖率的数值限制在你感兴趣的范围内,此时SV不再自动创建仓。计算功能覆盖率时只会使用你创建的仓。建议使用default仓标识语句来捕捉那些可能已经被忽略的数值。

使用$符号指定区间的上下界。

int i; covergroup range_cover; coverpoint i{ bins neg = {[$:-1]}; //下界 bins zero = {0}; bins pos={[1:$]}; //上界 } endgroup

9.5.6 条件覆盖率

可以使用关键字iff给覆盖点添加条件。应用场景常为在复位期间关闭覆盖以忽略掉一些杂散的触发。

covergroup CoverPort; //当reset==1时不要手机覆盖率数据 coverpoint port iff (!bus_if.reset); endgroup

也可以使用start和stop函数来控制覆盖组里的各个独立的实例。

initial begin CovPort ck = new(); # 1ns ck.stop(); //复位期间停止收集覆盖率数据 bus_if.reset=1; # 100ns bus_if.reset=0; //复位结束 ck.start(); //收集覆盖率数据 ... end

9.5.7 为枚举类型创建仓

对于枚举类型,SV会为每个可能值创建一个仓。

typedef enum{INIT, DECODE, IDLE} fsmstate_e; fsmstate_e pstate, nstate; covgroup cg_fsm; coverpoint pstate; endgroup

在这里插入图片描述

auto_bin_max在收集枚举类型的覆盖率时不起作用。

9.5.8 翻转覆盖率

covergroup CoverPort; coverpoint port{ bins t1=(0=>1), (0=>2), (0=>3); } endgroup

使用范围表达式可以快速地确定多个转换过程。表达式(1,2 => 3,4)创建了四个翻转过程,分别是(1=>3), (1=>4), (2=>3), (2=>4)。

还可以确定任何长度的翻转次数。(0=>1=>2)和(0=>1=>1=>2),(0=>1=>1=>1=>2)是不一样的。可以使用缩略形式来表示(0=>1=>1=>1=>2),(0=>1[*3]=>2)。1[*3:5]表示1重复3~5次。

9.5.9 在状态和翻转中使用通配符

使用关键字wildcard来创建多个状态或翻转。在表达式中,任何X,Z或?都会被当成0或1的通配符。

9.5.10 忽略数值

使用ignore_bins排除掉那些不用来计算功能覆盖率的数值。

bit [2:0] low_ports_0_5; //只使用数值0-5 covergroup CoverPort; coverpoint low_ports_0_5{ ignore_bins hi = {[6,7]}; //忽略最后的两个仓 } endgroup

ignore_bins和auto_bin_max可以结合使用。

bit [2:0] low_ports_0_5; covergroup CoverPort; coverpoint low_port_0_5{ options.auto_bin_max = 4; //仓最大数量为4 ignore_bins hi = {[6:7]}; //忽略最后两个值 } endgroup

上例中被忽略掉的仓hi不会被用于计算覆盖率。

9.5.11 不合法的仓

有些采样数值不仅应该被忽略,而且如果出现还应该报错。此情形最好在测试平台中使用代码进行检测,但也可以使用illegal_bins对仓进行标识。如果在覆盖组中发现了不合法的数值,那就是你得测试程序或者bin定义出了问题。

bit [2:0] low_ports_0_5; //只使用数值0-5 covergroup CoverPort; coverpoint low_ports_0_5{ illegal_bins hi = {[6, 7]}; //如果出现便报错 } endgroup

9.5.12 状态机的覆盖率

使用代码覆盖率工具来自定提取状态寄存器,状态以及翻转轨迹,查看翻转覆盖率,状态机覆盖率。

9.6 交叉覆盖率

交叉覆盖率可以同时测量两个或者两个以上覆盖点的值。注当你想测量两个变量的交叉覆盖率,其中一个有N种取值,另一个由M种取值,SV需要NxM个交叉仓来存储所有的组合。

9.6.1 基本的交叉覆盖率

SV使用cross关键字来记录一个组里两个或两个以上覆盖点的组合值。cross语句只允许带覆盖点或者简单的变量名。如果你想使用表达式,层次化的名字或者对象中的变量。例如handle.variable,必须首先对coverpoint里的表达式使用标号,然后把这个标号用在cross语句里。

class Transaction; rand bit [3:0] kind; rand bit [2:0] port; endclass Transaction tr; covergroup CovPort; kind: coverpoint tr.kind; //创建覆盖点kind port: coverpoint tr.port; //创建覆盖点port cross kind, port; endgroup

9.6.2 对交叉覆盖仓进行标号

可以对各个覆盖点的仓进行单独标号,SV在创建交叉仓时就会使用这些名称。

covergroup CovPortKind; prot: coverpoint tr.port; { bins port[] = {[0:$]}; } kind: coverpoint tr.kind { bins zero = {0}; //仓zero代表0 bins lo = {[1:3]}; //仓lo代表1~3 bins hi[] = {[8:$]}; //8个独立的仓 bins misc = default; //一个仓代表剩余的所有值 } endgroup

9.6.3 排除掉部分交叉覆盖仓

使用ignore_bins可以减少仓的数目。在交叉覆盖中,可以使用binsof和intersect分别指定覆盖点和数值集,这样可以使单个的ignore_bins结构清除掉很多个体仓。

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{ //排除掉所有port为7和任意kind值组合的仓,因kind为4bit,所以此语句排除掉了16个仓 ignore_bins hi=binsof(port)intersect{7}; //忽略掉port为0和kind为9,10,11的组合,一共3个仓 ignore_bins md=binsof(port)intersect{0}&& binsof(kind)intersect{9:11}; //忽略掉kind定义中的lo包含的所有数值 ignore_bins lo=binsof(kind.lo); } endgroup

注:binsof使用的是小括号(),而intersect指定的是一个范围,所以使用的是大括号{}。

9.6.4 从总体覆盖率的度量中排除掉部分覆盖点

一个组的总体覆盖率是基于所有简单覆盖点和交叉覆盖率的。如果只希望对一个coverpoint上的变量或表达式进行采样,而这个coverpoint将会被用到cross语句中,那么应该把他的权重设置成0,这样他就不会对总体的覆盖率造成影响。

covergroup CovPort; kind: coverpoint tr.kind { bins zero={0}; bins lo={[1:3]}; bins hi[]={[8:$]}; bins misc=default; option.weight=5; //在总体中所占的分量 } port: coverpoint tr.port { bins port[]={[0:$]; option.weight=0; //在总体中不占任何分量 } } cross kind, port {option.weight = 10;} //给予交叉更高的权重 endgroup

9.6.5 交叉覆盖的替代方法

假设有两个随机比特变量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 a1b0=binsof(a.a1) && binsof(b.b0); bins ba=binsof(b.b1); } endgroup

使用binsof的交叉覆盖率

class Transaction; rand bit a, b; endclass covergroup CrossBinsofIntersect; a: coverpoint tr.a { option.weight=0; } b: coverpoint tr.b { option.weight=0; } ab: cross a, b { bins a0b0=binsof(a)intersect{0} && binsof(b)intersect{0}; bins a1b0=binsof(a)intersect{1} && binsof(b)intersect{0}; bins b1=binsof(b)intersect{1}; } endgroup

使用通配符来定义交叉覆盖率

covergroup CrossManual; ab: coverpoint{tr.a, tr.b} { bins a1b0 = {2'b00}; bins a1b0 = {2'b11}; wildcard bins b1 = {2'b?1}; } endgroup 9.7 通用的覆盖组

SV允许创建一个通用的覆盖组,这样在对它进行实例化时可以指定一些独特的细节。SV不允许把覆盖组的触发参数传递给实例,作为变通,可以把覆盖组放到一个类里,然后把触发参数传递给构造函数。

9.7.1 通过数值传递覆盖组参数

bit [2:0] port; covergroup CoverPort(int mid); coverpoint port { bins lo = {[0:mid-1]}; bins hi = {[mid:$]}; } endgroup CoverPort cp; initial begin cp = new(5); //通过构造函数传递参数 end

9.7.2通过引用传递覆盖组参数

如果不仅想在调用构造函数时使用数值,还希望覆盖组在整个仿真过程中可以对数值进行采样,那么可以通过引用的方式来指定需要进行采样的变量。

bit [2:0] port_a, port_b; covergroup CoverPort(ref bit [2:0] port, input int mid); coverpoint port{ bins lo = {[0:mid-1]}; bins hi = {[mid:$]}; } endgroup CoverPort cpa, cpb; initial begin cpa = new(port_a, 4); cpb = new(port_b, 2); end

注:上例中,与任务和函数相似,覆盖组的参数在方向上遵循就近缺省的原则。如果mid遗漏了input的方向,则参数mid的方向就是ref。

9.8 覆盖选项

使用SV提供的选项为覆盖组指定额外的信息。选项分为两种类型,一种是实例选项,用于特定的覆盖组实例;一种是类型选项,用于所有的覆盖组实例。类似于类中的静态数据成员,选项可以放在覆盖组中并对组里的所有覆盖点有效,也可以防在单个覆盖点中以便实现更加精细的控制。

9.8.1 单个实例的覆盖率

如果测试平台对一个覆盖组进行了多次实例化,那么缺省情况下,SV会把所有实例的覆盖率数据汇集到一起。如果需要对每个实例单独查看覆盖率,则使用option的per_instance选项来指定单个实例的覆盖率。

covergroup CoverLength; coverpoint tr.length; option.per_instance = 1; //将此covergroup的每个实例单独计算覆盖率,不合并 endgroup

注:选项per_instance只能放在覆盖组里,不能用于覆盖点和交叉点。

9.8.2 覆盖组的注释

可以在覆盖率报告中增加注释以使报告更易于分析。

//为覆盖组添加注释 covergroup CoverPort; type_option.comment = "Section 3.2.14 Port number"; coverpoint port; endgroup

注:如果有多个实例,可以为每个实例加入单独的注释,前提是同时也是用了per-instance选项。

//为每个覆盖组实例指定注释 covergroup CoverPort(int lo, hi, string comment); option.comment=comment; option.per_instance=1; coverpoint port { bins range={[lo:hi]}; } endgroup ... CoverPort cp_lo = new(0,3,"Low port numbers"); CoverPort cp_hi = new(4,7,"High port numbers");

9.8.3 覆盖阈值

使用option.at_least可以设置覆盖阈值,即一个仓被命中覆盖阈值后,此仓才被认为被覆盖到。

option.at_least如果定义咋覆盖组里,那么会用作所有的覆盖点。如果定义在一个点上,那么就只对该点有效。

9.8.4 打印空仓

缺省情况下,覆盖率报告只会给出带有采样值的仓。使用cross_num_print_missing选项可以让仿真和报告工具给出所有的仓,尤其是没有命中的仓。

covergroup CovPort; kind: coverpoint tr.kind; port: coverpoint tr.port; cross kind, port; option.cross_num_print_missing = 1000; endgroup

9.8.5 覆盖率目标

一个覆盖组或覆盖点的目标是达到该组或该点被认为已经完全覆盖的水平。缺省情况下时100%的覆盖率。

covergroup CoverProt; coverpoint port; option.goal=90; //覆盖率目标设定为90% endgroup 9.9 在仿真过程中进行覆盖率统计

在仿真进行的过程中,可以查询功能覆盖率的水平,允许你检查是否已经达到覆盖目标,并且可以对随机测试施加控制。

在全局层面上,使用 g e t c o v e r a g e 可以得到所有覆盖组的总覆盖率。 get_coverage可以得到所有覆盖组的总覆盖率。 getc​overage可以得到所有覆盖组的总覆盖率。get_coverage返回一个介于0~100的实数,该系统任务可以查询到所有的覆盖组。

可以使用get_coverage()和get_inst_coverage()函数来缩小测量范围。

CoverGroup::get_coverage(); //使用覆盖组名作用域调用 CgInst::get_coverage(); //使用覆盖组实例名作用域调用 cgInst.get_inst_coverage(); //通过实例获取实例的覆盖率

以上函数的用处是:在一个长的测试中检测覆盖率数据。如果覆盖率水平在给定数量的事务或周期内之后并无提高,那么这个测试就应该停止了。

写在最后:SV绿皮书到此已经暂时全部更新完毕。剩余三章暂时用不到,以后用到时或时间充裕时再用一篇文章来更新。其中第10章高级接口有部分知识点比较实用,第11章SV的验证平台个人感觉可忽略直接学习UVM,第12章SV与C接口可以学习一下。SV绿皮书中知识点是使用比较频繁的内容,完整的内容可以学习SV官方手册。接下来验证部分更新UVM实战这本教材。

创作不易,感觉对自己有帮助的话希望不要吝啬自己的点赞,转发,收藏!!!

参考文献:

SystemVerilog验证 测试平台编写指南(原书第二版)张春 麦宋平 赵益新 译



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3