第三章:验证计划——如何制定验证计划、验证范围定义、功能覆盖点提取、验证矩阵

说实话,很多刚入行的朋友觉得验证计划就是写个文档应付一下。我当年也这么想过。直到有一次,一个项目做到一半,才发现有个关键功能点根本没覆盖到。那叫一个惨。从那以后,我每次做项目,第一件事就是老老实实把验证计划写好。

验证计划是什么?说白了,它就是一张地图。告诉你从哪出发,要去哪,路上会经过哪些坑。没有它,你就是在黑夜里乱撞。

一、验证范围定义——先搞清楚你要测什么

我见过最离谱的事,是有人花了两周时间测了一个根本不会用到的功能。为什么会这样?因为一开始就没把范围划清楚。

验证范围定义,核心就三件事:

  • 功能边界:芯片到底支持哪些功能?不支持哪些?
  • 接口协议:走的是AXI还是AHB?有没有自定义协议?
  • 工作模式:正常模式、低功耗模式、测试模式……每种模式都要测吗?

我个人习惯,先拉一张表格,把模块的输入输出信号全部列出来。然后问自己一个问题:“如果这个信号错了,系统会怎样?” 能回答上来的,就是需要验证的范围。回答不上来的,嗯,那更要测。

避坑指南:我曾经在一个项目中,忽略了复位信号的异步处理。结果仿真怎么跑都对,上板就挂。后来查了三天,才发现复位域没处理好。所以,复位、时钟、电源这些“基础设施”,一定要划进验证范围。

二、功能覆盖点提取——把需求翻译成可测的指标

功能覆盖点,听起来高大上。其实很简单:就是把设计规格书里的“人话”,翻译成验证工具能理解的“机器语言”。

举个例子:规格书上写“FIFO不能溢出”。你总不能直接测“不能溢出”吧?你得把它拆成:

  • 写指针追上读指针时,full信号拉高
  • full拉高后,写操作被阻塞
  • 读走一个数据后,full信号拉低

你看,一个“不能溢出”,拆成了三个覆盖点。每个覆盖点都是一个具体的、可仿真的场景。

我一般用SystemVerilog的covergroup来写覆盖点。举个例子:

covergroup fifo_cg @(posedge clk);
  // 覆盖FIFO满的情况
  full_cp: coverpoint fifo_full {
    bins full_asserted = {1};
    bins full_deasserted = {0};
  }
  
  // 覆盖写使能与满信号的关系
  wr_full_cross: cross wr_en, fifo_full {
    bins wr_when_full = binsof(wr_en) intersect {1} &&
                        binsof(fifo_full) intersect {1};
  }
endgroup

你想想看,如果没有这些覆盖点,你怎么知道你的测试用例到底测没测到“满”的情况?靠猜吗?

小技巧:提取覆盖点时,我习惯先画一个状态机。每个状态、每个跳转,都是一个潜在的覆盖点。尤其是那些“异常跳转”,比如从A状态直接跳到C状态,这种最容易漏掉。

三、验证矩阵——把计划变成可执行的清单

验证矩阵,也叫追溯矩阵。它的作用就一个:确保每个功能点都有对应的测试用例

我见过最标准的验证矩阵,长这样:

功能ID 功能描述 覆盖点 测试用例 验证方法 状态
FIFO_001 FIFO满信号正确拉高 full_cp test_fifo_full 定向测试 通过
FIFO_002 FIFO满时写操作被阻塞 wr_full_cross test_fifo_wr_when_full 随机测试 待验证
FIFO_003 FIFO空信号正确拉高 empty_cp test_fifo_empty 定向测试 通过

这张表,就是你的作战地图。每天打开它,看看哪些还没测,哪些测了没过。一目了然。

我个人习惯,在项目初期就把矩阵搭好。然后每周更新一次状态。这样到了项目后期,就不会出现“哎呀,这个功能好像没测过”的尴尬。

注意:验证矩阵不是写一次就完事了。设计改版、需求变更,矩阵都要跟着更新。我曾经吃过这个亏——设计改了FIFO深度,我忘了更新矩阵,结果用旧的测试用例去测,白白浪费了两天时间。

四、验证计划的落地——别让计划变成废纸

很多团队的验证计划写得漂漂亮亮,最后却成了摆设。为什么?因为计划太死板了。

我建议,验证计划要包含这四部分:

  1. 时间节点:什么时候出第一版测试用例?什么时候跑完回归?
  2. 资源分配:谁负责哪个模块?用多少台服务器跑仿真?
  3. 风险预案:如果某个模块延期了,怎么调整优先级?
  4. 验收标准:覆盖率达到多少才算通过?100%?别天真了,有些场景根本覆盖不到。

说到验收标准,我一般定两个指标:

  • 代码覆盖率:行覆盖、条件覆盖、跳转覆盖,至少95%以上
  • 功能覆盖率:所有定义的覆盖点,至少90%被命中

剩下的那5%和10%,要么是冗余代码,要么是根本不可能出现的场景。没必要死磕。

总结一下:验证计划不是写给别人看的,是给自己用的。范围定义清楚,覆盖点提取到位,矩阵维护及时,这三点做到了,你的验证工作就成功了一半。另一半?嗯,那是执行的事了。

好了,这一章就聊到这。下一章我们讲讲测试用例怎么写,才能既快又准地命中那些覆盖点。