一、覆盖率基础概念:什么是覆盖率、为什么需要覆盖率、覆盖率的分类

各位同学好,我是老李。做芯片验证十几年了,今天咱们聊聊覆盖率。

说实话,刚入行那会儿,我对覆盖率也是一知半解。领导说“把覆盖率跑到95%以上”,我就闷头跑。后来踩过坑、吃过亏,才真正理解这东西有多重要。

1.1 什么是覆盖率?

简单来说,覆盖率就是衡量验证工作“够不够”的一把尺子。

你写了一大堆测试用例,到底测全了没有?哪些代码被执行了?哪些功能点被覆盖了?覆盖率就是回答这些问题的。

我个人习惯把覆盖率比作“体检报告”。你身体好不好,不能光凭感觉,得看指标。覆盖率就是验证的体检指标。

核心定义:覆盖率 = 被验证到的设计空间 / 总的设计空间

举个例子。你写了一个加法器,输入范围是0到255。如果你只测了0+0和255+255,那覆盖率就很低。中间那些数呢?边界值呢?溢出情况呢?

1.2 为什么需要覆盖率?

这个问题我经常被问到。有人觉得覆盖率就是给领导看的数字,没什么实际意义。

嗯,这种想法很危险。

我在项目中遇到过一件事。一个同事跑完所有用例,功能都对了,就报了验证完成。结果流片回来,芯片在某种特定条件下死机了。后来一查,是有一段代码从来没被执行过——因为那个条件组合太特殊,测试用例没覆盖到。

那次之后,我们团队把覆盖率检查写进了流程规范。

覆盖率的作用,说白了就三点:

  • 衡量验证完整性:告诉你哪些地方测了,哪些地方没测
  • 指导用例开发:看到覆盖率缺口,就知道该补什么用例
  • 评估验证质量:给项目经理、给客户一个量化的信心指标

注意:覆盖率达标 ≠ 验证完成。覆盖率只是必要条件,不是充分条件。我曾经见过覆盖率100%但芯片还是有bug的情况——因为功能覆盖率没定义好。

1.3 覆盖率的分类

覆盖率不是单一概念。实际工作中,我们主要关注三类:代码覆盖率、功能覆盖率、断言覆盖率。

1.3.1 代码覆盖率

代码覆盖率是最基础的。它衡量的是RTL代码被“执行”的程度。

常见的代码覆盖率类型:

类型 含义 我的经验
行覆盖率 每行代码是否被执行过 最基础,但容易达标
分支覆盖率 if-else、case的每个分支是否都走到 这里容易漏,我吃过亏
条件覆盖率 组合逻辑中每个条件的真/假是否都测到 比分支更细,很多新人忽略
状态机覆盖率 FSM的每个状态、状态跳转是否都覆盖 这个很重要,状态机bug很难debug
翻转覆盖率 信号是否发生过0→1和1→0的变化 一般用于门级仿真

举个例子,看这段代码:

always @(posedge clk) begin
    if (enable) begin
        if (data > 8'h7F)
            out <= 1'b1;
        else
            out <= 1'b0;
    end
end

行覆盖率告诉你这三行代码都执行了。但分支覆盖率会告诉你:enable=0的情况测了吗?data>0x7F和data<=0x7F都测了吗?

我曾经遇到一个案例:分支覆盖率99%,唯一没覆盖到的分支是某个异常处理路径。结果那个异常真的发生了,芯片直接挂死。

1.3.2 功能覆盖率

代码覆盖率只管“代码有没有跑过”,不管“功能对不对”。功能覆盖率就是来补这个缺的。

功能覆盖率需要你手动定义——你想测哪些场景?哪些组合?哪些边界?

比如一个FIFO:

  • 空→写一个数据→非空
  • 满→读一个数据→非满
  • 同时读写
  • 写满后继续写(溢出)
  • 读空后继续读(下溢)

这些场景,代码覆盖率是看不出来的。你得用SystemVerilog的covergroup来定义。

covergroup fifo_cg;
    wr_en: coverpoint wr_en;
    rd_en: coverpoint rd_en;
    fifo_level: coverpoint fifo_count {
        bins empty = {0};
        bins full = {FIFO_DEPTH};
        bins mid = {[1:FIFO_DEPTH-1]};
    }
    wr_rd_cross: cross wr_en, rd_en;
endgroup

我个人习惯是:在写验证计划的时候就同步定义功能覆盖率。等用例写完了再补,很容易漏掉关键场景。

小技巧:功能覆盖率的bin定义不要太粗也不要太细。太粗了漏场景,太细了跑不动。我一般先定义关键边界和典型场景,跑一轮再看缺口。

1.3.3 断言覆盖率

断言覆盖率很多人不重视,其实它很有用。

断言是写在RTL或验证环境里的“检查点”。比如“握手信号valid和ready不能同时为低超过N个周期”、“状态跳转必须合法”。

断言覆盖率衡量的是:这些断言在仿真中是否被触发过。

你想想看,你写了一个断言“req必须保持到ack到来”,结果整个仿真这个断言从来没被触发过——那说明什么?要么这个场景没测到,要么这个断言写错了。

// 断言示例
property p_req_hold;
    @(posedge clk) req |=> req until ack;
endproperty
assert property(p_req_hold);

断言覆盖率低,通常意味着验证场景不够全。我曾经接手过一个项目,断言覆盖率只有30%。一查,很多断言对应的场景根本没构造出来。

1.4 三种覆盖率的关系

这三种覆盖率不是互相替代的,而是互补的。

类型 关注点 谁定义 自动化程度
代码覆盖率 代码执行情况 工具自动
功能覆盖率 功能场景覆盖 验证工程师 中(需手动定义)
断言覆盖率 协议/时序检查 设计/验证工程师 中(需手动编写)

我的建议是:

  • 代码覆盖率用来找“漏测的代码”
  • 功能覆盖率用来找“漏测的场景”
  • 断言覆盖率用来找“漏测的检查点”

三者都达标了,才能说验证比较充分了。

一句话总结:代码覆盖率告诉你“代码跑了没有”,功能覆盖率告诉你“该测的场景测了没有”,断言覆盖率告诉你“该检查的点检查了没有”。三者缺一不可。

好了,这一章就到这里。下一章我们聊聊如何设定覆盖率目标——定低了没意义,定高了跑不动,怎么把握这个度?我有些实战经验可以分享。