验证方法论

第 7 章 · 共 15 章

什么是验证计划,为什么它要放在最前面

“你会怎么验证这个模块”背后真正问的是什么——不是要测哪些测试,而是在任何测试存在之前,什么才算一份验证计划:一份从 spec 推导出来的、前瞻性的文档,跟这个赛道已经为 axil_regfile 建好的测试代码和覆盖率报告都不是一回事。

"你会怎么验证这个模块?"是 DV 面试里最常见的问题之一,也是一个对语法脱口而出的人最容易掉进去的陷阱。说出 covergroupuvm_sequence 回答的是另一个问题——已经知道要测什么之后该怎么写测试。真正的问题在更早、更难的地方:只拿到一份 spec,什么都没有,你要怎么系统地推导出值得测试的场景清单,还要知道自己没有漏掉真正重要的那些?这正是验证计划要回答的问题,也是这个模块的主题。

验证计划到底是什么

这个赛道已经针对 axil_regfile 产出过两份文档,但都不是验证计划:

  • 测试代码uvm-advanced 第 1-6 章)——真正跑起来打在 DUT 上的 sequence、driver、定向写操作。这是实际写出来的东西
  • 覆盖率报告和 waiver logdv-methodology 第 1-6 章)——line/branch/toggle/FSM 的数字,以及对每一个缺口的判断,是靠测量上面那些测试代码实际测到了什么产出来的。这是事后测出来的东西

验证计划两者都不是。它是本该出现在这两者之前的那份文档——把 spec 承诺过的功能映射到值得测试的场景上,依据是 spec 承诺了什么,而不是 RTL 恰好做了什么,也不是什么东西写起来最省事。测试代码执行一份计划(如果压根没人写过计划,那就是没有计划可执行)。覆盖率报告检查执行结果跟意图对不对得上。而这三者里,只有计划这一份,从一开始就把"意图"这件事说清楚了。

为什么它要放在最前面

具体的论据是成本,而且发现得越晚,成本就越高。一个漏掉的场景,如果在读验证计划的时候被发现,代价只是往文档里加一行字的时间。同一个缺口,如果是靠覆盖率报告发现的——就像 dv-methodology 第 3-5 章找到 axil_regfile 那些真实缺口的方式——代价是写一个新测试、调通它、重新跑一遍所花的时间,而且是事后补的,还要跟那一轮回归本来要做的其他事情抢时间。同一个缺口,如果是流片之后才被发现的,代价是一次重新流片。这些都不是假设出来的档位;是同一个漏掉的场景,只是被发现得越晚,价钱标得越高。

还有第二个理由,跟成本关系不大,更多是关于验证团队实际是怎么协作的:验证计划是一样能在任何人开始写代码之前,被一个 lead 或者同事拿去评审的东西。没有人评审过策略的测试代码,本质上是在赌一件事——某一个工程师对"什么重要"的直觉是完整的——而一份计划,能把"相信我,这个 DUT 我都覆盖到了"变成一件可以被别人核实的事,而不是等好几周的测试代码都写出来投入进去了才发现方向有问题。

这个模块写作时依据的"事后视角"

这一章没办法假装自己是在空白地带规划 axil_regfile——读者已经把它看过两遍了,先是在 uvm-advanced 第 1-6 章里被建出来、测过一遍,然后又在 dv-methodology 第 1-6 章里被测量过一遍。假装不是这样,只会让这一课变得更差,而不是更诚实。所以这个模块换了个做法:刻意重新构建出 axil_regfileuvm-advanced 第 1 章动笔之前本该有、但实际上从来没有过的那份验证计划——把这个已经建好的 DUT 和它真实的、已经被发现过的缺口(写 STATUS/COUNT/未映射地址从来没测过,aw_have/w_have 从来没翻转过,因为这个项目里每一个 driver 都是把 AW 和 W 一起发出去的)当成可以核实的证据来用,而不是当成要藏起来的剧透。第 3 章会直接用上这一点:把一套系统性的场景生成技术套用到 axil_regfile 的 spec 上,再把它的产出跟 Coverage 模块的工具实际找到的东西对照——这是对"一份严谨的验证计划本来会不会抓到这个"给出的一个真实的答案,不是一个假设出来的答案。

