第九章 功能覆盖

随着设计的复杂化,约束随机测试(CRT)是唯一有效的验证方法。这种方法使您摆脱了为设计中的每个特性编写单独的定向测试的单调乏味。然而,如果您的测试台在所有设计状态的空间中进行随机漫步,您如何知道您是否到达了目的地?即使是有指导的测试也应该对功能覆盖率进行双重检查。在一个项目的生命周期中,DUT的时间或功能上的微小变化可以微妙地改变定向测试的结果,因此它不再验证相同的特性。无论您使用的是随机dom还是定向刺激,您都可以使用覆盖率来评估进度。

功能覆盖率是测试执行了哪些设计特性的度量。从设计规范开始,并创建一个验证计划,其中包含要测试什么以及如何测试的详细列表。例如,如果您的设计连接到总线,则您的测试需要测试设计和总线之间所有可能的交互,包括相关的设计状态、延迟和错误模式。验证计划是一幅地图,它告诉你去哪里。有关创建验证计划的更多信息,请参见Bergeron(2006)。

在许多复杂的系统中,您可能永远不会达到100%的覆盖率,因为时间表不允许您达到每一个可能的极端情况。毕竟,您没有时间编写定向测试来获得足够的覆盖率,即使是CRT也受到创建和调试测试用例以及分析结果所需时间的限制。

图9.1显示了分析覆盖率结果并决定采取哪些行动以收敛于100%覆盖率的反馈循环。您的第一个选择是用更多的种子运行现有的测试;第二是建立新的约束条件。只有在绝对必要时才求助于创建定向测试。

当您专门编写定向测试时,验证计划是有限的。如果设计规范列出了100个特性,那么您所要做的就是编写100个测试。覆盖率在测试中是隐式的——“寄存器移动”测试来回移动寄存器的所有组合。度量进度很容易:如果您已经完成了50个测试,那么您已经完成了一半。本章使用“显式”和“隐式”来描述覆盖率是如何指定的。明确的覆盖范围

9.1. 约束的随机测试

导演testcase

最少的代码修改

图9.1覆盖收敛

9.2. 功能覆盖

直接在测试环境中使用SystemVerilog特性。隐式覆盖是由测试隐含的——当“register move”指向的测试通过时,您可能已经覆盖了所有的寄存器事务。

使用CRT,您不必手工制作每一行输入刺激,但现在您需要编写代码,根据验证计划跟踪测试的有效性。当您在更高的抽象级别上工作时,您仍然是更有效率的。您已经从调整单个部件转移到描述有趣的设计状态。达到100%的功能覆盖率会迫使您更多地思考您想要观察什么,以及如何将设计引导到这些状态。

9.3. 收集覆盖率数据

您可以反复运行相同的随机测试平台,只需更改随机种子以生成新的刺激。每个人的模拟产生一个数据库的功能覆盖信息,脚印的轨迹从随机行走。然后,您可以将所有这些信息合并在一起,使用功能覆盖率来度量您的总体进度,如图9.2所示。

然后您分析覆盖率数据,以决定如何修改您的测试。如果覆盖率水平稳步增长,您可能只需要用新的随机种子运行现有的测试,或者甚至只需要运行更长的测试。如果覆盖率的增长已经开始放缓,您可以添加额外的限制来产生更多“有趣的”刺激。当您到达一个平台期时,设计的某些部分没有得到执行,所以您需要创建更多的测试。最后,当你的功能覆盖率接近100%时,检查bug率。如果仍然发现bug,那么您可能没有对设计的某些领域进行真正的覆盖率度量。不要太急于达到100%的覆盖率,这只是表明你在所有常见的地方寻找漏洞。当你尝试着验证你的设计时,你可以在刺激空间中进行多次随机漫步;这可以产生许多意想不到的组合,如van der Schoot(2007)所示。

调试

设计

没有

覆盖数据库

通过吗?

TTeessttss

是的

覆盖率分析

图9.2覆盖流

每个模拟供应商都有自己的存储覆盖率数据的格式,以及自己的分析工具。您需要使用这些工具执行以下操作。

  • 用多个种子进行测试。对于一组给定的约束和覆盖组,将测试台编译并设计成一个单独的可执行文件。现在,您需要使用不同的随机种子反复运行这个约束集。您可以使用Unix系统时钟作为种子,但要小心,因为批处理系统可能会同时启动多个作业。这些作业可以在不同的服务器上运行,也可以在具有多个处理器的单个服务器上启动。因此,将所有这些价值结合起来,就能培育出真正独特的种子。为了保证种子的可重复性,必须保存模拟和覆盖结果。

  • 检查通过/失败。功能覆盖信息只对成功的模拟有效。当由于设计错误而导致模拟失败时,必须丢弃覆盖年龄信息。覆盖率数据度量验证计划中有多少项已经完成,并且该计划基于设计规范。如果设计不符合规范,覆盖率值是无用的。一些验证团队定期地从头开始测量所有的功能覆盖率,以便它反映设计的当前状态。

  • 跨多个运行分析覆盖率。随着时间的推移,您需要度量每个约束集的成功程度。如果你还没有在限制条件下的目标区域获得100%的覆盖率,但数量仍在增长,运行更多的种子。如果覆盖率水平已经趋于平稳,没有最近的进展,那么是时候修改约束条件了。只有当您认为达到一个特定部分的最后几个测试用例可能会花费太长时间来进行约束随机模拟时,您才应该考虑编写一个定向测试。即便如此,你也应该继续为设计的其他部分使用随机刺激,以防这种“背景噪音”发现漏洞。

9.覆盖类型

覆盖率是度量完成设计验证的进度的通用术语。当您试图覆盖所有合法组合时,您的模拟将慢慢地描绘出设计的画布。覆盖工具在模拟过程中收集信息,然后对其进行后处理以生成覆盖报告。您可以使用这个报告来寻找覆盖漏洞,然后修改现有的测试,或者创建新的测试来填补这些漏洞。这个迭代过程继续进行,直到您对覆盖级别感到满意为止。

9.3.1 代码覆盖率

