目录
一、FPV简介
二、FPV实际应用
三、基本的FPV环境
1.编写RTL
2.创建覆盖点
3、创建假设
4、创建断言
5、时钟和复位
6、运行验证
四、FPV不同于模拟
1、可以运行哪些类型和尺寸的模型
2、如何达到目标行为
3、检查哪些值
4、怎样约束模型
5、如何处理内部节点上的约束
6、我们使用什么进行调试(debug)
7、典型的轨迹(traces)有多长
五、决定在哪里以及如何运行FPV
1、使用Design exercise FPV
2、使用Bug hunting FPV
3、使用full proof FPV
4、使用Application-specific FPV
上图中,左边三个是FPV的输入内容,右边是输出内容。
1.输入:
2.输出:
上图中:
FPV工具基本上采用一个模型、一组属性和一组约束,并试图确定每个约束在该状态空间中的位置,这种基本技术可以用在典型的片上系统(SOC)设计和验证流程中,许多不同类型任务的基础:
组合锁RTL的基本框图:
组合锁RTL的顶层描述:
编写可在FPV中运行的RTL模块,现在所有的FPV工具都要求是可综合的RTL,通常排除以下SystemVerilog元素:
通常FPV是处理可综合RTL,bind可以将不可综合的RTL和可综合RTL分离,通过适当的计划,应该能够将可综合代码与任何不可综合代码分开,以便完全启用FPV 。
在一些没有太多FPV经验的项目中,覆盖点被视为FPV的事后考虑,但本书作者认为这是一个严重的错误,在FPV工具中,碰上覆盖点有助于确认,在当前设置的验证环境中,系统可能会出现期望的典型行为。(一些应用实例,FPV用户在忽略覆盖点的情况下创建了有用的断言页面,表明这些断言都是经过验证的,并告诉他们的经理,他们完成了一个主要的验证项目,但后来发现由于一些小问题,例如使用了错误的重置极性,实际验证的设计逻辑百分比为个位数。)
为模型创建一组合理的覆盖点,开始任何FPV工作。
对于合适的覆盖点集,没有简单的公式,因为它非常依赖于精确设计,经常想要涵盖的常见情况包括:
不一定都会有上述怎这么多覆盖点,但必须包含以下内容:
c1: cover property (open == 0);
c2: cover property (open == 1);
generate for (i=0; i<3; i++) begin
for (j=0; j<9; j++) begin
// We use a one-hot encoding
c3: cover property (digit[i][j] == 1);
end
end
创建假设:关于模型输入的基本行为的陈述,这将使FPV工具能够关注问题空间的有效部分。
在上述组合锁示例中,一个基于四位小数中每一位的独热编码模型的输入:
input bit [9:0] digits [3:0];
基于此建模,我们希望确保在四位插槽中的每个插槽中,只有一位处于有效状态:
generate for (i=0; i<4; i++) begin
a1: assume property ($onehot(digits[i]));
end
对于这个模型,可能会遇到更多需要的假设,但在调试过程中会找到答案。
理想情况下,能够使用FPV工具,来确保断言对模型上所有可能的仿真(模拟)都是真实的。最初在设计中提出FPV环境时,需要记住一个重要原则:首次提出模型时,查看覆盖点痕迹比试图证明断言更重要。
事实上,有时意外地过度约束了模型,用过度激进的假设排除了有趣的痕迹,使得断言全部经证明,并未覆盖到有风险的角落实例。覆盖点会迫使工具生成波形,代表当前验证环境支持的潜在模拟,这些覆盖点让人确信,该工具在探索证据空间。
理想情况下,我们希望根据输入编写断言,并检查输出,我们可以使用SVA序列为正确的组合属性编写端到端断言:
sequence correct_combo_entered;
(digits == COMBO_FIRST_PART) ##1
(digits == COMBO_SECOND_PART) ##1
(digits == COMBO_THIRD_PART);
endsequence
open_good: assert property (correct_combo_entered |=> open);
这意味着我们还需要一个断言,如果任何组合不好,它不会打开锁。我们真正想证明的是,如果三个循环组合中的任何一个部分都是错误的,锁就不会打开。因此,与其创建一个单一属性,不如创建三个更简单的属性:
open_ok2: assert property (
open |-> $past(digits,3)==COMBO_FIRST_PART);
open_ok1: assert property (
open|-> $past(digits,2)==COMBO_SECOND_PART);
open_ok0: assert property (
open |-> $past(digits,1)==COMBO_THIRD_PART);
我们将能够直接看到哪个逻辑条件失败,而不必与多个逻辑条件混合在一起进行分析。
使每个断言尽可能简单,且使用多个小断言而不是一个(逻辑上等价的)大断言。
创建FPV环境还需要两个关键的设置信息:clock和reset。
运行FPV工具然后查看结果,我们希望的结果包含以下内容:
第一次使用FPV工具,看到断言失败,不应该马上去找debug fail assertions,第一步应该做健全性检查,确保覆盖痕迹看起来合理。这里的覆盖点可以帮助我们演示一些显示预期行为的示例波形:让我们使用它们来确定行为是否合理,或者在设置环境时是否遗漏了一些关键内容。
在debug assertion failures 之前,观察主要覆盖点的波形,确保其符合RTL的预期行为。
上图为第一次运行的结果图。
覆盖点c1是为了演示锁未打开的情况:
上述设计应该需要三个周期才能进入完整的组合,但该工具声称它可以在两个周期内打开锁,存在错误。
上图是覆盖点2打开锁的波形图。
看看图4.7,可以看到锁是如何在循环2打开的:忘记了覆盖输入(override input),通过将此覆盖设置为1,我们可以随时打开锁。有两种主要方法可以解决这个问题:
c2:cover property(open == 1);
替换为:
c2:cover property((open == 1) && !$past(override));
fix1: assume property (override == 0);
根据我们的验证目标,上述任一解决方案都可能是合适的。
如果override逻辑被认为是有趣的、新的,或者是由于其他原因导致的潜在错误源,应该使用解决方案1,并在其他属性中考虑它;如果不认为override逻辑有趣或有风险,或者不想为了当前的验证而专注于它,那么解决方案2更容易实现。
现在使用方法2,加入假设并得到c2的波形图:
新的结果如下表所示:
失败的断言,可以要求工具为其中一个失败的断言提供一个失败跟踪或反例,下图显示了一个用于断言open_good的断言:
在本例中,可以对照波形中的值检查代码,以验证“数字”输入是否与代码中的常量匹配,但它们似乎不是实际打开锁的值,事实上,最初在RTL中使用的可疑规则常量{3210,3210,3210}(当从独热二进制转换为它们所代表的小数时),实际上只是占位符,不能正确定义所需的值。这在实际处理过程中很常见。
覆盖点c2覆盖了想要的条件,显示了一个特定的波形,我们可以看到每个循环的正确值,这将导致我们设计中的锁打开,如图4.10所示:
从这个覆盖波形可以看出,读取打开锁所需的三个周期的组合值:{2733, 0003, 2733},并更正RTL中的常数。
这是FV能力的一个很好的证明:不需要弄清(能够得到想要结果的)详细输入,指定了一个覆盖点,FPV工具能自己找到一种方法得到想要的结果。
一旦将常数更改为正确的值并重新运行验证,将看到表4.3所示的结果:
可以看到属性open_good Pass,表明常数中提到的组合确实打开了锁。
使用FPV查看其它fail的情况:
仔细观察这个断言的相关周期上的数字输入值,发现是组合{2735},与预期的{2733}只有最后一位不同。在研究了bad1和bad0的反例之后,可以看到一个类似的问题,帮助找到了一个剩余的RTL错误,该错误实际上允许组合的最后一位使用多个可选值。
在解决这个问题并对常数进行相关更改后,最终得到了一个完全整洁的运行,如表4.4所示:
FPV和模拟都是与RTL设计交互和测试的方法。
不同:
下表显示模拟和FPV不同之处:
语句类型 | 模拟 | FPV |
Assertion | 报告当前测试中使用的任何测试值是否违反了断言 | 报告任何一组测试值是否可能违反断言 |
Assumption | 报告当前测试中使用的任何测试值是否违反了假设 | 使用假设来限制可能测试值的空间 |
Cover points | 报告当前测试中使用的测试值中有多少个已使用覆盖点 | 报告是否有可能对任何一组测试值进行覆盖点测试 |
模拟工具总是在特定的测试向量上运行,检查代码中每个断言和假设的当前值是否符合预期,并检查它们是否达到覆盖点。
FPV工具分析受假设限制的可能模拟空间,这意味着它正在执行一项复杂得多的任务,并且可以发现是否有任何可能的方法违反断言或达到覆盖点。
下表显示了在早期FPV采用过程中的一些差异:
关键差异领域 | 模拟 | FPV |
可以运行哪些类型和尺寸的模型 | 全芯片级,可综合代码和行为代码。 | 单元级或集群级,或带有大量删除逻辑和可综合代码的完整芯片 |
如何达到目标行为 | 描述过程:生成输入值以创建所需的行为 | 描述目的:指定目标属性或状态,工具查找是否可以访问 |
检查哪些值 | 具体值:模拟器根据用户指定的输入检查设计 | 所有可能值的体系:工具分析当前约束下的合法值空间。 |
怎样约束模型 | 主动约束:通过限制模拟过程中传入的值来隐式约束模型 | 被动约束:使用假设排除所有非法案例 |
如何处理内部节点上的约束 | 强制值:内部信号的活(live)约束只影响下游逻辑 | 反向传播+正向传播:约束在其扇入和扇出逻辑中影响可能值的范围 |
我们使用什么进行调试(debug) | 单一跟踪:当模拟失败时,需要调试模拟的内容 | FPV空间示例:该工具提供了一系列可能故障的示例 |
典型的轨迹(traces)有多长 | 数千个周期:通常需要代表整个机器启动,并在遇到复杂的错误之前做许多随机的事情。 | 数十个周期:FPV通常生成一条到任何目标的最小路径,从而产生更小的traces,几乎没有额外的活动 |
由于其近似线性的复杂性,FPV通常必须针对集群或单元级别,建议的目标是模型大小小于40000个状态元素。
FPV分析模型上所有可能模拟的空间,而不仅仅是特定的值。这有效地提供了相同的结果(对设计进行指数级的模拟)。
如果使用更大的模型,需要使用智能技术,只关注模型的相关部分,而不是将整个模型输入工具。
这里需要记住的关键一点是,通过使用上述技术,能够在比最初预期的大得多的模型上可靠地运行FPV,在某些情况下,在全芯片模型上执行有针对性的FPV验证任务。
模拟:
在典型模拟环境中,需要设计一个测试台(testbench),将活动值驱动到模型中,在典型的模拟环境中,需要设计一个测试台,所以需要提供一个程序来决定驱动哪些随机值,然后检查这些值是否符合我们的期望条件。
在上面的组合锁例子中,如果想找到可能会错误打开锁的替代组合,需要设计测试台来驱动大量的随机值,通过猜测找到,导致在输出端错误地看到open == 1的随机值。
在上述所例子中,如果每次尝试打开锁都需要三个周期,在每个周期中驱动个可能的值,那么在我们偶然发现所需结果之前,我们平均需要经历或大约5000亿个模拟周期。
此外,即使花了所有这些模拟周期来寻找替代组合,然后修复错误,仍然不知道是否存在其他替代组合,必须运行一万亿个周期才能实现全面覆盖。
FPV:
一旦定义了这些属性,FPV工具就可以尝试找到一些违反给定断言的合法执行轨迹(traces)。不存在需要多次迭代的问题:一次FPV运行原则上覆盖了整个验证空间。如果没有遇到复杂性问题,该工具将返回一个证明不存在此类替代组合的证据,或者一个具体的反例,显示成功打开锁的某些替代组合。
最重要的是,一旦修复了最初的错误,并且相信不再存在其他替代组合,接下来确保RTL修复后在相同的属性上再次运行FPV,如果工具报告属性现在通过,则无需进行模拟或进一步的FPV运行。
模拟:
每次模拟运行都会检查通过模型驱动的一组特定值,即使在一个快速的随机模拟环境,它仍然只是检查一组特定的、有限的值,如果查看未成功打开锁的模拟运行结果,则无法确定其他值是否会成功。
FPV:
FPV检查所有可能的值,而不仅仅是特定的值。
如果FPV在模型上运行完成,并声称没有无效的组合打开锁,那么在当前的模型约束下,没有一组无效值可能达到打开状态。
如果一个模型没有受到假设的适当约束,许多不可能的执行路径也可能会被有效地检查,从而在一个不切实际的例子中,当断言失败时,会导致误判。因此,FPV工作的很大一部分将包括确定所需的约束,以确保只检查实际值,并消除错误故障。
在实际验证中,通常会在驱动到模型上的实际值中增加约束、限制,约束模型的典型原因有很多:
在上面的组合锁例子中,override输入是有问题的,这个信号绕过锁机制,并强制锁打开。对于我们试图检查组合正确性问题的验证环境,我们需要将override强制为0,如上所述:
fix1: assume property (override == 0);
模拟:
在模拟中,通常我们设置的环境仅驱动合法值到我们的信号中,在这种情况下,需要设置模拟驱动程序,以便它们始终为override信号提供值0。如果使用随机仿真,可以使用半被动的方法,比如SystemVerilog constraint 算子,这在表面上与使用正式假设类似。但即使在这种情况下,模拟器仍在驱动一组特定值。
如果模拟测试通过,在我们的约束下,对于一些可能值的子集,我们的设计是正确的。我们无意或试图涵盖该约束下所有可能的合法值。
FPV:
在FPV中,没有设置专门驱动活动值的环境:FPV覆盖整个验证空间,我们需要添加假设以排除非法值(提供约束以消除验证中对当前一组检查无效的部分)。
如果FPV证明成功,该工具已在给定的约束下测试了所有可能的值,它不仅检查override为0时的采样,还覆盖了所有可能的情况。
例如, 假设我们对上述组合锁出现的更大系统有一些了解,一旦三个十进制数字周期被组合并解码为内部信号上的64位二进制值,所有组合将小于一些已知值 MAX_COMBO。有没有一种简单的方法可以在内部comb信号上表示这种约束,而不必对传递到数字输入中的数字创建一组更复杂的约束?
模拟:
可以让模拟工具直接将值驱动到内部comb节点,从而在该节点上强制使用合法值,然而组合检查器中的后续逻辑将受到新值的影响,但digit(数字)输入和解码器中的值将完全无关。当我们观察波形时,必须认识到内部组合信号上游的任何值,或同时从其源逻辑生成的并行信号,实际上都是需要忽略的垃圾值。
FPV:
在FPV的情况下,任何约束都有效地限制了可能的模拟范围。因此约束一个内部节点,不仅会影响下游逻辑,还会有效地反向传播,导致我们只能看到其驱动程序上与约束一致的值。
可以对组合信号进行以下简单假设:
assume property (combo <= MAX_COMBO);
根据这一假设,在FPV环境中,digit输入上看到的任何值都是一致的:当发送到解码器块时,导致combo的合法值符合假设。
从同一源逻辑生成的其他内部信号也是如此。
如果很难为FPV任务编写好的输入假设,可以根据解码/中间内部信号编写假设。但这会带来风险,即驱动这些信号的逻辑并没有完全包含在我们的验证中。
模拟:
从随机模拟中收到的失败波形本质上是模型失败的单一实例,它显示了一次特定运行的结果,而获得一次完全不同的运行可能需要许多随机周期。
例如,假设我们查看上述例子中的一个覆盖点波,并确定它是非法的,因为输入没有得到适当的约束以提供有效的组合。修改测试环境以解决这个问题,然后启动一组新的随机模拟,希望发现另一个失败案例。
我们不确定是否已经正确排除了非法组合,除非在测试过程中偶然碰到了另一个坏值,或者运行了完全覆盖所需的不现实的模拟周期数。
FPV:
由FPV运行生成的波形看起来和模拟波形相似,因为它显示了模型中随时间变化的一组具体值。但有一个关键区别:FPV波形代表了验证引擎发现的一系列可能故障。如果用户发现了想要改变的东西,比如需要一个影响输入值的假设,则其可为这个效果添加一个约束,并要求引擎重新求解。该工具将返回一个波形,该波形仍符合新约束条件下要求的覆盖案例,或者尝试证明附加假设排除了所有此类案例。
调试波形的典型长度
模拟:
一个典型的模拟波形会包含上千个周期,可能会有一些复杂的模型重置,然后是许多随机或不相关的活动循环,直到达到特定的测试条件或失败。失败的实际原因可能埋在这个长期模拟的中间,实际原因的循环难以推导。在调试期间,要检查数千个数据周期,并准确确定哪些周期与当前故障特别相关,通常是非常困难的。
FPV:
FPV中典型的失败轨迹很短, 在很多案例中复位之后仅10-20个周期, 这是因为大多数FPV引擎搜索一个可能导致断言失败的最小示例,FPV波形可以准确地到达点,而不会在当前示例不需要的随机活动上浪费额外的周期。典型的FPV工具也显示更短的重置序列,它们通常会检测到在模型状态停止改变之前所需的最小重置量,而模拟则倾向于使用更宽松的边界来确定所需的重置长度。
运行FPV的动机:
首先了解何时以及为什么使用以下每种方法。
如果有以下情况,你应该考虑做设计练习FPV:
对于新的RTL,需要在testbench已就绪之前进行早期验证,想更好地理解局部已完成的设计,需要帮助理解遗留RTL模型,或者只是开始探索验证聚焦FPV用于设计的可能性,考虑使用Design exercise FPV。
如果有以下情况,考虑使用寻找漏洞FPV:
如果正在验证一个设计有很多角落案例,已经在随机测试中看到很多缺陷,已经成功完成Design exercise FPV,并想做进一步验证,或者刚刚添加了一个功能到一个成熟的模型,考虑使用Bug hunting FPV。
这个使用模式,生成机制证明设计是100%正确。我们将尝试编写定义完整规范的属性,然后证明所有这些属性都适用于我们的设计。
full proof FPV是最费力的,因为它涉及到试图完全证明设计的功能性,但也有一个好处:如果正确地运行了完整的证明,并包含了检查假设是否合理的措施,则可以确信已经完全验证了我们的RTL,并且不需要花费任何精力运行模拟或其他验证方法。
由于full proof FPV可能是一项巨大的工作,本书作者建议我们从设计练习FPV开始,然后将其扩展到bug搜索,最后是全面验证FPV。我们的设计探索和缺陷搜索将作为初始的健全性检查,检查是否选择了适合FPV的尺寸和样式,以及对于FPV工具来说具有合理复杂度的逻辑。
如果有以下情况,考虑使用全面验证FPV:
如果有一个详细的参考规范或表格来完全定义我们所期望的行为,那么就有标准的接口来提供大量的预先包装的FPV附加品,或者强烈要求设计中的一部分,因已知的风险而具有防弹规范,考虑full proof FPV。
以下是一些常见的Application-specific FPV:
为block设置通用FPV环境之前,请检查当前正在解决的问题是否存在特定于域的解决方案