SystemVerilog学习笔记——功能覆盖率(二)

文章目录

  • 覆盖组详解
    • 1、在类里定义覆盖组
  • 二、覆盖组的触发
    • 1、使用回调函数进行采样
    • 2、使用事件触发的覆盖组
  • 3、使用SV断言进行触发
  • 三、数据采样
    • 1、个体仓和总体覆盖率
    • 2、自动创建仓
  • 4、对表达式进行采样
    • 5、使用用户自定义的仓发现漏洞
    • 6、命名覆盖点的仓
    • 7、条件覆盖率
    • 8、为枚举类型创建仓
    • 9、翻转覆盖率
    • 10、在状态和翻转中使用通配符
    • 11、忽略数值
    • 12、不合法的仓
    • 13、状态机的覆盖率


覆盖组详解

覆盖组与类相似,一次定义后便可以进行多次实例化。它含有覆盖点、选项、形式参数和可选触发。一个覆盖组包含了一个或者多个数据点,全都在同一时间采集。
覆盖组可以定义在类里面,也可以定义在程序或模块层次上,它可以采样任何可见的变量或者信号。
在SV中,覆盖组应该定义在适当的抽象层次上,这个层次可以在测试平台和设计的边界上,在读写数据的总线交换单元中,在环境配置类里或者任何需要的地方。
对任何事务的采样都必须等到数据被待测设计收到以后,如果在事务中间注入一个错误,导致数据传输失败,那么就需要改变功能覆盖中对这种情况的处理方式。
一个类里可以包含多个覆盖组,每个组可以有单独的触发,可以从多个源头收集数据。
一个覆盖组必须实例化后才可以用来收集数据。

1、在类里定义覆盖组

覆盖组可以在程序、模块、类中定义,但都要进行明确地实例化后才可以采样。

类里的功能覆盖率

class Transactor;
	Transaction tr;
	mailbox 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.port;	//发送到待测设计中
			ifc.cb.data <= tr.data;
			CovPort.sample();		//收集覆盖率
		end
	endtask
endclass

二、覆盖组的触发

功能覆盖率的两个主要部分是采样的数据和数据被采样的时刻。当这些新数据都准备好了以后,测试平台便会触发覆盖组。可以直接使用sample函数来完成。
如果想在程序代码中显式地触发覆盖组,可以使用sample方法。
如果要借助已有的事件或信号来触发覆盖组,可以在covergroup声明中使用阻塞语句。

1、使用回调函数进行采样

使用回调函数可以在决定数据采集的位置和时间。

使用功能覆盖率回调函数的测试

program automatic test;
	Environment env;

	initial begin
		Driver_cbs_coverage dcc;		//把回调基类Driver_cbs扩展成Driver_cbs_coverage
		env = new();
		env.gen_cfg();
		env.build();
		//创建并登记覆盖率回调函数
		dcc = new();
		env.drv.cbs.push_back(dcc);		//放进驱动器的队列中
		env.run();
		env.wrap_up();
		
	end
endprogram

用于测试功能覆盖率的回调函数

class Driver_cbs_coverage extends Driver_cbs;
	covergroup CovPort;
		...
	endgroup

	virtual task post_tx(Transaction tr);
		CovPort.sample();		//采样覆盖率数值
	endtask
endclass

2、使用事件触发的覆盖组

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

带触发的覆盖组

event trans_ready;		//覆盖组CovPort在测试平台触发trans_ready事件时进行采样
covergroup CovPort @(trans_ready);
	coverpoint ifc.cb.port;		//测量覆盖率
endgroup

3、使用SV断言进行触发

带SV断言的模块

module mem(simple_bus sb);
	bit[7:0] data, addr;
	event write_event;
	
	cover property
		(@(postedge sb.clock) sb.write_ena == 1)
		-> write_event;
endmodule

使用SVA触发覆盖组

program automatic test(simple_bus sb);
	
	covergroup Write_cg @($root.top.m1.write_event);
		coverpoint $root.top.m1.data;
		coverpoint $root.top.m1.addr;
	endgroup
	
	Write_cg wcg;

	intitle begin
		wcg = new();
		//在此处添加激励
		sb.write_ena <= 1;
		...
		# 10000 $finish;
	end
endprogram

三、数据采样

