芯片验证漫游指南2验证的策略
1. 设计的流程
我们在第1章给出了芯片产品开发的流程图,在描述中我们将开发流程分为两条主线:
- 芯片功能的细分;
- 不同人员的任务分配。
也就是说,不同人员需要在硅前不同阶段实现和测试芯片的模块功能。如果我们从另外一个角度看,芯片的开发即是将抽象级别逐次降低的过程,从一开始的抽象自然语言描述到硬件HDL语言描述再到最后的门级网表。在我们介绍过RTL设计和门级网表后,这里需要引入一个更高抽象级的描述方式TLM(事务级模型,Transaction Level Model)。TLM一般在早期用于模拟硬件的行为,侧重于它的功能描述,而不在于严格的时序。同时各个 TLM 模型也会被集成为一个系统,用来评估系统的整体性能和模块之间的交互。
TLM模型在早期的设计和验证中,如果足够准确的话,可以替代验证人员的参考模型,一方面为硬件设计提供了可以参考的设计(来源于系统描述),一方面也加速了验证(无须再构建参考模型,而且TLM模型足够准确反映硬件描述)。
1.1 TLM模型的需求和ESL开发
早期的芯片开发模式是遵循从系统结构设计到芯片设计制造,再到上层软件开发的。随着产品开发的进展,需要让系统人员、硬件人员和软件人员保持充沛的工作量,同时对于一个芯片项目而言,我们也希望硬件人员和软件人员可以尽可能地同时进行开发。这听起来怎么可能?毕竟芯片还没有制造出来,没有开发板怎么去构建软件呢?
在这里,系统结构人员会在早期构建一个高抽象级的系统,该系统具备相应的基本功能,且各模块的接口保持数据交互。通过将功能描述变成可运行的系统,让硬件人员和软件人员在早期利用该系统进行硬件参照和软件开发。这种可以为复杂系统建立模型、让多个流程分支可以并行开发的方式称为ESL(电子系统级,electronic systemlevel)开发。
1.2 传统的系统设计流程
传统的系统设计流程如图2.1所示,是瀑布形式(waterfall)开发的,这种顺序开发的方式存在明显的边界:
- 时间边界:不同的开发子过程之间顺序执行,几乎没有交叠的空间来缩短整体的项目交付时间。
- 组织边界:开发小组之间的交流发生在上一个过程结束、下一个过程开始时,这引入了额外的沟通成本;

1.3 ESL系统设计流程
图2.2为ESL系统设计流程。为了模糊或者融合这种边界,ESL开发流程通过建立虚拟原型(virtual prototype),又称TLM模型,使参与到系统开发的各小组做并行开发。之所以有这种魔力,是因为 TLM 模型不再是一种无法被硬件开发和软件开发利用的抽象描述,而是一种更早期开发的软件模型。在ESL开发的协助下,其余的开发流程可以更早地与系统设计一块进行开发,从整体上看,这种方式有助于缩短芯片开发的时间。除此之外,ESL在前期产品定义的阶段有相对可量化的模型,有助于早期评估产品的功能、性能是否满足客户要求,也能减轻一些低配置性能的风险以及降低过多设计的成本。
这是为什么呢?原因有以下几点:
- 在早期定义产品时,市场部门将产品功能和性能要求从客户那里收集回来,交由系统结构人员来定义芯片结构。这中间存在一些问题,例如,系统结构人员无法深入到局部功能,更无从列举出所有的用例来判断功能是否满足需求,而在性能测试方面也只能通过一些表 格化数据做出静态估算。这时,TLM 模型可以帮助在系统级别完成模型搭建和系统集成,甚至测算系统的性能。这样,系统结构人员有更多的信心给出合理的结构配置。
- 正由于可以在芯片结构的定义阶段快速做出性能评估,系统结构人员才可以及时地做出资源调整来满足用户的需求。否则,尽管芯片可能是低缺陷率的,但如果它的执行速度不够快、功耗过高,那么仍然无法满足客户的要求。
- 过度设计的结构就像给一只袜子缀上水钻一样没有必要。客户给的报价摆在那里,你的设计过度,不但意味着成本的增长,也意味着更高的复杂度和风险。

