覆盖率优化与验证收敛策略
覆盖率优化与验证收敛策略
覆盖率数据收集完了,数字不达标怎么办?本文是覆盖率系列的第二篇,聚焦优化与收敛——覆盖率未达标分析、.el 豁免文件管理、FSM 深度解读、CDV 三阶段验证法,以及实战案例。
本系列共两篇:① 理论与实战篇 | ② 优化收敛策略篇(本文)
一、覆盖率未达标时的分析与优化
1.1 定位未覆盖点
在 Verdi 中直接点击红色未覆盖行或低覆盖率信号/状态,思考为何测试激励没有触发这些代码。
1.2 常见原因与解决方案
| 原因 | 解决方案 |
|---|---|
| 激励不足 | 优化随机约束增加多样性,或针对边界场景编写定向测试用例 |
| 环境配置/约束冲突 | 检查验证环境,确保约束条件正确且无冲突 |
| 设计/代码冗余 | 与设计工程师确认后,对冗余代码进行豁免(Waive) |
1.3 覆盖率豁免操作
对于确认无需覆盖的代码,在 Verdi 中右键点击该行 → 选择Exclude进行豁免。
关键注意:豁免操作必须
File → Save Exclusions保存到.el文件后才生效。下次打开数据库时,可通过-elfile waivers.el加载。
1.4 合理豁免的场景
- 模块复用:同一模块多次例化且功能相同,只需分析其中一个实例
- 厂商 IP:第三方提供的库模型,可 exclude
- 不可达代码:由于参数配置导致不可能执行的分支
二、直接用例与随机用例的协同策略
2.1 核心定义
| 直接用例 | 随机用例 | |
|---|---|---|
| 定义 | 激励序列仿真前已完全确定 | 激励由随机生成器在约束下动态产生 |
| 目标 | 精准验证已知功能和关键路径 | 高效探索庞大验证空间,冲击边角情况 |
| 可控性 | 极强,完全确定、可重复 | 体现在约束规则上,而非具体数值 |
| 覆盖率贡献 | 单次通过后重复仿真价值有限 | 更换种子即可自动提升覆盖率 |
2.2 实战策略:三阶段验证法
- 冒烟测试阶段— 用少量直接用例验证基本功能(复位、标准操作、已知边界)
- 随机探索阶段— 大量随机用例覆盖功能空间,推动覆盖率收敛
- 查漏补缺阶段— 针对覆盖率报告中的盲区编写定向用例
直接用例如同"精确制导导弹",目标明确攻克已知堡垒;随机用例如同"智能侦察集群",在规则下大规模探索发现未知雷区。两者相辅相成。
三、状态机覆盖率深度解读
3.1 FSM 覆盖率报告核心结构
一份典型的 FSM 覆盖率报告包含:
- 综合覆盖率分数— 宏观指标,必须深入细节
- 状态列表— 所有状态及是否被进入过(√ 已覆盖 / × 未覆盖)
- 状态转移矩阵— 所有转换路径及是否被测试,这是报告的精髓
一个拥有 N 个状态的 FSM,理论上最多有 N×(N-1) 种转换需要测试。
3.2 分步解读流程
第一步:定位未覆盖项— 寻找所有被标记为 Uncovered 的状态和转移路径
第二步:分析原因
| 原因类型 | 示例 |
|---|---|
| 激励不足 | 需构造特定错误场景才能进入 ERROR 状态 |
| 设计冗余/不可达 | 3-bit 编码共 8 种但只定义了 5 个有效状态 |
| 环境约束限制 | 配置寄存器被约束在固定值,阻止了某些路径 |
第三步:关联其他覆盖率— 未覆盖状态通常对应case语句中未执行的分支,交叉验证可精确定位 RTL 位置
3.3 FSM 覆盖率提升策略
| 策略 | 说明 |
|---|---|
| 优化随机约束 | 引导随机生成器产生能触发特定状态转移的数据包/控制信号 |
| 编写定向测试 | 针对随机激励难以覆盖的极端边角情况 |
| 审查与豁免 | 确认冗余/不可达逻辑后豁免,但必须谨慎并留文档 |
四、.el 豁免文件管理
4.1 创建豁免文件
两种方式:
- GUI 操作:Verdi 中右键 Exclude → Save Exclusions 保存为
.el文件 - 手动编写:直接编辑
.el文件指定豁免行
4.2 加载豁免文件
有两种加载方式:
方式一:仿真后查看时加载(Verdi)
verdi-cov-covdirsimv.vdb-elfilewaivers.el方式二:编译/仿真阶段直接排除(推荐用于固定豁免项)
vcs-full64\-cmline+cond+fsm+tgl+branch\-cm_excl_filecoverage_exclusions.el\-cm_dir./cov_db/simv.vdb\<其他编译选项>.el文件示例:
// 排除调试代码 EXCLUDE LINE "counter" "counter.sv" 13 // 排除不可达条件 EXCLUDE COND "counter" "counter.sv" 13 // 排除默认分支 EXCLUDE BRANCH "counter" "counter.sv" 16选择建议:固定且确认的豁免项建议用
-cm_excl_file在编译时就排除,这样生成的.vdb数据更干净;临时调试的豁免项用 Verdi GUI 操作后保存。
4.3 关键注意事项
| 要点 | 说明 |
|---|---|
| 版本更新 | RTL 代码修改后 checksum 变化,.el 文件中的条目会出现异常图标,需逐一处理 |
| Save As | 处理完豁免条目后,千万不要直接覆盖原文件,必须 Save As 新文件 |
| 批量操作 | 使用urg -dump full_exclusions导出所有豁免 |
| 绕过 checksum | -excl_bypass_checks选项仅对行覆盖率有效 |
| 核心原则 | 豁免前务必与设计工程师确认逻辑是否真的无需覆盖 |
4.4 覆盖率分析完整工作流速查
| 阶段 | 核心操作 | 产出 | 所用工具 |
|---|---|---|---|
| 环境设置 | 加载环境变量、配置工具 | 激活的环境 | shell |
| 编译+仿真 | 运行测试用例、-cm收集数据 | .vdb数据库 | VCS/simv |
| 数据合并 | urg合并多测试 vdb | merged.vdb | URG |
| 报告解读 | Verdi/DVE 分析未覆盖区域 | 漏洞列表 | Verdi/DVE |
| 优化迭代 | 改约束/写定向测试/合理豁免 | 更高覆盖率 | 全链路 |
这个5阶段闭环就是 CDV(Coverage-Driven Verification)的核心——覆盖率驱动验证,每轮迭代都从报告解读开始,直到所有关键指标达标。
五、实战案例:4-bit 逻辑运算单元
以一个 4-bit 逻辑运算单元logic_op的验证为例,12 个测试用例的回归结果:
| 指标 | 结果 | 是否达标 |
|---|---|---|
| DUT 行覆盖率 | 97.06% | ✅ 达标 |
| DUT 条件覆盖率 | 88.46% | ⚠️ 接近目标,已知 RTL bug 导致部分路径不可达 |
| DUT 分支覆盖率 | 93.33% | ✅ 达标 |
| DUT 翻转覆盖率 | 19.87% | ❌ 但 4-bit 设计此值属正常 |
分析结论:2 个失败测试(xnor、mode_switch)均因同一已知 RTL bug。通过覆盖率分析成功定位验证盲点,指导了后续测试优化。
CDV 三阶段实操复盘:
| 阶段 | 用例数 | Line | Branch | Condition | Toggle | 关键操作 |
|---|---|---|---|---|---|---|
| 冒烟测试 | 4 个直接用例 | 60% | 55% | 50% | 8% | 复位、ADD、SUB、基本边界 |
| 随机探索 | 6 个随机用例 | 95%→97% | 85%→93% | 75%→85% | 15%→19% | 随机 opcode+addr,种子轮换 |
| 查漏补缺 | 2 个定向用例 | 97.06% | 93.33% | 88.46% | 19.87% | 针对 xnor/mode_switch 写定向测试 |
可以看到:随机探索阶段是覆盖率跃升的关键,Line/Branch/Condition 都有 10~20% 的跳跃;而查漏补缺阶段精准补盲,Condition 从 85% 提升到 88.46%(差值看似不大,但覆盖的是最难触发的角落路径)。
六、总结
覆盖率优化与收敛的五大要点:
- 代码覆盖率是底线— 低覆盖率一定意味着验证不充分
- 功能覆盖率是深度— 100% 代码覆盖不等于没有 bug,需要功能覆盖补盲
- 断言覆盖率是保障— 协议检查和属性约束的触发情况
- 豁免必须谨慎— 每一条 exclude 都需要设计工程师确认和文档记录
- 三阶段验证法— 冒烟 → 随机探索 → 查漏补缺,直接用例和随机用例协同
覆盖率不是终点,而是验证收敛的度量工具。结合代码覆盖率、功能覆盖率和断言覆盖率,构建多维度验证度量体系,才能真正为流片加上最后一道安全锁。
参考资料:
- Synopsys VCS User Guide — Coverage Metrics & -cm_excl_file
- Synopsys Verdi User Guide — Coverage Exclusion Manager
- 《Writing Testbenches using SystemVerilog》— Janick Bergeron