关于覆盖率信息的收集,在覆盖点上指定一个变量或表达式时,SV便会创建很多的“仓(bin)”来记录每个数值被捕捉到的次数。这些仓是衡量功能覆盖率的基本单位。采样一个单比特变量,最多会有两个仓被创建。每次覆盖组被触发,SV都会在一个或者多个仓里留下标记。在每次仿真的末尾,所有带有标记的仓会被汇聚到一个新创建的数据库中。最后利用分析工具进行分析并生成覆盖率报告。

1、个体仓和总体覆盖率

为了计算出一个点上的覆盖率,首先必须确定所有可能数值的个数,这也被称为域。
一个仓中可能有一个或多个值,覆盖率就是采样值的数目除以域中仓的数目。例如一个3比特变量覆盖点的域是0:7,正常情况下会除以8个仓,仿真过程中如果有7个仓的值被采样到,那么覆盖率为7/8或者87.5%,所有这些点组合在一起便构成了一个组的覆盖率,而所有组组合在一起就可以得到整个仿真数据库的覆盖率。

2、自动创建仓

SV会自动为覆盖点创建仓,它通过被采样的表达式的域来确定可能值得范围。对于一个位宽为N的表达式,有2^N个可能值。

3、限制自动创建仓的数目
覆盖组选项auto_bin_max指明了自动创建仓的最大数目,缺省值是64。值域超过指定的最大值,SV会把值域范围平分到auto_bin_max个仓中。

使用auto_bin_max并把仓数设置成2

covergroup CovPort;
	coverpoint tr.port
		{options.auto_bin_max = 2;}
endgroup

覆盖率报告:
SystemVerilog学习笔记——功能覆盖率(二)_第1张图片
在所有覆盖点中使用auto_bin_max

covergroup CovPort;
	options.auto_bin_max = 2;		//影响port和data
	coverpoint tr.port;
	coverpoint tr.data;
endgroup

4、对表达式进行采样

在覆盖点里使用表达式

class Transaction;
	rand bit[2:0] hdr_len;			//范围0-7
	rand bit[3:0] payload_len;		//范围0-15
	rand bit[3:0] kind;			//范围0-15
	...
endclass

Transaction tr;

covergroup CovLen;
	len16 : coverpoint(tr.hdr_len + tr.payload_len);
	len32 : coverpoint(tr.hdr_len + tr.payload_len + 5'b0);		//带有额外常量哑元的表达式,可以计算达到5比特精度,从而把自动生成的最大仓数扩大到32。
endgroup

5、使用用户自定义的仓发现漏洞

自动生成的仓适用于匿名数值,如计数值、地址值或2的幂值,而对于其他数值,应该明确对仓命名。SV会自动为枚举类型的仓命名。

为事务长度定义仓

covergroup CovLen;
	len : coverpoint(tr.hdr_len + tr.payload_len + 5'b0);	
		{bins len[] = {[0:23]};}
endgroup

覆盖率报告:
SystemVerilog学习笔记——功能覆盖率(二)_第2张图片
从图中可以看到长度为23,即十六进制17的一项没有出现过,最长的头是7,最长的负载是15。0-7一共有八种情况,用3bit来表示,0-15一共有16种情况,用4bit来表示。所以总共是7+15=22,而不是23!,我们应该在仓的声明中改成{bins len[] = {[0:22]};}才对。

6、命名覆盖点的仓

当自定义仓时,SV不再自动创建仓,而且会忽略掉那些不被事先定义的仓所涵盖的数值,计算功能覆盖率时只会使用创建的仓。

指定仓名

//对一个4比特变量kind进行采样
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;	//一个仓用来保存没有被选中的值
		}
endgroup

7、条件覆盖率

使用关键字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

8、为枚举类型创建仓

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

枚举类型的功能覆盖率

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

9、翻转覆盖率

确定覆盖点的翻转次数

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)。

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

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

11、忽略数值

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

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

12、不合法的仓

使用== illegal_bins ==对仓进行标识,可以捕捉到那些被错误检查程序遗漏掉的状态

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

13、状态机的覆盖率

如果在状态机上使用了覆盖组,那么就可以使用仓来列出特定的状态和翻转轨迹,使用代码覆盖率工具可以自动提取状态寄存器、状态以及翻转轨迹。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_39794062/article/details/113477197

你可能感兴趣的:(sv,学习)