ESL和TLM对系统模型的要求使得需要有一门语言具备这些特性:
- 纵深多个抽象级别来进行模型描述;
- 标准开放;
- 高效的仿真性能和调试接口;
- 被主流仿真工具支持;
- 本身包含TLM事务级传输的接口。
这样的语言就是接下来要介绍的SystemC。SystemC是可以满足TLM模型开发的一种语言。严格来讲,它本身不是一种语言,而是建立在C++之上的一种类库(class library)。SystemC语言可以用来描述系统级别的硬件行为,而这一点恰是其他语言无法满足的。2006年,SystemC被IEEE收入IEEE 1666标准,它本身也易于学习,具有C++/Java基础和硬件设计概念的人使用起来都不需要太多的学习成本。
1.4 语言的抽象级比较
不同的硬件领域使用到的建模语言都有它们各自适合的抽象级,图2.3指出了各个语言擅长的抽象级领域。从左至右,VHDL和Verilog主要用做RTL仿真和数字电路的综合,也用来在早期搭建一些验证平台。SystemVerilog/Vera/e是用来做功能验证语言的,其中包括了它们的随机约束重要特性,同时可以发现,SystemVerilog 本身可以用来描述硬件做 RTL仿真和门级综合。在此之上,SystemC 更偏向于系统层,它在结构层面上可以做更高抽象级的描述,虽然本身无法描述电路的综合网表,但它能够作为虚拟平台为上层软件开发做准备。MATLAB在信号处理上面被用来作为描述和算法验证。

1.5 传统的系统集成视角
前面已经提到,传统的瀑布开发模型(见图2.4)无法让硬件人员和软件人员在系统结构定义早期参与其中。硬件的设计和验证人员需等待系统定义完成之后,才能将功能描述文档分别翻译出来,建立可综合模型和参考模型。软件人员只有在硬件流片以后才真正开始进行软件开发,尽管目前的FPGA有着比硬件更快的仿真优势,但无论从时间还是从速度来看,它仍然不是理想的软件开发平台。虽然FPGA等硬件加速工具对硅后系统测试有积极意义,但因介入较晚,加上基于速度层面的考量,其对软件系统层开发的贡献依然存在局限性。

1.6 ESL系统集成视角
新型的ESL系统开发方式(见图2.5)在系统定义阶段建立TLM模型。这一模型的建立对系统人员、硬件设计人员、验证人员和软件开发人员有显著帮助:
- 系统人员在TLM模型集成系统上更易评估系统性能。
- 硬件设计人员同时利用功能描述文档和TLM模型,更准确地翻译为可综合的RTL设计。
- 验证人员可以直接将TLM模型作为参考模型集成到验证环境中,省去额外开发参考模型的时间。
- 软件开发人员可以在TLM集成后的虚拟系统上进行软件开发,在芯片真正出片后,则只需做一些基于实际硬件的软件移植;这可以把软件开发的起点大大提前。
TLM 建模有很多优点。然而,在真正考虑施行 ESL 系统集成流程时,需要考虑一些实际的问题:
- TLM建模对系统人员有更高的技能要求。不但要求他们掌握SystemC开发,同时要求有硬件描述的基础。他们的工作量同时包括功能描述文档和 TLM 模型,且TLM需要准确翻译功能描述文档,确保一致性。从传统流程迈向 ESL 流程,可能需要做一些妥协,引入专门的虚拟建模(virtual prototyping)团队协助系统人员翻译功能描述文档。他们的共同产出最终作为一致的参考标准。
- 尽管已经有了可以被综合的 SystemC 的子集和代码规范,但这种方式目前仍未得到业界的广泛应用。在某个硬件模块没有就位或者需要加快仿真速度时,可以临时用TLM模型替换原先的硬件设计。前提是,系统的仿真行为保持不变,且TLM模型接口上的时序满足HDL仿真的要求。
- 当 TLM 模型被验证环境复用时,要求 TLM 与验证环境之间保持标准接口(TLM interface),以便于TLM模型的插拔。
- 软件开发需在完整的系统层面进行,因此当TLM协助软件开发时,TLM子模块要被尽早地集成到一起,作为整体系统为软件开发所用。TLM 模型需要具备标准接口,以便更快地实现系统集成。

目前常见的设计流程仍然是瀑布开发方式或类 ESL 开发方式。类 ESL 开发指的是开发流程并没有完全遵循上述流程,而是在一些地方引入TLM建模。在图2.6中,由于系统人员的技能限制,项目开发需要额外引入虚拟建模团队。虚拟建模团队服务的主要对象是软件开发一方,他们与硬件设计、验证团队的沟通会较少。这种类ESL的开发可能有多种组合,但需要警惕的是,在方便软件开发早期进入项目时,TLM模型应该与系统定义保持绝对的一致性,从而为硬件和软件方提供模型和代码参考。
从图2.6来看,这种类ESL的方式是存在风险的,因为虚拟建模团队从系统定义到TLM模型的过程存在二次翻译。如果翻译不准确、存在疏漏,可以想象,基于 TLM 模型的软件开发不会那么容易被移植到真正的硬件系统上,因为硬件本身也是二次翻译的。所以,理想的合作边界应该如图2.7所示,虚拟建模首先和系统定义保持原义的一致性,硬件和软件则可以将TLM模型视为功能描述的一致性翻译,然后各自在TLM模型上进行开发。

