SystemVerilog学习笔记7——覆盖率

目录

    • 代码覆盖率
    • 断言覆盖率
    • 漏洞率曲线
    • 功能覆盖率
    • 功能覆盖策略
    • 覆盖组
    • 覆盖组的采样触发
    • 数据采样
    • bin的创建和应用
    • 条件覆盖率
    • 翻转覆盖率
    • wildcard覆盖率
    • 忽略的bin
    • 非法的bin
    • 交叉覆盖率
    • 排除部分cross bin
    • 精细的交叉覆盖率指定
    • 覆盖选项
    • 注释
    • 覆盖次数限定
    • 覆盖率目标
    • covergroup方法
    • 数据分析


  • 覆盖率是衡量设计验证完备性的一个通用词语;
  • 随着测试逐步覆盖各种合理的组合,仿真过程会慢慢勾画出设计的情况;覆盖率工具会在仿真过程中收集信息,然后进行后续处理并得到覆盖率报告;
  • 通过该报告找出覆盖外的盲区,然后修改现有测试或创建新测试填补这些盲区;这个过程可以一直送代进行,直到对覆盖率满意为止。SystemVerilog学习笔记7——覆盖率_第1张图片

覆盖率是衡量哪些设计特征已经被测试程序测试过的指标。可使用一个反馈环路分析覆盖的结果,并决定采取哪种行动达到100%的覆盖率。首要选择是使用更多种子运行现有的测试程序,其次是建立新约束。只有在确实需要时才会创建定向测试。

代码覆盖率

  • 不添加任何额外的HDL代码,工具会通过分析源代码和增加隐藏代码来自动完成代码覆盖率的统计;
  • 当运行完所有测试,代码覆盖率工具便会创建相应的数据库。仿真器都带有代码覆盖率的工具,覆盖率数据也可被转换为可读格式;
  • 覆盖率:多少行代码已经被执行过;
  • 路径覆盖率:在穿过代码和表达式的路径中有哪些已经被执行过;
  • 翻转覆盖率:哪些单位比特变量的值为0或1;状态机覆盖率:状态机哪些状态和状态转换已经被访问过;
  • 代码覆盖率最终的结果用于衡量执行了设计的多少代码,关注点应放在设计代码的分析上,而不是测试平台;
  • 未经测试的设计代码里可能隐藏硬件漏洞,也可能仅仅就是冗余的代码;
  • 代码覆盖率衡量的是测试对于硬件设计描述的“实现"究竟测试得有多彻底,而非针对验证计划;
  • 代码覆盖率达到了100%,并不意味着验证的工作已经完成,但代码覆盖率100%是验证工作完备性的必要条件
    SystemVerilog学习笔记7——覆盖率_第2张图片

断言覆盖率

  • 断言是用于一次性或在一段时间对一个或多个设计信号在逻辑或时序上的声明性代码
  • 断言可以跟随设计和测试平台一起仿真,也可被形式验证工具所证实;
  • 可使用SV的程序性代码编写等效性检查,但用SVA(SV断言)表达会更容易;
  • 断言最常用于查找错误,例如两个信号是否应该互斥,或者请求与许可信号之间的时序等;一旦检测到问题,仿真就可立即停止;
  • 有些断言可以用于查找感兴趣的信号值或设计状态
  • 可以使用cover property测量这些关心的信号值或状态是否发生;
  • 在仿真结束时,仿真工具可以自动生成断言覆盖率数据;
  • 断言覆盖率数据以及其它覆盖率数据都会被集成在同一个覆盖率数据库中,verifier可以对其展开分析。

漏洞率曲线

一个项目实施期间,应保持追踪每周有多少个漏洞被发现。一开始创建测试程序时,通过观察可能就会发现很多漏洞。

  • 当设计逐渐稳定时,需要利用自动化的检查方式来协助发现可能的漏洞;
  • 设计临近流片时漏洞率会下降,甚至有望为零。即便如此验证工作仍不能结束;
  • 每次漏洞率下降时,应寻该找各种不同的办法去测试可能的边界情况(corner case);
  • 漏洞率可能每周都有变化,跟很多因素有关。不过漏洞率如果出现意外的变化,可能预示着潜在的问题。

