芯片验证漫游指南1芯片验证全视
1 功能验证简介
如果你在设计一款处理器,你需要考虑将其拆分成为运算器(算术逻辑运算单元,ALU,Arithmetic Logic Unit)、高速缓冲存储器(Cache)和连接它们的总线(Bus);如果你在设计一款系统集成芯片(SoC、System-on-Chip),那么它可能包括的子系统包括处理器、片上网络(NoC,Network-on-Chip)、存储器、I/O 控制器(例如 USB,PCIe)等。你会发现,随着系统集成度的提高,系统自身的复杂性增加,而且结合实际工程项目来看,系统复杂度的提高对于功能验证的要求是首当其冲的。由于功能验证在芯片全流程中占据关键位置,验证工程师需要充分理解系统验证的全过程,这个过程就是功能验证的生命周期。功能验证在项目的延续中(目前芯片迭代周期越来越短的一个重要原因,就是依靠剪裁以前项目来做快速的芯片设计)得到不断的提升,而这要求验证工程师考虑如何完善验证流程和环境。在本书一开始,路桑将带领读者从芯片开发流程进入,检视芯片验证在整个项目中的作用(见图1.1)。

一般而言,新的芯片项目首先从市场人员与客户沟通开始。市场人员收集客户对芯片的要求(主要包括功能、尺寸、功耗、性能),这些指标被记录在设计结构和产品文档中。随后,客户关心的系统层面功能被系统设计人员按照功能进一步划分为各个独立的子系统。这些子系统如果本身过于庞大,则被划分为功能模块,直到被划分出来的尺寸可以被小的设计团队进行硬件实现。硬件设计人员按照芯片的功能模块划分成不同的小组,同时系统设计人员的数目随系统复杂度的升高而增加。在硬件设计过程中,硬件设计工程师将具体的功能描述文本通过逻辑翻译成为硬件描述语言(HDL,Hardware DescriptionLanguage模型),目前广泛使用的HDL语言VHDL和Verilog均被各大EDA(Electronic Design Automation)公司的软件支持。由于SystemVerilog囊括了Verilog语法和更多高级设计属性,其也被用做一种设计语言。
当细分的模块初步完成RTL级(寄存器级别,Register Transistor Level)的设计之后,验证人员要做几项工作来检查设计:
- 设计文件是否正确地按照功能描述文档实施了?
- 硬件设计人员是否有遗漏掉的边界情况(corner case)?
- 硬件设计是否足够稳定以处理一些错误情况(error response)?
在实际项目中,硬件设计人员和功能验证人员的合作是紧密的,具体表现在:
- 系统设计团队将功能需求(抽象指标)翻译为功能描述(自然语言)之后,硬件设计团队和功能验证团队要围绕功能描述文档分别展开各自的工作。
- 在设计团队初步实现设计以后,验证团队要搭建验证环境展开各功能点的验证。
- 当验证环境测试出的结果与预期不符合时,需根据情况区别对待。如果设计与功能描述存在明显不符,验证人员应报告出设计缺陷,同时设计人员应修复设计,这样从验证到设计再转回到验证即完成一个缺陷检测和修正周期;当结果和预期有模糊边界时(例如时序 问题、状态机跳转问题),验证人员和设计人员对功能描述的理解可能存在分歧,此时他们应做初步讨论,确定哪一方的理解有偏差。当讨论未决时,双方应找系统设计人员进行“裁决”,以明确设计思想,统一对功能的理解。
因此,硬件设计的完成度和缺陷率在设计人员和验证人员的迭代周期中不断得到完善,最终可以达到目标。关于功能验证目标的定义,我们会在1.4节详细讲述。芯片硬件和软件的开发集成过程在图1.2中给出。