2. 验证的层次
从系统定义阶段开始,我们将芯片系统划分为子系统,进而将每个子系统划分为不同的功能模块,直到划分为复杂度合适的模块。到设计阶段,按照自底向上的方式开始做硬件设计和集成。从定义阶段到设计阶段,再到后端部分,整个硅前的流程都是将芯片按照层次划分的,一般称为芯片系统级(chip level /system level)、子系统级(sub-system level)和模块级(module level /unit level)。
这种层次划分的方式对于芯片开发有哪些好处呢?
- 便于拆解功能模块,实现人员的并行工作协同。这一点是从项目执行效率出发的。
- 对于系统定义而言,这是从主要的功能、性能要求量化为系统不同模块定义的方法。
- 从设计和验证角度出发,合适的复杂度模块有助于估计工作量和人员分配。设计最终是通过模块化来集成的,而验证环境在模块化后可方便地在更高层的环境中复用。
- 对于后端,在进行了合理的区域划分后,模块和芯片系统可以并行进行后续的物理设计流程。设计模块在每个阶段进行相关的设计检查,集成后的芯片系统最终进行设计检查并通过流片要求。

如果我们是在为一款手机设计通信芯片,如图2.8显示,一开始的系统定义阶段可能要规划出这么多的功能模块,还需考虑模块的性能因素。每一款芯片都包括多个子系统,每个子系统包含多个功能模块。从图1.3中的这款手机通信芯片结构来看,它包括的子系统有:
- 处理器子系统;
- 协处理器子系统;
- 本地存储子系统;
- 外部存储控制器系统;
- 数据接口系统;
- 系统模块外设;
- 多媒体子系统;
- 调制解调子系统。
核心模块调制解调子系统中的2G/3G/4G 由于自身的复杂性提高,可以进一步作为独立的系统来对待,进而细分下去。至于如何划分层次,我们一般从如下几个角度考虑:
- 系统的复杂性:如果系统相对独立,自身就有作为子系统的条件;如果本身仍过于复杂,则可以进一步细分。
- 芯片集成的便利性:对于顶层芯片集成而言,一个合适的子系统应与外界有清晰的功能边界,如系统信号边界、标准总线边界、与其他子系统交互的边界,同时这些信号边界也尽可能保持稳定和精简。这是从顶层集成的工作量和后端布局布线的角度出发的。
- 验证的阶段:验证人员需要清楚,哪些功能点在模块级验证、哪些在子系统级验证和芯片系统级验证、是否有必要在不同级别重复验证、各个层次是否会保证验证完备性。
- 后端的流程:如果一个子系统占芯片整体面积的10%以上,那么后端就有理由考虑将其单独综合,因为合理的划分子系统且并行综合有助于后期整个芯片综合的收敛。
接下来主要从验证的角度来考虑,如何选择合适的验证层次和对应的验证环境:
- 模块级(block level /unit level);
- 子系统级(sub-system level);
- 芯片系统级(chip level);
- 硅后系统级(post-silicon system level)
2.1 模块级
如果是图2.8中的处理器子系统,考虑先将DMA(Direct Memory Access)、Cache缓存和 Core0/Core1分别展开模块验证。每个模块验证首先要考虑的是,哪些功能点可以在模块一级完全验证。
决策基于如下因素:
- 内部功能如状态机验证;
- 内部数据存储验证;
- 数据打包功能、编解码功能;
- 指令执行;
- 寄存器配置。
同时需要考虑哪些功能无法在模块一级被验证到:
- 与其他相邻模块的互动信号;
- 与其他子系统的互动信号;
- 与芯片外部的互动信号;
- 与电源开关的验证。
我们需要考虑在更高的层次来验证这些部分。
2.2 子系统级
一个成熟的子系统,既拥有完备的功能可以执行专门的任务,也有足够稳定的接口用来在更高层做集成。与模块相比,子系统更稳定也更封闭,这对顶层集成是有好处的。也正是这种便于集成和相对封闭的特征,使得我们可以从公司外部或内部得到不同的子系统。
合格交付的子系统应该包含:
- 设计包;
- 验证包;
- 回归测试表;
- 覆盖率收集脚本和数据;
- 完整的文档(设计、验证、集成、后端)。
完备的交付可以增强顶层集成的信心,同时减少在集成过程中发生的一些接口理解分歧和参数化配置问题。单就验证而言,除了充分验证内部功能,如果对子系统的外部接口需要进行参数或编译预处理(compiler directive),验证人员则需要就这些参数和不同的编译选项(可能因此产生不同的硬件结构功能)给出完备验证。从子系统的封闭性和复用性来看,它们会在多个芯片项目中被使用,这对设计复用来讲是一件好事,而验证也需要将验证环境参数化以适应硬件的参数化配置。只有充分验证了参数化的子系统,才可能让它在不同的芯片项目中实现预期的功能。对验证管理而言,子系统验证是一个理想的可以切分的单元。这一层下面的模块之间互动很多,而这一层本身趋于封闭,与外围的接口有限,所以便于在子系统层设置独立的验证小组——“包产到组”。
2.3 芯片系统级
在芯片系统级,我们的验证平台的复用性较高,这主要是因为:
- 外围的验证组件不需要像模块级、子系统级的组件那样,数量多且需经常更新。它们主要侧重于验证芯片的输入输出。
- 芯片内部的子系统之间的交互、协作检查主要交给处理器和子系统,从寄存器检查和数据检查入手,实现定向测试(directed test)用例。
芯片系统级的验证侧重于不同子系统之间的信号交互以及实现更贴近实际场景的用例。这里的实际用例并非在系统软件层面,而是将系统软件层面的场景进一步拆分为多个模块互动情景后,再分开测试。
2.4 硅后系统级
尽管硅前验证部分与硅后系统软件开发联系较少,但尽早将硅后软件开发的实际用例用在硅前测试,能够发现一些实际使用中的问题。实际上,系统软件用例和硅前的随机测试具有互补的特性,功能验证中的缺陷如果没有在硅后测试、软件开发、用户使用中发现,那么隐藏的缺陷会静静地躺在那里,也许永远不会被发现(没有零缺陷的芯片,却有用户未发现缺陷的芯片)。所以,将硅后的驱动、固件和系统软件尽早在硅前引入验证过程,则可以与硅前的验证方法形成互补,使验证更加完善。
前面介绍了验证的4个阶段,给出了它们各自使用的测试场景。这里再给出可以遵循的几点原则,帮助大家在验证时选择适合的级别:
- 能够在更低的级别完成某一项功能验证,就不要在更高层次上去验证。小的验证环境更有利于控制激励场景的产生,能更加全面地覆盖功能点。
- 低层次已经充分验证过某一项功能,高层次就不要重复验证。低层次无法完全覆盖功能点验证时,应在高层次完全覆盖。
- 在低层次的验证阶段应适当考虑高层次的测试用例,并在低层次创造一些条件模拟发生的条件和场景。
- 在高层次的验证阶段,验证环境中的参考模型、数据比对、监视器等模块应首先考虑从低层次环境复用,无法满足时再考虑重新构建。
- 对于新的模块或者新的功能,应投入更多精力、给于更高优先级,在不同层次充分验证。
通过表2.1来更好地理解不同验证层次的侧重、性能和 使用方法。选择一个合适的验证层次,通过在不同层次分配不同的功能验证点,是最终迈向验证完备性的一项必备技能。

