验证方法论

第 9 章 · 共 15 章

场景分类法:合法、边界、非法、并发

把一个功能变成场景的四类检查清单,套用在第 2 章 axil_regfile 的 23 项功能清单上——这也是这个模块真正的回报:单靠这套分类法作用在 spec 上,就能抓到 Coverage 模块后来靠测量才找到的那些真实缺口,外加几个 Coverage 从来没有单独抓到过的。

第 2 章产出了一份功能清单:从 axil_regfile 的 spec 里拆解出的 23 个条目。光有功能清单还不算一份验证计划——它说的是 DUT 承诺了什么,不是针对每一条承诺该试点什么。这一章补上缺的这一步:一套把一个功能变成真正能测到它的场景的四类检查清单,套用在第 2 章产出的每一个条目上。跑出来的结果,就是这个模块真正的回报——不是一句断言,而是一个证明:单靠对 spec 严谨地过一遍,就能抓到 Coverage 模块后来靠测量才找到的同一批缺口。

四个类别,每个功能都过一遍

  • 合法 ——一个 spec 完全支持的访问。不是它的某一个实例:是 DUT 在所有已经写明的状态和前提条件组合下该有的完整行为范围,包括那些彼此之间差异细微、容易被忽略的部分。
  • 边界 ——恰好落在 spec 定义的某个限制上、或者刚好越过它的一个值或条件:合法范围里最小或最大的值,一个范围的第一个或最后一个元素,一个阈值被跨过的那个精确瞬间。
  • 非法 ——一个 spec 说不应该成功的访问,而且 spec 对它有一个真实的、明文规定的响应(一个错误码、一次拒绝),而不是未定义行为。测一个非法场景不是为了把 DUT 弄坏;是为了确认 DUT 正确地拒绝了它。
  • 并发 ——不止一个各自独立计时的事件,它们之间的相对顺序或重叠,spec 对此是有规定的。每一种合法的到达顺序都得能正常工作,不只是那个恰好最好驱动的顺序。

这几类并不总是互斥的——一个值完全可能恰好落在边界上,同时在越过边界之后的那一侧是非法的——但每一类问的是关于一个功能的不同问题,而一个功能清单如果被这四类都过一遍,浮现出来的场景是只过一遍绝对找不出来的。

合法:不止一个实例

第 2 章清单里的功能 C5——一次 DATA 写只有在写的那一刻 ENABLE 已经置位时才会让 COUNT 自增——无论哪种情况都是合法访问;写 DATA 永远是被允许的。但这里的"合法"不是一个场景,是两个:ENABLE=1 时写 DATA(自增发生),和 ENABLE=0 时写 DATA(自增不发生)。两者都是写明了的、正常的、完全合法的行为——而只有第一种真正跑过。这个项目里每一次 CTRL 写,都是在第一次 DATA 写之前就先把 ENABLE 置成了 1,每次都是这样,所以第二种合法场景一次都没有被真正测过。

功能 C2——CTRL.IRQ_CLR 从不被存储,之后的读操作也永远不会反映它——是同样的情况。写完 CTRL 之后再把它读回来,是一次完全普通、合法的访问。它同样从来没有发生过:这个项目的历史里,从来没有任何一个测试真正发起过一次 CTRL 读。

这两个都不需要什么反常的输入或者竞争条件才能被发现。它们需要的只是把合法这个类别当成不止一个代表性例子那么简单来认真对待。

边界:计数器跨过阈值的那一刻

功能 C8——COUNT 一到达 IRQ_THRESHOLDirq 就立刻置位,并且保持置位(是电平,不是脉冲),直到被清零——是一个真正的边界条件:距离阈值还差一次写的时候,irq 必须保持低电平;到达阈值的那次写,irq 必须升高;越过阈值之后继续写,irq 必须保持高电平,而不是表现成一个脉冲。这一条给出的是一个正面的结果,不是一个缺口:第 1 章的定向测试在第四次写时刚好跨过阈值,并且确认了 irq 在那一刻升高;uvm-advanced 第 6 章的 capstone 把 COUNT 一路打到 6——比阈值多两——确认了它会保持置位,而不是表现得像一个边沿脉冲。这套分类法不是只用来找缺口的;把一个功能套进去跑一遍,在这里确认了一个这个项目真正做对了的边界。

非法:Coverage 已经测量过的那个场景

功能 B2/B3,套用在 axil_regfile 可写的地址空间上,直接生成了三个非法场景:写 STATUS(已映射,只读 → SLVERR)、写 COUNT(已映射,只读 → SLVERR)、写一个什么都没映射的地址(→ DECERR)。三个里没有一个真正跑过。这跟 dv-methodology 第 3 章靠测量找到的是同一个真实缺口——写 case 语句的 SLVERR/DECERR 分支在一份 line/branch 报告里命中次数都是零。在这里,是用另一种方式抵达了同一个发现:写下这三个场景根本不需要仿真任何东西。看一眼寄存器映射表的读写列,问一句"这一行说不支持的那种访问,实际会发生什么",就能直接把它们生成出来。一个在 uvm-advanced 第 1 章动笔之前就用上这套分类法的验证工程师,从第一天起就会把这三个场景写进计划里——并且会在计划评审阶段就抓到,正是第 3 章的覆盖率报告后来在仿真之后才抓到的那个东西,而且成本正是这个模块第 1 章已经点过名的那个更高的成本。