功能验证完成后,后端人员(backend)将 RTL 文件综合生成门级网表(gate netlist),同时进行布局布线,最终使物理电路可以在设定的时钟频率上工作。在后端的各种流程中,与验证人员联系紧密的当属标准延时格式(SDF,Standard Delay Format)文件,该文件包含门级网表中各个门单元之间的延时信息,它们被用来准确描述物理电路的时序和检查要求。对于功能验证流程,我们所说的仿真可以 根据项目的实施流程将其划分为前端仿真和后端仿真:
- 前端仿真指的是进行 RTL 仿真,在这种仿真中是没有真实延时情况的。对于一个寄存器(register),它的输出端(Q port)相对时钟输入端(Clk port)的延时为零延时(delta delay)。
- 后端仿真指的是进行门级(gate level)仿真。在实际项目中,由于后端SDF文件本身需要不断迭代(直至满足时钟频率要求),我们进一步将门级仿真划分为零延时(zero delay)仿真、单位延时(unit delay)仿真和SDF仿真。
- 零延时仿真只有门级网表参与仿真,没有SDF文件来具体反向标注(back annotation)门级延时情况,所以门之间的延时仍然为零延时。这时门级零延时仿真与 RTL 仿真的区别仅在于前者是后者的逻辑映射,从寄存器级别到门级的逻辑转译,这一步是由后端的综合工具(synthesis tooling)完成的。
- 单位延时仿真类似于零延时仿真,也没有 SDF 输入,而是将各个单元门之间的路径延时和门内的延时都以单位延时计算,以此模拟时序的叠加,但并不准确,依然无法反映实际物理时序。此仿真同零延时仿真一样,只可用作逻辑实现检查,无法检查物理时序。
- 后端产生出SDF文件时,我们将门级网表反向标注上SDF文件中包含的时序信息,最终进行真实延时电路的仿真。
从验证完整性而言,前端仿真和后端仿真均需要在项目中实施,但它们侧重的目标有所不同。前端仿真是为了检测出功能逻辑的缺陷,而后端仿真是为了检测出门级电路由延迟导致采样失败所产生的功能缺陷。
因此,验证人员不能将前端仿真的功能缺陷检测任务下移到后端仿真阶段。就效率而言,前端仿真要显著高于后端仿真。同时,后端仿真之所以不能忽略,是因为它可以协助后端人员测试出实际生成电路中是否存在时序不满足的问题。
完成后端仿真以后,我们将后端生成的标准格式文件最终交付给芯片生产商进行流片(tape out)。从上面的描述来看,这是一颗完整的芯片从定义、分块、设计、验证和后端的硅前(pre-silicon)流程。
同时,芯片在流片以后所面临的硅后流程(post-silicon)也是一个完整的周期,包括组件测试、驱动、系统固件和应用软件编写,等等。由于功能验证处在硅前流程中,我们在这里主要阐述该流程, 同时将一些相对独立的部分略去(这并不代表它们不重要),例如,可测试性设计(DFT,Design For Test)。设计和验证团队协同工作示意于图1.3中。

2 验证的处境
2.1 验证语言的发展
简单而言,验证的目标就是在一定的时间内尽可能多地测试硬件设计,发现设计缺陷并报告出来。同时,验证本身也是一项棘手的挑战,这一点可以从语言发展和各种快速发展的EDA工具上得到佐证。
我们从VHDL的语言发展线路来看,它的标准IEEE Std 1076-1987逐步经历了1076-1993,1076-2002再到1076-2008,这中间的年份从1987年逐步发展到2008年,可是我们真正在使用的设计标准是哪一部分呢?可能超过90%以上的设计都基于1076-1987和1076-1993,这是将近20年之前的标准,可是设计人员用它来描述电路已经足够了。因为设计面临的问题不是语言自身的局限,更多的是设计人员的经验和思想。
同样,我们看看Verilog语言的发展从IEEE Std 1364-1995到1364-2001再到1364-2005。目前我们所使用的Verilog代码也基本是在遵循1364-2001的标准,EDA工具商也主要在支持这一年份标准。
再来看看目前的主流验证语言SystemVerilog的发展情况,如果不考虑它之前在Accellera坐板凳的日子,它正式被认定位IEEE Std 1800-2005是从2005年才开始的!可我们看看它在这10年中便经历了1800-2009和1800-2012,更重要的是,它的每一次更新都得到了工具商的及时支持。为什么呢?因为实际验证的需要,绝对需要。
2.2 验证面临的挑战
随着芯片自身复杂度的日渐提高,以及一直存在的项目进度压力,如何实现验证的完整性和高效性成一个大家都关注的话题。概括来讲,验证目前面临的两大挑战是:
- 如何穷尽所有可能的情况给设计产生激励。
- 如何在各种可能的激励情况下判断出不符合硬件描述的行为并报告出来。
我们先看看第一个挑战——如何穷尽所有可能的情况。在这里以手机屏幕显示为例,假设手机屏幕分辨率是1920×1080,像素点的色彩值是232,同时,每个像素点之间的状态是独立的,那么屏幕可能分布的状态应该是:232×(1920×1080)=8 906 044 184 985 600
如果再考虑到像素点色彩值的变化,那么在连续两个时钟下,像素点可能发生的状态跳转空间是:232×(1920×1080)×232×(1920×1080)=7.9×1031 这仅仅是屏幕色彩的一个基本功能,而可以预见到的状态空间数目足以让人抓狂。所以,面对这样的挑战,我们需要作出一些平衡,这种平衡来自于状态空间本身的庞大和项目实施中的进度压力。如何划分出有效的测试空间、如何给出随机约束激励是验证人员需要具备的职业素质。
接下来我们看看第二个挑战,如何在各种可能的激励下判断出硬件设计的缺陷。首先把常见的硬件设计划分为如表1.1所示的几类,同时再看看针对不同设计的激励输入类型和结果判断的方法。