3. 验证的透明度
可以按照激励的生成方式和检查的功能点分布将验证划分为三种基本方式:
- 黑盒验证;
- 白盒验证;
- 灰盒验证。
接下来,我们逐一解释这三种不同透明度的验证特征。
3.1 黑盒验证
如果验证人员对设计的细节缺乏认识,那么黑盒验证是一种合适的方式。因为验证环境只需要将激励给入设计的外部接口,检查设计的另一侧输出就足够了。测试成功与否只是根据一个输入是否得到一个正确的输出去判断,验证环境本身不会关注设计的内部。
图2.9是黑盒验证的结构。

从图2.9可以看到,激励生成器(stimulator)只负责给设计灌入激励,监视器(monitor)和检查器(checker)只查看和比较输出信号。黑盒验证的一个缺点是缺少设计的透明度和激励的可控性,由此带来的问题包括:
- 测试失败时无法更深层次地定位问题。验证人员只能判断测试是否成功,无法进一步定位到缺陷所在的位置进而与设计人员完成深度协作。
- 难以发现一些较深的缺陷。因为验证人员无法根据设计本身给出更窄的随机约束定向地生成一些激励,同时,这对设计内部功能点的功能覆盖率收敛没有太多的帮助。
设计的接口采用标准接口时,图2.9中的激励生成器或总线功能模型可以使用成熟度高的验证IP。这些验证IP一般由第三方公司提供,有时公司内部也有这样的IP,它们的特点是像标准接口一样易于在验证环境中插拔和控制,且接口时序严格按照总线文档定义。监测器也来自于验证IP,这减少了验证人员底层开发的工作量。所以,当模块的接口是标准接口时,验证环境可以复用一些验证IP。
由于黑盒验证本身不包含设计的内部逻辑信息,所以当设计因缺陷而更新或添加新的特性时,原有的测试列表仍然较稳定,验证人员只需要对新添加的特性考虑新的测试场景。黑盒验证有利于保持测试环境的稳定,当后续项目中更新了设计时,新的验证人员也只需要很少的力气来维护继承的验证环境。
3.2 白盒验证
白盒验证可以弥补黑盒验证的一些不足。验证人员了解设计的内部工作逻辑、层次、信号等,他们因而可以对更底层的设计细节进行测试。这种验证方式检查设计是否严格遵循功能描述文档,测试发生失败时可以更快速地定位到缺陷。对于白盒验证的环境,我们的参考模型逻辑非常简略,甚至不需要参考模型,只需要植入监视器和断言来检查各个内部逻辑。这种环境配置背后的原则是,充分检查各个逻辑驱动和结构以后,就不需要测试它的整体功能了。不过,使用白盒验证也面临一些方法学上的缺陷:
- 由于本身专注于设计内部逻辑检查而忽略整体功能的测试,设计本身违反规范时,白盒验证难以发现缺陷。
- 在数据一致性检查方面,白盒验证难以从整体入手给出实际测试用例。
- 白盒验证的测试用例很多是从设计细节入手的,所以,设计发生更新时,验证环境的维护成本偏高。这一点在项目间复用方面带来的影响更多,新接手验证环境的人要付出很大的成本去理解设计细节和验证环境的细节;这时候白盒验证环境的低复用性缺点就暴露出来 了。
3.3 灰盒验证
黑盒验证和白盒验证各有优势和劣势。在实际验证中,我们倾向于将黑盒和白盒两种方法结合起来,以一种称为灰盒验证的方式(其结构见图2.10),将监视器、断言、参考模型一同用来完善验证。这种糅合方式带来的好处包括:
- 监视器和断言可以有更好的透明度来着重检查设计的一些重要内部逻辑。
- 参考模型已经有了断言检查局部逻辑的帮助,所以可以降低一部分精确度,而主要专注在输入和输出数据的比较上。
从复用性角度考虑,灰盒验证也有灵活的变动方式:
- 对于新的设计,我们的验证人员需要更深入地理解设计本身。采用灰盒验证一开始通过监视器和断言来进行局部验证,待设计初步完善和趋于稳定时,验证人员就有了对设计更全局性的理解来构建参考模型。又因为前期监视器和断言保证局部逻辑的正确,参考模型的 构建不需要完全精确,只需要较少的精力来实现。
- 该设计移植到别的项目时,设计难免需要进行局部修改,这时,灰盒验证的复用性优势相对于验证环境就体现出来了。哪怕是新的验证人员接手这个验证环境,好的灰盒环境也可以清晰地将黑盒和白盒的部分划分开。在设计复用的项目中,建议首先打开黑盒开关, 这对新的验证人员来讲测试成本较低,也不需要对设计和验证环境了解太多。同时,这么做也可以第一时间保证原有功能的稳定性,并反馈给设计人员新的改动造成的影响。紧接着,验证人员可以针对新的特性创建特定的黑盒测试序列,新的黑盒测试序列因设计本身的稳定 而不需要关注设计内部的太多细节。
- 完成黑盒验证环节后,可以在时间允许的情况下有序引入白盒的开关。首先应考虑创建新的白盒断言点或功能检查点,关注新的功能部分;其次,完成新的功能点白盒覆盖后,考虑逐个打开原有的白盒功能检查开关。打开白盒检查开关时,我们也遵循着每次添加较少 的开关来跑回归测试的策略,便于发现问题后快速定位到新打开的开关一侧;另外,白盒检查点的开关有优先级,如果验证人员足够专业,在他的代码或者文档中会对这些白盒开关给出说明和重要性排列。文档说明有助于新的验证人员按照优先级的高低来打开不同的开关。
所以,灰盒验证不但可以继承黑盒验证和白盒验证的优势,而且对验证环境在新项目中的复用有明显优势。