功能覆盖率

  • 验证的目的就是确保设计在实际环境中的行为正确
  • 功能描述文档详细说明设计应如何运行,而验证计划列出相应功能应如何激励、验证和测量;
  • 当收集测量数据希望找出哪些功能已经被覆盖时,就是在计算"设计"的覆盖率;
  • 功能覆盖率和功能设计意图紧密相连,有时被称为“描述覆盖率",而代码覆盖率是衡量设计的实现情况;某个功能在设计中可以被遗漏,代码覆盖率不能发现这个错误,但功能覆盖率可以;
  • 每次仿真都会产生一个带有覆盖率信息的数据库,记录随机游走的轨迹;把这些信息全部合并在一起就可以得到功能覆盖率,从而衡量整体的进展程度;
  • 通过分析覆盖率数据可以决定如何修改回归测试集
  • 如果覆盖率在稳步增长,那么添加新种子或加长测试时间即可。如果覆盖率增速放缓,那么需要添加额外的约束产生更多有意思的激励。如果覆盖率停止增长,然而设计某些测试点没有被覆盖到,就需要创建新测试。如果覆盖率为100%但依然有新的设计漏洞,那么覆盖率可能没有覆盖到设计中的某些设计功能区域

下面对于功能覆盖率增长的描述正确的有:BCD
A、功能覆盖率达到100%可以说明验证的完备性;
B、如果覆盖率稳定增长,则只需要修改种子或增加测试时间;
C、如果覆盖率增长放缓,则需要修改约束;
D、如果覆盖率停止增长且不足100%,则需要考虑添加新的测试用例。SystemVerilog学习笔记7——覆盖率_第3张图片

功能覆盖策略

1、收集信息而非数据

  • 对于MCDF,要关心的是合法寄存器地址和非法寄存器地址,可写的寄存器域和非法的寄存器域,而不是具体的寄存器地址数值;
  • 一旦关注的地方着眼于感兴趣的状态,而不是具体数值,这对于如何定义功能覆盖率,以及如何收集信息会减轻很大的负担;
  • 设计信号如果数量范围太大,应拆分为多个小范围再加上边界情况。

2、只测量需要的内容

  • Verifier需要懂得,在使能覆盖率收集时,这一特性会降低很大的仿真性能;
  • 由于收集功能覆盖率数据的开销很大,所以应该只测量会用来分析并改进测试的那部分数据。同时也要设定合理的覆盖率采样事件,一方面提升采样效率,一方面也能降低收集覆盖率的开销。

3、验证的完备性

  • 完备的覆盖率测量结果和漏洞增长曲线,可以帮助确认设计是否被完整验证过;
  • 如果代码覆盖率低但功能覆盖率高,说明验证计划不完整,测试没有执行设计的所有代码;
  • 如果代码覆盖率高但功能覆盖率低,说明即使测试平台很好地执行了设计的所有代码,但测试没有把设计定位到所有感兴趣的状态上;
  • 目标是同时驱动高的代码覆盖率和功能覆盖率

下面对于功能覆盖率策略的描述哪些是正确的:CD
A、应尽可能监测并采样更多的信号;
B、应在每个时钟上升沿都采样感兴趣的信号;
C、如果采样的数值域数量过大,可切分为有限多的数值域,便于分析数据;
D、如果代码覆盖率100%而功能覆盖率不足100%,设计可能没有实现某部分功能;
SystemVerilog学习笔记7——覆盖率_第4张图片

覆盖组

  • 覆盖组(covergroup)与类相似,一次定义后便可多次实例化。covergroup可包含一个或多个coverpoint,且全都在同一时间采集;
  • covergroup可定义在类、interface或module中,covergroup可以采样任何可见的变量,例如程序变量、接口信号或者设计端口;
  • 一个类里可以包含多个covergroup;当拥有多个独立的covergroup时,每个covergroup可根据需要自行使能或禁止
  • 每个covergroup可以定义单独的触发采样事件,允许从多个源头收集数据。covergroup必须被例化才可以用来收集数据。
class Transaction;
	Transaction tr;
	mailbox mbx_in;
	covergroup CovPort;
		coverpoint tr.port;
	endgroup
	function new(mailbox mbx_in);
		CovPort = new(); //例化,建议采用Covport cg1 = new();的例化方式	
		this.mbx_in = mbx_in;
	endfunction
	task main;
		forever begin
			tr = mbx_in.get();		//获取下一个事务
			ifc.cb.port <= tr.port;	//发送到待测设计中
			ifc.cb.data <= tr.data;
			CovPort.sample();		//采样——收集覆盖率
		end
	endtask
endclass