从表1.1可以发现,不同类型的设计需要产生的激励类型和结果比对方法是不一样的。针对不同类型、不同复杂度、不同集成度的设计,应采用不同的验证方法。从验证工具的分类看,可以将其分为仿真验证和形式验证;从复杂度出发,可以划分为黑盒验证、白盒验证以及灰盒验证。关于验证的方式以及合适的运用场景,我们也会在第2章里面展开详细讨论。
3 验证能力的5个维度
列出验证人员需要具备的5个能力维度:
- 完备性
- 复用性
- 高效性
- 高产出
- 代码性能
3.1 完备性
完备性要求验证的充分。然而,充分一词对于验证而言边界是模糊的,很难量化到什么时候才可以达到完成验证的标准。所以,作为一名验证经理,需要引入各种数据来综合量化出验证的进度,其中包括验证功能点的覆盖率、代码覆盖率、是否经过效能验证流程(power aware verification)、是否经过跨时钟域(clock domain crossing)检查等。
通过数据量化,验证人员和验证经理更有信心来宣布在某一个项目节点中,设计已经得到“充分”的验证。当然,对于功能覆盖率部分,如何将功能描述文档充分理解,进而列出要测试的功能点并尽可能地细分出来,这需要系统人员、设计人员和验证人员的共同努力。同时,将抽象的验证计划转换到功能覆盖率要求验证人员具备这样的能力。
3.2 复用性
从项目的实际运用角度看,复用性和完备性是同等重要的。没有人愿意在下一个项目中将以前的验证环境做较大的更新,因为这意味着额外的资源消耗,包括时间、人力和项目进度的考虑。
在硬件设计角度而言,通过标准总线协议,可以最大限度地在模块之间实现相对独立和快速集成。对于目前项目周期不断缩短的现状来看,一方面是市场的瞬息万变导致的,一方面也是由于SoC自身趋向于软件的周期迭代方式而形成的。对于一个系列芯片而言,后续芯片的性能提高、功耗优化都是建立在前一代的基础上的。而这些不断的提高和优化具体到每一个硬件子系统而言,可能就是它们的存储大小、时钟快慢、动态电源开关、总线宽度、缓存深度来综合决定的,并且下一代硬件设计自身一般不会有第一代芯片的艰难历程(否则也就称不上是系列芯片了)。
那么从硬件设计的角度看,这些更新如果不在逻辑上面有大的变动,那么带来的工作量是可以估计的。而从验证角度看,我们很自然地希望验证的工作量不要太大——可事实并不一定是这样的。首先从芯片项目的人员安排看,验证人员相比于设计人员流动性更高。那么当一个验证人员在尝试维护和修改上一个项目的验证代码时,就要看他的“运气”,而他的运气与上一个验证人员的代码风格有直接关系。因此,如何标准化验证环境和测试规范,成为验证复用性的一个重要考量。同时,验证人员在处理一些总线协议时要有意识引入参数来为日后的复用做好准备。不断融合的验证方法学走到今天,UVM(Universal Verification Methodology)之所以划分出不同的功能组件,实现小的颗粒度,提供快速插拔式的环境集成能力,也是从复用性考虑的。
3.3 高效性
高效性指的是用尽可能少的工作量完成验证工作。在保证验证完备性的考虑下,实际上复用性和高效性会有存在冲突的可能。例如,验证人员会考虑如何“短平快”地在一个紧张周期内完成验证工作,但他可能不会采用UVM等方法学框架,也有可能不会考虑将参数引入到验证环境中。因为这些“额外”的因素虽然对复用性有帮助,但与高效性冲突(费时)。所以,验证人员需要针对不同的情况在维度之间做好平衡,至少需要保持一种意识,那就是工程学的执行阶段本身就是一种平衡。对于验证人员来讲,他需要做出的判断就是在每一个项目的验证任务中做好取舍,给出合适的验证考量综合维度。对于同一项验证任务而言,采取不同的验证策略有不同的验证效果。例如,一开始考虑采用随机约束的验证方法,那么单单就约束而言,它的约束一开始是比较窄合适,还是一开始比较宽合适?
这里我们给出图1.5来说明高效性在一项验证任务的不同周期需要有相应的变化。在开始阶段,考虑到设计不够完备且尚未经历过验证,我们将其称为基本功能验证阶段。这个阶段,我们将随机约束域降低到基本范围,尽可能少地触碰到边界情况,把重点放到如何先将各项基本功能都验证到。第二个阶段是在已经完成基本功能验证以后开始的完备功能验证阶段,这时可以逐渐放开随机约束域,而开放的域范围需要验证人员考虑到各种合理的情形再做限定。到了功能覆盖率一般上升到80%附近时,就处于最后的爬坡阶段。这时,再沿用之前广泛的约束域就会产生很多无效的随机种子,这些“无效”的随机种子对于剩下的验证覆盖率几乎没有什么帮助。这时,验证人员需要通过理解设计本身和随机约束两方面来考虑具体贡献覆盖率的测试序列,再进一步缩窄随机约束域,偏置(biasing)产生一些激励。对于最后这一阶段,一种极端的情况就是将随机约束域缩到尽可能地窄,甚至和定向测试(directed test)没有什么区别。