度量验证进度的最简单方法是使用代码覆盖率。给你测量有多少行代码被执行(行覆盖率),通过代码路径和表达式被执行(路径覆盖),这一位变量的值0或1(切换覆盖),和一个状态机的状态和转换被访问(FSM)。您不需要编写任何额外的HDL代码。该工具通过分析源代码和添加隐藏代码来收集统计数据,自动地为你的设计提供工具。然后运行所有的测试,代码覆盖工具会创建一个数据库。

大多数模拟器都包含代码覆盖工具。后处理工具将数据库转换成可读的形式。最终结果是对您的测试在多大程度上执行了设计代码的度量。请注意,您主要关心的是分析设计代码,而不是测试台。未经测试的设计代码可能会隐藏硬件缺陷,或者可能只是冗余代码。

代码覆盖率衡量的是您的测试对设计规范的“实现”的执行程度,而不是验证计划。仅仅因为您的测试达到了100%的代码覆盖率,您的工作就没有完成。如果你犯了一个测试没有发现的错误怎么办?更糟糕的是,如果您的实现缺少一个特性怎么办?示例9.1中的模块是用于d触发器的。你能看出错误吗?

样例9.1缺失路径的不完整d触发器模型

重置逻辑被意外地遗漏了。一个代码覆盖工具会报告每一行都被执行了,但是模型没有被正确地实现。回到描述重置行为的功能规范,并确保您的验证计划包括验证这一点的需求。然后在复位期间收集设计的功能覆盖期信息。

9.3.2 功能覆盖

验证的目标是确保设计在其真实环境(MP3播放器、网络路由器或手机)中正确运行。设计说明书详细说明了设备应该如何操作,而验证计划则列出了如何刺激、验证和测量该功能。当您收集关于所覆盖功能的度量时,您正在执行“设计”覆盖。例如,d触发器的验证计划不仅会提到它的数据存储,还会提到它如何重置到已知状态。在您的测试检查这两个设计特性之前,您不会有100%的功能覆盖率。

功能覆盖率与设计意图相联系,有时称为“规范覆盖率”,而代码覆盖率则度量您对RTL代码的测试情况,称为“实现覆盖率”。“这是两种截然不同的指标。考虑一下,如果设计中缺少代码块会发生什么情况。代码覆盖率不能捕获这个错误,并且可以报告您已经执行了100%的行,但是功能覆盖率将显示功能不存在。

9.3.3 虫率

测量覆盖率的一种间接方法是看新bug被发现的速率,如图9.3所示。在项目的整个生命周期中,您应该跟踪每周发现的bug数量。在一开始,当您创建测试工作台时,您可能会通过检查发现许多bug。在阅读设计规范时,您可能会发现不一致之处,希望在编写RTL之前解决这些问题。一旦测试平台启动并运行,当你检查系统中的每个模块时,就会发现大量的bug。随着设计接近尾声,bug率下降,有望降至零。然而,您还没有完成。每当比率下降时,就需要找到不同的方法来创建极端情况。

图9.3项目期间的Bug率

根据许多因素,例如项目阶段、最近的设计更改、正在集成的模块、人员更改,甚至假期计划,每周的bug率都会有所不同。汇率的意外变化可能是潜在问题的信号。

如图9.3所示,即使在设计完成后,甚至在设计交付给客户后,仍然不断地发现bug是很常见的。

9.3.4 断言的报道

断言是一段声明性代码,用于检查设计信号之间的关系,可以是一次,也可以是一段时间。这些可以通过设计和测试工作台进行模拟,或者通过正式的工具进行验证。有时,您可以使用SystemVerilog过程代码编写等价的检查,但是使用SystemVerilog断言(SVA)更容易表示许多断言。

断言可以有局部变量并执行简单的数据检查。如果您需要检查一个更复杂的协议,例如确定一个包是否成功地完全通过路由器,过程代码通常更适合这项工作。在程序编码或使用SVA的序列之间有很大的重叠。参见Vijayaraghavan和Ramanadhan (2005), Cohen等人(2005),以及VMM书中的第3章和第7章,Bergeron等人(2006),以获得关于SVA的更多信息。

最常见的断言查找错误,比如两个互斥的信号,或者一个请求从来没有被授予。这些错误检查应在检测到问题时立即停止模拟。断言还可以检查仲裁算法、fifo和其他硬件。它们是用assert属性语句编码的。

有些断言可能会查找感兴趣的信号值或设计状态,例如成功的总线事务。这些都是用cover属性语句编码的。您可以通过使用asser- tion覆盖来度量这些断言在测试期间被触发的频率。覆盖属性观察信号序列,而覆盖组(如下所述)在模拟过程中采样数据值和事务。这两个构造重叠,因为在序列完成时可以触发覆盖组。此外,序列可以收集覆盖组可以使用的信息。

9.4. 功能覆盖策略

在编写第一行测试代码之前,您需要预测哪些是关键的设计特性、边界用例和可能的故障模式。这就是您如何编写验证计划。不要只从数据值的角度考虑问题;相反,要考虑设计中编码了哪些信息。这个计划应该说明重要的设计状态。

9.4.1 收集信息,而不是数据

一个经典的例子是FIFO。如何确定已经彻底测试了1K的FIFO内存?你可以测量读和写索引中的值,

但是有超过一百万种可能的组合。即使您能够模拟那么多周期,您也不会想要阅读覆盖率报告。

在更抽象的级别上,FIFO可以保存0到N-1个可能的值。那么,如果您只是比较读和写索引,以衡量FIFO有多满或有多空呢?你仍然有1K的保险值。如果您的测试工作台将100个条目推入FIFO,然后再推入100个,您真的需要知道FIFO是否曾经有150个值吗?只要能成功读出所有的值就行。FIFO的边角情况是满的和空的。如果你可以让FIFO从空(重置后的状态)到满,再回到空,你就覆盖了所有的关卡。其他有趣的状态还包括通过时的指数

在所有1和所有0之间。这些案例的覆盖报告很容易理解。

您可能已经注意到,有趣的状态与FIFO大小无关。同样,要看信息,而不是数据值。

具有大范围(超过几十个可能的值)的设计信号应该被分解为更小的范围,再加上边缘情况。例如,你的DUT可能有一个32位的地址总线,但是你当然不需要收集40亿个样本。检查自然分区,如内存和IO空间。对于计数器,选择一些有趣的值,并始终尝试将计数器值从所有的1回滚到0。

