数字IC验证
关于数字IC的一些验证原则、思路、方法等
第1章 验证的目的
发现Bug,发现所有的Bug,或者证明没有Bug,是验证存在的唯一目的。
不要为了追求完美、高效的环境而沉迷其中,陷阱往往就在美丽的后面。
以SV为例,SV有高层次的语法和结构,能够更大限度发挥激励的控制和Random测试的效率。 想清楚SV能为达成最终的目的带来什么贡献才是关键。
以TC为例,对于一个验证人员,跑通全部TC,意味什么?代码覆盖率100%,意味什么?验证差不多完成? 其实不然,这只相当于验证工作大致完成了90%,实际上剩下10%,才是最艰辛的工作。找到最终剩余的部分进行验证才是最困难的。 也许永远也找不全剩余的10%,只能无限逼近,对于实际使用的现实条件是永远无法精准预测的。
第2章 验证的视角
有多大的视角,就能发现多少的Bug
一个验证人员跑来告诉设计人员,说某某TC Fail了,波形在XXX,请分析。这不是一个合格的验证人员, 真正的验证和设计是不分家的,至少在问题追踪上是这样的。
作为一个出色的验证人员。要对设计代码有一定的了解,即使不能像设计人员那样了解。 当发现一个问题时,应该有能力对设计代码进行追踪,发现问题相关的关键路径。
验证人员必须要懂得代码,懂得分析逻辑,甚至能够通过代码分析出可能的疑点。 更好的,能够理解整个系统的运作,理解前端后端的实现,找出设计人员视角的盲区,才能更好的发现Bug,解决Bug。
验证人员,一定要放开视角,努力去看所能够看到的。而对于验证人员,验证视角的载体就是TC。 一个TC就代表了对设计的视角,而TC对应的波形则是视角真实的体现。很多bug都是可以在波形上去追踪的。
第3章 bug就在那里
从设计之初,就存在着bug。bug静静地躺在那里,不会伪装,不会躲藏,就静静地躺在那里等着被发现。
3.1 表层bug
对于表层的bug,在验证之初最容易发现。而对于深层次的bug,则需要有针对性的去看。 下面这些区域极容易发现bug:
- 只知道跑TC验证人员负责的区域。
- 对实现理解不透彻设计人员负责的区域。
- 负责人变换频繁的区域。
- DFT相关的区域(验证人员对DFT知识缺乏)。
- 规格变化频繁的区域。
- 第一次做顶层连接的区域。
- 时钟域。
- 设计人员认为没有问题的区域(优先级排在后面,可能术语前面提到%10的部分)。
- 技术难度比较高的地方。
- 以前版本曾经发现问题的地方。(人不应该在同样的地方跌倒两次,但是也容易在同样的地方跌倒两次。)
- 协议和时钟转换的地方。
3.2 深层次bug
到这个时候关键部分已经都涉及到了。剩下的都是最最最难找到的bug。有两个经验可以采取:
- 反向思考:经历漫长的验证过程,思路已经固化了,可以多和局外人进行交流。 当然大多数的观点一键都是无用的,但只要出现一条有用的信息,可能就是发现bug的突破口。
- 和谐感觉:这个时候,看起来不靠谱的感觉开始变得靠谱了。拿着架构图,时钟结构图或者打开最复杂测例的波形去看。 曾经看了无数遍的东西再一次被重新拿了起来, 如果发现某个地方存在着些许不和谐的感觉,出现一个突兀的脉冲?信号达到晚了一些?偶可能是bug所在。
第4章 代码检视
代码检视是最容易发现问题的步骤,从写第一行代码开始,到最后一个行结束,都是如此。
设计人员对于自己的设计是极度乐观的、自信的。尤其是在代码刚刚完成的一刹那。同时也是问题最多的时候。
验证人员对代码检视的方法和设计人员不一样。需要编写几个最常用的读写测例,将主要数据通路跑通, 这时候可以不关注设计响应的代码收集。专注于波形代码对照。
- 将所有相关信号抓出来,
- x的,z的都分析一遍。
- 模块间的握手信号,是脉冲握手还是电平握手。
- 大量elseif条件语句相关的信号是重灾区,条件冲突,条件死锁都是有可能的。
- 协议理解有出入的。
第5章 追波形
先看X和Z任何一个波形,无论是验证的前期、中期、后期,到手之后,先刷屏,找X和Z,确认某些Z和X是可以存在的。 例如某些IP模型,或者未初始化的寄存器和RAM,但芯片开始正常后,Z和X,都不应当存在。 Z和X可能导致的BUG:
- IP(包括Memory、PLL、Serdes等等)例化时,某些信号悬空未接。 也许某些模型允许Power信号悬空,或者某些信号是悬空给DFT处理(当下给DFT处理的信号是接零), 但大多数IP,输入信号是不可悬空的。
- 信号位宽不匹配、信号多驱动、声明的信号名称写错、TB级互联错误或TB中遗漏的Force(额外小心隐藏的Force), 不要相信nLint,特别是在芯片顶层或后仿真时序不满足时的X态传递。
- 功能错误,某些模拟IP未能正确操作。
- 功能错误,导致管脚冲突。
- 功能错误,未能合理使用无复位端的寄存器和未初始化的Memory。
再看时钟 在看完X和Z后,要将所有时钟拉到波形中Check:
- 是否所有同步时钟(包括1:N倍频)的时钟沿是否严格对齐。
- CLKEN时钟能够正确将倍频时钟上升沿罩住。
- 关键地方寄存器赋值是否有Delay(如果时钟间不存在∆Delay,寄存器赋值可以没有Delay)
看波形之前的第一步是不要看波形。
要先思考,对照架构图,虚拟一个芯片运转的场景。 即在脑海中想像一下当前这个激励下,波形应当是怎样运作的,激励怎样进入系统,然后怎样完成协议解析和转换, 怎样到达了总线,然后出现DDR的吞吐,然后CPU取指、取数,完成处理。OK,也就是说,先要在心中预留一个完美的Scenario。
心中有了虚构的波形后,再使用Verdi打开波形。抓关键信号,分组,标识不同颜色。
以AMBA总线为例:
- APB先看PADDR、PSEL、PENABLE。
- AHB先看HADDR、HTRANS、HREADY。
- AXI先看各个通道ADDR、VALID、READY。
然后,将展开的波形和脑海中已有的场景进行对照,看数据流是否按照脑海中预期的构想而流动。
在对细节的进一步理解和澄清的过程中,逐步修正心中虚构的波形,使其逐渐接近真实的运作。 当然,在修正过程中,会出现某些确实表现异常的波形,一些明显出乎设计预期的时序出现。 OK,这是一个岔路口,先记录*.rc波形现场,然后对该出乎意料的时序进行进一步深入追溯。
第6章 后仿定位
后仿定位的思路,供参考。
- 还是先关注时钟,每一个模块,clk和clken相位是否正确(PR有时会对时钟取反,需要注意);
- 关注有门控功能的时钟和复位,确认是否存在毛刺;
- 把Top层所有管脚和oen、I、C信号抓出来。除掉红色黄色,观察有特殊变化的oen、i、c信号,尤其是和管脚值不符的,还有毛刺;
- 观察所有异步接口的时序,基本上都会发现毛刺。例如EBI、Efuse,确认毛刺是否影响功能;
- 观察顶层或Subsys独立处理的信号,特别针对还不够成熟的集成人员的特殊设计,例如testmode、rst_out、系统控制器特殊的配置和检测信号;
- 关注跨团队的模块,例如功能可控的Memory BIST;
以上所有这些,MAX和MIN会有不同,要仿真Typical时序,并且确认以上的内容;
后仿的波形,需要结合Log中的Warning一起Check
第7章 出现问题的讨论
发现了bug,或者还不能确定是bug。需要通相关人员进行讨论,讨论也是要讲究技巧的。
口头讨论
对于一些不理解的、模糊的设计点可以直接与设计人员进行口头讨论。这也是使用最普遍的方法。
书面加口头讨论
针对相关人员不在同一办公地点的问题,应先列出一个清单,说明相关情况:
- 问题描述。
- 激励驱动。
- 相关波形(截图或者列出相关信号)。
- 仿真环境副本,便于相关人员直接在本地debug。
- 可能的问题所在(这建立在验证人员对设计有一定的熟悉)。
在有了问题说明之后,可以先进行沟通,视情况组织会议共同讨论。
第8章 从设计规范到功能
编写验证计划的第一步是确定要被验证的功能,这可以从设计规范文档中得出。 对于不熟悉设计目的和特点的验证人员,新增的功能带你在设计规范中可能不是很明显,可以采用这样一种方法:
- 观察接口。
- 确定功能。
- 选定边界。
列举基于接口的功能
对于待验证设计的每个接口,列举出接口相关的功能:
- 必须采取哪些事务激励?
- 激励值的范围是什么?
- 事务的次序是什么?
- 相关事务比例是什么?
- 接口与接口之间或者说宏能与功能之间有什么影响?
- 一个接口的是事务是否需要与其他接口同步?
列举基于用途的功能
根据设计中的主要数据同步,列举出每个数据通路相关应用场景的功能:
- 相关配置是什么?
- 可能发生的数据变化有哪些?
- 变化顺序是什么?
- 变化之前和之后的敏感信号以及敏感信号值是什么?
- 存在什么样的错误机制,并如何检测和报告?
列举出基于边界的功能
在对设计边界有一定了解之后,给设计增加压力,推向极限:
- 如何使一个缓冲器发生溢出?
- 对哪些信号驱动哪些值可以到达相关功能的极限?
在提取功能点时,应该加上简短的标注,描述验证的条件以及期望的结果。