一、功能覆盖率
功能覆盖率是和设计意图紧密相连的,专注于功能特性的实现和覆盖,有时也被称为“规范覆盖率”,而代码覆盖率则是衡量设计的实现情况。设想某个代码块在设计中被漏掉的情况。代码覆盖率不能覆盖这个错误,但是功能覆盖率可以。
-
功能覆盖率:
-
定义:功能覆盖率是衡量验证过程中设计功能是否被充分测试的指标。它关注的是设计的功能需求是否被覆盖,而不是代码本身的执行情况。
-
目标:确保设计的功能需求(如协议规范、算法逻辑、接口行为等)在验证过程中被充分测试。
-
实现方式:通过定义覆盖率点(如
covergroup和coverpoint)来监控特定的功能行为。这些覆盖率点通常与设计的功能需求直接相关。
-
-
代码覆盖率:
-
定义:代码覆盖率是衡量验证过程中设计代码的哪些部分被执行的指标。它关注的是代码的执行情况,而不是功能需求。
-
目标:确保设计代码的每一部分(如行、分支、条件、状态机等)在验证过程中被充分执行。
-
实现方式:通过工具自动收集代码的执行情况,如行覆盖率、分支覆盖率、条件覆盖率等。
-
SystemVerilog语言本身提供了对功能覆盖率的支持。通过使用covergroup、coverpoint、cross等关键字,可以在SV代码中定义覆盖率模型,从而在仿真过程中收集功能覆盖率数据。
二、覆盖组的定义和建仓(bin)
2.1 覆盖组定义
covergroup cov;
coverpoint data;
endgroup:cov
covergroup uart_coverage @(posedge clk);
coverpoint baud_rate {
bins standard = {9600, 19200, 38400, 57600, 115200};
bins others = default;
}
coverpoint data_bits {
bins data_width = {5, 6, 7, 8};
bins others = default;
}
cross baud_rate, data_bits {
bins valid_combinations = (standard with data_width);
}
endgroup
covergroup 是一个 SystemVerilog 的结构,用于定义覆盖率点。它通常包含以下内容:
-
coverpoint:定义单个信号或表达式的覆盖率点。
-
cross:定义多个信号或表达式的交叉覆盖率点。
-
bins:定义覆盖率点的取值范围或特定值。
coverpoint 用于定义单个信号或表达式的覆盖率点。它可以包含以下内容:
-
bins:定义取值范围或特定值。
-
wildcard:定义通配符范围。
-
ignore_bins:定义忽略的取值范围。
-
illegal_bins:定义非法的取值范围。
coverpoint signal {
bins low = {0};
bins high = {1};
bins others = default;
}
cross 用于定义多个信号或表达式的交叉覆盖率点。它可以包含以下内容:
-
bins:定义交叉取值范围或特定值。
-
wildcard:定义通配符范围。
-
ignore_bins:定义忽略的交叉取值范围。
-
illegal_bins:定义非法的交叉取值范围。
cross signal1, signal2 {
bins valid_combinations = (signal1 == 0 && signal2 == 0);
}
2.2建仓(bin)
"仓(bin)"用来记录覆盖组中覆盖点的每个数值被捕捉到的次数,是衡量覆盖率的基本单位。
建仓就是在覆盖率点中定义具体的取值范围或特定值。
2.2.1自动创建仓
正常情况下,自动创建仓就是根据覆盖点的值域范围创建,变量位宽为N,则会有2的N次方个仓(不会超过64个)。
在 SystemVerilog 中,auto_bin_max 是一个非常有用的选项,用于限制自动创建的 bins 数目。默认情况下,auto_bin_max 的值为 64,但可以通过显式设置来调整其值。
auto_bin_max 的用法
auto_bin_max 用于控制自动创建的 bins 的最大数目。当覆盖点(coverpoint)的值域超过指定的 auto_bin_max 时,SystemVerilog 会将值域平均分配到 auto_bin_max 个 bins 中。
为单个 coverpoint 设置 auto_bin_max
covergroup CovPort;
coverpoint tr.port {
option.auto_bin_max = 2; // 为 tr.port 设置最大 2 个自动 bin
}
endgroup
在这个例子中,tr.port 的值域会被自动分成 2 个 bins。
为整个 covergroup 设置 auto_bin_max
covergroup CovPort; option.auto_bin_max = 2; // 影响该 covergroup 中的所有 coverpoint coverpoint tr.port; coverpoint tr.data; endgroup
在这个例子中,auto_bin_max 的设置会应用于 CovPort 中的所有 coverpoint,每个 coverpoint 的值域都会被自动分成 2 个 bins。
注意事项
-
默认值:
auto_bin_max的默认值是 64。 -
适用范围:
auto_bin_max可以应用于单个coverpoint或整个covergroup。 -
值域分配:如果值域不能均匀分配到指定的
auto_bin_max数目中,最后一个bin会包含剩余的值。 -
手动定义 bins:如果手动定义了
bins,则auto_bin_max不会生效。
通过合理设置 auto_bin_max,可以有效控制自动创建的 bins 数目,从而简化覆盖率报告并提高分析效率。
2.2.2动态和静态建仓
bins len[] 是一种动态建仓的方式,表示为 len 创建一个动态的 bins,其具体值在运行时确定。这种方式通常用于覆盖点的值域较大或不确定时,工具会根据实际采样的值动态创建 bins。
coverpoint len {
bins len[] = { [0:$] }; // 动态创建 bins,覆盖 len 的所有可能值
}
-
[0:$]表示从 0 到最大值的范围。 -
len[]是动态的,工具会根据实际采样的值动态创建bins。
bins len 是一种静态建仓的方式,表示为 len 创建一个固定的 bins,其值在编译时确定。这种方式通常用于覆盖点的值域较小或已知时。
coverpoint len {
bins len = {0, 1, 2, 3, 4}; // 静态创建 bins,覆盖 len 的特定值
}
-
len是一个固定的bins,只包含指定的值(0, 1, 2, 3, 4)。 -
覆盖率报告
-
bins len[]生成的覆盖率报告中,bins的数目会较多,因为工具会根据实际采样的值动态创建并命令仓。 -
bins len生成的覆盖率报告中,bins的数目是固定的,因为工具在编译时就已经确定了。
-

