4. 验证阶段根因:验证计划遗漏、仿真环境错误、覆盖率不足

验证阶段出问题,说实话是最让人头疼的。为什么?因为设计代码已经写完了,你满心以为可以 tapeout 了,结果验证告诉你——还有坑。而且这些坑往往藏得很深,流片回来才暴露。

我个人习惯把验证阶段的根因分成三类:计划没想全环境搭错了覆盖没到位。咱们一个一个聊。

4.1 验证计划遗漏:corner case 没覆盖

什么叫 corner case?就是那些「正常情况不会出现,但一旦出现就崩了」的场景。比如 FIFO 满的时候还在写、跨时钟域采到亚稳态、复位释放瞬间的毛刺……

我在项目中遇到过最典型的一个例子:一个 SPI 接口的从机,验证只测了标准模式(CPOL=0, CPHA=0),结果客户用模式3(CPOL=1, CPHA=1)去配,数据全错。流片回来才发现,改版又花了两周。

为什么会遗漏?说白了,验证计划写得太「理想化」了。大家只盯着功能列表,没去思考边界。

避坑清单:
  • 接口协议的所有模式(SPI、I2C、UART 的每种配置)
  • FIFO 空/满/半满/溢出
  • 复位释放时序(异步复位同步释放的窗口)
  • 时钟门控使能/关闭瞬间
  • 低功耗模式切换(唤醒、睡眠、保持)
我的习惯:写验证计划时,先列一张「最不可能出现的场景」清单,然后挨个问自己——如果这个场景出现了,芯片会怎样?往往 corner case 就藏在这里。

4.2 仿真环境错误:testbench bug 与激励错误

这个坑更隐蔽。你以为是 DUT 的 bug,结果查了半天,是 testbench 写错了。我见过最离谱的一次:验证工程师把时钟周期配成了 20ns,但实际设计跑的是 10ns,所有时序仿真都通过了——因为仿真环境里根本测不到 setup time 违例。

testbench 的 bug 通常分两类:

  • 激励错误:比如给错了配置寄存器、发错了命令序列、数据宽度不匹配
  • 监测错误:比如 scoreboard 比较逻辑写反了、断言条件写错了

嗯,这里要注意:仿真环境本身也是「代码」,它一样需要 review 和验证。我建议把 testbench 当成 RTL 代码一样对待——做代码检查、做 lint、做覆盖率分析。

我曾经踩过的坑:一个 AXI 总线验证环境,master 和 slave 的握手信号逻辑写反了,导致所有传输都显示成功,但实际上数据根本没写进去。流片回来才发现寄存器全是默认值。那次之后,我要求所有 testbench 必须做「反向验证」——故意注入错误,看环境能不能抓到。

4.3 覆盖率不足:代码、功能、断言

覆盖率这东西,很多人觉得是「交差用的」。其实不然。覆盖率是你验证工作的体检报告。

我把它分成三层:

覆盖率类型 检查什么 常见遗漏
代码覆盖率 RTL 代码的每一行、每个分支、每个状态是否被执行 default 分支、else 分支、casex 的未匹配项
功能覆盖率 设计规格中定义的功能点是否被覆盖 跨时钟域场景、异常中断、错误恢复路径
断言覆盖率 你写的断言(assertion)是否被触发过 断言写了但从来没被激活过,等于没写

举个例子:代码覆盖率 100% 不代表功能没问题。你想想看,一个状态机所有状态都跑到了,但状态跳转的时序错了——代码覆盖率看不出来。这时候就需要功能覆盖率和断言来补位。

我的经验:每次仿真跑完后,先看断言覆盖率。如果某个断言从来没被触发过,要么是它写错了,要么是那个场景根本没测到。两种情况都需要追查。

下面这张图是我自己总结的验证阶段根因分析框架,帮你快速定位问题出在哪一层:

验证阶段根因分析框架 验证计划遗漏 corner case 未覆盖 → 接口模式、FIFO边界、复位时序、低功耗切换 仿真环境错误 testbench bug → 激励错误、监测错误、时钟/复位配置错误 覆盖率不足 代码覆盖率 100% ≠ 功能正确 → 断言覆盖率、功能覆盖率需联动 流片失败 ← 验证阶段根因
一个小技巧:每次 tapeout 前,我会把验证报告打印出来,用红笔圈出所有覆盖率低于 90% 的模块。然后问自己一句:「这个模块如果流片回来有问题,我能接受吗?」如果答案是「不能」,那就继续补 case。

验证阶段的问题,说白了就是「你以为你测全了,其实没有」。我见过太多团队在 tapeout 前一周疯狂补 case,结果越补越慌。为什么?因为前期计划没做细,环境没搭稳,覆盖率没盯紧。

嗯,这一章的内容就到这里。记住:验证不是走流程,是给芯片上保险。每一行 testbench 代码、每一个断言、每一个覆盖率点,都可能是救你一次流片的关键。


公众号:蓝海资料掘金营,微信deep3321