9.4.2 只衡量你将要使用的东西

收集功能覆盖率数据是很昂贵的,所以只测量您将分析和使用来改进您的测试的数据。当模拟器监控信号的功能覆盖时,您的模拟可能运行得更慢,但是这种方法比收集波形轨迹和测量代码覆盖的开销更低。模拟完成后,数据库被保存到磁盘。使用多个测试用例和多个种子,您可以用功能覆盖数据和报告填充磁盘驱动器。但是,如果您从来没有查看过最终的覆盖报告,那么就不要执行最初的度量。

有几种方法可以控制覆盖数据:在编译、实例化或触发时。您可以使用模拟供应商提供的开关、条件编译或抑制覆盖率数据的收集。最后一个是不太理想的,因为后处理报告充满了覆盖率为0%的部分,这使得找到少数启用的部分变得更加困难。

9.4.3 测量完整性

就像家庭度假时,你的孩子坐在后座上,你的经理不断地问你:“我们到了吗?”“你怎么知道你已经对设计进行了全面测试?您需要查看所有的覆盖率度量,并考虑bug率,以确定您是否到达了目的地。

在项目开始时,代码和功能覆盖率都很低。在您开发测试时,使用不同的随机种子反复运行它们,直到您不再看到功能覆盖率的增加值。创建额外的约束和

探索新领域的测试。保存高覆盖率的测试/种子组合,这样您就可以在回归测试中使用它们。

图9.4覆盖率比较

低 高

代码覆盖率

如果功能覆盖率很高,但是代码覆盖率很低,如图9.4左上角所示,该怎么办?您的测试没有执行完整的设计,可能来自一个不充分的验证计划。也许是时候回到硬件规范并更新您的验证计划了。然后您需要添加更多的功能覆盖点来定位未测试的功能。

更困难的情况是代码覆盖率高,但功能覆盖率低。即使你的测试台给设计一个很好的锻炼,你也不能把它放在所有有趣的状态。首先,查看设计是否实现了所有指定的功能。如果它存在,但是您的测试无法达到它,您可能需要一个正式的验证工具,可以提取设计的状态并创建适当的刺激。

目标是提高代码覆盖率和功能覆盖率。然而,先不要计划你的假期。感染率的趋势是什么?仍然会出现重大的bug吗?更糟糕的是,它们是被故意发现的,还是您的测试平台偶然发现了一个没有人预料到的状态的特定组合?另一方面,低bug率可能意味着你现有的策略已经失去了动力,你应该寻找不同的方法。尝试不同的方法

比如设计块和错误生成器的新组合。

9.5. 简单的功能覆盖示例

为了测量功能覆盖率,您从验证计划开始,并为模拟编写一个可执行版本。在您的系统Verilog testbench中,采样变量和表达式的值。这些样本点被称为掩蔽点。同时采样的多个覆盖点(例如在事务完成时)被放在一个覆盖组中。

样例9.2有一个事务,有8种形式。testbench随机生成dst变量,验证计划要求尝试每个值。

示例9.2简单对象的函数覆盖

样例9.2创建一个随机事务并将其驱动到接口。testbench使用CovDst2覆盖组采样dst字段的值。8个可能的值,32个随机事务——你的testbench生成了它们吗?示例9.3和示例9.4包含了来自VCS的部分覆盖率报告。由于随机性的存在,每个模拟器都会给出不同的结果。

如您所见,testbench生成的dst值为1、2、3、4、5、6和7,但从未生成0。“至少”列指定需要多少次命中才能认为一个容器被覆盖。关于at_least选项,请参阅9.10.3节。

