一、啥是功能覆盖率和代码覆盖率

在 Verilog 设计验证里,功能覆盖率和代码覆盖率可是俩重要的指标。简单来说,功能覆盖率就是看看设计的功能有没有都被测试到;代码覆盖率呢,就是瞅瞅代码里的每一行是不是都执行过。

举个例子,假如你设计了一个简单的加法器,功能覆盖率就是看各种可能的输入组合(比如正数加正数、正数加负数等)是不是都测试到了;代码覆盖率就是看加法器代码里的每一行是不是都在测试的时候执行过。

二、功能覆盖率的实践应用

2.1 功能覆盖率的定义

要想统计功能覆盖率,得先把要覆盖的功能点定义好。比如说,还是那个加法器,我们可以定义功能点为不同位数的加法运算。

// Verilog 技术栈
// 定义一个简单的加法器
module adder (
    input [3:0] a,
    input [3:0] b,
    output reg [4:0] sum
);
    always @(*) begin
        sum = a + b;
    end
endmodule

// 定义功能覆盖率组
covergroup adder_cg @(posedge clk);
    option.per_instance = 1;
    // 定义一个覆盖点,覆盖不同的输入组合
    coverpoint {a, b} {
        bins zero_plus_zero = {8'b0000_0000};
        bins all_ones = {8'b1111_1111};
        bins other = default;
    }
endgroup

在这个例子里,我们定义了一个覆盖组 adder_cg,里面有一个覆盖点,覆盖了几种不同的输入组合。

2.2 功能覆盖率的收集

在测试的时候,仿真工具会自动收集功能覆盖率的数据。我们可以用这些数据来判断测试的完整性。

// 测试平台
module testbench;
    reg [3:0] a;
    reg [3:0] b;
    wire [4:0] sum;
    adder uut (.a(a), .b(b), .sum(sum));

    initial begin
        // 初始化覆盖组
        adder_cg cg = new();
        // 生成一些测试用例
        a = 4'b0000; b = 4'b0000; #10;
        a = 4'b1111; b = 4'b1111; #10;
        a = 4'b0101; b = 4'b1010; #10;
        // 打印功能覆盖率信息
        $display("Functional coverage: %0.2f%%", cg.get_coverage());
        $finish;
    end
endmodule

在这个测试平台里,我们生成了几个测试用例,然后打印出功能覆盖率信息。

三、代码覆盖率的实践应用

3.1 代码覆盖率的类型

代码覆盖率有好几种类型,常见的有行覆盖率、分支覆盖率和条件覆盖率。行覆盖率就是看代码里的每一行是不是都执行过;分支覆盖率就是看代码里的每个分支(比如 if - else 语句)是不是都执行过;条件覆盖率就是看条件表达式的每种可能结果是不是都出现过。

3.2 代码覆盖率的收集

很多仿真工具都支持代码覆盖率的收集。我们可以在仿真的时候开启代码覆盖率选项,然后工具会自动统计覆盖率数据。

// 还是上面那个加法器代码
module adder (
    input [3:0] a,
    input [3:0] b,
    output reg [4:0] sum
);
    always @(*) begin
        if (a == 4'b0000 && b == 4'b0000) begin
            sum = 5'b00000;
        end else begin
            sum = a + b;
        end
    end
endmodule

// 测试平台
module testbench;
    reg [3:0] a;
    reg [3:0] b;
    wire [4:0] sum;
    adder uut (.a(a), .b(b), .sum(sum));

    initial begin
        // 生成测试用例
        a = 4'b0000; b = 4'b0000; #10;
        a = 4'b0101; b = 4'b1010; #10;
        // 这里我们可以用仿真工具的命令来查看代码覆盖率
        // 不同的仿真工具命令可能不同,比如 VCS 可以用 urg 命令
        $finish;
    end
endmodule

在这个例子里,我们的加法器代码里有一个 if - else 语句,通过不同的测试用例来覆盖不同的分支。

四、应用场景

4.1 芯片设计

在芯片设计中,功能覆盖率和代码覆盖率可以帮助我们确保芯片的功能正确性。比如说,一个复杂的处理器芯片,有很多不同的指令集和功能模块。通过功能覆盖率,我们可以确保每种指令的各种使用场景都被测试到;通过代码覆盖率,我们可以确保处理器代码里的每一行都执行过,避免出现未被测试到的代码漏洞。

4.2 数字电路设计

对于数字电路设计,比如一个 FPGA 上的数字滤波器。功能覆盖率可以帮助我们测试滤波器在不同输入信号下的滤波效果;代码覆盖率可以确保滤波器代码的完整性。

五、技术优缺点

5.1 优点

  • 功能覆盖率:可以从功能的角度来衡量测试的完整性,确保设计的所有功能都被测试到。比如在设计一个网络路由器时,功能覆盖率可以帮助我们测试各种网络协议和数据包处理功能。
  • 代码覆盖率:可以发现代码里未被执行的部分,有助于找出潜在的代码漏洞。比如在一个复杂的状态机代码里,代码覆盖率可以帮助我们发现哪些状态转换没有被测试到。

5.2 缺点

  • 功能覆盖率:功能点的定义可能比较主观,不同的人可能定义出不同的功能点。而且有些功能可能很难用覆盖率来衡量,比如一些复杂的用户交互功能。
  • 代码覆盖率:代码覆盖率高并不一定意味着功能正确。有时候代码虽然执行了,但可能执行的结果是错误的。比如一个计算函数,代码覆盖率达到了 100%,但计算结果可能是错误的。

六、注意事项

6.1 功能覆盖率

  • 功能点的定义要准确、全面,不能遗漏重要的功能。比如在设计一个游戏控制器时,要考虑到各种按键组合和操作模式。
  • 要定期检查功能覆盖率的数据,及时发现未覆盖的功能点,补充测试用例。

6.2 代码覆盖率

  • 不要只追求高代码覆盖率,要结合功能覆盖率一起考虑。比如有些代码虽然执行了,但可能没有覆盖到关键的功能。
  • 对于一些复杂的代码逻辑,要仔细分析代码覆盖率数据,找出可能存在的问题。

七、文章总结

功能覆盖率和代码覆盖率在 Verilog 设计验证中都非常重要。功能覆盖率从功能的角度衡量测试的完整性,代码覆盖率从代码执行的角度发现潜在的漏洞。在实际应用中,我们要根据具体的设计需求和场景,合理定义功能点和收集覆盖率数据。同时,要注意功能覆盖率和代码覆盖率的优缺点,不能只依赖其中一个指标。通过综合使用这两个指标,可以提高 Verilog 设计验证的效率和质量。