第二章:测试激励基础——激励的类型与覆盖率的关系

大家好,我是老李。做芯片验证这么多年,我越来越觉得一个道理:测试激励的质量,直接决定了验证的成败。你想想看,设计再复杂,如果激励写不好,覆盖率就是虚的,bug 也跑不出来。

这一章,我们来聊聊测试激励的基础。我会结合自己的项目经验,把定向激励、随机激励、约束随机激励这些概念讲透。更重要的是,我会告诉你它们和覆盖率之间到底是什么关系。

2.1 激励的三种基本类型

我个人习惯把激励分成三类:定向、随机、约束随机。这三类各有各的用处,也各有各的坑。

2.1.1 定向激励

定向激励,说白了就是「我明确知道要测什么」。比如我要测一个加法器,我就直接给它输入 3 和 5,然后检查输出是不是 8。

优点:

  • 目标明确,能快速验证特定功能
  • 调试方便,出问题一眼就能看出来
  • 适合回归测试中的关键场景

缺点:

  • 覆盖范围有限,你只能测到你想测的
  • 写起来费劲,尤其是复杂协议
  • 容易遗漏边界情况
我的经验:我在一个 PCIe 项目中,一开始全用定向激励,写了 200 多个 case,结果覆盖率才到 40%。后来我才意识到,定向激励只能作为「打底」,不能全靠它。

2.1.2 随机激励

随机激励,就是让工具自动生成各种输入组合。你想想看,如果让一个加法器随机跑 10000 次,它能覆盖到多少种情况?

优点:

  • 覆盖范围广,能发现你没想到的 bug
  • 自动化程度高,写一个随机生成器就能跑很多 case
  • 适合压力测试和长时测试

缺点:

  • 完全随机可能产生大量无效场景
  • 覆盖率提升慢,很多随机值其实在重复测同一个功能
  • 调试困难,复现问题需要固定种子
注意:我曾经在一个项目中,完全随机跑了三天三夜,结果覆盖率只从 40% 涨到 45%。后来一分析,发现大部分随机值都在测同一个数据通路。这就是「随机陷阱」——你以为在测很多,其实在重复测一个。

2.1.3 约束随机激励

约束随机,就是给随机加一些「规矩」。比如我要测一个 FIFO,我可以约束:写操作和读操作的比例在 3:1 到 1:3 之间,这样既能随机,又能保证场景有效。

优点:

  • 兼顾了随机性和有效性
  • 能定向覆盖边界条件
  • 覆盖率提升效率最高

缺点:

  • 约束写不好,可能过度限制随机性
  • 调试时约束冲突比较头疼
  • 需要一定的经验来设计约束
核心观点:约束随机是目前业界的主流做法。我个人建议,80% 的激励用约束随机,15% 用定向,5% 用完全随机做压力测试。

2.2 激励与覆盖率的关系

很多人问我:「老李,激励和覆盖率到底是什么关系?」

嗯,这个问题问得好。我打个比方:激励就像你撒出去的网,覆盖率就是网眼的大小。网撒得再大,如果网眼太大,小鱼小虾(小 bug)还是会漏掉。

2.2.1 激励决定覆盖率的「广度」

你想想看,如果只写定向激励,你的覆盖率就是「点状」的——只覆盖了你想到的那些点。而随机激励能帮你覆盖到「面」——那些你没想到的输入组合。

举个例子:

// 定向激励:只测几个固定值
write_data(0x0000);
write_data(0xFFFF);
write_data(0xAAAA);

// 约束随机:覆盖整个地址空间
rand bit [15:0] addr;
constraint c_addr {
  addr inside {[0:65535]};
  addr % 4 == 0;  // 对齐约束
}

你看,定向激励只测了 3 个值,而约束随机能测到 16384 个对齐地址。这就是广度上的差异。

2.2.2 激励决定覆盖率的「深度」

广度重要,深度更重要。什么叫深度?就是同一个功能点,你用不同的激励去反复验证。

我记得在一个 AXI 总线项目中,我们测「写响应」这个功能点。定向激励只测了「正常写完成」和「写错误」两种情况。但约束随机能生成:

  • 正常写完成
  • 写错误(地址不对)
  • 写错误(数据不对)
  • 写错误(保护属性不对)
  • 写错误(多个错误同时发生)
  • 写完成但延迟很大
  • 写完成但乱序返回

这就是深度。同一个功能点,用不同的激励去「敲打」它,才能发现隐藏的 bug。

2.2.3 覆盖率反馈驱动激励优化

这里我要说一个很重要的理念:覆盖率不是终点,而是起点。

我见过很多团队,跑完一轮回归,看一眼覆盖率报告就完事了。这不对。正确的做法是:

  1. 跑一轮回归,收集覆盖率
  2. 分析哪些功能点覆盖率低
  3. 针对性地优化激励
  4. 再跑一轮,看覆盖率是否提升
  5. 重复这个过程,直到达标
我的习惯:我每周五下午都会做一件事——打开覆盖率报告,用红色标记那些覆盖率低于 70% 的功能点。然后下周一,我就专门针对这些点写新的约束或定向激励。这叫「覆盖率驱动的激励优化」。

2.3 三种激励的覆盖率对比

为了让你更直观地理解,我整理了一个对比表:

激励类型 代码覆盖率 功能覆盖率 断言覆盖率 适用场景
定向激励 低(20-40%) 低(10-30%) 中(30-50%) 关键路径、回归测试
随机激励 高(60-80%) 中(40-60%) 低(20-40%) 压力测试、长时测试
约束随机 高(70-90%) 高(60-85%) 高(50-70%) 主流验证场景

你看,约束随机在三种覆盖率上表现都比较均衡。这也是为什么它成为主流的原因。

2.4 知识体系图

下面我用一张 SVG 图来总结本章的核心逻辑:

测试激励与覆盖率关系图 定向激励 随机激励 约束随机激励 代码覆盖率(低) 代码覆盖率(高) 代码覆盖率(高) 功能覆盖率(低) 功能覆盖率(中) 功能覆盖率(高) 结论:约束随机激励是主流选择

2.5 避坑指南

最后,我分享几个我在项目中踩过的坑:

坑一:过度依赖随机
我曾经在一个项目中,觉得随机能搞定一切,结果跑了两个月,覆盖率死活上不去。后来发现,有些边界条件随机根本碰不到。从那以后,我学会了「定向+随机」混合使用。
坑二:约束写得太死
有一次我写约束,为了让覆盖率好看,把随机范围限制得很小。结果覆盖率是上去了,但 bug 一个都没发现。后来我才明白,覆盖率是手段,不是目的。我们的目标是发现 bug,不是刷覆盖率。
坑三:忽略种子管理
随机激励需要固定种子才能复现问题。我建议每个回归测试都记录种子号,这样出了问题才能回放。我见过有人跑完回归不记种子,结果 bug 复现不了,只能重写激励,白白浪费一周时间。

好了,这一章就到这里。记住一句话:激励是验证的子弹,覆盖率是瞄准镜。子弹再好,瞄不准也打不中目标。

可可精品资料库