3.4 高产出
高产出指的是在一定的时间,能够调试、报告、帮助修正出多少设计缺陷,以及如何建立完整的验证环境。多年来,数字设计(RTL级别)的基础并没有发生太多变化,同时EDA厂商提供的自动化工具又进一步提高了设计的可靠性。但这一情况却并不适用于数字验证,因为EDA工具目前仍然只能作为辅助手段(例如提供更多的调试功能和接口),却不能帮助自动化建立复杂的验证环境。这也就不难解释2017年IC行业功能验证领域的调查数据显示,当前在设计和验证领域面临着最大的挑战之一就是为快速的芯片产品迭代和员工数量增长之间找到一个平衡点,实现单位产出的提高。
3.5 代码性能
代码性能似乎也跟高效性、高产出有冲突的地方。因为验证代码的整洁性、复用性甚至一点点的美感都与验证完备性没有直接联系。这也包括你的验证经理可能有好长时间都不会注意到你写的验证代码,除非有一天你验证的那个设计出了一个缺陷,而且是一个显而易见的缺陷却没有被发现,才会引起验证经理的注意并专门来回顾可能是一团糟的代码结构。每一位验证人员需要记住一句台词“出来混,迟早是要还的”。不管是别人的验证代码中存在着没有注释、没有缩进、超长函数等不良问题,还是你因为项目紧张,在快速搭建验证环境和编写测试用例时没有考虑到“后来阅读者”和“你后来阅读”而偷的各种懒。相信我,时间会让你为此买单的。所以,作为一名验证人员,请你在写每一行代码时把它当做你日后行业名声的荣誉墙。尽管你迫于项目的压力需要快速建立环境疲于完成验证计划,但等到你闲时会去改善那些代码吗?不要再相信这些鬼话了,现在就去做吧!
从上面的5个维度来看,做一名合格的验证人员实属不易,更不要说考虑到每一项验证任务量体裁衣制定出合适的5个维度指数。虽然项目执行没有尽善尽美,但针对验证人员自身,如果可以意识到这5个维度的存在,并且能够在实际工作中综合考量它们,那你就是有意识地在培养自己成为一名优秀的验证师了。
4 验证的任务和目标
验证师的工作就是完成分配给他的任务,这个任务可能是模块级(module level)、子系统级(subsystem level)或者系统级(chip level)的。准确来讲,验证的目标就是“按时保质低耗完成”目标硬件设计的验证工作,这句话实际也包含了要完成验证目标需要考虑到的三个方面。
- 按时保质低耗
- 按时
验证师需要按照项目预先的进度来考虑验证的节点(milestone),在项目开始时就将节点记挂在心上。之前提到的验证师的5个能力维度,在面对项目进度时,也需要考虑哪个维度为主、哪个维度为辅。例如,硬件设计的验证计划、验证环境的复杂度和复用性、大概需要用多少测试用例来尽快达到验证工作量的80%,这些都是要与项目进度一同考虑的。要知道“一个都不能少”在芯片流程中的重要性,没有一款芯片可以因为其中一个模块的验证延迟而有信心去流片。所以,整支验证队伍自上到下,覆盖到各个模块的验证师都应该有这种意识:即,无论何时,时间总是第一位的,时间就意味着客户的耐心和市场的窗口。
- 保质
保质指的是尽可能少地将缺陷暴露在流片以后,至少要尽可能少地暴露在客户和市场面前。因为从成本的角度看,缺陷暴露在不同的阶段造成的损失有指数级的差别。如果芯片交付给客户以后才被反馈出一些大的缺陷,那么芯片设计方就会背负很大的压力,除了要同客户一起进行高密度的对话、联调外,整个产品链都要为这个缺陷付出更大的人力、物力成本;如果芯片是在客户方通过测试却被市场发现自身性能不如预期的话,那么会对芯片设计公司和客户双方都造成消极的影响,无论是在市场反馈还是用户对品牌的认知度上,都是如此。
- 低耗
低消耗有两方面,用更短的时间、更少的人力来完成芯片设计任务,这是一笔前期看得见的可以预期控制的成本;同时,也有一些成本是突发的,其中一个就是缺陷的暴露问题。从图1.6可以看出,暴露在不同研发阶段的缺陷对芯片项目造成的额外成本是随着项目进程指数级递增。
硅前验证中RTL验证发现的缺陷带来的影响要明显小于Gate验证中带来的影响。因为RTL阶段发现的缺陷,只需要修改RTL代码,而Gate验证发现的缺陷除了需要同时做RTL修改和网表修改,更是要后端一系列流程的反复。如果在硅后测试中发现了缺陷,就需要考虑这个缺陷是不是致命的。所谓致命的,就是它无法使用一些重要功能,甚至本身会导致一些重要功能的失效和错误行为,且没有办法通过软件层面来做修复。这样的致命性缺陷就意味着芯片要做第二次流片,要针对致命缺陷做出修复、功能验证、后端流程,这样的过程通常耗时三个月以上。如果一个致命缺陷等到被交付给客户以后才发现,那么造成的损失则是双方的。对于客户来讲,他们需要为这个致命缺陷吞下产品延迟上市的苦酒;而对于芯片公司来讲,恐怕这可能是双方最后一次合作了。
- 按时
- 芯片研发与客户反馈
进一步来看,如果我们将硅前流程、硅后流程同客户反馈联系在一起(见图1.7),就能对芯片流程有一个更清晰的认识。从图1.7可以发现,芯片在出片以后被检测出的严重缺陷会直接导致芯片的二次流片,这对成本控制而言是一种额外的损失,同时将时间和人力资源消耗在本可以避免的二次流片上。所以,功能验证是唯一可以用低成本在硅前流程将上述目标“按时、保质、低耗”达成的方法。也正因如此,对于功能验证而言,验证经理通过量化的方式来衡量验证产出的进度。用来衡量的两个标准,一个是时间,一个是发现的缺陷数量。
- 缺陷增长曲线
通过缺陷数量在时间线上的记录,我们可以绘制出缺陷数量的增长曲线,如图1.8所示。
一般来讲,缺陷数量的增长曲线是逐渐逼近趋于缓慢的。功能验证需要保证的是将缺陷数量的增值(至少是致命缺陷数量)保证在硅前阶段,不应该让其发生在硅后测试阶段。针对缺陷的类型,我们一般遵循先易后难的验证方法,这表现在两个方面:
- 我们给出的激励向量应该是先易后难,先从简单的激励向量测试设计的基本功能,这一点我们在1.3节提到随机约束域的宽窄设定和验证阶段之间的关系。当验证将基本功能测试完毕后,我们再朝着更复杂的情景着手去测试其他功能。
- 我们查找出的缺陷也应该是先基本后高级。这么做有两方面的好处,当开始的激励向量是基本形态的时,有助于设计本身在缺陷报告反馈中逐步稳定,同时留出一定的时间用来帮助设计师和验证师针对设计细节交换意见,在硬件描述上面统一理解。
- 这种缓冲会使得在其后的复杂测试中,设计师和验证师双方就复杂情形中的硬件输出结果快速达成一致,因为之前已经就功能描述达成一致了。对于验证师而言,这么做也符合验证的曲线,也就是前期的缺陷曲线斜率较高,是因为设计本身容易被发现一些基本设计问题;随着验证周期的进展,缺陷曲线率慢慢减小,说明设计自身的稳定和功能完备情况趋于最终的设计目标。
- 对于验证经理,如果有追踪缺陷率曲线的习惯,那么一般建议检查两个地方:
- 缺陷率曲线是否在收敛,或者斜率是否在变小,这一定程度上可以说明验证的状态是否在收敛和趋于完备。
- 需要注意验证过程中发现的缺陷种类,应从基本缺陷再到高级缺陷。假如到了后期,尽管缺陷率收敛,却发现了基本缺陷,这时应对整个验证质量打一个问号。
有必要的话,同验证师一起回顾验证计划、验证环境和测试序列。因为越到后期越不应该发现基本类型的缺陷,否则验证经理无法对于整个验证任务的完成有足够的信心。
5 验证的周期
5.1 验证周期中的检查点
功能验证有着一整套完备的流程,从硬件系统定义贯穿到硅后测试部分。一般来讲,验证团队会基于时间差同时进行多个项目,多个项目之间自然也存在着借鉴、更新的关系,所以验证的环境和复用性也是在不断提高的。每一个项目在进行瀑布模式开发时,验证团队也 会在细分的流程中完成任务,同时在展开下一项任务之前进行一些重要检查点(checkpoint)的回顾工作。验证人员不断地在新项目中完善验证环境,验证的周期因而也是不断往复、螺旋上升的过程。
图1.9将功能验证的各个关键节点罗列并使之成为一个周期。验证周期的起始点从创建验证计划开始,验证计划需要参照系统工程师给出的功能详述文档。接着验证人员开发验证环境,在创建验证环境的过程中,验证人员一般会邀请设计人员和系统人员一同回顾验证计划,确保验证计划没有明显的遗漏,所以验证计划的回顾是第一个检查点。