验证计划不是什么

有三条边界值得说精确,因为在实践中,混淆其中任何一条都是验证计划常见的翻车方式:

  • 不是覆盖率模型。 一个覆盖率模型(dv-methodology 第 2 章)是在测试已经存在、可以运行之后,用 SystemVerilog 去测量一份计划里的场景有没有真正被测到的手段。计划在前,说清楚该测什么、为什么;覆盖率模型是后来把"检查这件事"变成可操作的东西。写一个 covergroup 和决定这个 covergroup 里该放什么,根本不是同一件事。
  • 不是一份没有排序理由的待办清单。 "测写路径、测读路径、测复位"是一份清单,不是一份计划——它缺了让别人能判断这份清单完不完整所需要的那个为什么。一条真正的验证计划条目会把每一个场景跟一条具体的 spec 要求或者一个具体的风险绑在一起,正是这一点让它变得可评审,而不只是写给自己看的备忘。
  • 不是从 RTL 推导出来的。 一份靠读 axil_regfile 的实现、然后针对它恰好做的事情写场景的验证计划,本质上是在拿实现去检验它自己——它天生就会每次都通过,也永远抓不到 RTL 悄悄做了 spec 没有承诺过的事情这种情况。验证计划要从 spec 出发来建:AXI4-Lite 要求什么、寄存器映射承诺了什么,跟任何一份具体的 RTL 到底是怎么满足这些要求的没有关系。测的是契约,不是实现——这才是一个测试有可能因为正确的原因真正失败的前提。

一份真正的验证计划长什么样

四个部分,每一个都会变成自己的一整章,而不是停留在抽象层面:

  1. 一份功能清单——把 spec 做出的每一条承诺,拆解成具体的条目(第 2 章)。
  2. 每个功能对应的场景——对每一条,系统地生成合法、边界、非法、并发这几类值得测试的场景,而不是想到什么测什么(第 3 章)。
  3. 优先级——时间有限的时候先写哪些场景,依据是风险,而不是从头到尾按顺序做(第 4 章)。
  4. 可追溯性——把一条 spec 要求、一条计划条目、一个覆盖率条目、一个真正的测试串起来的那条线,以及这份计划要怎么随着真实缺口的浮现被修订,而不是写一次就冻结(第 5 章)。

小结

  • 验证计划是一份前瞻性的、从 spec 推导出来的文档——该测什么、为什么——跟执行它的测试代码、检查执行结果跟意图对不对得上的覆盖率报告,都不是一回事。
  • 成本是把它放在最前面写的理由:同一个漏掉的场景,在纸面上发现最便宜,流片之后发现最贵,覆盖率报告加上一个要调通、重新跑一遍的测试,夹在中间。
  • 验证计划也是一份评审用的产物——它让作者之外的人能在几周的测试代码投入进去之前,先核实一下这个策略。
  • 这个模块以事后视角重新构建出 axil_regfile 在被建出来之前本该有、却从来没有过的那份计划——把它真实的、已经知道的缺口当成可以核实的证据来用,而不是假装自己是在空白地带规划。
  • 验证计划要从 spec 出发来建,不是从 RTL 的实现出发——测的是一个 DUT 承诺要遵守的契约,不是它恰好已经在做的事情。

一个 DV 面试官问“你会怎么验证这个模块”,只给了你一份 spec。这个问题真正想问的是什么?

为什么这个赛道主张验证计划应该在任何测试代码写出来之前就存在,而不是事后从覆盖率报告里反推出来?

为 axil_regfile 写验证计划的时候,靠通读 RTL 的 always_ff 块、然后针对代码恰好做的事情写场景。这种做法问题在哪?