最后,我们通过表2.2来总结黑盒验证、白盒验证和灰盒验证的特点。可以看出,灰盒验证可以最好地平衡设计增量和验证复用。在设计阶段,如果验证人员有充分的经验来实现灰盒验证环境,清晰划分黑盒部分和白盒部分,那么在后续的项目中,灰盒验证的良好复用性和灵活性会给模块的集成带来便利。在高度集成化的今天,我们对设计复用性的考量,是从整个设计交付包(design delivery package)出发的,包括之前提到的设计包、验证包、文档包、回归测试包和覆盖率包等。
无论设计人员还是验证人员,都需要从各自的角度考虑复用性,全盘考虑设计的整个流程。设计交付只有能带来更好的“用户体验”,缩短集成时间(设计和验证),这才是好的设计和验证方式。
4. 激励的原则
激励的原则实际上就是为了解决一个问题,即如何保证激励源最大的自由度。只有从环境结构上保证了激励源最大的自由度,才能在输入一侧提供更丰富的组合,有条件地穷历一些测试序列。
按照这个核心原则,我们可以按下面这些因素评估激励的自由度:
- 接口类型;
- 序列颗粒度;
- 可控性;
- 组件独立性;
- 组合自由度。
下面我们具体了解激励在这些方面的表现和评估方法。
4.1 接口类型
面对一个设计的输入接口,我们可以先判断接口的类型 (interface type)。如果设计的接口类型复杂多样,可以通过接口类型的划分,化繁为简地找到从哪里下手给出激励。常见的接口类型可以分为:
- 系统控制接口(system control interface)。例如,时钟、复位、安全、电源开关等,以及这些系统控制信号旁生出的控制信号,如时钟门控信号(clock gating signal)。
- 标准总线接口(standard bus interface)。公开的行业标准总线协议,例如常见的AMBA系列协议、OCP、MIPI系列协议等,其文档详细,同时有丰富的验证IP提供服务。
- 非标准总线接口(non-standard bus interface)。公司内部定义的接口,或者根据模块功能需求定义的接口。接口时序相对简单,文档也较粗略。公司内部即使有可复用的验证IP,验证IP本身也可能未经过充分测试,而且在非标准协议定制的背景下,验证IP的复用性较低。
- 测试接口(test interface)。该测试接口主要留给可测性(DFT,Design For Test)功能使用,在功能验证中禁用即可。
- 其他控制接口(miscellaneous control interface)。如果被测设计是处于子系统中的功能模块且与相邻多个模块交互,那么该控制接口的信号数量较多、功能较分散;如果该设计是子系统,则该类型的控制接口数量较少且功能较集中。
有了清晰的分类,验证人员就可根据不同的接口类型选择验证IP,或者自己着手搭建激励组件(verification component)了。
4.2 序列颗粒度
对不同的接口我们会引入不同的验证组件。激励生成器(stimulus generator/driver)是接口验证组件的重要部分,它提供一些基本的功能方法用来生成小颗粒度的激励(sequence granularity)。用户也可以进一步做上层封装,从更高抽象级的角度生成大颗粒度或宏颗粒度的激励序列。我们按照软件层概念将激励序列颗粒度划分为下面的层级:
- 基本颗粒层;
- 高级颗粒层;
- 宏颗粒层;
- 用户自定义颗粒层。
从激励生成器提供的方法继承和封装入手,可以将这些颗粒层的关系表现为,基本颗粒层提供基本颗粒生成方法,而在其上,高级颗粒层和宏颗粒层做了深入的封装,帮助建立数据包(packet)和帧(frame)的概念。用户也可以依赖基本颗粒层,根据实际场景的需要实现自定义颗粒层,以方便特定场景的激励控制。序列的颗粒层次如图2.11所示。

