芯片验证的策略篇(作者良心大作,验证必看)
本文分六个部分:
验证的策略篇之一:设计的流程
验证的策略篇之二:验证的层次
验证的策略篇之三:验证的透明度
验证的策略篇之四:激励的原则
验证的策略篇之五:检查的方法
验证的策略篇之六:集成的环境
验证的策略篇之一:设计的流程
我们在上一章芯片 芯片验证全视 中给出过芯片产品开发的流程图,而在描述中我们将开发流程分为了两条主线:
芯片功能的细分
不同人员的任务分配
即是说不同人员需要在硅前的不同阶段实现和测试芯片的模块功能。
如果我们从另外一个角度看,芯片的开发即是将抽象级别逐次降低的过程,从一开始的抽象自然语言描述到硬件的HDL语言描述再到最后的门级网表。而在我们已经介绍过RTL设计和门级网表以后,这里需要引入一个目前更高抽象级的描述TLM(事务级模型,transaction level models)。
TLM一般会在早期用于构建硬件的行为,侧重于它的功能描述,不需要在意时序。同时各个TLM模型也会被集成为一个系统,用来评估系统的整体性能和模块之间的交互。同时TLM模型在早期的设计和验证中,如果足够准确的话,甚至可以替代验证人员的参考模型,一方面为硬件设计提供了可以参考的设计(来源于系统描述侧),一方面也加速了验证(无需再构建参考模型,而且TLM模型足够准确反映硬件描述)。
TLM模型的需求和ESL开发
早期的芯片开发模式是遵循先从系统结构设计、到芯片设计制造、再到上层软件开发的。但随着产品开发的压力,一方面我们需要让系统人员、硬件人员和软件人员都保持着充沛的工作量,同时对于一个芯片项目而言,我们也希望硬件人员和软件人员可以尽可能的同时进行开发。这听起来怎么可能?毕竟芯片还没有制造出来,没有开发板怎么去构建软件呢?在这里我们系统结构人员会在早期构建一个高抽象级的系统,同时该系统必须具备该有的基本功能和各模块的接口保持信息交互,通过将功能描述变成可运行的系统,让硬件人员和软件人员可以在早期就利用该系统进行硬件参照和软件开发。这种可以为复杂系统建立模型,让多个流程分支并行开发的方式被称作ESL(电子系统级,electronic system-level)开发。
传统的系统设计流程
传统的系统设流程是瀑布形式(waterfall)开发的,这种顺序开发的方式存在明显的边界:
时间边界:不同的开发子过程之间是保持顺序执行的,几乎没有可以交叠的空间来缩短整体的项目交付时间。
组织边界:不同的开发小组之间的交流是计划是发生在前一个过程结束,后一个过程开始的,这也引入了额外的沟通成本。
ESL系统设计流程
为了模糊或者融合这种边界,ESL开发流程通过建立虚拟原型(virtual prototype),又或者称之为TLM模型来使得整个参与到系统开发的小组做并行开发。之所以可以有这种魔力,是因为TLM模型不再是一种无法被硬件开发和软件开发利用的抽象描述,而是一种更早期开发的软件模型。所以在ESL开发的协助下,更多的自开发流程可以更早跟随系统设计一块进行开发,那么从整体上来看这种方式有助于缩短芯片开发的时间。
除此之外,在前期产品定义的阶段有相对可量化的模型,更有助于早期验证产品的功能、性能是否满足客户要求,也能减轻一些低配置性能的风险和降低过多设计的成本。这是为什么呢?有以下几点:
在早期定义产品的时候,市场部会将客户对于产品特性收集回来,而交由系统框架师来定义芯片结构。这中间会存在一些问题,例如系统框架师无法深入到局部功能更无从列举出所有的用例来判断功能是否满足,而对于性能测试方面也只能通过一些表格化数据做出静态估计。这时候,TLM模型可以帮助在系统级别完成模型搭建和虚拟系统集成,甚至帮助测算系统的性能,这对于系统框架师而言会有更多的信心来给出合理的结构配置。
正由于可以在早期做出性能评估(而且快速、发生在芯片结构的定义阶段),框架师可以及时地做出资源调整来满足用户的需求。否则,尽管芯片可能是低缺陷率的,但如果它的执行速度不够快、功耗又过高,那么也仍然无法满足客户的要求。
过度设计的结构就跟给一只袜子缀上水钻一样不差但也没有必要。客户给的报价摆在那里,你的设计越过度,不但意味着成本的增长,也意外着更高的复杂度和风险。
ESL和TLM对系统模型的要求使得需要有一门语言可以:
纵深多个抽象级别来进行模型描述
标准开放的语言
高效的仿真性能和调试接口
被主流仿真工具支持
本身包含TLM事务级传输的接口
这样的一本语言即是我们接下来介绍的SystemC。
SystemC介绍
SystemC是可以满足TLM模型开发的一种语言。严格来讲,它本身不是一种语言,而是建立在C++之上的一种类库(class library)。SystemC语言可以用来描述系统级别的硬件行为,而这一点恰是其它语言无法满足的。SystemC从2006年被IEEE收入IEEE 1666标准,它本身也易于学习,对于有C++/Java基础和硬件设计概念的人使用起来都不需要太多的学习成本。SystemC语言介绍不在本章的重点,所以我们略去它的更多的语言特性介绍。
语言的抽象级比较
而不同的硬件领域使用到的建模语言都有它们各自适合的抽象级,下图就指出了各个语言擅长的抽象级领域。从左至右,VHDL和Verilog主要用作RTL仿真和数字电路的综合,它们也用来在早期搭建一些验证平台。对于SystemVerilog/Vera/e是用来做功能验证语言的,这其中也包括了它们的随机约束重要特性。同时我们也可以发现SystemVerilog本身可以用来描述硬件做RTL仿真和门级综合。在此之上,SystemC关注的地方要更偏向于系统层,它在结构层面上可以做更高抽象级的描述,而本身也无法去描述电路的综合网表,但它能够以自己为平台为上层的软件开发做准备。而Matlab和其它语言被用在了数字信号处理上面用来描述和验证算法。
传统的系统集成视角
我们前面已经提到,传统的瀑布开发模型无法让硬件人员和软件人员在系统结构定义早期就进入。对于硬件的设计人员和验证人员而言他们都需要等待系统定义完成之后将功能描述文档分别做出自己的翻译来建立可综合模型和参考模型。软件人员只有在硬件流片以后才会真正开始进行软件开发,尽管目前的FPGA有着比硬件更快的仿真优势,但无论从时间段(硬件设计的后期)还是从速度(相比软件模型)而言,仍然不是理想的软件开发平台。我们可以认为FPGA等硬件加速工具对于硅后系统测试有积极意义,但是对于更上层的软件层开发的帮助则并没有那么明显。
ESL系统集成视角
新型的ESL系统开发方式会在系统定义阶段就建立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建模。例如下面这张图,由于系统人员的技能限制,项目开发需要额外引入虚拟建模团队。此外,由于地域上的限制,虚拟建模团队主要服务的对象是软件开发一侧,而他们与硬件设计、验证团队的沟通较少。这种类ESL的开发可能有多种组合,但我们需要警惕的是,在方便软件开发早期进入项目时,TLM模型是否会同系统定义保持绝对的一致性,从而为硬件和软件方提供文本和代码参考。
从图上来看,这种类ESL的方式是存在风险的,因为虚拟建模团队从系统定义到TLM模型的过程就存在二次翻译,如果翻译不准确,存在疏漏,那么可以想象基于TLM模型的软件开发不会那么容易被移植到真正的硬件系统上面,因为硬件本身也是二次翻译。所以,理想的合作边界应该如下图一样。虚拟建模首先需要和系统定义保持原义的一致性,而硬件和软件则可以将TLM模型视为功能描述的一致性翻译,然后各自在TLM模型上进行开发。
下篇文章我们将会探讨《验证的层次》,以及在各个层次上面的验证方法。
验证的策略篇之二:验证的层次
从系统定义阶段开始,我们就会将芯片系统划分为子系统,进而又为每个子系统划分为不同的功能模块,直到划分为复杂度合适的模块。而到了设计阶段,我们又会按照自底向上的方式开始做硬件设计和集成。从定义阶段到设计阶段再到后端部分,我们整个硅前的流程都是将芯片按照层次划分的,一般我们称之为芯片系统级(chip level/system level)、子系统级(sub-system level)和模块级(module level/unit level),这种层次划分的方式对于芯片的好处有哪些呢?
便于拆解功能模块,实现人员的并行工作协同,这一点是从项目执行效率出发的。
对于系统定义而言,这是从主要的功能、性能要求量化为系统不同模块定义的方法。
从设计和验证角度出发,合适的复杂度模块也有助于估计合适的工作量和人员分配。设计最终是通过模块化来集成的,而验证的环境在模块化以后,也可以方便在更高级的验证环境中复用。
对于后端,在进行了合理的区域划分后,模块和SoC可以并行进行后续的物理设计流程,在每个设计阶段再合成进行相关电源设计、时序分析等设计项检查。基于模块化的设计最终再进行SoC级别的设计检查并通过流片要求。
如果我们是在为一款手机设计通讯芯片,那么如图显示,一开始系统定义阶段可能要规划出来这么多的功能模块,而且还需要考虑模块的性能因素。每一款芯片都会包括多个子系统,而每个系统也会包含多个功能模块,从举的这款手机通讯芯片来看,他包括的功能子系统有:
处理器子系统
协处理器系统
本地存储系统
外部存储控制器系统
数据接口系统
系统模块外设
多媒体子系统
调制解调子系统
对于核心模块调制解调子系统来看,其中的2G/3G/4G又因为自身的复杂性依次提高,可以进一步作为独立的子系统来对待,进而细分下去。所以,如何划分层次,我们一般会从如下几个角度考虑:
系统的复杂性:如果该系统相对独立,那么它自身有作为子系统的条件。如果它本身任然过于复杂,可以进一步细分。
芯片集成的便利性:对于顶层芯片继承而言,应该一个合适子系统应该与外界应该有清晰的功能边界,例如系统信号边界、标准总线边界、与其它子系统交互的边界,同时这些信号边界也尽可能保持稳定和精简,这是从顶层集成的工作量和后端布局布线的角度出发的。
验证的阶段:验证人员需要清楚哪些功能点在模块级验证、哪些属于子系统和芯片系统、是否有必要在不同级别重复验证、最终各个层次是否会保证验证完备性。
后端的流程:如果一个子系统占到了芯片整体面积的10%以上,那么后端就没有理由不考虑将其单独做综合,因为这样子系统综合有助于后期整个芯片综合的收敛速度。
在这里,我们将主要从验证的角度来考虑,如何选择合适的验证层次到下面不同的验证环境中:
模块级(block level/unit level)
子系统级(sub-system level)
芯片系统级(chip level)
硅后系统级(post-silicon system level)
模块级
如果是图中的处理器子系统,我们会考虑先将DMA(direct memory access)、cache缓存、和core0/core1分别展开模块验证。每个模块验证首先要考虑的是哪些功能点是可以在模块一级完全验证的,这基于如下考虑:
内部功能如状态机验证
内部数据存储验证
数据打包功能、编解码功能
指令执行
寄存器配置
同时我们也需要考虑哪些功能无法在模块一级被验证到:
与其它相邻模块的互动信号
与其它子系统的互动信号
与芯片外部的互动信号
与电源开关的验证
这些部分我们需要考虑在更高的层次来验证他们。
子系统级
对于一个成熟的子系统而言,它既拥有完备的功能可以执行专门的任务,也有足够稳定的接口用来在更高级做集成。与模块相比,子系统更稳定也更封闭,这对顶层集成是有好处的。也正是这种便于集成相对封闭的特征,我们可以从公司外部或者内部得到不同的子系统。合格的子系统交付不单单包含着它的设计部分,也应该包括如下:
设计包
验证包
递归测试表
覆盖率收集脚本和数据
完整的文档(设计、验证、集成、后端)
完备的交付才会增强顶层集成的信任,同时减少在集成过程中发生的一些接口理解分歧和参数化配置问题。
那么单就验证而言,除了充分验证内部功能以外,对于子系统的外部接口如果存在参数或者编译预处理(compiler directive)时,验证人员需要就这些参数和不同的编译选项(可能因此产生不同的硬件结构功能)给出完备验证。因为从子系统的封闭性和复用性来看,它们会在多个芯片项目中被使用,这对于设计复用来讲是一件好事,而验证也需要将验证环境参数化来适应硬件的参数化配置。只有充分验证了参数化的子系统,才可能让它在不同的芯片项目中都能够按照预期实现它的功能。
对于验证管理而言,子系统验证也是一个理想的可以切分的单元,因为这一层下面的模块之间互动很多,而这一层本身又趋于封闭,也即是与外围的接口有限,所以便于在子系统层建立验证小组——包产到组。
芯片系统级
在芯片系统级,我们的验证平台的复用性较高,这主要是因为
外围的验证组件不需要像模块级、子系统级的组件数量多且经常需要更新,它们主要侧重于验证芯片的输入输出
芯片内部的子系统之间的交互、协作检查主要交给了处理器和子系统,从寄存器检查和数据检查入手,写直接测试(directed test)用例
在芯片系统级的验证侧重于不同子系统之间的信号交互问题,以及实现更贴近实际使用的用例。这里的实际用例并非是在系统软件层面的,而是将系统软件层面的场景进一步拆分为多个模块互动情景,再分类测试的。
硅后系统级
尽管我们硅前验证部分与硅后系统软件开发联系较少,但是如果可以尽早将硅后软件开发的实际用例在硅前测试,也能够可以发现一些实际使用中的问题。实际上,系统软件用例和硅前的随机测试有着互补的特性,对于功能验证上面缺陷的理解是,如果没有被硅后测试、软件开发、用户使用的过程中发现,那么隐藏的缺陷也会永远静静躺在那里,也许永远也不会被发现(没有零缺陷的芯片,却有用户没有发现缺陷的芯片)。所以,如果可以将硅后的驱动、固件和系统软件尽早在硅前就引入验证过程的话,这可以跟硅前的验证方法形成互补,使得验证更加完善。
我们上面介绍了验证的四个阶段,也给出了他们各自使用的测试场景。这里我们再给出几点可以遵循的原则帮助大家选择适合的级别来进行验证:
如果更低的级别可以完成某一项功能验证,那么就不要在更层去验证它。因为更小的验证环境更有利于控制激励场景的产生,更加全面的覆盖功能点。
如果低层次已经充分验证过某一项功能,那么高层次不需要重复验证。而对于低层次无法完全覆盖功能点验证的时候,应该在高层次完全覆盖。
如果是低层次的验证阶段,应该适当地考虑高层次的测试用例,而在低层次创造一些条件来进行模拟发生。
如果是高层次的验证阶段,验证环境中的参考模型、数据比对、监视器等模块首先考虑从低层次环境复用,在无法满足的情况下再考虑重新构建。
如果是新的模块或者新的功能,应该投入更多的精力和优先级在不同层次充分验证。
最后,我们通过一张列表来更好的理解,不同的验证层次的验证侧重、性能、使用方法:
懂得选择一个合适的验证层次,并且通过在不同层次分配不同的功能验证点,是最终迈向验证完备性的一项必备技能。
下一篇文章我们将来讨论《验证的透明度》,来看看一名验证师采取黑盒、白盒及灰盒测试的场景依据是什么。
验证的策略篇之三:验证的透明度
我们可以按照激励的生成方式和检查的功能点分布将验证划分为三种基本方式:
黑盒验证
白盒验证
灰盒验证
接下来,我们逐个解释这三种不同透明度的验证特征。
黑盒验证
如果验证人员对于设计的细节缺乏认识,那么黑盒验证是一种合适的方式。因为验证环境只需要将激励给入设计的外部接口,而检查设计的另一侧输出就够了。而测试的成功与否只是根据一个输入是否得到一个正确的输出,验证环境本身不会关注设计的内部。
从上面这张图可以看到,激励生成器(stimulator)只负责给设计灌入激励,监视器(monitor)和检查器(checker)也只查看和比较输出信号。
黑盒验证的一个缺点是它缺少设计的透明度和激励的可控度,而由此带来的一些问题包括:
当测试失败以后,无法更深层次地定位问题。对于验证人员而言,他只能判断是测试是否成功,但无法将报告进一步定位到缺陷所在的位置,与设计人员完成深度协作。
这种方式对于发现一些较深的缺陷比较困难,因为验证人员无法根据设计本身给出更窄的随机约束来定向生成一些激励。同时,这也对设计内部功能点的功能覆盖率收敛没有太多的帮助。
当一些设计的接口采用标准接口时,图中的激励生成器或者总线功能模型可以使用复用性高的验证IP。这些验证IP一般由第三方公司提供,有时候公司内部也有这样的IP,它们的特点是像标准接口一样易于在验证环境中插拔,易于控制且接口时序严格按照总线文档定义。同时对于监测器而言,它也来自于验证IP,这也减少了验证人员的工作量。所以当模块的接口标准化时,验证环境也可以复用一些验证IP。
由于黑盒验证本身不包含设计的内部逻辑信息,所以当设计由于缺陷做了更新或者添加新的特性之后,原有的测试列表仍然较稳定,验证人员只需要对新添加的特性考虑新的测试场景。黑盒验证有利于保持测试环境的稳定,在后续项目中一旦更新了设计,新的验证人员也只需要很少的力气来维护继承的验证环境。
白盒验证
相比黑盒验证,白盒验证可以弥补一些不足。由于验证人员了解设计的内部工作逻辑、层次、信号等,他就可以对更底层的设计细节进行测试。由于对于设计中各种组件和逻辑细节都了如指掌,这种验证方式可以验证设计是否更严格地遵循功能描述文档,而且一旦测试发生失败也可以更快速地定位到缺陷。
对于白盒验证的环境,我们的参考模型逻辑非常简略,甚至不需要参考模型,而只需要植入监视器和断言来检查各个内部逻辑。这种环境配置背后的原则是说,当我们充分检查各个逻辑驱动和结构以后,就不需要测试它的整体功能,因为白盒验证是穷举逻辑路径的方法。
使用白盒验证也面临一些方法学上的缺陷:
由于本身专注于设计内部逻辑检查而忽略整体功能的测试,在设计本身违反规范的情况下,白盒验证难以发现缺陷。
在数据一致性检查的方面,白盒验证难以从整体入手给出实际测试用例。
由于白盒验证的测试用例很多都是从设计的细节入手,所以一旦设计发生更新,那么对于验证环境的维护成本就偏高。这一点在项目间复用方面带来的影响更多,甚至新接手验证环境的人要付出很大的成本去理解设计细节和验证环境的细节。这时候白盒验证环境的低复用性缺点就暴露出来了。
灰盒验证
从黑盒验证和白盒验证来看,他们各自都有着优势和劣势。在实际验证中,我们更倾向于将黑盒和白盒两种方法掺杂在一起,让监视器、断言、参考模型一同来完善验证。这种糅合的方式带来的好处包括:
监视器和断言可以有着更好的透明度来着重检查设计的一些重要内部逻辑。
参考模型由于已经有了断言检查局部逻辑的帮助,会减少很大一部分精确度,而主要专注在输入和输出的数据比较上。
而从复用性角度考虑,灰盒验证也有着灵活地变动方式:
对于新的设计,我们的验证人员需要更深入地理解设计本身,而采用灰盒验证一开始通过监视器和断言来进行局部验证。待设计初步完善和趋向稳定时,验证人员此时也有了对设计更全局的理解来构建参考模型。又因为前期监视器和断言保证局部逻辑的正确,那么参考模型的构建不需要完全精确,只需要较少的精力来实现。
如果该设计移植到别的项目,尽管难免设计需要进行局部修改,对于验证环境而言,灰盒验证的复用性优势就体现出来了。即便是新的验证人员接手这个验证环境,好的灰盒环境也可以清晰地将黑盒和白盒的部分划分开来。在设计本身复用的项目中,我们首先建议打开黑盒开关,这对于新的验证人员来讲是较低的测试成本,也无需对设计和验证环境了解太多。同时,这么做也可以第一时间保证原有功能的稳定性,并反馈给设计人员新的改动造成的影响。紧接着,应该验证人员可以针对新的特性创建特定的黑盒测试序列,由于设计本身的稳定,新的黑盒测试序列不需要关注与设计内部太多。
当黑盒环节进行完毕以后,我们可以在时间允许的情况下,有序引入白盒的开关。首先,我们应该考虑先创建新的白盒断言点或者是功能检查点,来专注在新的功能部分。其次,在完成了新的功能点白盒覆盖以后,我们再考虑逐个打开原有的白盒功能检查开关,逐次打开白盒检查开关的方式我们也遵循着每次添加尽可能少的开关来跑递归测试,这便于发现问题以后可以快速定位到新打开的开关一侧;并且,白盒检查点的开关也有逻辑重要性的优先级,如果之前验证人员足够专业的话,在他的代码或者文档中也应该对这些不同白盒开关给出说明和重要性的排列,这种说明会有助于新的验证人员先开高优先级的开关,而依次按照优先级的降序来打开不同的开关。
所以灰盒验证不但可以继承黑盒验证和白盒验证的优势,同时也对于验证环境在新项目中的复用有着明显优势。
最后,我们列出一个信息比较表来量化总结黑盒验证、白盒验证和灰盒验证的特点。从上面的语言描述和下面列表的归纳我们可以一致得出,灰盒验证方式可以最好平衡新设计验证和稳定设计验证复用的工作量。如果在新设计验证阶段,验证人员有着充分的经验来实现灰盒验证环境,将黑盒部分和白盒部分清晰划分,考虑到日后其它验证人员的接手和环境复用,那么这样的稳定设计伴随着复用性高的环境,在今后更多的项目中也能证明该设计包的复用度。在高度集成化的今天,我们衡量一个设计复用性的优良,是从整个设计交付包(design delivery package)衡量的,这包括了我们之前提到过的设计包、验证包、文档包、递归测试包和覆盖率包等等。
所以无论对于设计人员还是验证人员也都需要从各自的角度考虑复用性,将设计的整个流程全盘考虑,也只有可以让使用设计交付包的项目可以有更好的“用户体验”,缩短集成时间(设计和验证),才是好的设计和验证环境。
下一篇内容我们将进入验证的环境部分,首先认识《激励的原则》。
验证的策略篇之四:激励的原则
我们这篇文章《激励的原则》实际上就是解决一件事情,那就是如何保证激励源最大的自由度?因为只有从环境结构上保证了激励源最大的自由度,我们才有可能在输入一侧提供更丰富的输入组合,有条件地穷历一些测试序列。
按照这个核心原则,我们可以将激励的自由度按照如下去考虑评估:
接口类型
序列颗粒度
可控性
组件独立性
组合自由度
我们一一来看激励在这些方面的表现和评估方法。
接口类型(interface type)
首先面对一个设计的输入接口,我们可以先判断接口的类型,如果该设计的接口类型复杂多样,我们也可以通过接口类型划分,化繁为简找到从哪里下手来给出激励。常见的接口类型可以分为如下几种:
系统控制接口(system control interface):常见的有时钟、复位、安全、电源开关和这些系统控制信号旁生出来的控制信号,例如时钟门控信号(clock gating signal)。
标准总线接口(standard bus interface):公开的行业标准总线协议,常见的有例如AMBA总线系列协议、OCP、SRAM、MIPI系列协议等,文档详细,有丰富的验证IP提供服务。
非标准总线接口(non-standard bus interface):公司内部定义的接口,或者根据模块功能需求定义的接口,接口时序相对简单,同时文档也较粗略。公司内部即使有可复用的验证IP,验证IP本身也没有得到充分的质量保证,而且在非标准协议和总线多样性定制的背景中,验证IP的复用性较低。
测试接口(test interface):该测试接口主要留给可测性(DFT,design for test)功能使用,在功能验证中我们只需要禁用即可。
其它控制接口(miscellaneous control interface):如果被测设计是处于子系统中的功能模块且与相邻多个模块交互的话,那么该控制接口信号的数量较多、功能较分散还有较高的复杂度;如果该设计是子系统,那么子系统从标准复用的设计角度出发,该种类型的控制接口数量会较少且功能也较集中。
有了清晰的分类,验证人员就好入手根据不同的接口类型来选择验证IP或者自己着手搭建激励组件(verification component)了。
序列颗粒度(sequence granularity)
对于不同的接口我们会引入不同的验证组件,每个验证组件一般都会包含激励生成器(stimulus generator / driver)。激励生成器会提供一些基本功能方法用来生成小颗粒度的激励,同时用户也可以进一步做上层封装,来从更高抽象级的角度生成大颗粒度或者是宏颗粒度的激励序列。我们按照软件的层概念将激励序列颗粒度划分为如下:
基本颗粒层
高级颗粒层
宏颗粒层
用户自定义颗粒层
从激励生成器提供的方法进行继承和封装入手,我们可以将这些颗粒层的关系表达为:
这里我们拿出一个商业总线验证IP举例,该验证IP包会含有基本颗粒层和高级颗粒层来生成不同级别的测试序列,一些情况下验证IP也提供宏颗粒层的定义来满足更高规模的数据传输。这里的抽象级指的是从时序和数据量传输的角度出发,越高的抽象级,越不关注底层的时序而更重视数据量的传输,也是TLM(transaction level model)含义的延伸。当验证人员不能从已有的各种颗粒层中生成自己期望的测试序列时,便会利用已有的基本颗粒层和高级颗粒层来构建自己的颗粒层。
可控性(controllability)
可控性是从对不同颗粒层的控制来出发的,那么按照序列颗粒度的划分,对应的可控性应该是:
从功能验证的周期出发,在初期阶段,我们应该从基本颗粒层中选择,这有利于我们在接口的基本功能中调节和测试不同的总线传输情况,这里的验证点侧重于协议功能和时序检查。随着设计的稳定,我们会逐渐选择高级颗粒层和宏颗粒层,将验证精力逐渐转移到数据量的一致性传输和性能评估上面,而这两层的颗粒控制性也没有像基本颗粒那样可以细致调节到每一个参数变量,它们会同验证重点保持一致,主要提供跟数据量有关的可约束参数。
组件独立性(component independency)
在我们将一个设计的边界信号划分为不同的接口类型,并且创建出对应的接口验证组件之后,我们就应该考虑一下各个组件之间的独立性了。组件的独立性实际上也是协调性的基本保障,因为有了独立性,各个组件之间才会最大程度地不受另外组件的制约,同时又可以通过有效的通讯机制来实现组件之间的协调。我们接下来看看,要实现组件独立性需要考虑的因素:
必须按照接口类型来划分组件
对于系统控制信号组件,尽可能将信号的关系按照实际集成关系做控制,例如多个时钟是否是同步关系,多个复位信号是否可以单独控制等。
对于总线接口(标准或者非标准),实现一对一的控制关系。例如存在两组相同的总线,那么我们应该引入两个总线组件分别控制,而非建立一个总线组件却拥有两套总线接口,后者对于有悖于可控性和复用性。
对于其他控制接口,应从实际相邻设计那里准确了解各个信号的使能极性、脉冲有效还是电平有效、是否存在握手关系、时序信息等真实的设计信息,来给予日后更高层集成环境中的控制场景。同时又由于这部分信号偏于杂乱,在尽可能梳理信号的不同来源和功能以后,也需要在接口组件中定义封装好的方法来实现灵活的驱动。
验证环境中的系统控制信号组件也会跟其他接口组件发生连接,例如提供必要的时钟和复位信息,那么这些连接也应该遵循实际集成的情况,确保组件驱动端的时钟输入与设计的时钟输入端保持同步。
组合自由度(combination space)
最后一个衡量因素是对上述因素的整体评估,只有通过底层的精细划分,进而建立抽象级更高的颗粒度,通过独立组件之间的协调来给出激励,才会提供较高的组合自由度。在这里,除过组件的独立性以外,我们也需要考虑组件之间的协调方式。一般我们将协调方式分为两种:
中心统筹式(centrally organized):通过中心的调遣段统一分派给各个接口组件不同的任务,进而产生不同的激励组合场景。
分布事件驱动式(distributed event driven):将激励控制权交给各个接口组件,而通过接口组件之间的通信来实现分布式的事件驱动模式,即组件之间的通讯通过事件(event)、信箱(mailbox)、接口信号(interface signal)等方式实现同步通信。
通过上述因素,我们可以评定出一个验证环境中各个接口组件之间的组合是否可以提供足够的自由度,最终有可能穷历出预定的激励序列。
我们下一篇内容将带大家认识各种《检查的方法》。只有检查的方法也同样强大,才能在各种可能的激励组合面前保持一颗淡定的心。
验证的策略篇之五:检查的方法
我们在上一节《激励的原则》里面给出了几点用来评估激励自由度的方法,在这篇文中,我们则需要考虑在各种可能的激励组合下,如何选择适当的检查来完成一项核心要素:检查就是查看设计是否按照功能描述做出理应的行为,并且识别所有错误的输出从而发现设计缺陷。
对于激励我们是从接口类型来划分的,那么在检查阶段,我们划分则是基于被检查逻辑的层次,这些层次指的是:
模块的内部设计细节
模块的输入输出
模块与相邻模块的互动信号
模块在芯片系统级的应用角色
对于这些不同的检查层次,我们会考虑采用不同的方法来应对:
从上面这张方法选择列表里我们可以看出,经常使用的方法有监测器(monitor)、断言(assertion,用于仿真)、参考模型(reference model)、比较器(comparator/scoreboard)、直接测试和形式断言等等。至于常用的验证方法和工具,我们会在下一季的《验证的工具篇》给大家详细介绍,在这一篇中我们就主要的方法来看看我们之所以这样划分的考虑是什么。
一般而言,监测器(monitor)是一项必备的组件,有了它方便我们观察内部信号,所以你在各个层次都可以找到监测器的身影,而要查看设计内部信号,另外一个可行的办法是使用SystemVerilog绑定(bind)的特性。由于监测器可能被置入到各种方法中,我们需要本着复用的角度,在构建监测器的时候考虑如下的地方:
监测器一般跟激励发生器的作用域一致。这指的是,如果该激励发生器对应一组总线,那么也应该有一个对应的监视器来负责监视总线的传输。
监测器应根据检查的层次将信号检测分为模块内部和模块边界。
对于断言(assertion),我们主要依靠它检查模块的内部逻辑细节和时序信息。利用断言,我们可以通过仿真或者形式验证来测试对应的逻辑和时序。这里,是否选用仿真或者形式验证的方式,给的建议是:
如果是模块级别,断言通过形式验证完全覆盖设计的多数功能点从效率和完备性来看是可靠的。同时我们建议在子系统或者芯片一级创建基本的测试用例进行仿真,作为形式验证的补充。
如果断言验证的功能点较分散,或者主要是关切于模块的核心逻辑、时序的时候,我们倾向于使用仿真验证,采取灰盒模式,用断言来验证重要设计细节。
如果断言总体可以覆盖模块的所有设计功能部分,采取形式验证或者白盒仿真验证两种方法都是可取的。
参考模型(reference model)的构建除过待测设计本身的尺寸、复杂度以外,也跟验证方法有关。从之前《验证的透明度》来看,白盒验证对于参考模型的要求是最低的,而黑盒验证却会将最多的压力交给参考模型。
就比较器(comparator)而言,它的结构相对简单。一般依靠足够稳定的监测器和准确的参考模型,比较器只需要将检测的待测输出和参考模型的输出做比较,给出充分的比较信息。在测试用例结束的时候可以给出自定义的测试报告即可。
当模块完成了模块测试、子系统测试,迁入到芯片系统级测试以后,我们会在系统一级复用监测器和断言。这些从低层次复用来的监测器和断言在上层测试中主要覆盖于其它模块互动的功能点。同时由于在系统测试中,我们会采用直接测试从实际系统应用场景出发,写C或者汇编代码由系统中的处理器来执行。直接测试的另外一个好处是可以为硅后测试提供可以复用的测试代码。
在我们分析了检查的方法之后,我们下一篇文章会给大家总览《集成的环境》,看看常用的验证环境的构成。
验证的策略篇之六:集成的环境
在分析完激励的原则的检查的方法之后,我们关于验证平台(testbench)的核心要素就大致齐备了。接下来我们将进一步分析整个验证集成环境要考虑的部分有哪些,通过这些分析了解这些部分之间的关系。从下面这张图我们将验证集成环境分为了:
验证平台(verification platform)
运行环境(runtime environment)
待验设计(design under verification)
验证管理(verification management)
验证平台
验证平台是验证人员日常工作的对象,在建立或者复用验证框架时,主要从激励分类和检查方法两部分考虑,这两部分会直接影响验证的框架。
激励分类
直接激励:一般通过文本激励、C代码激励、预先生成激励码等形式给入测试激励。
随机激励:通过约束随机给入测试激励,这里的随机方式不局限于SV,也包括其他随机验证语言,或者脚本语言用来的随机生成。
检查分类
线上检查(online check):在仿真的过程中动态比对数据,并且给出比较结果信息。
线下检查(offline check):在仿真结束之后将仿真中收集的数据进行比对,再给出比较结果。
断言检查(assertion check):可以通过仿真或者形式验证的方式利用断言检查设计的功能点。
待验设计
硬件设计根据功能描述的定义阶段和功能划分,可以分为两个部分:
HDL硬件模型:即使用HDL语言描述的硬件模型,按照硬件层次划分可以分为RTL和网表。该模型的特定是与硬件设计师距离最近,也是最贴合硬件逻辑行为的模型。
虚拟原型(virtual prototye):在硬件定义的早期阶段,我们会引入虚拟原型来对硬件的框架和性能进行评估。同时,在数字信号处理模块中,需要较为复杂的算法参与,所以在硬件实施之前,我们也可以采用软件算法模型来代替硬件的功能(不考虑时序替代)。常用的虚拟原型语言包括SystemC、C/C++、Matlab等。
在仿真过程中,我们也可以将HDL硬件模型与虚拟原型组合进行联合仿真,这时需要考虑虚拟原型的接口是否可以较为方便地在硬件环境中集成,以及是否对虚拟原型的接口时序有要求。
运行环境
运行环境的主要职责是将验证平台和待验设计做一个融合(软件激励端和硬件模型端的互动),而根据上面验证平台和待验设计的分类,运行环境需要考虑的因素有:
验证平台:运行环境需要传入参数来实现根据测试场景来选择特定测试序列、随机种子数值、参数化验证环境的结构、实例化验证平台等跟所有跟通过运行参数来控制验证平台的因素。
待验模型:除了考虑如何实现HDL硬件模型与虚拟原型在仿真器中协同仿真之外,还需要实现验证平台和待验设计的接口对接,这包括硬件信号接口连接和内部信号的接口连接。
仿真全流程建立:这包括了验证和设计的文件提取(extraction)、文件依赖度分析(dependancy analysis)、编译(compilation and elaboration)、仿真(simulation)、结果分析(result analysis)和递归测试(regression test)等。全流程的建立一般是由环境建设者(environment builder)通过脚本(script)语言来做管理的,常见的用于仿真流程建立脚本语言包括Shell、Makefile、Perl、Tcl、Python等。
验证管理
无论我们芯片的尺寸有多大,作为验证人员和验证经理都需要对自己负责的模块或者芯片做一个量化的验证管理,除了常见的Excel表格管理之外,我们也会通过其它验证管理工具来进行管理。这些验证管理工具需要考虑的因素有:
验证计划和进度管理(verification plan and progress management):验证计划需要从抽象文字一级与量化后的测试用例、功能覆盖率对应,进而给出一个可视化的验证进度。
文件版本控制管理(file version control management):文本版本控制在团队协作中几乎是必需品,常见的工具有SVN、Git、Clearcase。
项目环境配置管理(project environment configuration management):项目环境的配置文件不但包括有项目中使用的各种工具的版本、单元库的版本、验证IP的版本,也包括验证环境的顶层配置,通过这些环境配置管理每一个参与到项目的人可以很快地实现环境配置,省去同步协调验证环境的工作。
缺陷率跟踪管理(defect tracking management):在之前提到的缺陷率曲线,需要验证人员和验证经理保持记录的习惯,除了可以通过缺陷率曲衡量验证的进度,还需要通过记录的缺陷来跟踪缺陷的修复、后续验证的工作。
有了足够稳定的验证平台,能够在更早期利用不同抽象级别的待验模型展开验证环境的搭建和设计验证,通过模块化自动化的运行环境实现环境从建立到检查,这是一个完善的验证环境能够提供给验证人员的。在此之上,从项目管理角度出发,我们也需要一个完善的工具(可能是几个工具共同协助)帮助我们完成验证管理,最终可以达到验证的目标。
到这里,我们的《验证的策略篇》就介绍完了。从下一季开始,我们将进入《验证的方法篇》,将结合常用的验证方法、运行环境、验证管理等验证构成的要素来谈一谈贯穿验证周期内的工具。
(完)
来源:路科验证(微信订阅号ROCKER-IC) 及 EETOP BLOG
关注微信号eetop-1,回复以下红色关键词,阅读推荐文章:
YZ01:验证漫谈
YZ02:UVM/OVM中的factory应用总结-1
YZ03:UVM/OVM中的factory应用总结-2
YZ04:对验证的一些理解
YZ05:浅说DFT 工程师三大法宝的使用
YZ06:DFT去哪儿-可测性设计技术的回顾与展望
YZ07:如何搭建SoC项目的基本Testbench
YZ08:如何测试一颗芯片:全面了解DFT技术(超详细的PPT!)
趣图:
趣图02:
mn01 : 模拟数字产品开发流程
mn02 : 好的模拟IC工程师应该具有的素养
mn03 : 模拟IC设计领域的经典之作
mn04 : 极点零点之我见
mn05 : 六本经典模拟IC书籍精彩评论及总结
mn06 : 模拟设计的100条圣经
mn07 : 模拟电路学习入门的建议
mn08 : 模拟IC流片经验分享
mn09 : 模拟IC年薪几十万师兄的模电学习经历
mn10 : 想成为一名模拟ic设计师在本科期间应该做哪些准备?
mn11 : 模拟电路设计的九重进阶
mn12 : AnalogIC难在哪里,结构?参数?版图?系统?
icsj01 : IC设计完整流程及工具简述
IC芯片设计及生产流程
射频半导体工艺介绍
IC 芯片的成本从哪里来?
icsj02 : 说说芯片设计这点事
icsj03 : 关于IC设计的想法
icsj04 : 数字IC设计的完整流程(非常详细!)
icsj05 : 数字IC Design技术全局观(110页PPT!)
icsj06 : ASIC设计中各个阶段需要注意的问题
icsj07 : 集成电路反向分析的争议性