覆盖组的采样触发

  • covergroup由采样的数据和数据被采样的事件构成;当这两个条件都准备好后,测试平台便会触发covergroup;
  • 该过程可通过直接使用sample()函数完成,也可在covergroup中采用阻塞表达式或使用wait或@实现在信号或事件上的阻塞;
  • 若希望在代码中显式地触发covergroup采样,或不存在采样时刻的信号或事件,又或者一个covergroup被例化为多个实例需要单独触发,可使用sample()方法。若借助已有事件或信号触发covergroup,可在covergroup声明中使用阻塞语句。
event trans_ready;		//覆盖组CovPort在测试平台触发trans_ready事件时进行采样
covergroup CovPort @(trans_ready);
	coverpoint ifc.cb.port;		//测量覆盖率
endgroup

与直接调用sample方法相比,使用事件触发的好处在于能够借助已有事件进行采样

数据采样

  • 当在coverpoint指定采样一个变量或表达式时,SV会创建很多的“仓(bin)"来记录每个数值被捕捉到的次数,这些bin是衡量功能覆盖率的基本单位
  • covergroup中可以定义多个coverpoint,coverpoint中可自定义多个cover bin或SV帮助自动定义多个cover bin;
  • 每次covergroup采样,SV都会在一个或多个cover bin中留下标记,记录采样时变量的数值和匹配的cover bin;
  • 仿真之后可使用分析工具读取这些数据库生成覆盖率报告,包含各部分和总体的覆盖率;
  • 计算一个coverpoint上的覆盖率,首先需要确定可能数值的个数,也被称为
  • 覆盖率就是采样值的数目除以bin的数目。例如一个3比特变量的域是0:7,正常情况下会自动分配8个bin,如果仿真过程有7个值被采样到,则最终该coverpoint的覆盖率是7/8;
  • 所有coverpoint的覆盖率最终构成一个covergroup的覆盖率;所有的covergroup的覆盖率构成了整体的功能覆盖率。

bin的创建和应用

  • SV会默认为某个coverpoint创建bin,用户也可自定义bin的采样域;
  • 如果采样变量的域范围过大而又没有指定bin,那么系统会默认分配64个bin,将值域范围平均分配给这64个bin;用户可通过covergroup的选项auto_bin_max来指定自动创建bin的最大数目。实际操作中自动创建bin的方法不实用,建议用户自行定义bin,或减小auto_bin_max的数值。
covergroup CovPort;
	options.auto_bin_max = 8; //所有coverpoint auto_bin数量=8
	coverpoint tr.port
		{options.auto_bin_max = 2;} //特殊部分coverpoint auto_bin数量=2
endgroup
//对一个4比特变量kind进行采样
covergroup CovKind;
	coverpoint tr.kind{
		bins zero = {0};		//1个仓代表对kind采样值为0进行计数
		bins lo = {[1:3],5};	//1个仓代表1-3和5的值
		bins hi[] = {[8:$]};	//8个独立的仓8-15,分别保存
		bins misc = default;	//一个仓用来保存没有被选中的值
		} //注意coverpoint定义使用{ }而不是begin...end。
		//大括号结尾没有分号,和end一样
endgroup  //根据结果,覆盖率为10/11

SystemVerilog学习笔记7——覆盖率_第5张图片

条件覆盖率

  • 可使用关键词iff给coverpoint添加条件。·这种做法常用于在复位期间关闭覆盖以忽略不合理的条件触发
  • 可使用startstop函数控制covergroup各个独立实例。
initial begin
	CovPort ck = new();		//实例化覆盖组
	//复位期间停止收集覆盖率数据
	#1ns ;
	ck.stop();
	bus_if.reset = 1;
	#100ns bus_if.reset = 0;
	ck.start();
	...
end
covergroup CoverPort;
	//当reset=1时不要收集覆盖率数据
	coverpoint port iff(!bus_if.reset);
endgroup

翻转覆盖率

  • coverpoint可用来记录变量从A值到B值的跳转情况,还可确定任何长度的翻转次数。
covergroup CoverPort;
	coverpoint port{
		bins t1 = (0 => 1), (0 => 2), (0 => 3);
		}
endgroup

wildcard覆盖率

  • 可使用关键字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};
		}

忽略的bin

在某些coverpoint可能始终无法得到全部域值;对于那些不计算功能的域值可用ignore_bins排除,最终它们并不会计入coverpoint的覆盖率。

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

