头疼的数字验证:谈谈设计验证的成本
本来以为经历流片之后再次面对设计验证能够得心应手,最近的项目验证又狠狠给了我一巴掌。验证就像一个黑洞,不断吞噬你的时间,砸进去一个又一个夜晚和周末却总草木皆兵感觉到处都有坑 搞的都没时间写博客了。今天来好好倒腾倒腾验证。
验证大致上来说就分为两部分,搭建平台(Setup)和验证 debug (Verify)。理论上来说平台搭建得越好,测试用例准备得越完善,覆盖率就越高,bug 越容易暴露和追踪。但实际情形并不可能存在无限的时间开发验证平台和无限的计算资源遍历测试用例。沉没在平台搭建中冒烟测试遥遥无期、开发 golden model 时间和 RTL 开发差不多甚至更多、测试平台不充分出现 bug 只能依靠人力挨个信号挨个周期比对、新项目需求几乎把原有框架推倒重来…… 以上情形在过去的项目中反复发生,验证难在面对不确定性,精准分配验证资源,什么时候做加法,什么时候做减法,包丁解牛一招毙命。
拆解设计-验证成本
在提出解决策略前,先来掰扯一下设计-验证开发成本构成。
验证平台的构成和代价
如图是一个验证平台组件构成,从图直观看出 DUT 设计仅仅包含很小一部分。以数据结构为中心,我将验证平台划分为三部分:定义数据结构(Definition),数据结构表征的转换 (Conversion),以及产生数据结构 (Generation)。转换前后认为是同一数据结构在不同环境下的表现,而产生则认为数据结构发生改变。比如 INT 32 加法器的输入,在 C++ 下可能用 int 32 表示,在 Verilog 里则是 32 位宽信号,这是转换;而从输入相加得到输出,这是生成。

数据结构需要定义以下对象:
- Test Vector
- 输出结果 Result,图中引出了多个模块接口
- debug 信息,一般包含三类,(1)量化指标比如通过率、覆盖率,(2)一些规则检查 log 以及(3)波形
数据格式转换主要负责同一数据结构在硬件语言(Verilog) 和软件语言测试平台下的表征转换:
- Driver 负责 Test Vector 转换
- Monitor 负责 Result 转换
数据结构生成包括:
- Sequence:生成 Test Vector
- DUT :输入硬件表征 Test Vector ,生成硬件表征 Result
- Golden Model :输入软件表征 Test Vector,生成软件表征 Result
- Scoreboard:输入两个模型的 Result,生成 Debug 信息
将每一个部分开销用符号表示,其中 \(N\) 是测试平台中一共引出了多少输出 Result:
验证的构成和代价
对于数字设计,我们往往面对一个超级复杂的系统,人脑无法直接应付复杂度思考。分而治之方法被广泛运用,将复杂系统抽象成一个个相对独立的组件,每个组件的复杂度在可控范围之内。模块的划分也是一个有趣的命题 [1],这里不深入讨论。假设系统划分为 \(N_m\) 个独立的单元,每个单元复杂度相对一致且可控,进而 \(N_m\) 某种程度上代表了设计复杂度。并且验证加入的接口数量 \(N\le N_m\) 。
验证的对象是 Bug,所谓 bug,不知道 bug 才会造成 bug (隔这隔这), bug 的规律就是没有规律,以高度随机性和不可预测性建模。具体来说,我不知道 bug 会出现在什么地方,也不知道会出什么样的 bug,因此 debug 分为三个过程:
- 检测 (Detection):通过产生更多测试数据,更改采样逻辑提高系统覆盖率,这一代价已包含在设计 \(G_{\text{test vector}}\) 及 Sequence 之中
- 定位(Localization):找到出错源
- 修正(Fixing):理解出错逻辑并进行修正
定义“单位 bug” 为系统中单一组件引起,由系统中的多个组件产生的 bug 建模成一系列单位 bug 的组合。定义单位 bug 的好处则是每个 bug 的修正成本相对固定,建模成 \(F\) ,是一个可控的常量。
bug 的产生高度随机,系统中 \(N_m\) 个模块都可能产生 bug。最悲观的假设,我们 debug 过程中,按某种次序依次检查所有模块,总共一共有 \(A_{N_m}^{N_m}=N_{m}!\) 种排序可能。假设我们选择次序的方式也是均匀分布,每种次序下要检查的模块数量取决于出错模块在序列中所在的位置,那么我们要检查模块数量的期望为 \(E=\frac{N_{m}+1}{2}\),每次查看的成本是 \(L\) , debug 的代价是:
其中 \(E[N_{bug}]\) 是出现单位 bug 数量的期望,如果验证平台将待测系统划分成了 \(N\) 个部分,每个部分平均有 \(\frac{N_m}{N}\) 个模块,则开销变为
至此,写出总验证开销:
敏捷开发和抽象复用相辅相成
结合公式,举个例子看看验证平台设计思路对总体成本影响,比如参数 \(N\) 表示测试平台监控的信号接口数量:
第一项为正,代表构建验证系统的成本;第二项为负,代表 debug 成本。随着 \(N\) 增加,需要投入更多时间开发接口,但 debug 定位更快。\(N\) 的抉择取决于设计者、验证者对系统出错概率 \(E[N_{\text{bug}}]\) , 系统 \(N_{m}\) 复杂度的评估,很显然这超级依赖经验和玄学。
具体来说,数字验证面对两个困难
- 验证平台非常复杂繁琐,成本很高;
- 不确定性,那个模块开发复杂,哪个环节容易出错,涉及项目需求、人物力成本、分工管理等等各种因素,很难在尚未开始执行时准确预测,投资对应单元。
这两个困难有两个应对策略
- 抽象项目之间的共性和特性,维护一套可以复用的轮子降低建立系统成本;
- 敏捷开发,先粗粒度开发系统,快速获取反馈后针对性分配更多验证资源。
对于硬件设计验证来说,二者是相辅相成的,因为(1)验证系统的复杂,即使用最粗粒度 top 模块从设计到验证跑通获取第一个反馈时间也相当之久;(2)具体实践往往是自顶向下设计,自底向上实现。因此应当以静态的预制轮子应对不变降低 Setup 复杂度快速获取反馈,再根据 Verify 反馈及时调整应对。问题甩到了如何抽象出项目不变的共性,即造一套好用、能一直用的轮子。
从测试平台中抽象轮子
设计之间的共性和特性随着验证对象粒度变化,越细粒度,越定制化越难以抽象,反之亦然:
- 最粗粒度的抽象接口大都由社区维护并规范成了协议,比如 AXI 、DDR、I 2C 、SPI 等通讯协议;
- 中等粒度的抽象接口大致可分为 Valid 和反压接口 Valid-Ready,是接下来的讨论对象;
- 最细粒度和设计高度耦合难以固化,比如控制流,老老实实看波形吧。
Golden Model 和牺牲粒度
整套验证平台以 Golden Model 的完全准确为基石。但 DUT 由人开发,Golden Model 也由人开发,DUT 会出错,Golden Model 为何就不会出错呢?Golden Model 的准确性一般由以下三点保证:
- 牺牲沟通成本消除设计主体的偏差,消除人员理解差异、技能差异和一些思维锚定偏差 (Anchor Bias),比如设计和验证由不同人员分工交叉检验,让 AI 辅助设计检验等;
- 从已有生态迁移准确性,比如想要实现 IEEE-754 浮点算术,调用 C++ 库的实现,经过社区持久维护出错概率较低,某些正确率接近 100% 任务交给 AI 等;
- 牺牲建模粒度降低复杂度和出错概率,比如 Golden Model 一般不用 cycle-level 级别时序建模,而用 TLM 事务级建模。
哪一点更加重要和具体项目需求和人物力资源而定,但随着开发者技术技能熟练,设计越来越前沿新颖,准确性保证逐渐收敛到第三点“降低 Golden Model 复杂度”,个人习惯是设计验证系统时是都以开发粗粒度的 Golden Model 为前提。
粗粒度的 Golden Model 也为抽象共性奠定了基础。
时间维度抽象:事务级建模
在时间维度上,event-driven 的建模不关注时刻级别的对齐,但维护了事件发生的顺序不变,是一种守序的抽象。换句话说,这种抽象具体某个时刻(微分)无法对齐,但在更广时间尺度(积分)上结果保持一致。而数据对时间积分则是存储,一般最常用的存储结构 FIFO 和 Buffer,UVM 中都有对应支持前后门组件实现。
通过事物级接口抽象(Valid, Valid-Ready)以及测试存储 IP 的实现,将 Driver /Monitor 定义和行为解耦。面对不同的设计,我们只需要实现转换口 (下图 Interface),而复用平台内置收发数据行为。