我们以一个商业总线验证IP为例。该验证IP包具有基本颗粒层和高级颗粒层来生成不同级别的测试序列,在一些情况下,验证IP也提供宏颗粒层的定义来满足更高规模的数据传输。这里的抽象级指的是从时序和数据量传输的角度出发,越高的抽象级越不关注底层的时序而更重视数据的传输量,也是 TLM(Transaction Level Model)含义的延伸。当验证人员不能从已有的各种颗粒层中生成自己期望的测试序列时,便可以利用已有的基本颗粒层和高级颗粒层来构建自己的颗粒层。
4.3 可控性
可控性(controllability)是从对不同颗粒层的控制角度出发的。按照序列颗粒度的划分,对应的可控性评估如表2.3所示。

在功能验证周期的初期阶段,应从基本颗粒层中选择激励方法,这有利于我们在接口的基本功能中调节测试不同的总线传输情况,这里的验证点侧重于协议功能和时序检查。随着设计趋于稳定,我们逐渐选择高级颗粒层和宏颗粒层,将验证精力转移到数据量的一致性传输和性能评估上,而这两层的颗粒控制性没有像基本颗粒层那样细致到可以调节每一个参数变量,它们会同验证重点保持一致,主要提供跟数据量有关的可约束参数。
4.4 组件独立性
将一个设计的边界信号划分为不同的接口类型,并创建出对应的接口验证组件之后,我们就应该考虑各个组件之间的独立性了。组件的独立性(component independency)实际上也是协调性的基本保障,因为有了独立性,各个组件之间才会最大程度地不受其他组件的制约,同时又可以通过有效的通信机制实现组件之间的同步协调。我们接下来看看实现组件独立性需要考虑的因素:
- 必须按照接口类型来划分组件。
- 对于系统控制信号组件,尽可能将信号的关系按照实际集成关系做控制,例如,多个时钟是不是同步关系,多个复位信号是否可以单独控制等。
- 对于总线接口(标准或者非标准),实现一对一的控制关系。例如,若有两组相同的总线,则应引入两个总线组件分别控制,而非建立一个总线组件却拥有两套总线接口;后者有悖于可控性和复用性的要求。
- 对于其他控制接口,应从实际相邻设计那里准确了解各信号的使能极性、脉冲有效还是电平有效、是否存在握手关系、时序等真实的设计信息,来模拟高层集成环境中的控制场景。同时由于这部分信号偏于杂乱,在尽可能梳理信号的不同来源和功能后,需要在接口组 件中通过封装好的方法来实现灵活驱动。
- 验证环境中的系统控制信号组件会与其他接口组件发生连接,例如提供必要的时钟和复位信息,那么这些连接也应遵循实际集成的情况,确保组件驱动端的时钟输入与设计的时钟输入端同步。
4.5 组合自由度
最后一个衡量因素——组合自由度(combination space)是对上述因素的整体评估。只有通过底层的精细划分,建立抽象级更高的颗粒度,通过独立组件之间的协调来给出激励,才能提供较高的组合自由度。在这里,除了组件的独立性外,需要考虑组件之间的协调方式。
一般将协调方式分为两种:
- 中心统筹式(centrally organized)。通过中心化的调遣,将不同的任务统一分派给各个接口组件,产生不同的激励组合场景。
- 分布事件驱动式(distributed event driven)。将激励控制权交给各个接口组件,通过接口组件之间的通信来实现分布式的事件驱动模式,即组件之间的通信通过事件(event)、信箱(mailbox)、接口信号(interface signal)等方式实现同步通信。
通过上述因素,我们可以评定出一个验证环境中各个接口组件之间的组合,是否可以提供足够的自由度,最终有可能穷历出预定的激励序列。
5. 检查的方法
懂得了如何实现和评估激励自由度的方法后,需要考虑在各种可能的激励组合下如何选择适当的检查,以完成验证环境的另外一项核心要素——检查。检查就是查看设计是否按照功能描述做出期望的行为,识别所有错误的输出,发现设计缺陷。我们是按照接口类型来划分激励的;
对于检查,类型的划分方式则基于被检查逻辑的层次,这些层次包括:
- 模块的内部设计细节;
- 模块的输入输出;
- 模块与相邻模块的互动信号;
- 模块在芯片系统级的应用角色。
不同的检查层次,可以考虑采用不同的检查方法,如表2.4所示。

