System Verilog功能覆盖率(Functional Coverage)

本文介绍了SystemVerilog中的功能覆盖率分析,包括covergroup、coverpoint和交叉覆盖率的概念、使用方法及示例。功能覆盖率用于评估测试集对设计功能的覆盖情况,确保验证的完备性。covergroup是覆盖率模型的容器,coverpoint用于定义要覆盖的变量或表达式,而交叉覆盖率则关注多个coverpoint的组合取值。文章还讨论了覆盖率计算和相关选项,以及如何通过覆盖率驱动验证来优化测试用例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


摘要
本文介绍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 可以在 packagemoduleprograminterfacechecker class 中定义。其语法格式如下:
	covergroup covergroup_identifier [ ( [tf_port_list] ) ] [coverage_event];
		{coverage_spec_or_option}
	endgroup [:covergroup_identifier]

2.2 covergroup参数

tf_port_list : 可选参数列表。端口类型只能为input或者refoutputinout类型是非法的。定义为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()函数采样。samplecovergroup的内建函数,可以调用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传入的参数只能作为coverpointcross中需要采集的一部分, 其他的使用方式会报错(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;

可以使用关键词iffcoverpoint添加条件,这种做法常用于在复位期间关闭覆盖以忽略不合理的条件触发。

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[30] 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[] = {[18]} 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用来指定coverpointintersect来指定值域。

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例化多份,可在例化时通过参数传入不同的commentcovergroup
option.per_instance=boolean0为1表示例化多份时,每一个实例一个覆盖率;为0时覆盖率合并covergroup
option.weight=number1计算覆盖率时所在covergroup或coverpoint或cross的权重。常用于在不关心的coverpoint种的weight设置为0all
option.at_least=number1表示每个bin最少被采样多少次才算入命中all
option.goal=number100覆盖率目标all
option.auto_bin_max=number64自动产生bins的最大数量all

除了option选项,还有type_option,type_option常用选项包括type_option.weighttype_option.goal
type_option作用于某类型,而option作用于某个实例。
option.weighttype_option.weight的区别如下:
type_option:顾名思义为类型选项,如果设置type_option.weight=0,则covergroup的类型在整体覆盖率计算时的其权重为0,不计入总体覆盖率,但是在单个实例覆盖率中仍然计算。
option: 为实例选项,如果设置某coverpoint的option.weight=0,则计算单个实例时,该coverpoint的权重为0,但是在计算整个类型的覆盖率时,依然考虑其权重。

6 覆盖率系统方法

SV提供了一些内建的方法,如下表:

functionCan be called onDespription
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在在gv1gv2中有重复,covergroup gtb[0] b[1] b[2]这3个bin,如果gv1采样到了a=0,而gv1gv2都采样到了a=1,那么对于gt::get_coverage()结果为66.67(2/3),而gv1.get_inst_coverage()=100gv1.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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值