用一个仓表示剩余的所有值
可以使用 default 关键字。default 用于捕获所有未明确列出的值。
coverpoint len {
bins specific = {0, 1, 2, 3, 4}; // 明确列出的值
bins others = default; // 捕获所有未明确列出的值
}
2.2.3交叉建仓
交叉覆盖点(cross)用于定义多个信号或表达式的组合覆盖率点。交叉覆盖点的 bins 定义方式与单个 coverpoint 类似,但需要考虑多个信号的组合。同时,可以通过 ignore_bins 和 illegal_bins 来忽略或标记某些组合。
监控组合覆盖率:
covergroup my_coverage @(posedge clk);
coverpoint signal1 {
bins low = {0};
bins high = {1};
}
coverpoint signal2 {
bins low = {0};
bins high = {1};
}
cross signal1, signal2 {
bins both_low = (signal1.low && signal2.low);
bins both_high = (signal1.high && signal2.high);
bins mixed = (signal1.low && signal2.high) || (signal1.high && signal2.low);
}
endgroup
忽略一部分交叉仓
使用 ignore_bins
ignore_bins 用于忽略某些特定的组合,这些组合不会被计入覆盖率统计。
covergroup my_coverage @(posedge clk);
coverpoint signal1 {
bins low = {0};
bins high = {1};
}
coverpoint signal2 {
bins low = {0};
bins high = {1};
}
cross signal1, signal2 {
bins both_high = (signal1.high && signal2.high);
bins mixed = (signal1.low && signal2.high) || (signal1.high && signal2.low);
ignore_bins both_low = (signal1.low && signal2.low);
}
endgroup
2.2.4覆盖点的权重设置
覆盖点(coverpoint)和交叉覆盖点(cross)的权重可以通过 option.weight 属性来设置。权重用于调整不同覆盖点或交叉覆盖点在总覆盖率计算中的重要性。通过设置权重,可以更灵活地控制覆盖率的计算方式,确保重点覆盖重要的功能场景。
权重的用途
权重用于调整覆盖率计算中各个覆盖点或交叉覆盖点的贡献。权重越高,该覆盖点或交叉覆盖点在总覆盖率中的贡献越大。最终得到不同的总体覆盖率。
设置覆盖点的权重
可以通过 option.weight 属性为每个覆盖点或交叉覆盖点设置权重。
covergroup my_coverage @(posedge clk);
coverpoint signal1 {
option.weight = 2; // 设置权重为 2
bins low = {0};
bins high = {1};
}
coverpoint signal2 {
option.weight = 1; // 设置权重为 1
bins low = {0};
bins high = {1};
}
endgroup
covergroup my_coverage @(posedge clk);
coverpoint signal1 {
bins low = {0};
bins high = {1};
}
coverpoint signal2 {
bins low = {0};
bins high = {1};
}
cross signal1, signal2 {
option.weight = 3; // 设置交叉覆盖点的权重为 3
bins both_low = (signal1.low && signal2.low);
bins both_high = (signal1.high && signal2.high);
bins mixed = (signal1.low && signal2.high) || (signal1.high && signal2.low);
}
endgroup
权重的默认值
如果未显式设置权重,option.weight 的默认值为 1。
权重对覆盖率计算的影响
权重会影响覆盖率的计算结果。覆盖率计算公式通常为:
总覆盖率=∑覆盖点权重∑(覆盖点权重×覆盖点覆盖率)
总覆盖率覆盖点权重覆盖点覆盖率覆盖点权重
2.2.5合理地创建仓
当需求不明确时,在一大堆自动创建的仓里很难寻找到覆盖不到的点。
(1)明确需求
在建仓之前,需要明确设计的功能需求和验证目标。覆盖率点应与设计的功能需求直接相关,确保覆盖所有重要的功能场景。
(2)合理划分 bins
-
单值 bins:
-
定义特定的取值。例如:
coverpoint signal { bins low = {0}; bins high = {1}; }
-
-
范围 bins:
-
定义一个取值范围。例如:
coverpoint baud_rate { bins standard = {9600, 19200, 38400, 57600, 115200}; bins range = [1200:9600]; }
-
-
通配符 bins:
-
使用通配符定义部分匹配的值。例如:
coverpoint address { wildcard bins addr = {8'b0000_????, 8'b1111_????}; }
-
-
默认 bins:
-
捕获未明确列出的其他值。例如:
coverpoint baud_rate { bins standard = {9600, 19200, 38400, 57600, 115200}; bins others = default; }
-
(3)使用 ignore_bins 和 illegal_bins
-
ignore_bins:
-
定义忽略的取值范围,这些值不会被计入覆盖率统计。例如:
coverpoint baud_rate { bins standard = {9600, 19200, 38400, 57600, 115200}; ignore_bins invalid = {0, 1}; }
-
-
illegal_bins:
-
定义非法的取值范围,这些值表示设计不应出现的情况。如果出现这些值,可能表示设计或测试用例有问题。例如:
coverpoint baud_rate { bins standard = {9600, 19200, 38400, 57600, 115200}; illegal_bins invalid = {0, 1}; }
-
2.3覆盖组定义的位置
基本上可以定义在任意位置,包括program、module、interface、class、package当中。
一般会写在组件中(class)中。
定义位置对代码的可读性、可维护性和功能实现有重要影响。选择合适的定义位置可以提高代码的结构清晰度和验证效率。
(1)在模块(Module)内部定义
如果覆盖组仅与特定模块相关,可以在模块内部定义。这种方式的优点是覆盖组的作用域被限制在模块内部,不会与其他模块的覆盖组冲突。
适用场景
-
模块内部信号:当覆盖组仅与模块内部的信号相关时,适合在模块内部定义。
-
局部验证:当验证目标仅限于模块内部时,这种方式可以避免全局污染。
(2)在模块外部定义
如果覆盖组需要在多个模块或测试环境中复用,可以在模块外部定义。这种方式的优点是可以提高代码的复用性,避免重复定义。
适用场景
-
复用性:当覆盖组需要在多个模块或测试环境中复用时,适合在模块外部定义。
-
全局验证:当验证目标涉及多个模块时,这种方式可以方便地在不同模块中实例化相同的覆盖组。
(3)在类(Class)中定义
如果覆盖组与特定的类相关,可以在类中定义。这种方式的优点是可以将覆盖组与类的其他逻辑紧密集成,提高代码的封装性。
适用场景
-
类封装:当覆盖组与类的其他逻辑紧密相关时,适合在类中定义。
-
面向对象验证:当验证目标涉及类的实例时,这种方式可以方便地在类实例中使用覆盖组。
(4)在测试环境中定义
如果覆盖组主要用于测试环境,可以在测试环境中定义。这种方式的优点是可以集中管理覆盖组,便于在测试环境中动态调整覆盖组的配置。
适用场景
-
测试环境:当覆盖组主要用于测试环境时,适合在测试环境中定义。
-
动态调整:当需要在测试环境中动态调整覆盖组的配置时,这种方式可以方便地进行调整。
(5)在包(Package)中定义
如果覆盖组需要在多个文件或模块中复用,可以在包中定义。这种方式的优点是可以提高代码的复用性和可维护性。
适用场景
-
复用性:当覆盖组需要在多个文件或模块中复用时,适合在包中定义。
-
项目结构:当项目结构较大,需要集中管理覆盖组时,这种方式可以提高代码的可维护性。
基于uvm结构考虑:
(1)driver中定义
某些模块的driver在系统级集成复用时会被passive掉,这时就无法查看功能点的覆盖情况。
(2)monitor中定义
系统级集成后可以重用
(3)refm中定义
参考模型中既有dut接口信号上的数据,也有寄存器的配置值,还有环境中需要用到的配置参数,也有队数据处理之后的结果
存在的问题是所有代码写在一个文件中将会非常大。
提供以下几种思路来解决这个问题:
1)结构化设计。把参考模型分解成多个,写成多个小的参考模型,在每个参考模型中编写该部分的功能覆盖率。
2)把覆盖组单独写在一个文件中,然后include进来
3)采用uvm提供的callback机制,把功能覆盖率部分的代码单独写在callback文件中。但是实现起来比较复杂,不能使用参考模型当中得到的中间值
4)采用port,将要采样的数据发送到单独一个文件中
(4)testcase中定义
不推荐。只能反复不断运行该用例,直到覆盖率得到100%,不通用不可复用。
综上,推荐把覆盖组写在参考模型中,使用第二种思路。
三、覆盖组的触发
覆盖组(covergroup)的触发是指在仿真过程中何时对覆盖组中的覆盖点(coverpoint)和交叉覆盖点(cross)进行采样。触发机制对于确保覆盖率数据的准确性和完整性至关重要。
3.1使用时钟边沿触发
最常见的方式是通过时钟信号的边沿(如上升沿或下降沿)触发覆盖组的采样。这可以通过在 covergroup 定义中指定采样事件来实现。
在 covergroup 定义中,可以通过 @(posedge clk) 或 @(negedge clk) 指定采样事件。
covergroup my_coverage @(posedge clk);
coverpoint signal1 {
bins low = {0};
bins high = {1};
}
coverpoint signal2 {
bins low = {0};
bins high = {1};
}
endgroup
my_coverage 的采样事件是时钟信号 clk 的上升沿。
3.2使用事件触发
覆盖组也可以通过特定的事件触发采样,例如某个信号的变化或某个条件的满足。
可以通过 $rose、$fell、$stable 等事件控制语句来触发覆盖组的采样。
covergroup my_coverage @(posedge clk);
coverpoint signal1 {
bins low = {0};
bins high = {1};
}
coverpoint signal2 {
bins low = {0};
bins high = {1};
}
option.per_instance = 1; // 每个实例独立采样
endgroup
module my_module;
logic clk;
logic signal1;
logic signal2;
initial begin
my_coverage cov = new();
forever @(posedge clk) begin
if ($rose(signal1)) cov.sample(); // 当 signal1 上升沿时采样
end
end
endmodule
覆盖组的采样被限制在 signal1 的上升沿。
3.3显式触发
在某些情况下,可以通过显式调用 sample 方法来触发覆盖组的采样。
可以通过显式调用 sample 方法来触发覆盖组的采样。这种方式提供了最大的灵活性,允许在任何需要的时刻进行采样。
covergroup my_coverage;
coverpoint signal1 {
bins low = {0};
bins high = {1};
}
coverpoint signal2 {
bins low = {0};
bins high = {1};
}
endgroup
module my_module;
logic clk;
logic signal1;
logic signal2;
my_coverage cov;
initial begin
cov = new();
forever @(posedge clk) begin
if (some_condition) cov.sample(); // 根据条件显式采样
end
end
endmodule
覆盖组的采样由 some_condition 控制。
3.4使用自定义 sample函数触发
自定义方式允许调用 sample 函数时显式地传递参数,而不是依赖于覆盖组内部的信号,并对这些参数进行覆盖率采样。
以在代码中的任何位置显式地调用 sample 方法,并传递需要采样的值。这种方式特别适用于以下场景:
-
动态采样:在运行时动态生成或计算的值需要采样。
-
条件采样:只有在满足特定条件时才需要采样。
-
跨模块采样:在不同的模块或类中生成的值需要采样。
covergroup cov with function sample(bit[3:0] da);
coverpoint da {
bins low = {0};
bins mid = {1, 2, 3};
bins high = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
}
endgroup : cov
module my_module;
logic [3:0] signal;
cov my_cov;
initial begin
my_cov = new(); // 创建覆盖组实例
end
always @(posedge clk) begin
if (some_condition) begin
my_cov.sample(signal); // 显式调用 sample 方法
end
end
endmodule
通过定义带有自定义 sample 函数的 covergroup,可以在代码中的任何位置显式地调用采样方法,并传递需要采样的值。这种方式特别适用于动态采样、条件采样和跨模块采样等场景。
3.5使用回调函数触发
在 SystemVerilog 和验证环境中,回调函数(Callback Function) 是一种允许用户在特定事件发生时执行自定义代码的机制。回调函数通常用于在验证过程中插入用户定义的行为,例如在事务发送、接收或完成时进行数据采样、覆盖率收集或调试信息输出。
回调函数的基本概念
回调函数是一种设计模式,允许用户在某个事件发生时提供一个函数(或方法),该函数会在事件触发时被调用。在验证环境中,回调函数通常用于以下场景:
-
事务处理:在事务发送、接收或完成时执行特定操作。
-
覆盖率收集:在特定事件发生时收集覆盖率数据。
-
调试信息输出:在关键事件发生时输出调试信息。
回调函数的实现方式
在 SystemVerilog 中,回调函数通常通过以下步骤实现:
-
定义回调类:创建一个继承自基础回调类的扩展类,并在其中定义回调方法。
-
注册回调实例:将回调类的实例注册到验证组件(如 Driver、Monitor 等)中。
-
触发回调:当特定事件发生时,验证组件会调用注册的回调方法。
1. 定义回调类
定义一个回调类,继承自基础回调类,并在其中定义覆盖组和回调方法。
class Driver_cbs_coverage extends Driver_cbs;
covergroup CovPort;
coverpoint tr.port {
bins low = {0};
bins high = {1};
}
coverpoint tr.data {
bins low = {0};
bins high = {1};
}
endgroup
function new();
CovPort = new();
endfunction
virtual task post_tx(Transaction tr);
CovPort.sample(); // 在回调函数中触发采样
endtask
endclass
2. 注册回调实例
在测试环境中,创建回调类的实例,并将其注册到验证组件中。
program automatic test; Environment env; initial begin Driver_cbs_coverage dcc; // 创建回调类的实例 env = new(); env.gen_cfg(); env.build(); dcc = new(); // 初始化回调类 env.drv.cbs.push_back(dcc); // 将回调实例注册到驱动器的回调队列中 env.run(); env.wrap_up(); end endprogram
3. 触发回调
当特定事件(如事务传输完成)发生时,验证组件会调用注册的回调方法(如 post_tx),从而触发覆盖组的采样。
回调函数的作用
-
灵活性:允许用户在特定事件发生时执行自定义代码,而不依赖于固定的时钟边沿或其他事件。
-
精确性:可以在事务完成或其他关键事件发生时精确地触发采样,确保覆盖率数据的准确性。
-
可扩展性:可以为不同的组件或事务类型定义多个回调类,从而实现细粒度的覆盖率监控。
回调函数的常见应用场景
-
事务发送和接收:
-
在事务发送或接收时进行数据采样或覆盖率收集。
-
示例:
pre_tx(事务发送前)、post_tx(事务发送后)、pre_rx(事务接收前)、post_rx(事务接收后)。
-
-
覆盖率收集:
-
在特定事件发生时触发覆盖组的采样。
-
在事务完成时收集覆盖率数据。
-
-
调试信息输出:
-
在关键事件发生时输出调试信息。
-
在事务发送或接收时打印事务内容。
-
回调函数的实现细节
-
回调类的定义:
-
回调类通常继承自验证环境中定义的基础回调类(如
Driver_cbs、Monitor_cbs等)。 -
在回调类中定义覆盖组和回调方法(如
post_tx、pre_rx等)。
-
-
回调方法的实现:
-
回调方法通常是一个任务(
task)或函数(function),在特定事件发生时被调用。 -
在回调方法中可以执行任何用户定义的操作,如数据采样、覆盖率收集或调试信息输出。
-
-
回调实例的注册:
-
将回调类的实例注册到验证组件中,通常通过调用组件的
push_back方法或类似的注册接口。 -
env.drv.cbs.push_back(dcc);
-
3.6使用断言进行触发
和用事件触发类似,只是用断言来触发事件,进而触发覆盖组采样
module top; ..... cover property ((@vif.mck) rd_en==1) ->cover_event; covergroup cov @cover_event; coverpoint data; endgroup:cov endmodule
四、查看功能覆盖率报告
4.1报告生成
4.1.1编译仿真参数
功能覆盖率不需要增加额外的编译仿真参数。
在编译和仿真时,代码覆盖率需要增加额外参数,确保启用了覆盖率收集选项:
# 编译命令 vcs -cm line+cond+fsm+tgl+branch+assert -cm_dir ./cov_dbs/covdb -cm_name my_test my_design.sv # 仿真命令 simv -cm line+cond+fsm+tgl+branch+assert -cm_dir ./cov_dbs/covdb -cm_name my_test -l sim.log
这些选项用于启用代码覆盖率的收集。
4.1.2覆盖率合并
urg是vcs的覆盖率合并功能,用于合并和生成覆盖率报告。
基本合并命令
urg -dir <path_to_vdb1> -dir <path_to_vdb2> ... -dir <path_to_vdbN> -dbname <output_vdb> urg -dir ./*.vdb -dbname merged.vdb
-
-dir:指定要合并的覆盖率数据库(.vdb)文件路径。 -
-dbname:指定合并后的输出数据库文件名。
4.1.3生成报告
在合并覆盖率数据后,urg还可以生成详细的覆盖率报告(网页):
urg -dir <merged_vdb> -report <output_report_directory> urg -dir ./cov_dbs/covdb -dbname my_test -report coverage_report
-
-report:指定生成报告的输出目录。
4.2报告查看
4.2.1使用SystemVerilog内置的覆盖率报告
在SystemVerilog中提供了大量的用于获得coverage的方法,方便了用户进行功能覆盖率的收集查看,比较常见的主要有:$get_coverage、get_coverage和get_inst_coverage,在SystemVerilog中关于这三个方法的描述如下:
$get_coverage:用来获取当前测试平台总体覆盖率,其值由所有的covergroup类型的覆盖率决定;
get_coverage:用来获取当前实例所对应的covergroup的覆盖率;
get_inst_coverage:用来获取当前covergroup实例的覆盖率;
4.2.2. 使用VCS的覆盖率报告
VCS提供了强大的覆盖率分析工具,可以通过命令行选项和图形化界面查看覆盖率数据。
生成的覆盖率报告可以通过以下工具查看:
-
使用 DVE 查看:
dve -full64 -covdir cov_path/simv.vdb
-
使用 Verdi 查看:verdi -cov -covdir cov_path/simv.vdb
-
使用浏览器查看 HTML 报告:
firefox cov_report/dashboard.html
这些工具会以图形化的方式显示覆盖率情况,方便分析。
509

被折叠的 条评论
为什么被折叠?