从表2.4可以看出,经常使用的方法有监测器(monitor)、断言(assertion)、参考模型(reference model)、比较器(comparator/scoreboard)、定向测试和形式验证等。接下来,我们简要分析不同检查方法的要点,关于这些方法的更多介绍,读者可以在8.6节详细了解。
一般而言,监测器(monitor)是必备的组件,它便于我们观察硬件信号。所以,在各个层次都可以找到监测器的身影。查看设计内部信号另外一个可行办法是使用 SystemVerilog绑定(bind)的特性。由于监测器可能被置入到各种方法中,我们需要从复用的角度,在构建监测器的时候考虑如下因素:
- 监测器一般跟激励发生器的作用域一致。这指的是,如果该激励发生器对应一组总线,那么应该有一个对应的监视器负责监视总线的传输。
- 监测器应根据检查的层次将信号监测分为模块内部和模块边界。
对于断言(assertion),我们主要依靠它检查模块的内部逻辑细节和时序信息。利用断言,我们可以通过仿真或形式验证来完成测试。是否选用仿真或形式验证的方式,这里给的建议是:
- 如果是模块级别,断言通过形式验证完全覆盖设计的多数功能点从效率和完备性来看是可靠的。同时建议在子系统或芯片一级创建基本的测试用例进行仿真,以作为形式验证的补充。
- 如果断言验证的功能点较分散或主要关切于模块的核心逻辑、时序,则倾向于使用仿真验证,采取灰盒模式,而用断言来验证重要设计细节。
- 如果断言总体可以覆盖模块的所有设计功能部分,采取形式验证或者白盒仿真验证两种方法都是可取的。
除了待测设计本身的尺寸、复杂度以外,参考模型(reference model)的构建也与验证方法有关。从2.3节来看,白盒验证对参考模型的要求最低,而黑盒验证却将最大的压力交给如何实现准确的参考模型。
比较器(comparator)的结构相对简单,一般依靠足够稳定的监测器和准确的参考模型,比较器只需要将监测的硬件输出和参考模型的输出做比较,给出充分的比较信息。测试用例结束时,给出自定义的测试报告即可。
当模块完成了模块测试、子系统测试,迁入到芯片系统级测试后,我们在系统一级复用监测器和断言。这些从低层次复用来的监测器和断言,在高层测试中主要用于覆盖目标模块与其他模块互动的功能点。在系统测试中,从实际应用场景出发,我们一般采用定向测试即C/C++代码,编译后由系统中的处理器来执行。定向测试的一个好处是为硅后测试提供可复用的测试代码。
6. 集成的环境
分析完激励的原则和检查的方法后,验证平台(testbench)的核心要素就大致齐备了。接下来将进一步分析验证环境的集成需要考虑的因素,并梳理各部分之间的关系。从图2.12可以看出,验证集成环境分为:
- 验证平台(verification platform);
- 待验设计(design under verification);
- 运行环境(runtime environment);
- 验证管理(verification management)。