数据格式抽象:以面对 AI 加速器设计为例
虽然事务级建模降低了 Driver/Monitor 成本,但平台中仍然存在各种接口的定义和转换,那么有没有办法能够用一种统一的数据结构表达各类接口呢?
如果我们将领域缩窄到 AI 加速器领域,AI 计算对象是 Tensor,在各类深度学习框架比如 pytorch /onnx 内有良好支持,AI 算法的 Golden Model 天然以 Tensor 为数据对象表征。若将所有的数据格式限制在 Tensor 类型上,则可节省出一大笔数据类型定义和转换的成本。以上我开发 torchbit 验证框架的动机[2],这里不再过多展开。

展望:验证的最后一里路
最近 AI Agent 验证这个话题越来越火了,知乎上也有博主体验用 Gemini 设计 GPU 的完整软硬件系统[3],从仓库来看,似乎完成得还不错。各大 EDA 厂商火速跟进 AI 工具支持,覆盖从 RTL 到 GDS 全流程,奈何 license 费用如天高,只能干巴巴眼馋 怎能少了这一件袈裟啊...。
我自己使用体感是 AI 肯定是大大加速了设计验证过程。确实比以前更加理解硬件设计,能够很快搭建出一个完整可跑的框架,但好比一共要走 10 里路,AI 只是走了 9 公里路,最后 1 里路目前还需要人力参与。一些极端 corner case AI 理解似乎不足,比如先前用 AI 生成 BF 16 算术单元,在 rounding 模式上一直错误。并且最后由于代码全是 AI 接管不熟悉细节,这 1 里路 debug 修正比以前更加困难。
但就任务性质讨论,验证确实非常适合 AI 开发,繁琐且机械的流程天然适合 AI 来做。也许走完最后一里路并不遥远 还是好想用先进 EDA 啊...。

浙公网安备 33010602011771号