并发:三个场景,不是一个

功能 A5——AW 和 W 可以按任意顺序到达,也可以一起到达——自己就生成了三个场景:AW 先于 W、W 先于 AW、两者一起。只有第三种真正跑过;这个项目写过的每一个 driver 都是在同一个周期里拉高 awvalidwvalid,永远如此。这跟 dv-methodology 第 4、5 章靠测量找到的是同一个缺口——aw_have/w_have 从来没有翻转过,AW_ONLY/W_ONLY 这两个 FSM 状态从来没有被进入过——在这里是用跟非法类别那个缺口一样的方式抵达的:靠读 spec 说什么是合法的,不是靠跑任何东西。

并发这个类别还找到了另外两个第 2 章的清单里已经写明、但 Coverage 的工具从来没有单独抓到过的真实缺口:

  • A7,单笔未完成事务的限制——上一次写操作的 B 响应还没被消费之前,不应该接受新的 AW/W。测它意味着故意把 BREADY 拉低,在第一笔写还挂着的时候尝试发起第二笔。这个项目写过的每一个 driver 都是把 BREADY 常年拉高的,所以 s_axi_bvalid 从来没有真正保持置位超过一瞬间——这条限制从来没有被真正有意义地测过,而且 Coverage 模块的任何一章都没有建过能注意到这件事的检查,因为那个模块的 functional 或者 code coverage 模型里,没有任何一个把"BVALID 还是高电平的时候来了第二个 AW/W"单独当成一个场景抓出来。
  • D2,在一笔事务进行到一半时触发复位——这个项目历史上的每一个测试都只在仿真最开始触发过一次复位,再也没有触发过第二次。axil_regfile 在复位落在一次 AW/W 握手或者一个待处理的 B 响应中间时,能不能干净地恢复——从来没有人试过,Coverage 第 1-6 章也没有测量过这件事。

那个连接两边的想法,直接说出来

Coverage 的工具靠测量找到的每一个真实缺口——STATUS/COUNT/未映射地址的写操作(第 3 章的 line/branch 报告)、aw_have/w_have 从来没翻转过(第 4 章的 toggle 报告、第 5 章的 FSM 报告)——恰好分别落在两个分类里,非法和并发,而这两个分类是套用在一份完全从 spec 建出来的功能清单上得到的。这一切都不需要仿真器、覆盖率工具,甚至不需要 axil_regfile 的 RTL 已经存在。需要的只是读一眼寄存器映射表的读写列和协议的独立性规则,问一遍这一章开头那四个问题。这正是第 1 章成本论证的具体版本:同一批缺口,在纸面上被找到,而不是在一份报告里。

反过来的方向同样成立,而且这一半跟前一半一样重要:并发这个类别还浮现出了 A7D2——两个真实的、可以核实的缺口,Coverage 模块里没有任何一章真正抓到过,因为那个模块里的 covergroup、line/branch 检查、toggle 检查、FSM 检查,没有一个恰好把这两个具体场景单独隔离出来过。验证计划和覆盖率测量不是彼此多余的两件事。一份好的计划能在任何代码存在之前抓到一些真实的缺口;一个好的覆盖率模型能抓到另外一些,包括写计划的人根本没想到要写下来的那些;而正如这里 A7D2 所展示的——有些缺口,只有两个方向的系统性排查都做过,才会被真正抓到。

小结

  • 这套分类法有四个类别:合法(完整的、写明了的行为范围,不是一个例子)、边界(恰好落在一个定义好的限制上、或者刚好越过它的值)、非法(spec 要求必须有真实错误响应的访问)、并发(相对顺序有讲究的、各自独立计时的事件)。
  • 单靠"合法"这一个类别过一遍 axil_regfile 的功能清单,就找到了两个不需要任何反常输入的真实缺口(ENABLE=0 时写 DATA、把 CTRL 读回来)——只需要把合法这个类别当成一个范围,而不是单独一个例子来对待。
  • 阈值跨越这个边界场景,是一个确认过的正面结果,不是缺口——这个项目的测试真的覆盖到了它,横跨两章。
  • 非法这个类别直接从 spec 里重新生成了 dv-methodology 第 3 章的真实缺口(写 STATUS/COUNT/未映射地址),而且不需要任何仿真存在。
  • 并发这个类别用同样的方式重新生成了第 4、5 章 aw_have/w_have 的缺口,还额外找到了两个(A7 的背压场景、D2 的事务中途复位)——Coverage 模块里没有任何一章真正单独抓到过这两个——证明了计划和测量抓到的是不同的东西,不是把同一件事重复抓了两遍。

功能 C5(一次 DATA 写只有在 ENABLE 已经置位时才会让 COUNT 自增)在“合法”这个类别下生成了两个场景,不是一个。为什么这里的“合法”需要不止一个场景?

写 STATUS、写 COUNT、写一个未映射地址,是直接从功能 B2/B3 在“非法”类别下生成出来的,完全没有用到仿真器。这说明了什么?

A7(单笔未完成事务的限制)和 D2(事务中途复位)是“并发”这个类别浮现出来的真实缺口。为什么 Coverage 模块里没有任何一章抓到它们?

这一章找到了 Coverage 第 3-5 章找到的同一批缺口,外加两个(A7、D2)Coverage 从来没有抓到过的。这个组合说明了验证计划和覆盖率测量之间是什么关系?