6.1 验证平台
验证平台是验证人员日常工作的对象。在建立或复用验证框架时,主要考虑激励分类和检查方法两部分,这两部分直接影响验证平台的框架。
激励分为如下两类:
- 定向激励。一般通过文本激励、C代码激励、预先生成激励码等形式给入。
- 随机激励。通过随机约束给入激励,这里的随机方式不局限于SV,也包括其他随机验证语言,或者利用脚本语言来产生随机激励。
检查一般分为如下三类:
- 线上检查(online check)。在仿真的过程中动态比对数据,并给出比较结果。
- 线下检查(offline check)。在仿真结束后比对仿真中收集的数据,给出比较结果。
- 断言检查(assertion check)。通过仿真或形式验证的方式利用断言检查设计的功能点。
6.2 待验设计
根据功能描述的建模方式,硬件设计可以分为两类:
- HDL 硬件模型。即,使用 HDL 语言描述的硬件模型,按照硬件层次可以分为RTL和网表。该模型的特点是与硬件设计师距离最近,也是最贴合硬件逻辑行为的模型。
- 虚拟原型(virtual prototype)。在硬件定义的早期阶段,引入虚拟原型对硬件的框架和性能进行评估。在数字信号处理模块中需要复杂的算法参与,所以在硬件实现之前,可以采用软件算法模型来代替硬件的功能(不考虑时序替代)。常用的虚拟原型语言包括 SystemC、C/C++、MATLAB等。
仿真过程中可以将 HDL 硬件模型与虚拟原型混合,进行联合仿真。这时,需要考虑虚拟原型的接口是否可以在硬件仿真环境中较为方便地集成,以及是否有对虚拟原型的接口时序的要求。
6.3 运行环境
运行环境的主要职责是将验证平台和待验设计进行融合(软件激励端和硬件模型端的互动)。
根据前面对验证平台和待验设计的分类,运行环境需要考虑的因素有:
- 验证平台。运行环境需要传入参数,根据测试场景选择测试序列、随机种子数值、参数化的环境结构和实例化验证平台。
- 待验模型。除了考虑如何实现HDL硬件模型与虚拟原型在仿真器中协同仿真之外,还需要实现验证平台和待验设计的接口对接,包括硬件信号接口连接和内部信号的接口监测。
- 仿真全流程建立。包括验证和设计的文件提取 (extraction)、文件依赖度分析(dependency analysis)、编译(compilation and elaboration)、仿真(simulation)、结果分析(result analysis)和回归测试(regression test)等。全流程的建立一般是由环境建设者(environment builder)通过脚本(script)语言来做管理的,用于仿真流程建立的常见脚本语言包括Shell、Makefile、Perl、Tcl、Python等。
6.4 验证管理
无论芯片的尺寸有多大,验证人员和验证经理都需要对自己负责的模块或芯片做量化的验证管理。除常见的 Excel 表格管理外,也可使用其他验证管理工具进行管理。这些验证管理工具需要考虑的因素有:
- 验证计划和进度管理(verification plan and progress management)。验证计划需要将抽象内容与量化后的测试用例、功能覆盖率相对应,进而给出可视化的验证进度。
- 文件版本控制管理(file version control management)。文件版本控制在团队协作中几乎是必需品,常见的工具有SVN、Git、Clearcase等。
- 项目环境配置管理(project environment configuration management)。项目环境的配置文件不但包括项目中使用的各种工具的版本、单元库的版本、验证IP的版本,也包括验证环境的顶层配置。通过这些环境配置管理,每一个参与项目的人都可以很快地实现环境配置,省去同步验证环境的工作。
- 缺陷率跟踪管理(defect tracking management)。之前提到的缺陷率曲线,需要验证人员和验证经理保持记录的习惯,除了通过缺陷率曲线衡量验证的进度,还需要通过记录跟踪缺陷的修复、后续验证的工作。
足够稳定的验证平台,能够在更早期利用不同抽象级别的待验模型展开验证环境的搭建和验证工作。通过模块化和自动化的运行方式,实现环境的从建立到检查,一个完善的验证环境能够给验证人员助力不少。从项目管理的角度,也需要一个完善的工具(可能是几个工具共同协助)帮助我们完成验证管理,最终达到验证目标。
7. 作者结束语
笔者还记得接手的第一个验证环境是Specman eRM结构,当时苦于缺少合适的文档,很多代码和工具的使用让我吃了不少的苦头。经历了OVM的验证阶段后,再回头思考eRM,发现验证的思想有很多是可以借鉴和优化的。就是说,尽管我们验证者(Verifier)需要用更多的时间去适应变化更快的验证技术和工具,但在这背后都是逐步继承的关系。你学习到的知识,多年以后有一部分将被替代,而核心的部分还将被保留下来。本章关于验证策略的通识,就是属于那些不会被替代的部分。你会在任何一种验证语言或者验证方法学中捕捉到与验证策略有关的信息,因为它是验证作为一种综合技术能力的基础。
芯片验证漫游指南2验证的策略