第1章 验证导论
- 本章将就测试平台的构建和设计给出一些引导性的建议和编码风格方面的参考。
- 在开始学习有关SystemVerilog语言的细节之前,需要理解如何制定计划来验证设计、以及这个验证计划对测试平台结构的影响。
- 作为一个验证工程师,要学到的最重要的原则是“程序漏洞利大于弊”。不要因为害羞而不敢去找下一个漏洞,每次找到漏洞都应该果断报警并记录下来。整个项目的验证团队假定设计中存在漏洞,所以在流片之前每发现一个漏洞就意味着最终到客户手里少一个漏洞。应该尽可能细致深入地去检验设计,并提取出所有可能的漏洞,尽管这些漏洞可能很容易修复。
- 硬件验证语言(Hardware Verification Language,HVL),具有一些典型的性质,而以下特性允许创建高度抽象的测试平台、其抽象层次比使用HDL或C语言还高:
(1) 受约束的随即激励生成
(2) 功能覆盖率
(3) 更高层次的结构,尤其是面向对象的编程
(4) 多线程及线程间的通信
(5) 支持HDL数据类型,例如Verilog的四状态数值
(6) 集成了事件仿真器,便于对设计施加控制。
1.1 验证流程
- 硬件设计的目的:创建一个基于设计规范并能完成特定任务的设备。
- 验证工程师的目的:确保设备能够成功地完成预定的任务,即实现的设计是对规范的一种准确表达。设备在超出预定目标之外的行为可以不关心、但需知道边界在哪。
- 验证流程并行于设计流程:
- 对于每个设计模块,设计者:需要首先阅读硬件规范,解析其中的自然语言表达,然后使用RTL代码之类的机器语言创建相应的逻辑。为了完成这个过程,设计者需要知道输入格式、传输函数以及输出格式。解析过程中总是会有模糊的地方,原因可能是规范文档本身的表述不清楚,遗漏了细节或者前后不一致。
- 作为验证工程师:也必须阅读硬件规范并拟定验证计划,然后按照计划,创建测试来检查RTL代码是否准确地实现了所有的特性。(强调:作为验证工程师,必须阅读同样的硬件规范并对其含义做出独立的判断,然后利用测试来检查对应的RTL代码是否与你的解读相一致。)
1.1.1 不同层次上的测试
-
代码块(block)层次
- 含义
- 代码块本身
- 代码块边界(多个设计者对统一规范产生不同解读的情况)
- 优势:仿真运行快
- 劣势:设计和验证都可能找到漏洞;激励较复杂。
- 含义
-
系统级层次
- 含义:待测设计的最高层次,整个系统。
- 优势:仿真过程相对简单
- 劣势:耗时长。
-
检查方面
- 待测设计是否能够执行所有预期的功能
- 当出现错误时,待测设计是否能够收敛(错误注入和处理是验证中最具有挑战性的部分):
- 能否应对只进行一半的事务;
- 能否应对已经受损的数据或控制字段
-
永远无法证明没有任何漏洞留下,故而需要不停尝试新的验证策略。
1.1.2 验证计划
- 定义:描述需要验证什么样的特性,以及采用哪些技术
- 步骤范围:
- 定向测试
- 随机测试
- 断言
- 软硬件协同验证
- 硬件仿真
- 形式验证
- 验证IP的使用
1.2 验证方法学
- VMM(SystemVerilog验证方法学):
- 从业界的实践出发、利用项目经验定义的方法和概念
- 使用与否
- VMM面向的是有经验的用户,在处理复杂问题方面十分出色(如负责验证一个亿门规模的设计,里面含有很多通信协议、复杂的错误处理机制和IP库);
- 面对较小模块、只带有单一协议,则可能不需要。
- 记住:
- 个人的代码块只是更大系统里面的一部分
- VMM兼容的代码对于当前和以后的项目都是可用的
- 验证的代价会超出当前的项目
- VMM具体:
- 有一套针对数据和环境的基本类
- 有用于日志文件管理和线程间通信的机制
- … …
1.3 基本测试平台的功能
- 测试平台的用途:确定待测设计的正确性。
- 测试平台包含的步骤:
- 产生激励
- 把激励施加到DUT上
- 捕捉响应
- 检验正确性
- 对照整个验证目标测算进展情况
1.4 定向测试
- 定向测试的流程:
- 首先需要阅读硬件规范
- 然后写下验证计划,计划上列有各种测试,每个测试针对一系列相关的特性。
- 按照这个计划,接着编写出针对待测设计具体特性的激励向量
- 然后使用这些向量对待测设计进行仿真
- 仿真结束后,手工查看一下结果文件和波形,确保设计的行为与预期的一致。
- 一旦测试结果争取,就可以在验证计划中把它勾掉
- 然后开始下一个测试。
- 定向测试的优点
- 比较容易取得稳步的进展(因而很受那些喜欢看到项目持续向前推进管理者的欢迎)
- 定向测试的结果很快得到(因为创建每个激励向量时不需要什么基础设施)
- 只要给予足够的时间和人力,定向测试对于大部分设计验证来讲都是可以胜任的。
- 定向测试的缺点
- 如果设计复杂度翻倍,那么测试就需要增加一倍的时间或者人力。
1.5 方法学基础
-
定向测试 VS 随机测试
- 定向测试:可以找出设计中预定的漏洞
- 随机测试:能够找出预料不到的漏洞
-
随机测试的测试平台所采用的原则:
- 受约束的随即激励
- 功能覆盖率
- 使用事务处理器的分层测试平台
- 对所有测试通用的测试平台
- 独立于测试平台之外的个性化测试代码
-
随机测试原则相关性:
- 随机激励 => 功能覆盖率:当使用随机激励时,需要用功能覆盖率来评估验证的进展情况。
- 自动生成激励 => 自动预测结果:一旦卡斯和使用自动生成的激励,就需要一种能够自动预测结果的方式——通常时计分板或者参考模型。
- 大工作量 => 分解成小块问题:建立包括自预测在内的测试平台基础设施,是一件工作量很大的事情。一个分层的测试平台能够把问题分解为容易处理的额小块,这样有利于控制复杂度。
- 小块问题 => 事务处理器:事务处理器能够为构建这些小块提供有用的模式。在适当的规划下,可以建立一个测试平台所需的基础设施,其能够在所有测试中通用并且不需要经常性修改。只需放置“钩子”(当前理解为callback,或者cfg类中的一些option),以便测试能够在这些地方执行调整或注入错误这样的特定操作。
- 测试平台通用化 => 单一测试独立化:针对单一测试的个性化代码,必须与测试平台分开,这样可以避免增加基础设施的复杂度。
-
随机测试的测试平台的缺点或者注意点:
- 建立平台所需时间相对传统定向测试平添要多得多——尤其是自检的部分。
- 可能需要很长的准备时间才能进行第一次可运行的测试。
- 给项目管理者带来阵痛,所以需要在测试时间表上把这部分考虑进去。
- 建立平台所需时间相对传统定向测试平添要多得多——尤其是自检的部分。
-
随机测试的测试平台的优势:
- 每个随机测试都可共享这个通用的测试平台,而不像每个定向测试都要从零开始编写。
- 每个随机测试都会包含一部分代码,用于把激励约束到特定方向上并触发任何期望的异常。
- 即,受约束的随机测试平台比很多定向测试更快找到漏洞。
-
随机测试平台的注意点:
- 随着漏洞出现率的下降,应该创建新的随即约束去探索新的区域。
- 最后的几个漏洞可能只能通过定向测试来发现(绝大部分的漏洞都应该会在随机测试中出现)
1.6 受约束的随机激励
- 一个随机测试的覆盖范围往往比一个定向测试大,多出来的覆盖部分可能会与其他测试发生交叠、或者探测到实现没有预料到的新区域。
- 如果对新区域的测试不合法,则需编写更多的约束去阻止随机测试产生非法的功能。
- 对于受约束的随机测试覆盖不到的地方,还需编写定向测试。
- 达到完全覆盖的技术路线:
- 从基本的受约束的随机测试开始,使用不同的种子运行;
- 查看功能覆盖率报告,找出覆盖率中的间隙,即覆盖率盲区;
- 针对盲区尝试做最小程度的代码修改,可能是使用新的约束、也可能是把错误或延迟加入到待测设计中;
- 仅对少数使用随机测试达不到的特性才编写定向测试。
1.7 你的随机化对象是什么
- 针对数据路径上的漏洞(如比特级的错误):
- 随机“数据字段”;
- 针对控制逻辑上的漏洞:广泛地考虑所有的设计输入。
- 设备配置;
- 环境配置;
- 输入数据;
- 协议异常;
- 错误和违例;
- 时延;
1.7.1 设备和环境配置
-
验证RTL时找不到BUG的最常见的原因:没有尝试足够多的不同配置。
- 常见单一不足验证思路:仅经过复位、或者施加固定的初始化把设计引向一个已知的状态;
- 事实:实际应用中,随着待测设计使用时间增加,其配置会越来越随机。
-
验证即是模拟真实应用:
- 在实际的应用中,设备所在的环境会包含其他的部件;
- 当对待测设计进行验证时,实际上就是把测试平台连接起来模拟这个环境(实际运行环境)。
- 验证时,应对整个环境的配置进行随机化,包括仿真的时长、设备的数量、以及它们的配置方式;还需创建约束以确保配置的合法性。
1.7.2 输入数据
- 事先估计好所有的分层协议和错误注入、以及记分板的内容和功能覆盖率,选取一个总线写入事务或ATM信元,然后把随机数值填充到其中的数据字段里。
1.7.3 协议异常、错误和违例
-
产品内部逻辑遇到错误无法恢复,会使得设备无法正常工作。
- 为阻止此类问题,应尽量尝试去仿真在实际的硬件中可能出现的错误;确保设备在出错的情况下还能继续正常工作。
- 如果一个总线事务没有完成会怎么样?
- 如果遇到一个非法的操作呢?
- 设计规范中有没有指出哪两个信号互斥?
- 为阻止此类问题,应尽量尝试去仿真在实际的硬件中可能出现的错误;确保设备在出错的情况下还能继续正常工作。
-
在尝试使用不当的命令去激励硬件的同时,也应该注意捕捉出现的问题。
- 可以增加用于检验的代码来帮忙找出问题所在;
- 新增的代码应至少能够在出错的地方打印一个警告信息;如果能够报告出错误并且使测试停下来则更好;
- 只要能够确保能够使代码在出错的地方停止仿真,则很容易应对测试中的错误。
- 可以增加用于检验的代码来帮忙找出问题所在;
1.7.4 时延和同步
-
使用受约束的随机时延有助于捕捉协议上的漏洞。
- 时延最短的测试运行速度最快,但无法产生所有可能的激励。
- 可以创建一个测试平台以最快的速度与另一个代码块通信,但隐蔽的漏洞往往在引入间歇性时延之后才被发现的。
- 时延最短的测试运行速度最快,但无法产生所有可能的激励。
-
一个代码块对于来自同一接口的所有可能激励也许都能正常工作,但如果同时面对多个输入,隐蔽的漏洞可能就会出现。
- 尝试协调各个驱动器使它们能够在不同的速率下进行通信。
- 如果输入以可能的最快速率到达,而输出却被卡在一个较低的速率上,该怎么办?
- 如何处理来自多个输入的激励同时到达的情况?
- 如果这些激励带有不同的时延又该怎么办?
- 尝试协调各个驱动器使它们能够在不同的速率下进行通信。
1.7.5 并行的随机测试
- 随机测试,使用多个种子运行同一个测试可以加大覆盖率,同时也能减少工作量。
- 需要为每次仿真选定一个独特的种子。
- 可以使用自然时间作为种子;
- 当在不同的计算机上启动时,有可能得到相同的随机种子运行相同的激励;故而应该把处理器的名称加入到种子中;
- 如果在集群系统里面有多核计算机,那还是游客坑出现两个相同的种子;所以,应该把处理器核的编号也加到种子里。
- 还需要对并行仿真的文件组织进行规划。
- 可以让每个仿真在不同的目录里运行;
- 可以尝试给每个文件取不同的名字。最简单的办法时在目录名后面加上随机种子的值。
- 需要为每次仿真选定一个独特的种子。
1.8 功能覆盖率
- 随机测试无法确定已经验证全部,所以需要知道哪些部分已经被验证过,这样才能对验证计划中的项目进行核对。功能覆盖率,就是为了了解哪些部分已经被验证过了。
- 功能覆盖率测量和使用的步骤:
- 首先,需要在测试平台中加入代码,用于监控进入设备中的激励,以及设备对激励的反应,并据此确定哪些功能已经被验证过。
- 多次运行仿真;
- 把仿真结果合并到一个报告中;
- 然后,需要对结果进行分析;
- 最后,决定如何采用新的激励来达到那些尚未被测试到的条件和逻辑。
1.8.1 从功能覆盖率到激励的反馈
-
随机测试需要使用反馈???
-
“覆盖率驱动的验证”:随着功能覆盖率逐渐接近极限,需要改变测试,以期能找出新的方法去达到那些尚未被覆盖的区域。
-
更有效的测试策略:使用随机总线事务和终止判断。
- 好处一:运行时间越长,覆盖率越高;
- 好处二:在创建激励时灵活性很高,足以应对设计时序有改变的情形。
- 实现方式一:在测试代码中加入一个反馈循环,用于监测已生成的激励,并根据情况调整约束的权重(把写的权重降到零)。(TODO:不太懂)
- 好处:大大缩减达到完全覆盖的时间,而且只需要很少量的人工干预。
- 缺点:需要对设计有深入的了解,而且还需要高超的形式验证技术。
- 示例:有些形式分析工具用到了反馈,如Magellan。它首先对设计进行分析并找出所有可以达到的互异状态。然后运行一小段仿真看有多少状态被访问到。最后,在状态机和设计输入之间进行搜索并计算出达到所有遗留状态所需要的激励,然后Magellan再把这些激励施加到待测设计上。
- 实现方式二:人工分析覆盖率报告,然后调整随机约束。
1.9 测试平台的构件
-
测试平台VS测试仪器
- 同:都会产生激励并捕捉响应;
- 异:测试平台需要工作在一个很宽的抽象层次范围内,同时创建事务和激励序列并最终转换成比特向量;测试仪器只工作在比特级上。
-
总线功能模型(BFM):
- 可以看成测试平台构建——从待测设计的角度看,其与真实构建没什么不同,但其只是测试平台的组成部分,并非RTL设计。
- 如果实际应用中设备被连接到AMBA、USB、PCI和SPI总线上,那么就必须在测试平台中建立能够产生激励并校验响应的等效构件。
- 这些构建并不是带有细节的可综合模型,而是遵循协议并且执行速度更快的高层次事务处理器。
- 如果把设计原型在FPGA上实现或者是进行硬件仿真,那么这些BFM就需要是可综合的。
1.10 分层的测试平台
- 分层看似会使测试平台更复杂,但其能够把代码分而治之,有助于减轻工作负担。
- 不要试图编写一个包含所有功能的子程序,例如用它随机产生所有类型的激励、包括合法的和非法的,并使用多层协议进行错误注入。
- 因为这样的子程序很快就会变得很复杂,并且难以维护。
1.10.1 不分层的测试平台
- 对于重复性的操作,尝试创建普通操作的任务;
- 通过把一些通用的操作()放到一个子程序中,可以提高工作效率并减少出错。这样在一定程度上,建立了物理层和命令层,而此只是分层测试平台实现的第一步。
1.10.2 信号和命令层
- 一个测试平台中最低的几个层次——命令层和信号层:
- 信号层:包含待测设计和把待测设计连接到测试平台的信号。
- 命令层:
- 【驱动器】执行总线读或写命令的驱动器,驱动待测设计的输入;
- 【监视器】待测设计的输出与监视器相连,监视器负责检测信号的变化,并把这些变化按照命令分组;
- 【断言】断言也穿过命令层和信号层,它们负责监视独立的信号以寻找穿越整个命令的信号变化。
1.10.3 功能层
- 功能层向下面对的是命令层。包含:代理、计分板、检验器。
- 【代理】代理(在VMM中称为事务处理层),接收到来自上层的事务,把它们分解成独立的命令。
- 【计分板】命令也会被送往用于预测事务结果的计分板。
- 【检验器】检验器负责比较来自监视器和计分板的命令。
1.10.4 场景层
- 功能层被位于场景层中的发生器所驱动。
- 【!!!】验证工程师的工作:确保待测设备能够完成预期的任务。
- 场景,是指待测设备工作中涉及到的所有的操作、或者说步骤。
- 场景层,就是负责组织协调设备所涉及的所有步骤。操作的参数都采用受约束的随机值。
- 测试平台中所有的组件,即上述层次中的各个块:
- 是在刚开始开发的时候画出来的。
- 随着项目的进展,它们可能会有一些变化,但这些块对于每个独立的测试都是不应该改变的。可以通过在代码中留下“钩子”来做到,这样即使这些块的行为需要在测试时改变,也不必重新编写代码。“钩子”可以使用工厂模式和回调函数来创建。
1.10.5 测试的层次和功能覆盖率
- 测试平台的最顶层——测试层。
- 待测设计模块间的漏洞时较难发现的,因为这些模块可能是不同的人按照不同的规范设计出来的。
- 功能覆盖率,可以衡量所有测试在满足验证计划要求方面的进展。
- 随着各项测试标准的完成,功能覆盖率代码在整个项目过程中会经常变化。由于代码经常被修改,所以它不作为测试环境的组成部分。
- 可以在受约束的随机环境中创建“定向测试”。
- 只需要在随机序列中间插入定向测试的代码,或者把两部分代码并列。
- 定向代码执行期望的任务,而随机的“背景噪声”可能会使漏洞暴露出来,而且漏洞还有可能是你从来没有想到过的模块里。
- 测试平台中所需的层次:
- 是否需要所有的层次,要视待测设计而定。
- 设计越复杂,所需的测试平台要越完备。
- 测试层是必须的。
- 当有多个协议层时,应该在测试平台环境中都有对应的层。
- 注意:测试平台模块间的连接的多样性或者说独特性。
1.11 建立一个分层的测试平台
-
【驱动器】模块:
- 接收来自代理的命令;
- 可能会注入错误或者增加时延,然后再把命令分解成一些信号的变化。
- 【驱动器】模块 = 一种【事务处理器 transactor】;
- 核心:一个循环;如下:
task run(); done=0; while(!done) begin // 获取下一个事务 // 进行变换 // 发送事务 end endtask
-
基本的OOP+如何创建一个对象并使对象里面包含事务处理器所需要的子程序和数据 (第5章中)
-
【代理】 = 另一种【事务处理器】;
- 可能会把一个复杂的事务分解成多个总线命令。
-
如何创建一个对象并使对象里面包含构成一个命令所需要的数据和子程序。(第5章中)
-
使用SystemVerilog信箱可以实现这些对象在不同的事务处理器之间传递。
-
用于在不同层之间交换数据并使事务处理器实现同步。(第7章)
1.12 仿真环境的阶段
-
三个基本阶段:建立(build)、运行(run)和收尾(wrap-up)。
- 建立阶段,其可分步骤:
- step1-生成配置:把待测设计的配置和周围的环境随机化。
- step2-建立环境:基于配置来分配和连接测试平台构建。
- 测试平台构件,指的是存在于测试平台中的部分,注意与设计中的物理构件区分开,后者是采用RTL代码描述的。
- step3-对待测设计进行复位。
- step4-配置待测设计:基于第一步中生成的配置,载入待测设计的命令寄存器。
- 运行阶段,指测试实际运行的阶段,其可分步骤:
- step1-启动环境:运行测试平台构件。
- step2-运行测试:启动测试然后等待测试完成。
- 定向测试的完成很容易判断,但随机测试却比较困难:
- 可以使用测试平台的层作为引导:从顶层启动,等待一个层接收完成来自上一层(如果有的话)的所有输入,接着等待当前层空闲下来,然后再等待下一层。
- 应该同时使用超时检测以确保待测设计或测试平台不出现死锁。
- 定向测试的完成很容易判断,但随机测试却比较困难:
- 收尾阶段,其可分步骤:
- step1-清空:在最下层完成以后,需要等待待测设计清空最后的事务。
- step2-报告:一旦待测设计空闲下来,就可以清空遗留在测试平台中的数据了。
- 有时候保存在计分板里面的数据从来没有送出来过,这些数据可能是被待测设计丢弃掉的。可以根据这些信息创建最终报告,说明测试通过或者失败。
- 如果测试失败,务必把相应的功能覆盖率结果删除,因为它们可能是不正确的。
- 有时候保存在计分板里面的数据从来没有送出来过,这些数据可能是被待测设计丢弃掉的。可以根据这些信息创建最终报告,说明测试通过或者失败。
- 建立阶段,其可分步骤:
-
测试启动环境的具体细节,见第8章。
1.13 最大限度的代码重用
-
验证一个带有数百个特性的复杂设备:
- 采取定向测试时:必须编写数百个定向测试;
- 采取随机激励时:如果使用受约束的随机激励,需要编写较少的测试;主要工作是,构件测试平台,使它包含所有较低的层:场景、功能、命令以及信号。
- 该测试平台代码要能够被所有的测试使用,所以需要有很好的通用性。
-
受约束的随机激励的测试平台:
- 在测试平台中每输入一行,就等于给每个单独的测试都减少了一行。这相当于在同时创建很多个测试,而这也正是建立一个复杂测试平台所能获得的巨额回报。
- 在读第8章时,想着这点。
- 在测试平台中每输入一行,就等于给每个单独的测试都减少了一行。这相当于在同时创建很多个测试,而这也正是建立一个复杂测试平台所能获得的巨额回报。
1.14 测试平台的性能
- 定向测试短时间内可以运行完,但要花较长时间编写、调试和验证。
- 创建受约束的随机测试,其步骤:
- 第一步:建立分层的测试平台,包括自检的部分。
- 第二步:按照验证计划中列举的目标创建激励。
- 可以采取随机约束,也可以采取注入错误或协议违例等迂回的方式。
- 第三步:功能覆盖率。
- 开始:创建一个强有力的验证计划,必须带有清晰而且便于测量的目标。
- 接着:创建SystemVerilog代码,在环境中添加工具用于收集数据。
- 最后:对结果进行分析,并据此判断是否满足目标要求,如果不满足,应如何修改测试。