为了提高功能覆盖率,最简单的策略是运行更多的模拟周期,或者尝试新的随机种子。在示例9.2中,下一个随机事务(#33)的dst值为0,覆盖率为100%。或者,如果您使用不同的种子开始模拟,那么对于这种简单的情况,您可能在更少的事务中达到100%。在一个真正的设计中,你可能会看到覆盖的停滞期

覆盖点越来越多,但有一些顽固的点永远不会被击中,不管你跑了多长时间,不管种子价值如何。在这种情况下,您可能必须尝试一种新的策略,因为测试工作台没有创建适当的刺激。任何报道报告中最重要的部分都是没有命中的点。

示例9.3一个简单对象的覆盖率报告

示例9.4简单对象的覆盖率报告,100%覆盖率

本书对如何计算覆盖率给出了一个粗略的解释。LRM在四页中对覆盖率计算进行了非常详细的解释,在整个章节中有更多的细节。请查阅它以获得最准确的细节。

9.6. 封面组的剖析

覆盖组类似于类——定义它一次,然后实例化它一次或多次。它包含覆盖点、选项、正式参数和可选触发器。一个覆盖组包含一个或多个数据点,所有数据点都是同时配置的。

您应该创建非常清晰的覆盖组名称,明确表示您所度量的内容,如果可能的话,参考验证计划。Parity_ Errors_In_Hexaword_Cache_Fills这个名称可能看起来有些冗长,但是当您试图读取包含几十个覆盖组的覆盖率报告时,您将会欣赏这些额外的细节。您还可以使用comment选项来获得额外的描述性信息,如9.9.2节所示。

覆盖组可以在类或程序或模块级别定义。它可以采样任何可见变量,如程序/模块变量,接口信号,或设计中的任何信号(使用层次引用)。类中的封面组可以抽样该类中的变量,也可以抽样嵌入对象中的数据值。

不要在数据类(如事务)中定义覆盖组,因为这样做会在收集覆盖年龄数据时造成额外的开销。想象一下,你正试图追踪酒吧里顾客喝了多少啤酒。你会试着跟着每一瓶酒从装货区流过,越过吧台,进入每一瓶吗

儿子吗?不,相反,你可以让每位顾客检查啤酒的种类和数量,如van der Schoot(2006)所示。

在SystemVerilog中,您应该在适当的抽象级别定义覆盖组。这个级别可以位于测试台和设计之间的边界,在读写数据的事务中,在环境配置类中,或者在任何需要的地方。任何交易的抽样必须等待到DUT实际接收到它。如果您在事务的中间注入了一个错误,导致它在传输中被中止,那么您需要更改处理它的方式,以实现功能覆盖。您需要使用仅为错误处理而创建的另一个掩蔽点。

一个类可以包含多个覆盖组。这种方法允许您拥有可以根据需要启用和禁用的单独组。此外,每个组可能有一个单独的触发器,允许您从许多源收集数据。

封面组必须实例化以收集数据。如果您忘记了,那么在运行时不会打印关于空句柄的错误消息,但是覆盖率报告将不会包含任何提到覆盖组的内容。该规则适用于定义在类内部或外部的覆盖组。

9.6.1 在类中定义覆盖组

覆盖组可以在程序、模块或类中定义。在任何情况下,都必须显式实例化它以开始抽样。如果封面组在类中定义,则称为嵌入式封面组。在这种情况下,在构造它时不使用单独的名称;用原来的封面组名就行了。必须在类的构造函数中构造嵌入式覆盖组,这与可以在任何时候构造的非嵌入式覆盖组相反。

样例9.5与本章的第一个样例非常相似,只是它在一个事务处理类中嵌入了一个覆盖组,因此不需要单独的实例名。

样例9.5类中的函数覆盖率

9.7. 触发一个掩护组

功能覆盖的两个主要部分是被采样的数据值和被采样的时间。当有新的值准备好时(比如事务完成时),您的testbench会触发覆盖组。这可以直接通过示例函数完成,如示例9.5中所示,或者通过在covergroup定义中使用覆盖率事件。覆盖事件可以使用@来阻塞信号或事件。

如果您希望从过程代码显式地触发覆盖率,如果没有任何现有信号或事件告诉您何时进行采样,或者一个覆盖组有多个实例单独触发,则使用sample。

如果您希望利用现有的事件或信号来触发覆盖率,请使用covergroup声明中的覆盖率事件。

9.7.1 使用回调采样

将功能覆盖集成到您的测试台中更好的方法之一是使用回调,正如最初在8.7节中所示。这种技术允许您构建一个灵活的测试平台,而不限制何时收集覆盖率。您可以决定验证计划中的每个点在什么地方和什么时候采样值。如果你需要一个额外的“钩子”在环境中回调,你总是可以以一种不显眼的方式添加一个,因为回调只会在测试注册回调对象的模拟过程中“触发”。您可以为每个覆盖组创建许多单独的回调,而开销很小。如8.7.4节所述,回调优于使用邮箱将testbench连接到coverage对象。您可能需要多个邮箱来从测试台上的不同位置收集事务。邮箱需要一个事务处理程序来接收事务,而多个邮箱会导致您在多个线程之间来回切换。使用被动回调代替主动事务处理。

示例8.26 - 8.28显示了一个驱动程序类,它有两个回调点,分别在事务传输之前和之后。样例8.26显示了基本回调类,样例8.28有一个带有扩展回调类的测试,它将数据发送到记分板。使用基本回调类Driver_cbs的自己的扩展Driver_cbs_coverage来调用post_tx中覆盖组的示例任务。将覆盖回调类的实例推入驱动程序的回调队列中,您的覆盖代码会在正确的时间触发覆盖组。示例9.6和9.7定义并使用回调Driver_cbs_coverage。

UVM建议通过监视DUT来收集覆盖,并通过分析端口(类似于邮箱)将事务发送到覆盖组件。

示例9.7函数覆盖的回调

9.7.2 包含用户定义的样例参数列表的覆盖组

在样例9.5中,封面组在类内部定义的事务对象中示例了一个变量。如果封面组是在类的外部定义的,则可以通过定义自己的参数列表通过sample方法传递变量。现在您可以从testbench中的任何地方采样变量。

在示例9.8中,覆盖组也被扩展为覆盖低数据位。run方法的最后一条语句将传递目标地址和高速模式的配置变量。

样例9.8定义样例方法的参数列表

9.7.3 用事件触发器覆盖组

在示例9.9中,当testbench触发时,对CovDst9覆盖组进行采样

trans_ready事件。

样本9.9带有触发器的覆盖组

与直接调用示例方法相比,使用事件的优点是您可以使用现有的事件,例如由断言触发的事件,如示例9.11所示。

9.7.4 在系统Verilog断言上触发

如果您已经有一个寻找有用事件(如完整事务)的SVA,那么您可以添加一个事件触发器来唤醒覆盖组,如9.10所示。

带有SystemVerilog断言的示例9.10模块

9。11样本触发了一个有安全措施的掩护组

9.8. 数据采集

如何收集覆盖信息?当您在覆盖点中指定一个变量或表达式时,SystemVerilog会创建许多“bins”来记录每个值被看到的次数。这些箱子是衡量功能覆盖率的基本单位。如果对一个位变量进行取样,则最多创建两个bins。您可以想象,每当覆盖组被触发时,System Verilog都会在一个或另一个bin中丢弃一个令牌。在每次模拟结束时,都会创建一个数据库,其中包含所有带有令牌的bins。然后运行一个分析工具,它读取所有数据库,并生成一个包含设计的每个部分的覆盖率和总覆盖率的报告。

9.8.1 单个垃圾箱和全部覆盖

要计算一个点的覆盖范围,首先必须确定可能值的总数,也称为域。每个容器可以有一个值,也可以有多个值

值。覆盖率是采样值的数量除以域中的容器数量。

掩蔽点是一个3位变量,其域为0:7,通常被分为8个箱。如果在模拟过程中,对属于7个箱子的值进行抽样,报告将显示7/8或87.5%的覆盖率。所有这些点结合起来显示整个组的覆盖率,然后所有组结合起来给出所有模拟数据库的覆盖率百分比。

这是单个模拟的状态。您需要随着时间的推移跟踪覆盖率。查找趋势,以便查看在何处运行更多模拟或添加新的约束或测试。现在,您可以更好地预测何时完成设计的验证。

9.8.2 自动创建垃圾箱

正如您在示例9.3的报告中看到的,System Verilog自动为掩蔽点创建箱子。它查看采样表达式的域,以确定可能值的范围。对于N位宽的表达式,有2N个可能的值。对于3位变量dst,有8个可能的值。枚举类型的范围见9.6.8节。枚举数据类型的域是命名值的数目。您还可以像9.6.5节中那样显式地定义bins。

9.8.3 限制自动垃圾箱创建的数量

封面组选项auto_bin_max指定要自动创建的最大垃圾箱数量,默认为64个垃圾箱。如果覆盖点变量或表达式中的值域大于此选项,System Verilog将该范围划分为auto_bin_max bins。例如,一个16位变量有65,536个可能的值,因此64个bin中每个bin包含1024个值。

在现实中,您可能会发现这种方法不切实际,因为很难在一堆自动生成的箱子中找到缺失覆盖的针。将这个限制降低到8或16,或者更好的是,如章节9.6.5所示显式地定义bins。

示例9.12采用了本章的第一个示例,并添加了一个覆盖点选项,将auto_bin_max设置为两个箱子。被采样的变量仍然是dst,对于一个有8个可能值的域,dst是3位宽的。第一个容器存放范围的下半部分,即0-3,另一个存放范围的上半部分,即4-7。

示例9.12使用auto_bin_max设置为2

来自VCS的覆盖率报告显示了这两个区域。这个模拟实现了100%的覆盖率,因为8个dst值被映射到两个箱子。由于两个箱子都有采样值,你的覆盖率是100%,如示例9.13所示。

示例9.13 auto_bin_max设置为2的报告

示例9.12仅使用auto_bin_max作为覆盖点的选项。您还可以使用它作为整个组的选项,如示例9.14所示。

示例9.14对所有覆盖点使用auto_bin_max

9.8.4 抽样表达式

您可以对表达式进行示例,但总是要检查覆盖率报告,以确保您获得了您期望的值。您可能需要调整计算表达式的宽度,如2.16节所示。例如,采样3位报头长度(0:7)加上4位有效载荷长度(0:15)只创建24或16个垃圾箱,这可能不够,如果你的事务实际上可以从0到22字节长。

示例9.15在掩蔽点中使用表达式

示例9.15有一个采样总包长度的覆盖组。封面点有一个标签,使它更容易阅读封面报告。此外,表达式有一个额外的虚拟常量,因此事务长度以5位精度计算,对于最多32个自动生成的bins。

对随机数据包的长期运行表明,len16的覆盖率为100%,但这只覆盖了16个垃圾箱。(cover point只有16个bin,因为在Verilog中3位和4位的值之和只有4位。)掩护点len32在32个垃圾桶中有72%的覆盖率。(将5位值加到bin32的表达式中会得到5位结果。)这两个覆盖点都不正确,因为最大长度的范围是0:22(0+0:7+15)。自动生成的垃圾箱不能工作,因为最大长度不是2的幂。您需要一种精确定义箱子的方法。

9.8.5 用户定义的垃圾箱发现一个Bug

自动生成的bins可以用于匿名数据值,例如计数器值、地址或2的幂值。对于其他值,您应该显式地命名箱子,以提高准确性并简化覆盖率报告分析。System Verilog自动为枚举类型创建bin名称,但对于其他变量,您需要为感兴趣的状态命名。指定bins最简单的方法是使用[]语法,如示例9.16所示。

样例9.16为事务长度定义bins

在抽样许多随机事务后,该组的覆盖率为95.83%。快速浏览一下示例9.17中的报告就会发现这个问题——23(17十六进制)的长度从未出现过。最长的头部是7,最长的有效载荷是15,总共是22,不是23!如果您将bins声明更改为使用0:22,覆盖率将跳转到100%。用户定义的垃圾箱在测试中发现了一个错误。

示例9.17事务长度的覆盖报告

9.8.6 命名盖点箱

示例9.18对一个4位变量kind进行了采样,该变量有16个可能的值。第一个箱子被称为零,并计算这种类型在抽样时为0的次数。接下来的4个值(1-3和5)都被分组到一个单独的容器中,即lo。hi_8、hi_9、hi_a、hi_b、hi_c、hi_d、hi_e、hi_f分别放在8个桶中。注意hi bin表达式中的$是如何用作所采样变量的最大值的速记符号的。最后,misc保存所有之前没有选择的值:4、6和7。

示例9.18指定bin名称

注意,关于coverpoint的额外信息是用花括号:{}分组的。这是因为bin规范是声明性代码,而不是按begin…end分组的程序代码。最后,最后的花括号后面没有分号,就像结尾一样。

现在您可以很容易地在示例9.19中看到哪些垃圾箱没有命中-在本例中是hi_8。

示例9.19显示bin名称的报告

当您定义箱子时,您将限制用于覆盖率的值为您感兴趣的那些。SystemVerilog不再自动创建箱子,它将忽略不属于预定义箱子的值。更重要的是,只有您创建的箱子才用于计算函数覆盖率。你得到100%的覆盖率,只有当你得到命中在每个指定的垃圾箱。

不属于任何指定bin的值将被忽略。如果采样值(如事务长度)不是2的幂,则此规则很有用。如果您正在指定bin,您可以使用默认的bin指示符来捕获您可能已经忘记的值。然而,LRM说在覆盖率计算中不使用默认垃圾箱。

在示例9.18中,hi的范围使用右边的美元符号($)来指定上限。这是一个非常有用的快捷方式-现在你可以让编译器计算

一个范围的限制。您可以使用范围左侧的美元符号来指定下限。在示例9.20中,bin neg的范围内的$表示距离0最远的负数:32h8000_0000,或-2,147,483,648,而bin pos中的$表示最大的正数,32 ' h7fff_ffff,或2,147,483,647。

示例9.20使用$指定范围

9.8.7 条件覆盖

您可以使用iff关键字为掩蔽点添加条件。这样做的最常见的原因是在复位期间关闭覆盖,这样杂散触发器就会被忽略。样例9.21只在rst为0时采集dst值,其中rst为active-high。

9.21条件覆盖-在重置期间禁用

另外,您还可以使用start和stop函数来控制覆盖组的单个实例,如示例9.22所示。

示例9.22使用stop和start函数

9.8.8 为枚举类型创建箱子

对于枚举类型,SystemVerilog为每个值创建一个bin,如示例9.23中所示。

示例9.23枚举类型的函数覆盖

下面是来自VCS的覆盖率报告的一部分,示例9.24显示了枚举类型的箱子。

示例9.24包含枚举类型的覆盖率报告

如果想将多个值分组到一个容器中,就必须定义自己的容器。除非使用默认说明符定义一个bin,否则枚举值之外的任何bin都将被忽略。当您收集枚举类型的覆盖率时,auto_bin_max不适用。

9.8.9 过渡覆盖

您可以为掩蔽点指定状态转换。这样,您不仅可以知道看到了哪些有趣的值,还可以知道这些序列。例如,您可以检查dst是否从0到1、2或3,如示例9.25中所示。

样例9.25为覆盖点指定转换

您可以使用范围快速指定多个转换。表达式(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[*3]=> 2).如果要将值1重复3、4或5次,使用1[*3:5]。

9.8.10 通配符状态和转换

您可以使用通配符关键字创建多个状态和转换。任意X Z或?在表达式中被视为0或1的通配符。示例9.26创建了一个掩蔽点,该掩蔽点带有一个存放偶数值和一个存放奇数值的容器。

示例9.26用于掩护点的通配符箱

9.8.11 忽视价值观

对于一些掩蔽点,你永远不会得到所有可能的值。例如,一个3位变量可以仅存储6个值,即0-5。如果您使用自动创建的箱子,您永远不会超过75%的覆盖率。有两种方法可以解决这个问题。您可以显式地定义您想要覆盖的箱子,如9.6.5节所示。另外,您可以让SystemVerilog自动创建bins,然后使用ignore_bins来告诉哪些值应该从函数覆盖计算中排除,就像示例9.27中那样。

示例9.27覆盖点与ignore_bins

原来的3位变量low_ports_0_5的范围是0:7。ignore_ bins排除了最后两个bins,这将范围缩小到0:5。所以这一组的总覆盖率是有样本的箱数,除以总箱数,这里是5。

示例9.28覆盖点与自动bin max和忽略垃圾箱

如果显式或使用auto_bin_max选项定义垃圾箱,然后忽略它们,被忽略的垃圾箱不会对覆盖率的计算产生影响。在示例9.28中,最初使用auto_bin_max选项创建了四个垃圾箱:0:1、2:3、4:5和6:7。然而,最上面的垃圾箱被ignore_bins删除,所以最后只创建了三个垃圾箱。这个覆盖点可以覆盖0%,33%,66%,或100%。

9.8.12 非法的垃圾箱

一些采样值不仅应该被忽略,而且如果看到它们也应该导致错误。这最好在testbench的监视器代码中完成,但也可以通过使用illegal_bins标记bin来完成,如示例9.29所示。使用它来捕获测试的错误检查错过的状态。这也会双重检查你的bin创建的准确性:如果一个非法的值被覆盖组发现,这是testbench或者你的bin定义的问题。

示例9.29使用illegal_bins的覆盖点

9.8.13 状态机的报道

您应该注意到,如果在状态机上使用了覆盖组,那么您可以使用bins来列出特定的状态,以及弧的转换。然而,这并不意味着您应该使用SystemVerilog的功能覆盖率来度量状态机覆盖率。你必须手动提取状态和弧线。即使您第一次正确地执行了此操作,您也可能会错过将来对设计代码的更改。相反,使用代码覆盖工具自动提取状态寄存器、状态和弧,避免可能的错误。

然而,一个自动工具准确地提取信息编码,错误和所有。您可能希望使用功能覆盖手动监视小型、关键的状态机。

9.9. 交叉覆盖

覆盖点记录单个变量或表达式的观测值。您可能不仅想知道发生了什么总线事务,还想知道在这些事务期间发生了什么错误,以及它们的源和目标。为此,您需要交叉覆盖来度量同时在两个或多个覆盖点上看到的值。请注意,当您测量一个具有N个值的变量和另一个具有M个值的变量的交叉覆盖时,SystemVerilog需要NM个交叉箱来存储所有的组合。

9.9.1 基本交叉覆盖例子

前面的示例已经测量了事务类型和目标端口号的覆盖范围,但是将两者结合起来又如何呢?你在每个港口都试过各种交易了吗?SystemVerilog中的交叉构造记录一组中两个或多个覆盖点的组合值。交叉语句只接受覆盖点或简单的变量名。如果您想在对象中使用表达式、层次名称或变量(如handle)。变量时,必须首先在带有标签的coverpoint中指定表达式,然后在cross语句中使用标签。

样例9.30为tr.kind和tr.dst创建覆盖点。然后将这两点交叉表示所有的组合。SystemVerilog总共创建128个箱子(8个16)。注意:即使是简单的交叉也会导致大量的垃圾箱。

样本9.30基本交叉覆盖

一个随机测试平台创建了56个事务,并生成了示例9.31中的覆盖率报告。注意,即使所有可能的种类和dst值都产生了,也只有1/3的杂交组合被看到。这是一个非常典型的结果。还要注意,集团的总覆盖范围是交叉覆盖加上覆盖年龄和dst。

示例9.31基本交叉覆盖的覆盖摘要报告

9.9.2 标记交叉覆盖箱

如果您想要更易于阅读的交叉覆盖bin名称,您可以像示例9.32中所演示的那样标记单个覆盖点bin,并且SystemVerilog在创建交叉bin时将使用这些名称。

示例9.32指定交叉覆盖的bin名称

如果您定义了包含多个值的箱子,覆盖率统计信息就会改变。在下面的报告中,垃圾箱的数量已从128个减少到80个。这是因为kind有10个bins: 0、lo、hi_8、hi_9、9hi_a、hi_b、hi_c、hi_d、hi_e和hi_f。请记住,使用默认值定义其值的misc bin不会添加到覆盖总数中。覆盖率的百分比从87.5%跃升到90.91%,如示例9.33所示,因为罗仓中的任何单个值,如2,允许将该仓标记为covered,即使没有看到其他值,如1或3。

带有标签箱的交叉覆盖报告

9.9.3 不包括交叉覆盖箱

要减少垃圾箱的数量,请使用ignore_bins。使用交叉覆盖,您可以使用binsof指定覆盖点,使用intersect指定一组值,这样一个ignore_bins构造可以清除许多单独的bins。

样品9.34不包括交叉覆盖的箱子

示例9.34中的第一个ignore_bins只是排除了dst为7和任何kind值的bins。由于kind是一个4位值,因此该语句排除了12个bins,因为misc的值4-7不计算,因为是默认值。第二个ignore_bins选择性更强,忽略dst为0和kind为9、10或11的bins,总共有3个bins。ignore_bins可以使用在各个覆盖点中定义的bins。ignore_bins lo使用bin名称来排除kind。这是1 2 3。垃圾箱必须是在编译时定义的名称,比如0和lo。hi_8, hi_9, hi_a,…hi_f和任何自动生成的bins都没有可以在编译时在其他语句中使用的名称,例如ignore_bins;这些名字是

在运行时或报告生成期间创建。

注意,binsof使用了圆括号(),而intersect指定了一个范围,因此使用了花括号{}。

9.9.4 从总覆盖度量中排除覆盖点

一个组的总覆盖范围基于所有简单覆盖点和交叉覆盖。如果仅对要在交叉语句中使用的coverpoint中的变量或表达式进行抽样,则应该将其权重设置为0,这样它就不会对总覆盖率产生影响。

样品9.35指定交叉覆盖权重

有两种类型的选项:特定于covergroup实例的选项,以及为整个covergroup类型指定选项的选项。特定于实例的选项类似于局部变量,使用option关键字指定,如option中所示。示例9.12中的auto_bin_max=2。备选项是用type_option关键字指定的,并且与封面组绑定,就像类中的静态变量一样。在示例9.35中,type_option。权重应用于此组的所有实例。LRM详细解释了它们之间的区别,本书展示了最常见的选项及其用法。

9.9.5 合并来自多个域的数据

交叉覆盖的一个问题是,您可能需要从不同的定时域采样值。您可能想知道您的处理器是否在缓存填充过程中收到过中断。中断硬件与缓存硬件是分开的,并且可能使用不同的时钟,这使得很难知道什么时候触发覆盖组。另一方面,你要确保你已经测试过这种情况,因为以前的设计有这种类型的错误。

解决方案是创建一个独立于缓存或中断硬件的定时域。将信号复制到临时变量中,然后将它们抽样到一个新的覆盖组中,以衡量交叉覆盖。

9.9.6 交叉覆盖的替代品

当你的交叉覆盖定义变得更加详细时,你可能会花费相当多的时间来指定哪些箱子应该被使用,哪些应该被忽略。你可能有两个随机的位a和b,它们有三种有趣的状态,{a==0, b==0},

{a==1, b==0}, {b==1}。

示例9.36展示了如何在覆盖点中命名容器,然后使用这些容器收集交叉覆盖。

示例9.36使用bin名称的交叉覆盖

示例9.37收集了相同的交叉覆盖,但是现在使用binof来指定交叉覆盖值。

样品9.37箱交叉覆盖

或者,您可以制作一个覆盖点,对值的连接进行示例。

然后,您只需使用不那么复杂的覆盖点语法定义bins。

示例9.38使用连接模拟交叉覆盖

如果您已经为单个覆盖点定义了容器,并且想要使用它们来构建交叉覆盖容器,请使用示例9.36中的样式。如果您需要构建交叉覆盖箱,但没有预定义的覆盖点箱,请使用示例9.37。如果您想要tersest格式,请使用示例9.38。

9.10. 一般覆盖组

当你开始写封面组的时候,你会发现有些是非常相似的。SystemVerilog允许您创建一个通用的覆盖组,以便在实例化它时可以指定一些惟一的详细信息。

9.10.1 按值传递覆盖组参数

示例9.39显示了一个使用参数将范围分成两部分的封面组。只需将中点值传递给覆盖组的新函数。

示例9.39 Covergroup,带有简单参数

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

您可以指定一个变量,通过引用传递进行采样。这里,您希望覆盖组在整个模拟过程中对值进行采样,而不仅仅是在调用构造函数时使用值。

9.40示例引用传递

像任务或函数一样,覆盖组的参数具有粘性方向。在9.40样例中,如果你忘记了输入方向-,mid参数将有ref方向。这个例子无法编译,因为你不能将常量(4或2)传入ref参数。

9.11. 覆盖选项

您可以使用选项在封面组中指定其他信息。有两种类型的选项:应用于特定封面组实例的实例选项和应用于封面组所有实例的类型选项,它们类似于类的静态数据成员。选项可以放在盖组,使他们适用于所有盖点在组,或他们可以放在一个单一的盖点,以更好的控制。您已经看到了auto_bin_max和weight选项。这里还有几个。

9.11.1 每个覆盖

如果您的testbench多次实例化一个覆盖组,默认情况下System- Verilog将所有实例中的覆盖数据组在一起。然而,如果你

有几个生成器,每个生成器创建非常不同的事务流,您将需要看到单独的报告。例如,一个生成器可能创建长事务,而另一个生成器创建短事务。示例9.41中的覆盖组可以在每个单独的生成器中实例化。它跟踪每个实例的覆盖范围,并拥有一个唯一的注释字符串,其中包含覆盖组实例的层次路径。

示例9.41指定每个实例的覆盖率

每个实例选项只能在覆盖组中给出,而不能在覆盖点或交叉点中给出。

9.11.2 覆盖集团发表评论

您可以向覆盖率报告中添加注释,使它们更容易分析。注释可以像从验证计划到报表解析器用于自动从大量数据中提取相关信息的标记的区号那样简单。如果您有一个只实例化一次的覆盖组,请使用type_选项,如示例9.42所示。

样例9.42为封面组指定注释

但是,如果您有多个实例,您可以给每个实例单独的注释,只要您还使用示例9.43中所示的per-instance选项。

样例9.43为覆盖组实例指定注释

9.11.3 报道阈值

对于设计,您可能没有足够的可见性来收集健壮的覆盖率信息。假设您正在验证DMA状态机是否可以处理总线错误。您不能访问它的当前状态,但是您知道传输所需的周期范围。所以如果你在这个范围内重复地造成错误,你可能已经覆盖了所有的状态。所以你可以设置选项。at_least to 8 or more指定在一个容器上命中8次后,您就确信您已经执行了该组合。

如果您定义选项。至少在覆盖组水平上,它适用于所有覆盖点。如果你在一个点内定义它,它只适用于这个点。

然而,正如样本9.2所示,即使在32次尝试之后,随机变量仍然没有达到所有可能的值。因此,只有在没有直接测量覆盖率的方法时才使用at_least,比如当测试台不能探测DUT细节时。

9.11.4 打印空箱子

默认情况下,覆盖率报告只显示带有示例的箱子。您的工作是验证验证计划中列出的所有内容,因此您实际上对没有示例的容器更感兴趣。使用选项cross_num_print_missing来告诉模拟和报告工具向您显示所有箱子,特别是没有击中的箱子。将其设置为一个较大的值,如示例9.44中所示,但不要大于您愿意读取的值。

报告所有箱子,包括空箱子

9.11.5 覆盖目标

覆盖组或点的目标是该组或点被认为完全覆盖的水平。默认的覆盖率是100%。如果你把这个级别设置为低于9.45样例中的100%,你要求的覆盖率低于完全覆盖,这可能是不可取的。此选项仅影响覆盖率报告。

示例9.45指定覆盖目标

9.12. 分析覆盖率数据

一般来说,假设你需要更多的种子和更少的约束。毕竟,运行更多的测试比构造新的约束更容易。如果不小心,新的约束很容易限制搜索空间。

如果你的掩蔽点只有0个或一个样本,你的约束可能根本就没有针对这些区域。您需要添加约束,将解算器“拉”到新的领域。在示例9.16中,事务长度分布不均匀。示例9.46显示了完整的类。这种情况类似于掷两个骰子并查看总价值时所看到的分布。

示例9.46包长度的原始类

这个类的问题是len的权重不均匀。看看覆盖率报告,注意到低值和高值是如何很少被触及的。图9.5是来自报告的值的图表。

160

140

120

One hundred.

80

60

40

20.

0

0 5 10 15 20. 25

包长度

图9.5数据包长度的不均匀概率

如果你想让总长度均匀分布,使用solve…之前

约束如图9.47所示,并绘制在图9.6中。

示例9.47在包长度约束之前解决

120

One hundred.

80

60

40

20.

0

0 5 10 15 20. 25

包长度

图9.6之前求解…时数据包长度的偶概率

之前解决…的常规选择是dist约束。然而,这行不通,因为len也受到两个长度之和的限制。

9.13. 在模拟过程中测量覆盖率统计

您可以在模拟过程中实时查询功能覆盖级别。这允许您检查您是否达到了您的覆盖目标,并且可能控制一个随机测试。

在全球范围内,您可以获得所有覆盖群体的总覆盖率

$get_coverage,它返回一个介于0之间的实数。和100年。这个系统任务检查所有覆盖组。

您可以使用get_coverage()和get_ inst_coverage()方法来缩小度量范围。第一个函数使用覆盖组名和实例,以覆盖覆盖覆盖组的所有实例,例如CoverGroup::get_coverage()或cginst1 .get_coverage()。第二个函数返回特定覆盖组实例的覆盖率,例如cgInst。get_inst_coverage()。您需要指定option。如果您想收集每个实例的覆盖率,则per_instance=1。

这些函数的最实际用途是在长期测试中监视覆盖率。如果覆盖级别在给定数量的事务或周期之后没有提高,那么测试应该停止。希望,另一个种子或测试将增加覆盖率。

虽然有一个能够根据功能覆盖结果执行一些复杂操作的测试是很好的,但是编写这类测试是非常困难的。每个测试+随机种子对可能会发现新的功能,但可能需要多次运行才能达到一个目标。如果一个测试发现它没有达到100%的覆盖率,它应该做什么?跑更多的周期?有多少?它是否应该改变所产生的刺激?如何将输入中的更改与功能覆盖级别关联起来?一个可靠的改变是随机种子,你应该在每次模拟中只做一次。否则,如果刺激依赖于多个随机种子,你如何重现设计漏洞?

如果您想创建自己的覆盖率数据库,您可以查询功能覆盖率统计信息。验证团队已经建立了他们自己的SQL数据库,这些数据库提供了来自模拟的功能覆盖数据。这种设置允许他们更好地控制数据,但需要在创建测试之外进行大量的工作。

一些正式的验证工具可以提取设计的状态,然后创建输入刺激以达到所有可能的状态。不要试图在你的测试台上复制这个!

9.14. 结论

当您从编写直接测试(手工制作每个刺激点)转换到受限随机测试时,您可能会担心测试不再受您的控制。通过度量覆盖率,特别是功能覆盖率,您可以通过了解哪些特性已被测试而重新获得控制权。

使用功能覆盖需要详细的验证计划和大量时间来创建覆盖组、分析结果和修改测试以创建适当的刺激。这可能看起来工作量很大,但比编写等效的定向测试所需要的工作量要少。此外,收集覆盖率所花费的时间可以帮助您更好地跟踪验证设计的进展。

9.15. 练习

  1. 对于下面的类,编写一个covergroup来收集测试计划需求的覆盖率,“必须测试所有ALU操作码”。“假设操作码在信号clk的正边缘有效。

  1. 将解决方案扩展到练习1,以覆盖测试计划需求,“Operand1应取最大值为负(−128)、零和最大值为正(127)。”为每一个值定义一个覆盖箱以及一个默认箱。将coverpoint标记为operand1_cp。

  2. 将解决方案扩展到练习2,以涵盖以下测试计划需求:

    1. “操作码应该采用ADD或SUB值”(提示:这是一个覆盖bin)。

    2. “操作码应该采用添加的值和SUB的值”(提示:这是第二个覆盖bin)。

标记coverpoint opcode_cp。

  1. 将解决方案扩展到练习3,以覆盖测试计划需求,“Opcode必须不等于DIV”(提示:使用illegal_bins报告错误)。

  2. 将解决方案扩展到练习4,以收集测试计划需求的覆盖范围,“当operand1是最大的负值或最大的正值时,opcode应采用ADD或SUB值。“将交叉覆盖率加权为5。

  3. 假设你的covergroup被称为Covcode,并且covergroup的实例化名是ck,将练习4展开到:

    1. 显示由实例名称引用的coverpoint operand1_cp的覆盖范围。

    2. 显示由covergroup名称引用的coverpoint opcode_cp的覆盖范围。

results matching ""

    No results matching ""