验证环境准备完毕且有一些可供测试的激励时,验证人员会比对设计的输出结果。如果发现有比对错误,验证人员首先要自己去调试环境,定位到硬件HDL文件存在缺陷的大致位置。如果验证人员有充分的经验,他还可以进一步给设计人员修改代码的建议。硬件设计经过一定数量的激励测试,验证人员就可以准备回归(regression)测试了。回归测试就是将已有的所有测试序列都执行一次。一般来讲,随机序列的回归测试覆盖率贡献要大于直接序列的回归测试,不过这种优势会随着验证率曲线的增长而变得不那么明显,具体的原因就是随机激励无法给出定向激励来填补剩余的验证空间,而定向测试则可以被有经验的验证人员运用,用来验证边界情况。在完成回归测试之前,我们需要进行第二个检查点——验证代码检查,这一检查点的作用是通过回顾验证代码从而发现可能遗漏的测试激励、不恰当的随机约束、代码结构的缺陷等。
完成回归测试后进行第三个重要的检查点——流片前验证完备性检查。一般这项检查是验证经理最后签字的。验证经理根据一份检查清单来将验证进度做量化的综合评定,最后判读是否完成验证的任务。当然,这一过程并非只有在流片前才会评估,而是发生在这一期间内若干阶段,包括模块验证阶段、子系统验证阶段、芯片系统验证阶段和最后的网表验证阶段。每一个阶段验证经理都有相应的通过标准和检查清单,判定模块、子系统和最终的芯片系统是否达到验证的目标。
即使在最终流片以后,验证团队也需要和硅后系统测试团队完成对接。这是由于,硅后系统测试阶段才是真正能够判定小到每一个功能模块大到整个芯片系统的各项功能能否正常工作的标准。通常,系统测试团队参考功能验证团队的验证计划,从底部测试每个模块的功能,逐步向上层走,最终测试整个芯片的联合功能。在系统测试环节中,如果发生了功能测试失败,系统测试人员与验证人员协作,最终定位到是硬件自身缺陷还是测试中的环境配置,或者是寄存器设置问题。如果最终测试发现了硬件缺陷,那么硬件团队和软件团队也会一起评估该缺陷是否是不可修复的。针对硅后测试发现缺陷的情况,一般首先考虑是否有软件修复的可能,接下来才考虑硬件上有无变通的办法。当两方面都无法解决时,我们只能宣告,一个无法硅后修复的缺陷在测试阶段发现了。当然,更糟糕的情况是,这个缺陷竟然是一个致命缺陷。
经过系统测试后,验证团队最终被硅后测试发现的缺陷展开逃逸分析,来检讨为什么漏洞会在硅后测试环节中被发现(而不是在硅前验证环节)。
可能引起漏洞在硅前验证阶段逃逸的情况包括:
- 验证计划制定不充分,没有完全覆盖功能验证点。
- 激励序列生成不完全,没有覆盖全部的有效激励场景。
- 验证环境不完备,例如比较器(checker/scoreboard)没有足够完善去比对输出结果。
展开逃逸分析之后,要进行验证周期的最后一项检查——吸取教训。吸取教训是一种被动的方式,我们在完成的项目中犯了一些错误,如果不想被同一块石头绊倒两次(没有人会愿意吧),就需要吸取教训。这种被动的方式和主动提高验证效率没有冲突,恰恰是在我们没有考虑到的地方吸取教训,在我们考虑到的地方主动完善,使之成为一种内外结合提高验证质量的方式。
关于吸取教训,在这里我们给出一些建议:
- 请在整个验证周期内保持收集与验证完善相关信息,比如,突发状况以及如何克服,陷阱从哪里来,有哪些遗憾,等等。之所以这么做,是因为我们通常在项目结束以后会懈怠下来,我们的记忆无法保存事发当时的一些细节,也容易忘掉当时一些心理上的痛苦。所以 就像做一份验证记录一样,保持着这样一份完整记录,将来我们可以从中很快地回溯起来我们一路是如何走过的。
- 除了一些个别情况,验证缺陷的暴露与整个模块验证团队都有关系。因为可能我们一起制定的验证计划不够充分,一起回顾测试序列的时候也不够仔细……要思考团队整体的疏忽在什么地方。每个人都需要考虑到自己在验证周期的不同阶段应该充分履行的责任是什 么。
- 尽量从一些教训中量化今后可以加强的地方。比如,如果功能覆盖率和代码覆盖率的指标是硬性的,那么验证人员就不应该妥协,应想办法达到这个标准;又比如,一些跨时钟域的问题没有被发现或者在网表仿真时才被发现,以后就应该将跨时钟域检查、同步单元检 查作为标准在验证过程中执行下去。
5.2 功能详述
对于一个芯片,大到芯片自身,小到可以细分的模块,都需要系统工程师给出功能详述文档。这里以较小颗粒度的模块功能文档为例,看看一个基本大小的模块如何依靠功能文档来实现硬件设计和功能验证。
一份功能文档,通常包含如下几方面的信息:
- 接口信息。是不是标准接口、是标准接口的哪一个版本。如果接口是标准接口,那么功能详述中不需要详尽列出接口的时序信息、命令、数据传输等,而只需给出基本的时钟、复位、接口信号名。对于标准接口,设计人员和验证人员可以下载标准接口文档来更详尽地了解接口信息。如果接口是公司内部定义的接口,则需要参照内部定义的接口文档;如果是自定义接口,由于这种接口没有被规范化,功能文档中应尽可能周全地描述需要给出的信息,以方便日后设计人员和验证人员双方参考。
- 结构信息。结构信息将模块进一步细分为各个功能组件,以及包含组件之间的逻辑关系。各功能组件对设计人员而言可以匹配出对应的 RTL 文件,其后可以自底向上进行集成;对于验证人员而言,为了尽可能与设计保持同步,验证环境的开发可以同设计组件同步展 开。从设计组件A和验证环境VA,再到组件B和验证环境VB,再到组件C和验证环境VC,最后集成出模块M(A+B+C)和验证环境V(VA+VB+VC),就可以完成模块M的集成验证了。
- 交互信息。由于模块稍后会被集成到更高一级的子系统当中,所以功能详述文档中包含模块 M 同外界模块交互的示意图。必要时,这些交互信号之间也会给出准确的时序信息,确保集成后两个模块之间的交互按照预期定义的时序发生。比如一对握手(handshake)信号,需要指明输入信号的频率、是否需要考虑同步、是电平信号还是脉冲信号、大致维持几个时钟周期,相应的输出信号也要有类似的考量,以满足输出信号接收方的要求。
功能详述文档是硬件设计和功能验证的基础部分,也是共同参考依照的标准。设计人员通过自己的理解将其实现成 RTL 文件,而验证人员也按照自己的理解为设计构建出验证环境。尽管看起来验证人员重复了一次功能上的理解,但正也是因为这样,确保了功能描述文件 可以被设计和验证双方理解一致。验证人员自己设计的参考模型(reference model)才也会按照功能详述文档做出正确的行为和数据输出。参考模型对应硬件设计,通过结果比对检查是否有不符合预期结果的情况。这种方式可以让功能文档变得易读清晰,降低设计人员误解功能描述和实现错误硬件的可能性。
5.3 制定验证计划
验证计划是为了完成验证目标的,因此它本身要回答两个问题——验证对象是谁、如何验证。制定验证计划的主体在不同公司可能不同,例如,公司A是由系统人员制定验证计划的,而公司B是由验证人员制定验证计划的。不过可以肯定的一点是,最后回顾验证计划 时,会将系统人员、设计人员和验证人员组织到一起来回顾,检查可能存在的验证漏洞。验证计划也存在颗粒度,与模块大小、处在系统的层次相关。
这里我们仍然以模块M为例,考虑验证计划中的检查事项:
- 验证方法:是采用直接验证、随机约束验证、形式验证还是其他的方式。
- 验证工具:选择需要的验证工具来支持验证方法。
- 验证完备标准:量化出一些参数以衡量验证任务是否完成。
- 验证资源:包括人力、时间、硬件、软件等所有与项目预算有关的内容。
- 验证的功能点:需要给出验证的功能点以及在什么层次去验证它,更具体的包括生成何种激励、检查设计的何种状态和数据输出。
5.4 开发验证环境
验证环境的开发是验证人员花费时间较多的部分。验证人员从搭建环境开始,实现激励产生器(stimulus generator)、参考模型(reference model)和数据比较器(data comparator)。
验证环境的运行需要软件工具的支持,目前的主流仿真工具均可以对仿真验证提供广泛支持。当然,制定验证计划时需要考虑采取何种验证方法,之后才开发验证环境。不同的验证方法决定不同验证环境的结构和所用的软件。伴随着设计缺陷的发现和修正,验证环境也需要保持更新,最终同硬件设计一样趋于稳定,进入验证的下一个阶段。
5.5 调试环境和HDL文件
验证人员在调试方面的时间投入最多。环境的建立在验证早期投入较多,设计的功能调试却是一步步向前推进的。验证刚开始时,验证人员调试的对象主要集中在环境的协调整合上;环境稳定后,验证人员递交测试,进行仿真验证。针对每一个功能点的验证均需要给出一个或者多个激励向量,在激励给入后,将参考模型和实际输出进行比较,发现比较错误时需要进一步定位问题的源头:
- 环境是否有瑕疵;
- 测试序列是否合理;
- 参考模型是否遵循功能详述文档;
- 硬件设计本身是否存在功能缺陷。
定位问题时,一般建议验证人员:
- 先从环境着手,试图去稳定环境部分,因为这一部分是我们可以控制的。
- 让环境趋于稳定后,我们再去定位问题是否来源于硬件设计。判定设计存在缺陷时,验证人员需要了解设计、定位到缺陷的位置,提交给设计人员并得到反馈。
- 设计缺陷被修复后,应重复递交同一个测试用例,用例中产生的测试向量也不应该改变。如果使用的是随机约束方式,应记住上一次仿真出错的时间位置和随机种子(random seed),在后面重新递交时采用同一个随机种子以产生同样的测试向量,确保外部激励的场景是一致的。这种方式背后的逻辑是,在调试过程中应尽量减少变量的数量,理想情况下只有一个变量。对于上面的场景,这个变量就是设计缺陷在修复前和修复后的功能表现。
- 至于如何设定随机种子,在仿真器的用户使用文档中可以找到相应的使用方法和仿真选项。
5.6 回归测试
回归测试指的是验证硬件在某个缺陷修复或添加了某项新功能后,仍然可以通过以前的所有测试用例(test case)和可能添加的新的测试用例。可能存在的环境变化包括硬件设计自身的改进、缺陷修复、功能添加和验证环境的更新。在每次的回归测试中都可能发现新的缺陷、添加新的测试用例或者更新验证环境。
每次回归测试都会帮助完成两个目标:
- 确保这次改动没有引入新的缺陷,并修复了之前的漏洞,或者按照预定目标实现了新的功能。
- 随机验证在每次递交时默认的随机种子不同,这对重复递交一套回归测试表也是有意义的。伴随着功能覆盖率,可以通过往复的回归测试和补充的定向测试来将逐步提高验证完备性。
当代的回归测试逐步趋向自动化,需要一种合适的回归测试工具协助完成回归测试表的提交、分配到不同的服务器上面以计算量来换取时间的缩短、自动识别仿真的结果、到最后给出验证报告。
这种回归测试工具,可以从 EDA 公司的工具表中找到商业化的产品,同时大中型公司也有适合自己团队工作流程和需求的定制工具。回归测试是实现验证完备性的一项重要手段,因为只有通过将大量测试用例并行提交到服务器群,才可能完成覆盖率的快速上升,满足项目进度的要求。
5.7 芯片生产
经历过回归测试阶段(RTL 回归和门级网表回归),意味着芯片的逻辑和物理数据都经过各项检查了。在将芯片最后送交给半导体生产商(fabrication facility)之前,项目经理与设计经理、验证经理、后端经理一起回顾整个检查表(checklist),确保所有的标准都已经通过。芯片的数据提交给生产商后,最终制造出来,我们称之为流片(tape-out)。
值得注意的是,此时功能验证的流程并没有全部走完,仍然需要提交回归测试,通过保持不停的随机测试,在新的状态空间上测试,可能发现新的问题。如果在递交给厂商生产以后发现新的缺陷,要像硅后测试发现的缺陷一样对待。通过分析这些缺陷,考虑是否有软件补救办法,或者提交设计修改意见,在下一次流片前准备好设计方案和验证方案,将其计划到下一次验证周期内。
5.8 硅后系统测试
芯片返回后,系统测试人员依照系统集成的顺序从底层模块开始测试。测试前,需将芯片同测试开发板结合起来,或将芯片植入到待开发的系统上。随后硅前人员(设计人员、验证人员、系统人员)和硅后人员(测试人员)保持频繁的沟通,一旦测试出了问题,第一时间判断是测试的方法不恰当还是硬件自身的问题。之所以要求硅前人员参与,是因为我们不期望硅后测试出现太多的问题,尤其是致命的缺陷。当一个硬件缺陷被发现之后,硅前人员需要讨论这个缺陷的严重性,从软件层面上讨论可行的补救办法,再从硬件层面看是否有其他办法使能这项功能,或者不使这项缺陷扩大影响面导致重要功能失败。如果最终无法避免这个缺陷,且该缺陷严重影响系统功能,就需要在下个芯片设计周期内去修改和验证这项功能。
5.9 逃逸分析
有时,我们难以避免个别的验证漏洞一直被忽视,导致它们可以从硅前验证阶段“逃走”,到硅后测试才被发现。遇到这样的情况,硬件设计人员和验证人员都要与测试人员沟通,尝试在硅前的仿真环境中重现遇到的测试失败场景。如果可以复现,设计人员和验证人员要再次思考这个漏洞逃脱的原因;如果无法复现,则仍旧无法保证硬件做出的更改可以在下次流片后修复这次测试的问题。这种硅后测试失败要求硅前验证重现的难度,相较于在交给客户之后遇到的应用失败场景还是容易很多的。因为一旦从硬件级别向上堆叠经过驱动层、固件层再到客户的应用层,更加难以在硅前验证环境中重现客户应用失败的场景。作为验证人员,如果你有幸遇到过这样的场景重现和失效点定位的问题,那么想必你会深深记住它的。当逃逸分析完成以后,这一过程会对下一个芯片周期中,设计人员如何规避设计陷阱、验证人员如何完善验证方案、产生尽可能多的有效测试序列都是很有意义的。在整个芯片过程中都贯穿着“吸取教训”四个字,因为要完成芯片从硅前到硅后的过程本身就很漫长(相比软件的迭代开发而言)。要积累尽可能多的经验,芯片工程师应该在每一个关键节点养成总结的习惯,并在下一个阶段有意识地去完善,保持一种不断成长的态度。
6 作者结束语
在刚进入职场时,我每天都在公司工作到很晚,倒不是工作太多、效率不高导致的,而是给自己设定了目标,需要在更短的时间熟悉公司的流程。我翻阅了公司内部和外部的很多文档,一天从早到晚除了睡觉以外,手边都放着各种文档。如果将这些文档分类,那么一部分是技术文档,我大致用了三年的时间掌握了主流验证技术;而另外一部分是验证流程的相关文档,这部分文档是常读常新的,我总可以在不懂时翻阅文档,或者请教更有经验的同事。有句话说,“自己知道得越多,知道自己不知道的也就越多”。在IC验证这条道路上我们面对的不仅仅是更新很快的验证知识,也包括一些“常识”。所谓常识,就同前辈教的道理一样,往往在遇到挫折时才想得起来,对此我也深有体会。之所以将这一章作为本书的开头,就是为了让读者能够清楚:在通往专业化的验证道路上,需要经历不少的磨炼。接下来,我们将从验证通识的各个方面分别展开论述。当然,你也可以跳过验证通识的内容部分,转而阅读SystemVerilog和UVM的知识。但请不要忘记一点,验证通识对于你提升自己的专业化素质会有很大的帮助。
芯片验证漫游指南1芯片验证全视