非法的bin

  • 有些采样值不仅应该被忽略,而且如果出现还应报错;这种情况可以在测试平台中监测,也可使用illegal_bins对特定的bin进行标示。
bit[2:0] low_ports_0_5;		//只使用数值0-5
covergroup CoverPort;
	coverpoint low_ports_0_5{
		illegal_bins hi = {[6,7]};		//如果出现6,7两个仓就会报错
		}
endgroup

交叉覆盖率

coverpoint是记录单个变量或表达式的观测值。如果想记录在某一时刻多个变量之间值的组合情况,需要使用交叉(cross)覆盖率;cross语句只允许带coverpoint或简单的变量名

class Transaction; 
	rand bit [3:0] kind; 
	rand bit [2:0] port; 
endclass 
Transaction tr; 
covergroup CovPort; 
	kind: coverpoint tr. kind; 
	port: coverpoint tr. port; 
	cross kind, port; 
endgroup

排除部分cross bin

通过使用ignore_bins、binsof和intersect分别指定coverpoint和值域,这样可以清除很多不关心的cross bin。

covergroup Covport; 
	port: coverpoint tr.port
		{bins port[] = {[0:$]};  //8
}
	kind: coverpoint tr.kind {
		bins zero = {0};  //1
		bins lo = {[1:3]};  //1
		bins hi [] = {[8:$]};   //8
		bins misc = default;  //1
}
cross kind, port {
	ignore bins hi = binsof(port) intersect {7}; 
	ignore bins md = binsof(port) intersect {0} &&
	binsof (kind) intersect {[9:11]};  //排除port的0、7、[9:11]
	ignore bins lo = binsof (kind.lo); //排除kind的[1:3]
}
endgroup

精细的交叉覆盖率指定

  • 随着cross覆盖率越来越精细,可能需要花费不少时间指定哪些bin应该被使用或被忽略;
  • 更适合的方式是不使用自动分配的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 a1b0 = binsof(a.al)&& binsof(b.b0);
	bins bl = binsof(b.b1)}
endgroup
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

下面关于bin的描述哪些是正确的:ABC
A、coverpoint中可以指定多个ignore_bins;
B、如果采样到illegal_bins,则仿真会停止;
C、cross覆盖率可以监测三个变量的值的组合情况;
D、如果CP_A有4个bin,CP_B有6个bin,那么cross CP_A,CP_B会自动分配24个bin;
解析:D——前提是得CP_A和CP_B涵盖了所有值。

覆盖选项

  • 如果对一个covergroup例化多次,那么默认情况下SV会将所有实例的覆盖率合并到一起。如果要单独列出每个covergroup实例的覆盖率,需要设置覆盖选项
covergroup CoverLength; 
	coverpoint tr.length; 
	option.per_instance = 1; 
endgroup

注释

  • 如果有多个covergroup实例,可通过参数对每个实例传入单独的注择。这些注释最终会显示在覆盖率数据的总结报告中。
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");

覆盖次数限定

  • 默认情况下数值采样了1次就可计入有效的bin,可通过修改at_least修改每个bin的数值最少的采样次数,如果低于at_least数值,则不会被计入bin中
  • option.at_least可以在covergroup中声明影响所有coverpoint,也可在coverpoint中声明来只影响该coverpoint下所有bin。

覆盖率目标

  • 一个covergroup或一个coverpoint的目标是100%覆盖率,不过也可将其设为低于100%的目标,这个选项只会影响覆盖率报告
covergroup CoverPort; 
	coverpoint port;
	option.goal = 90; 
endgroup

covergroup方法

  • sample():采样;
  • get_coverage()/get_inst_coverage():获取覆盖率,返回0-100的real数值;
  • set_inst_name(string):设置covergroup的名称;
  • start()/stop():使能或关闭覆盖率的收集。

数据分析

  • $get_coverage()——得到总的覆盖率;
  • covergroup_inst.get_inst_coverage()——获取单个covergroup实例的覆盖率;
  • 这些函数最实际的用处是在一个测试当中监测覆盖率的变化。如果覆盖率在一段时间之后没有提高,那么这个测试就应该停止;
  • 重启新的随机种子或测试可能有望提高覆盖率;
  • 如果测试可基于功能覆盖率采取一些深入的行动,例如重新限定随机的约束,那将是一件非常好的事情,但这种测试很难编写。

你可能感兴趣的:(SystemVerilog,学习笔记,测试覆盖率,功能测试,测试用例)