zl程序教程

您现在的位置是:首页 >  工具

当前栏目

SystemVerilog学习-10-验证量化和覆盖率

学习 10 验证 量化 覆盖率 SystemVerilog
2023-09-14 09:13:03 时间

类型转换

静态转换

静态转换操作符不对转换值进行检查。转换时指定目标类型,并在要转换的表达式前加上单引号即可。Verilog对整数和实数类型,或者不同位宽的向量之间进行隐式转换。

动态转换

可以将子类的句柄赋值给父类的句柄。但是在将父类的句柄赋值给子类的句柄时,编译将会报错。$cast()系统函数可以将父类句柄转换为子类句柄,只要该父类句柄指向的是一个子类的对象。

function int $cast(singular dest_var, singular source_exp) ;

或者

task $cast(singular dest_var,singular source_exp);

测试量化

伴随着复杂SoC系统的验证难度系数成倍增加,无论是定向测试还是随机测试,我们在验证的过程中终究需要回答两个问题:是否所有设计的功能在验证计划中都已经验证?代码中的某些部分是否从未执行过?覆盖率就是用来帮助我们在仿真中回答以上问题的指标覆盖率已经被广泛采用,作为衡量验证过程中的重要数据。

一旦通过覆盖率来量化验证,我们可以在更复杂的情况下捕捉一些功能特性是否被覆盖:

  • 当我们在测试X特性的时候,Y特性是否也在同一时刻被使能和测试?
  • 是否可以精简我们已有的测试来加速仿真,并且取得同样的覆盖率?
  • 覆盖率在达到一定的数值的时候,是否停滞,不再继续上升?

简单而言,覆盖率就是用来衡量验证精度和完备性的数据指标覆盖率可以告诉我们在仿真时设计的哪些结构被触发,当然,它也可以告诉我们设计在仿真时的哪些结构从未被触发过。

只有满足以下三个条件,才可以在仿真中实现高质量的验证:

  • 测试平台必须产生合适的激励来触发一个设计错误。
  • 测试平台仍然需要产生合适的激励使得被触发的错误可以进一步传导到输出端口
  • 测试平台需要包含一个监测器(monitor)用来监测被激活的设计错误,以及在它传播的某个节点(内部或者外部)可以捕捉到它。

覆盖率的种类

概述

没有任何一种单一的覆盖率可以完备地去衡量验证过程。可以达到100%的代码覆盖率,但这并不意味着100%的功能覆盖率。原因在于代码覆盖率并不是用来衡量设计内部的功能运转,或者模块之间的互动,或者功能时序的触发等。

类似地,我们即便达到了100%功能覆盖率,也可能只达到了90%的代码覆盖率。原因可能在于我们疏漏了去测试某些功能,或者一些实现的功能并没有被描述。从上述关于代码覆盖率和功能覆盖率简单的论述就可以证明,如果想要得到全面的验证精度,我们就需要多个覆盖率种类的指标。

分类

最常见的划分覆盖率的两种方法

  1. 按照覆盖率生成的方法,即隐性生成还是显性生成。
  2. 按照覆盖率溯源,即它们从功能描述而来还是从设计实现而来。

例如功能覆盖率是显性的需要人为定义的覆盖率,而代码覆盖率则是隐性覆盖率这是因为仿真工具可以自动从RTL代码来生成。

image-20211222001426839

如果将上述两个分类的方式进行组合,那么可以将代码覆盖率、断言覆盖率以及功能覆盖率分别置入到不同的象限。但是需要注意,目前有一个象限仍然处于研究阶段,没有隐性的可以从功能描述生成某种覆盖率的方法,这也是为什么功能覆盖率依然需要人为定义的原因。
接下来我们将认识主要的两种覆盖率:

  • 代码覆盖率(隐性覆盖率)
  • 功能覆盖率(显性覆盖率)

代码覆盖率

代码覆盖率并不是SV独有的,在软件测试领域很早之前就有了这一项测试指标。代码覆盖率的一个优势在于它可以由仿真工具自动收集,继而用来指出在测试程序中设计源代码哪些被激活触发,而哪些则一直处于非激活的状态。由于代码覆盖率自动的特性,在仿真过程中使能它变得很简单,它不需要修改设计或者验证环境。

局限

代码覆盖率100%并不意味着足够的功能覆盖率。
研究发现,在回归测试实现了90%的代码覆盖率时,平均只有54%的代码会被监测,这意味着即便代码覆盖率的完备性满足,但依然可能在这些代码中存在漏洞。上述的推断是因为不是所有被覆盖的代码都会得到足够的监测,也由于没有得到足够的监测,因此一些即便被触发的漏洞也在传播的过程中没有到达监测点上——漏洞便从眼皮底下"逃逸"了。

另外,代码覆盖率的数据无法直接映射到哪些设计功能被测试了,所以从这一点来看,代码覆盖率和功能覆盖率之间又是互相独立的。

