一、验证计划的核心价值

验证计划,说白了就是一张作战地图。

我刚入行那会儿,总觉得写计划是浪费时间。心想:直接写测试用例不就完了?结果呢?项目做到一半,发现功能点漏了,覆盖率不达标,回头补用例,工期全乱套。嗯,从那以后,我再也不敢跳过验证计划这一步了。

验证计划要回答三个问题:

  • 测什么?——功能点分解
  • 怎么算测完?——覆盖率模型
  • 怎么记录?——计划文档模板

今天我们就一个一个来拆解。

二、功能点分解方法

2.1 从规格书出发

功能点分解的源头,是芯片的规格书(Spec)。我个人习惯,拿到规格书先通读三遍:

  • 第一遍:了解整体功能
  • 第二遍:圈出所有"边界条件"
  • 第三遍:标注所有"异常场景"

举个例子,存储控制器的写操作,规格书里可能只写了一句"支持4KB对齐写"。但实际分解时,你要考虑:

  • 对齐写(地址4KB对齐)
  • 非对齐写(地址偏移1字节、2字节...)
  • 跨4KB边界的写
  • 写后立即读(写读冲突)
  • 写操作被中断

你看,一个功能点能拆出5-6个子场景。

2.2 功能点分解的"三刀法"

我在项目中总结了一套方法,叫"三刀法":

  1. 第一刀:按协议层切
    • 事务层(Transaction Level)
    • 传输层(Transfer Level)
    • 物理层(PHY Level)
  2. 第二刀:按操作类型切
    • 读操作、写操作、擦除操作
    • 正常操作、异常操作
    • 单次操作、连续操作
  3. 第三刀:按边界条件切
    • 地址边界(0地址、最大地址、跨页地址)
    • 数据边界(全0、全1、随机数据)
    • 时序边界(最快时钟、最慢时钟、时钟抖动)

关键提醒:功能点分解不是一次完成的。我建议先粗分解,再细分解,最后做交叉检查。交叉检查时,把两个维度的功能点组合起来,往往能发现遗漏。

2.3 功能点分解示例

以NAND Flash控制器的"页读取"功能为例:

功能模块 功能点 子场景
页读取 正常读取 单页读取、多页连续读取
异常读取 读空页、读坏块、读ECC错误页
边界读取 读第0页、读最后一页、跨plane读取
并发读取 读与写并发、读与擦除并发

三、覆盖率模型设计

3.1 覆盖率不是数字游戏

我曾经见过一个项目,代码覆盖率99%,但芯片流片回来还是挂了。为什么?因为功能覆盖率只有30%。

覆盖率模型设计,核心是回答一个问题:"哪些场景必须被覆盖到?"

3.2 三种覆盖率的关系

  • 代码覆盖率:检查代码有没有被执行到。这是基础,但不够。
  • 功能覆盖率:检查功能点有没有被覆盖。这是核心。
  • 断言覆盖率:检查协议时序有没有被验证。这是保障。

我的经验:功能覆盖率要优先于代码覆盖率。先把功能点列全,再考虑代码覆盖。代码覆盖率高但功能覆盖低,等于白干。

3.3 覆盖率模型设计步骤

  1. 定义覆盖组(covergroup)
    • 每个功能模块一个覆盖组
    • 每个覆盖组包含多个覆盖点(coverpoint)
  2. 定义覆盖点
    • 地址范围、数据模式、操作类型
    • 状态机状态、FIFO深度
    • 错误类型、中断类型
  3. 定义交叉覆盖(cross coverage)
    • 两个覆盖点的组合
    • 例如:操作类型 × 地址范围

3.4 覆盖率模型代码示例

// 存储控制器写操作覆盖率模型
covergroup write_cg @(posedge clk);
  // 覆盖点:写地址范围
  addr_range: coverpoint wr_addr {
    bins low = {[0:4KB]};
    bins mid = {[4KB+1:8KB]};
    bins high = {[8KB+1:16KB]};
    bins max = {16KB};
  }
  
  // 覆盖点:写数据模式
  data_pattern: coverpoint wr_data {
    bins all_zero = {0};
    bins all_one = {-1};
    bins random = default;
  }
  
  // 覆盖点:写操作类型
  op_type: coverpoint wr_type {
    bins single = {SINGLE_WRITE};
    bins burst = {BURST_WRITE};
    bins atomic = {ATOMIC_WRITE};
  }
  
  // 交叉覆盖:操作类型 × 地址范围
  cross op_type, addr_range;
  
  // 交叉覆盖:数据模式 × 操作类型
  cross data_pattern, op_type;
endgroup

注意:交叉覆盖不要滥用。两个覆盖点交叉,如果每个有10个bin,就是100个组合。三个交叉就是1000个。我曾经见过有人交叉了5个点,结果仿真跑了三天还没跑完。合理控制交叉覆盖的粒度。

四、验证计划文档模板

4.1 文档结构

我个人习惯用这个模板,用了七八年,改过很多版,现在分享给你:

章节 内容 说明
1. 概述 验证范围、验证目标 一句话说清楚要验证什么
2. 功能点列表 功能点分解表 每个功能点编号、描述、优先级
3. 验证环境架构 环境框图、组件说明 用SVG画清楚
4. 测试用例清单 用例名称、对应功能点 每个用例覆盖哪些功能点
5. 覆盖率模型 覆盖组定义、覆盖点说明 附上代码或伪代码
6. 回归策略 回归频率、种子数量 每天/每周回归
7. 风险与缓解 已知风险、应对方案 比如:某个场景仿真太慢

4.2 功能点列表模板

| 编号 | 功能模块 | 功能点 | 子场景 | 优先级 | 对应用例 |
|------|----------|--------|--------|--------|----------|
| F01  | 写操作   | 对齐写 | 4KB对齐 | P0     | TC_WR_01 |
| F02  | 写操作   | 非对齐写 | 偏移1B | P1     | TC_WR_02 |
| F03  | 写操作   | 跨页写 | 跨4KB边界 | P0   | TC_WR_03 |
| ...  | ...      | ...    | ...    | ...    | ...      |

4.3 验证计划的核心原则

  • 可追溯:每个功能点都能追溯到用例和覆盖率
  • 可量化:覆盖率目标明确(比如功能覆盖率达到95%)
  • 可执行:计划要能落地,别写一堆做不到的

避坑指南:我曾经写过一个验证计划,功能点列了200多个,结果项目周期只有3个月。最后只跑了50%的用例,剩下的全废了。所以,优先级一定要分清楚。P0是必须覆盖的,P1是尽量覆盖的,P2是有时间再覆盖的。

五、本章知识体系

下面这张图,是我画的功能点分解与覆盖率模型的核心流程:

验证计划制定:核心流程 芯片规格书 功能点分解(三刀法) 覆盖率模型设计 代码覆盖率 功能覆盖率 断言覆盖率 验证计划文档

这张图展示了从规格书到验证计划文档的完整链路。你写计划的时候,就按这个流程走,基本不会漏东西。