跳转覆盖率(toggle)

用来衡量寄存器跳转的次数(0->1,1->0),一般项目会要求模块的端口实现至少1次O到1,以及1次1到0的跳转来保证模块的集成和模块之间的互动。也有的项目会要求所有的寄存器都应该同端口一样满足跳转的最少次数。

端口跳转覆盖率经常用来测试IP模块之间的基本连接性,例如检查一些输入端口是否没有连接,或者已经连接的两个端口的比特位数是否不匹配,又或者一些已经连接的输入是否被给定的固定值等,都可以通过跳转覆盖率来发现问题。

行覆盖率( statement/line)

用来衡量源代码哪些代码行被执行过,以此来指出哪些代码行没有被执行过。从每一行执行的次数,如果设置最小的执行次数,也可以用来做充足代码测试的一项指标。

代码覆盖率可以指出在一些缺乏输入激励的情况下,某些赋值的代码行没有被执行的情况,它也可以指出在一些漏洞影响或者无用代码的影响下,一些代码行也无法被执行的情况。对于那些无用的代码,也就是永远不会被执行的代码,在代码分析时,我们可以将它们从覆盖率数据中过滤掉。

分支覆盖率(branch)

分支覆盖率是用来对条件语句( if/else,case,?😃,指出其执行的分支轨迹。

例如判断下列分支的布尔表达式为true或者false。

if (parity == ODD || parity == EVEN) begin

条件覆盖率(condition/expression)

条件覆盖率是用来衡量一些布尔表达式中各个条件真伪判断的执行轨迹。

状态机覆盖率(FSM)

仿真工具由于可以自动识别状态机,因此在收集覆盖率时,也可以将覆盖率状态的执行情况监测到。
每个状态的进入次数,状态之间的跳转次数,以及多个状态的跳转顺序都可以由仿真工具记录下来。

功能覆盖率

功能验证的目标在于确定设计有关的功能描述是否被全部实现了?
这一检查中可能会存在一些不期望的情况:

  • —些功能没有被实现
  • —些功能被错误地实现了
  • 一些没有被要求的功能也被实现了

我们无法通过代码覆盖率得知要求的功能是否被实现了,而需要显性地通过功能覆盖率与设计功能描述做映射,继而量化功能验证的进程。

在约束随机测试流行于验证时,由于随机测试在仿真时会自动产生上千条测试激励,但我们却无法知道随机产生的激励究竟测试了什么功能。功能覆盖率是最好地可以协助在回归测试时自动监测哪些功能被激活的方法。

创建功能覆盖率模型需要完成以下两个步骤:

  • 从功能描述文档提取拆分需要测试的功能点。
  • 将功能点量化为与设计实现对应的SV功能覆盖代码。

覆盖组(covergroup)

覆盖组与类相似,在一次定义以后便可以多次进行例化。覆盖组含有覆盖点(coverpoint) 、选项(option)、形式参数(argument)和可选触发(trigger event) 。一个覆盖组包含了一个或者多个数据点,全都在同一时间采集。覆盖组可以定义在类里,也可以定义在模块或者程序(program)中。

覆盖组可以采集任何可见的变量,比如程序或模块变量、接口信号或者设计中的任何信号。在类中的覆盖组也可以采集类的成员变量。覆盖组应该定义在适当的抽象层次上。对任何事务的采样都必须等到数据被待测设计接收到以后。一个类也可以包含多个覆盖组,每个覆盖组可以根据需要将它们使能或者禁止。

covergroup…endgroup来定义覆盖组。内部可以定义多个coverpoint,如果不在covergroup声明时指定采样事件,那么默认该覆盖组只能依赖于其另外一个手动采样函数sample()。

我们更多地将covergroup定义在类中,从而可以去覆盖类的成员变量在类中声明的covergroup的方式被称为嵌入式覆盖组声明。一个类中可以声明多个covergroup。covergroup可以与其它对象同步(@m_obj.m_ev)。

覆盖点(coverpoint)

一个covergroup可以包含一个或者多个coverpoint,一个coverpoint可以用来采样数据或者数据的变化。一个coverpoint可以对应多个bin (仓)。这些仓可以显性指定,也可以隐性指定。coverpoint对数据的采样发生在covergroup采样的时候。

在定义coverpoint时可以不给名字或者给名字,我们建议给不同的coverpoint以不同的名字。这些有名字的coverpoint可以用来做更进—步的处理,例如在交叉覆盖率中使用某个coverpoint,可以通过iff在一些情况下禁止coverpoint的采集。

covergroup g4 ;
	coverpoint s0 iff ( !reset) ;
endgroup

仓(bin)

关键词bins可以用来将每个感兴趣的数值均对一个独立的bin,或著将所有值对应到一个共同的bin。iff语句也可以用在bin的定义,它表示条件为false,那么在采集该bin的时候,该bin的采样数自不会增长。

covergroup的参数也可以被传递到bin的定义中。在定义bin时,可以使用with来进一步限定其关心的数值,with可以用表达式或者函数来衡量。

值变化覆盖

除了可以覆盖数值,还可以覆盖数值的变化。除了使用->来表示相邻采样点的变化,也可以使用->来表示非相邻采样点的数值变化,在=>序列后的下一个时序必须紧跟=>序列的最后一个事件。与[ -> repeat_range]类似的有[ = repeat_range ]也表示非连续的循环,只是与->有区别的在于,跟随->序列的下一次值变化可以发生在->结束后的任何时刻。

自动生成

如果coverpoint没有指定任何bin,那么SV将会为其自动生成bin,遵循的原则是:

  • 如果变量是枚举类型,那么bin的数量是枚举类型的基数(所有枚举数值的合)。
  • 如果变量是整形(M位宽),那么bin的类型将是2^M和auto_bin_max选项的较小值。

通配应用

默认情况下,数值的变化可以针对四值类型变量进行覆盖,例如bin如果包含x或者z,则表示只有该变量对应位也为x或者z的时候,bin也才可以被采样到。
wildcard修饰符可以使得bin中包含x,z和?的数值都将用来表示0或者1,也就是通配符的意思。
wildcard bins g12_15 = { 4 'b11?? } ;g12_15可以用来表示12到15之间的值,即1100,1101,1110,1111

忽略类型和非法类型

ignore_bins用来将其排除在有效统计的bin集合之外。illegal_bins用来指出采样到的数值为非法制,如果illegal_bins被采样到,那么仿真将报错。

交叉覆盖率(cross)

covergroup可以在两个或者更多的coverpoint或者变量之间定义交叉覆盖率(cross coverage)。

在对a和b产生交叉覆盖率之前,系统会先为它们隐性产生对应的coverpoint和bin,每个coverpoint都有16个自动产生的bin。
两个coverpoint交叉以后将生成256个交叉的bin

bit [3:0] a, b;
covergroup cov @(posedge clk);
	aXb : cross a,b;
endgroup

下面的CPA有10个bin, b_var对应的CP将有16个bin,因此CC将会有10x16=160个bin。

bit [31:0] a_var;
bit [3:0]  b_var;
covergroup cov3 @(posedge clk);
	A: coverpoint a_var { bins yy [] = { [0:9] }; }
	cc: cross b_var,A;
endgroup

被声明为default/ignore/illegal的bin将不会参与cross coverage的运算,交叉覆盖率只允许在同一个covergroup中定义的CP参与运算。

除了系统会自动为交叉覆盖率生成bin以外,用户还可以自己定义交叉覆盖率的bin。binsof()的参数可以是coverpoint或者变量,表示对应的bin总和,可以利用binsof()对其结果做进—步的过滤。

自己为cross定义了bin,但是系统仍然会自动生成bin,自动生成bin的原则是,只要该bin不在自定义bin的范围,那么就仍然会保留。

指定覆盖率选项

  • 覆盖率选项可以在covergroup、coverpoint或者cross中分别设置。

  • 常见的参数,譬如weight可以在covergroup设置来表示它实例的比重,也可以在coverpoint/cross中设置来表示它们在covergroup中的比重,默认值是1。

  • per_instance如果设置为1,将会记录每个covergroup实例的覆盖率数据,默认值是0。

  • comment则是用来对covergroup/coverpoint/ cross做进一步的说明,方便在覆盖率数据分析时阅读理解。

    覆盖率选项按照在covergroup、coverpoint和cross不同的域中是否具备,下表做了归纳:

image-20220630170145072

预定义覆盖率方法

image-20220630170210167

从功能描述到覆盖率

要实现功能覆盖率的收敛,就需要按照以下步骤考虑:

  • 哪些功能需要测试。
  • 明白在什么条件下需要测试对应的功能。
  • 为了测试这些功能,需要提供什么样的测试平台组件以便提供激励和监测。
  • 测试平台如何检查这些功能正常工作。

由于功能覆盖率不是自动的过程,因此它需要将功能描述同设计实现对应起来。

过程指南

  • 功能覆盖率应该基于对DUT端口或者内部信号的监测。
  • 功能覆盖率的有效性应该首先建立在测试通过的情况下,这还需要测试平台有自动检查(scoreboard/reference model)来保证对应的功能得到检验。
  • 在创建覆盖率模型时,同创建property (assertion)一样,不要贪大求全,而应该对每一个covergroup、coverpoint、bin和cross都做到简明扼要,尽量将逻辑和时序都拆分开,便于后期的覆盖率分析。
  • 选取一个适当的范围进行覆盖率收集,例如一个32位的整形变量,在收集数值时可以结合其与设计功能的特点,找到边界值、分段范围,再在合适的抽象层次上进行收集。
  • 采用统一的覆盖率编码风格,例如covergroup的名字,采样的方式,是否填写comment,覆盖率模型置于何处等。

reference

  1. 西电路科验证PPT