systemverilog中covergroup的用法
SystemVerilog中的covergroup是一种用于功能覆盖的语言结构,它允许用户定义一组覆盖点(coverpoints)和交叉覆盖(cross coverage),以衡量验证的完整性。下面将详细说明covergroup的用法。
SystemVerilog Covergroup用法详解
一、基本概念
Covergroup是SystemVerilog中用于收集覆盖率的构造,它可以在类中定义,也可以在模块或接口中定义。Covergroup包含覆盖点(coverpoint)和交叉覆盖(cross),用于测量设计中的代码功能覆盖率。
二、基本语法
2.1 定义Covergroup
systemverilog
covergroup covergroup_name [([arguments])] [coverage_event];
// 覆盖点定义
// 选项设置
endgroup
2.2 简单示例
systemverilog
class my_class;
logic [3:0] data;
logic valid;
// 定义covergroup
covergroup data_cg @(posedge clk); // 在时钟上升沿采样
coverpoint data {
bins low = {[0:7]};
bins high = {[8:15]};
}
coverpoint valid;
endgroup
// 实例化covergroup
function new();
data_cg = new();
endfunction
endclass
三、覆盖点(Coverpoint)
3.1 基本覆盖点
systemverilog
covergroup cg;
// 覆盖一个变量
coverpoint var_name {
// 仓定义
}
endgroup
3.2 仓(Bins)定义
systemverilog
covergroup cg;
coverpoint data {
// 自动仓:根据变量的取值范围自动创建仓
// 手动定义仓
bins zero = {0};
bins small = {[1:10]};
bins large = {[11:100]};
// 忽略某些值
ignore_bins ignore_vals = {7, 8, 9};
// 非法值
illegal_bins illegal_vals = {101, 102};
}
endgroup
3.3 条件覆盖
systemverilog
covergroup cg @(posedge clk);
// 使用iff条件
coverpoint data iff (valid) {
bins low = {[0:127]};
bins high = {[128:255]};
}
// 使用条件表达式
coverpoint addr {
bins low_addr = {[0:'h3FF]} with (data == 0);
bins high_addr = {['h400:'h7FF]} with (data != 0);
}
endgroup
四、交叉覆盖(Cross Coverage)
4.1 基本交叉覆盖
systemverilog
covergroup cg;
coverpoint a;
coverpoint b;
// 交叉覆盖a和b
cross a, b {
// 可以在这里添加仓限制或条件
}
endgroup
4.2 限制交叉覆盖
systemverilog
covergroup cg;
coverpoint a {
bins a_bins[] = {[0:3]};
}
coverpoint b {
bins b_bins[] = {[0:3]};
}
cross a, b {
// 只关注某些交叉
bins a0_b0 = binsof(a) intersect {0} && binsof(b) intersect {0};
bins a1_b1 = binsof(a) intersect {1} && binsof(b) intersect {1};
// 忽略某些交叉
ignore_bins ignore = binsof(a) intersect {2} && binsof(b) intersect {2};
// 非法交叉
illegal_bins illegal = binsof(a) intersect {3} && binsof(b) intersect {3};
}
endgroup
五、选项(Options)
5.1 Covergroup选项
systemverilog
covergroup cg (int low, int high) @(posedge clk);
// 覆盖组选项
option.per_instance = 1; // 每个实例单独收集覆盖率
option.comment = "My covergroup";
option.at_least = 2; // 每个仓至少命中2次
option.auto_bin_max = 10; // 自动仓的最大数量
option.cross_auto_bin_max = 5; // 交叉覆盖自动仓的最大数量
coverpoint data {
option.weight = 2; // 此覆盖点的权重
option.goal = 90; // 覆盖率目标百分比
bins low = {[low:high]};
}
endgroup
5.2 覆盖点选项
systemverilog
covergroup cg;
coverpoint data {
option.auto_bin_max = 8;
option.weight = 1;
option.goal = 80;
// 其他仓定义...
}
endgroup
六、在类中使用Covergroup
6.1 类中定义和实例化
systemverilog
class transaction;
rand logic [7:0] addr;
rand logic [31:0] data;
rand logic wr_en;
// 定义covergroup
covergroup addr_data_cg;
coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
coverpoint data {
bins zero = {0};
bins small = {[1:1000]};
bins large = {[1001:$]};
}
coverpoint wr_en;
// 交叉覆盖
cross addr, wr_en {
bins wr_low = binsof(addr.low) && binsof(wr_en) intersect {1};
bins rd_low = binsof(addr.low) && binsof(wr_en) intersect {0};
}
endgroup
// 构造函数中实例化
function new();
addr_data_cg = new();
endfunction
// 采样函数
function void sample();
addr_data_cg.sample();
endfunction
endclass
七、采样触发
7.1 事件触发
systemverilog
covergroup cg @(posedge clk); // 使用时钟事件
coverpoint data;
endgroup
7.2 显式采样
systemverilog
covergroup cg;
coverpoint data;
endgroup
// 在代码中显式采样
cg cg_inst = new();
always @(posedge clk) begin
cg_inst.sample();
end
八、实际示例
8.1 存储器接口覆盖
systemverilog
class memory_if_monitor;
logic [31:0] addr;
logic [31:0] data;
logic wr_en;
logic rd_en;
logic valid;
logic ready;
// Covergroup for memory interface
covergroup mem_cg @(posedge clk iff valid);
// 地址覆盖点
cp_addr: coverpoint addr {
bins mem_range[4] = {[32'h0000_0000:32'h0000_FFFF]};
bins io_range = {[32'h1000_0000:32'h1000_0FFF]};
bins config_range = {[32'h2000_0000:32'h2000_00FF]};
}
// 数据覆盖点(只关注写数据)
cp_wr_data: coverpoint data iff (wr_en) {
bins zero = {0};
bins all_ones = {32'hFFFF_FFFF};
bins positive = {[1:32'h7FFF_FFFF]};
bins negative = {[32'h8000_0000:32'hFFFF_FFFE]};
}
// 操作类型覆盖点
cp_op_type: coverpoint {wr_en, rd_en} {
bins idle = {2'b00};
bins write = {2'b10};
bins read = {2'b01};
illegal_bins both = {2'b11}; // 不能同时读写
}
// 握手覆盖点
cp_handshake: coverpoint {valid, ready} {
bins idle = {2'b00};
bins request = {2'b10};
bins response = {2'b01};
bins active = {2'b11};
}
// 交叉覆盖:地址范围与操作类型
cross_addr_op: cross cp_addr, cp_op_type {
// 忽略无效交叉
ignore_bins ignore = binsof(cp_op_type.idle);
}
// 交叉覆盖:操作类型与握手
cross_op_handshake: cross cp_op_type, cp_handshake {
// 我们关心写操作时的握手序列
bins write_request = binsof(cp_op_type.write) &&
binsof(cp_handshake.request);
bins write_active = binsof(cp_op_type.write) &&
binsof(cp_handshake.active);
}
endgroup
// 实例化covergroup
function new();
mem_cg = new();
endfunction
endclass
8.2 带参数的Covergroup
systemverilog
class param_cg_example;
int min_val, max_val;
// 带参数的covergroup
covergroup range_cg (int min_val, int max_val) with function sample(int val);
// 覆盖传入的值
coverpoint val {
bins low = {[min_val:min_val+(max_val-min_val)/2]};
bins high = {[min_val+(max_val-min_val)/2+1:max_val]};
}
endgroup
// 实例化
function new(int min, int max);
range_cg = new(min, max);
endfunction
// 采样
function void sample_val(int val);
range_cg.sample(val);
endfunction
endclass
九、覆盖率查询和控制
9.1 查询覆盖率
systemverilog
// 获取覆盖率百分比
real coverage_percentage = cg_inst.get_coverage();
// 获取覆盖组实例的覆盖率
real inst_coverage = cg_inst.get_inst_coverage();
// 获取所有相同类型covergroup的覆盖率
real total_coverage = cg_inst::get_coverage();
9.2 覆盖率控制
systemverilog
// 设置覆盖率目标
cg_inst.set_inst_name("instance_name");
cg_inst.start(); // 开始收集覆盖率
cg_inst.stop(); // 停止收集覆盖率
十、最佳实践
明确覆盖目标:定义covergroup前明确要覆盖的功能点
避免过度覆盖:不要创建不必要的覆盖点,以免影响仿真性能
使用有意义的仓名:便于理解覆盖报告
定期检查覆盖率报告:指导测试用例的编写
结合断言使用:covergroup用于衡量覆盖率,断言用于检查设计正确性
十一、常见问题
性能问题:过多的覆盖点或交叉覆盖会降低仿真速度
内存消耗:大量的覆盖组实例可能消耗大量内存
覆盖率合并:多个实例的覆盖率可以合并,但需要注意选项设置
通过合理使用covergroup,可以有效地衡量验证的完整性,确保设计的所有重要功能都被测试到。
SystemVerilog Covergroup用法详解
一、Covergroup基本概念和语法
1.1 基本定义
Covergroup是SystemVerilog中用于收集功能覆盖率的构造,它可以:
- 定义覆盖点(coverpoints)
- 定义交叉覆盖(cross coverage)
- 在测试过程中自动或手动采样
// 基本语法
covergroup covergroup_name [(arguments)] [coverage_event];
// 选项(options)
// 覆盖点(coverpoints)
// 交叉覆盖(cross coverage)
endgroup
// 或者使用带sample方法的定义
covergroup covergroup_name [(arguments)] with function sample(arguments);
// 覆盖点定义
endgroup
1.2 简单示例
// 在类中定义covergroup
class packet;
rand bit [3:0] data;
rand bit [1:0] mode;
rand bit valid;
// 定义covergroup
covergroup cg @(posedge clk);
// 覆盖点
cp_data: coverpoint data {
bins low = {[0:7]};
bins high = {[8:15]};
}
cp_mode: coverpoint mode;
cp_valid: coverpoint valid;
endgroup
// 构造函数
function new();
cg = new(); // 实例化covergroup
endfunction
endclass
二、覆盖点(Coverpoint)详解
2.1 基本覆盖点
covergroup cg;
// 自动创建bins(根据数据类型)
coverpoint data; // 为4-bit数据自动创建16个bins
// 手动定义bins
coverpoint addr {
bins low = {[0:127]};
bins middle = {[128:255]};
bins high = {[256:511]};
}
// 指定数量的bins
coverpoint data {
option.auto_bin_max = 8; // 最多8个自动bins
}
endgroup
2.2 Bins类型和定义
covergroup cg;
coverpoint data {
// 1. 值bins(单个值或多个值)
bins zero = {0};
bins small = {1, 2, 3};
bins medium[] = {[4:7]}; // 4个bins: {4}, {5}, {6}, {7}
bins large = {[8:15]};
// 2. 条件bins
bins valid_data = data with (data > 0 && data < 10);
// 3. 转换bins(序列)
bins seq = (0 => 1 => 2);
bins seq_range = ([0:2] => [3:5]);
// 4. 忽略的bins
ignore_bins ignore = {8'hFF, 8'h00};
// 5. 非法的bins
illegal_bins illegal = {8'hFE};
}
endgroup
2.3 带条件的覆盖点
covergroup cg;
// 使用iff条件
coverpoint data iff (valid == 1'b1) {
bins valid_data = {[1:255]};
}
// 使用with条件
coverpoint addr {
bins low_addr = {[0:'h3FF]} with (data == 0);
bins high_addr = {['h400:'h7FF]} with (data != 0);
}
// 带权重的bins
coverpoint cmd {
option.weight = 2; // 这个覆盖点的权重
bins read = {0} with (weight = 3); // 这个bin的权重
bins write = {1};
bins other = default;
}
endgroup
三、交叉覆盖(Cross Coverage)
3.1 基本交叉覆盖
covergroup cg;
coverpoint a {
bins a_bins[] = {[0:3]};
}
coverpoint b {
bins b_bins[] = {[0:3]};
}
// 交叉覆盖a和b(产生16个交叉bin)
cross a, b;
// 命名的交叉覆盖
cross a_x_b: cross a, b;
endgroup
3.2 交叉覆盖的限制和选择
covergroup cg;
coverpoint a {
bins a0 = {0};
bins a1 = {1};
bins a2 = {2};
}
coverpoint b {
bins b0 = {0};
bins b1 = {1};
bins b2 = {2};
}
cross a, b {
// 只选择特定的交叉组合
bins a0_b0 = binsof(a.a0) && binsof(b.b0);
bins a1_b1 = binsof(a.a1) && binsof(b.b1);
// 忽略某些交叉组合
ignore_bins ignore = binsof(a) intersect {2} && binsof(b) intersect {2};
// 非法交叉组合
illegal_bins illegal = binsof(a.a0) && binsof(b.b1);
// 使用binsof函数选择
bins all_a0 = binsof(a.a0);
bins all_b1 = binsof(b.b1);
}
endgroup
3.3 带条件的交叉覆盖
covergroup transaction_cg with function sample(bit[31:0] addr, bit[31:0] data, bit wr);
coverpoint addr {
bins mem_range[4] = {[32'h0000_0000:32'h0000_FFFF]};
bins io_range = {[32'h1000_0000:32'h1000_0FFF]};
}
coverpoint data {
bins zero = {0};
bins positive = {[1:32'h7FFF_FFFF]};
bins negative = {[32'h8000_0000:32'hFFFF_FFFF]};
}
coverpoint wr {
bins read = {0};
bins write = {1};
}
// 条件交叉:只在写操作时交叉地址和数据
cross addr, data iff (wr == 1) {
// 自定义交叉bin
bins mem_write_zero = binsof(addr.mem_range) && binsof(data.zero);
bins io_write_positive = binsof(addr.io_range) && binsof(data.positive);
}
// 限制交叉的维度
cross wr, addr, data {
// 减少交叉数量
bins read_trans = binsof(wr.read) && binsof(addr.mem_range) && binsof(data.positive);
bins write_trans = binsof(wr.write) && binsof(addr.io_range) && binsof(data.zero);
}
endgroup
四、Covergroup选项
4.1 Covergroup级别选项
covergroup cg (int id) @(posedge clk);
// 基本选项
option.per_instance = 1; // 每个实例单独收集覆盖率
option.comment = "Covergroup for module XYZ";
option.name = "my_cg";
// 采样控制
option.strobe = 1; // 在时钟周期结束时采样
option.at_least = 2; // 每个bin至少命中2次
option.auto_bin_max = 64; // 自动bin的最大数量
option.cross_auto_bin_max = 8; // 交叉自动bin的最大数量
// 权重和阈值
option.weight = 5; // 这个covergroup的权重
option.goal = 95; // 覆盖率目标百分比
// 覆盖点
coverpoint data {
option.weight = 2;
option.goal = 100;
option.comment = "Data coverage";
}
// 交叉覆盖选项
cross data, mode {
option.weight = 3;
option.goal = 80;
option.at_least = 1;
}
endgroup
4.2 实例特定的选项
covergroup cg with function sample(int data);
option.per_instance = 1;
option.get_inst_coverage = 1; // 使能实例覆盖率查询
coverpoint data {
bins range[] = {[0:255]};
}
endgroup
// 创建多个实例
cg cg1 = new();
cg cg2 = new();
// 设置不同的选项
initial begin
cg1.option.at_least = 1;
cg2.option.at_least = 3;
cg1.option.comment = "Instance for port A";
cg2.option.comment = "Instance for port B";
end
五、实际应用示例
5.1 AXI总线覆盖组
class axi_coverage;
// AXI信号
bit [31:0] awaddr, araddr;
bit [31:0] wdata, rdata;
bit [2:0] awprot, arprot;
bit [3:0] wstrb;
bit [1:0] bresp, rresp;
bit awvalid, awready, wvalid, wready;
bit arvalid, arready, rvalid, rready;
bit bvalid, bready;
// 写地址通道covergroup
covergroup cg_write_addr @(posedge clk);
option.per_instance = 1;
option.name = "AXI_Write_Address";
cp_awaddr: coverpoint awaddr {
bins zero = {32'h0};
bins aligned_4k = {[32'h1000:32'h1FFF]}; // 4K对齐
bins unaligned = {[32'h1001:32'h1003]}; // 非对齐
bins high_mem = {[32'h8000_0000:32'hFFFF_FFFF]};
bins other = default;
}
cp_awprot: coverpoint awprot {
bins normal = {3'b000};
bins privileged = {3'b001};
bins secure = {3'b010};
bins cacheable = {3'b100};
illegal_bins illegal = {3'b111}; // 保留值
}
cp_aw_handshake: coverpoint {awvalid, awready} {
bins idle = {2'b00};
bins request = {2'b10};
bins response = {2'b01};
bins active = {2'b11};
}
// 交叉覆盖
cross_aw: cross cp_awaddr, cp_awprot {
bins normal_access = binsof(cp_awaddr.aligned_4k) &&
binsof(cp_awprot.normal);
bins secure_high = binsof(cp_awaddr.high_mem) &&
binsof(cp_awprot.secure);
}
endgroup
// 写数据通道covergroup
covergroup cg_write_data with function sample(bit[31:0] data, bit[3:0] strb);
option.name = "AXI_Write_Data";
cp_wdata: coverpoint data {
bins zero = {0};
bins all_ones = {32'hFFFF_FFFF};
bins byte_pattern = {32'hA5A5_A5A5};
bins increment = {32'h0000_0001, 32'h0000_0002, 32'h0000_0003};
}
cp_wstrb: coverpoint strb {
bins no_write = {4'b0000};
bins byte0 = {4'b0001};
bins byte1 = {4'b0010};
bins byte2 = {4'b0100};
bins byte3 = {4'b1000};
bins half_word = {4'b0011, 4'b1100};
bins full_word = {4'b1111};
bins partial = default;
}
cp_w_handshake: coverpoint {wvalid, wready} {
bins idle = {2'b00};
bins request = {2'b10};
bins response = {2'b01};
bins active = {2'b11};
}
// 数据与字节使能的交叉
cross_data_strb: cross cp_wdata, cp_wstrb {
bins zero_with_full = binsof(cp_wdata.zero) &&
binsof(cp_wstrb.full_word);
bins ones_with_partial = binsof(cp_wdata.all_ones) &&
binsof(cp_wstrb.partial);
}
endgroup
// 构造函数
function new();
cg_write_addr = new();
cg_write_data = new();
endfunction
// 采样函数
function void sample_write_addr();
cg_write_addr.sample();
endfunction
function void sample_write_data();
cg_write_data.sample(wdata, wstrb);
endfunction
endclass
5.2 状态机覆盖组
module fsm_coverage;
// 状态机定义
typedef enum logic [2:0] {
IDLE = 3'b000,
START = 3'b001,
DATA = 3'b010,
PARITY = 3'b011,
STOP = 3'b100,
ERROR = 3'b101
} state_t;
state_t current_state, next_state;
bit [7:0] data_reg;
bit parity_error;
int data_count;
// 状态转移covergroup
covergroup cg_fsm_transitions @(posedge clk);
option.name = "FSM_State_Transitions";
option.comment = "Cover state transitions and sequences";
cp_current: coverpoint current_state {
bins states[] = {[IDLE:ERROR]};
illegal_bins invalid = {3'b110, 3'b111};
}
cp_next: coverpoint next_state {
bins states[] = {[IDLE:ERROR]};
}
// 状态转移序列
cp_transition: coverpoint {current_state, next_state} {
// 合法转移
bins idle_to_start = {IDLE, START};
bins start_to_data = {START, DATA};
bins data_to_parity = {DATA, PARITY};
bins parity_to_stop = {PARITY, STOP};
bins stop_to_idle = {STOP, IDLE};
bins any_to_error = {[IDLE:STOP], ERROR};
// 非法转移(如果发生则报错)
illegal_bins illegal = {
{START, IDLE},
{DATA, START},
{PARITY, DATA},
{STOP, PARITY},
{ERROR, STOP}
};
}
// 状态驻留时间
cp_state_duration: coverpoint data_count {
bins short = {[0:1]};
bins medium = {[2:5]};
bins long = {[6:10]};
bins very_long = {[11:20]};
}
// 带条件的状态覆盖
cp_state_with_error: coverpoint current_state {
bins normal_states = {IDLE, START, DATA, PARITY, STOP}
with (parity_error == 0);
bins error_state = {ERROR} with (parity_error == 1);
}
// 复杂交叉:状态转移与数据计数
cross_transition_duration: cross cp_transition, cp_state_duration {
// 只关注数据状态到校验状态的转移
bins data_to_parity_with_count =
binsof(cp_transition.data_to_parity) &&
binsof(cp_state_duration.medium);
}
// 序列覆盖
cp_state_sequence: coverpoint current_state {
// 检查特定的状态序列
bins idle_start_data = (IDLE => START => DATA);
bins full_packet = (IDLE => START => DATA[*1:8] => PARITY => STOP => IDLE);
}
endgroup
// 实例化covergroup
cg_fsm_transitions fsm_cg = new();
// 数据覆盖组
covergroup cg_data @(posedge clk iff (current_state == DATA));
option.name = "FSM_Data_Coverage";
cp_data_value: coverpoint data_reg {
bins zero = {0};
bins max = {255};
bins low = {[1:127]};
bins high = {[128:254]};
bins ascii_printable = {[32:126]};
bins control_chars = {[0:31], 127};
}
cp_parity: coverpoint ^data_reg { // 奇偶校验位
bins even = {0};
bins odd = {1};
}
// 数据值与奇偶校验的交叉
cross_data_parity: cross cp_data_value, cp_parity {
bins zero_even = binsof(cp_data_value.zero) &&
binsof(cp_parity.even);
bins max_odd = binsof(cp_data_value.max) &&
binsof(cp_parity.odd);
}
endgroup
cg_data data_cg = new();
endmodule
5.3 缓存控制器覆盖组
class cache_controller_coverage;
// 缓存信号
bit [31:0] addr;
bit [63:0] data;
bit rd_req, wr_req;
bit hit, miss;
bit [1:0] cache_state; // 00:Invalid, 01:Shared, 10:Exclusive, 11:Modified
bit [2:0] burst_count;
// 地址空间covergroup
covergroup cg_address_space with function sample(bit[31:0] address);
option.per_instance = 1;
option.name = "Cache_Address_Space";
// 按cache line对齐划分
cp_addr_aligned: coverpoint address[5:0] { // cache line内的偏移
bins aligned_0 = {0};
bins aligned_8 = {8};
bins aligned_16 = {16};
bins aligned_32 = {32};
bins aligned_48 = {48};
bins aligned_56 = {56};
bins unaligned = default;
}
cp_addr_range: coverpoint address {
// 按内存区域划分
bins dram_range[4] = {[32'h0000_0000:32'h3FFF_FFFF]};
bins mmio_range = {[32'h4000_0000:32'h4000_FFFF]};
bins flash_range = {[32'h8000_0000:32'h9FFF_FFFF]};
bins reserved = {[32'hC000_0000:32'hFFFF_FFFF]};
}
cp_cache_set: coverpoint address[11:6] { // cache set index
option.auto_bin_max = 64;
}
cp_cache_tag: coverpoint address[31:12] { // cache tag
bins zero_tag = {0};
bins non_zero = {[1:20'hFFFFF]};
}
endgroup
// 缓存命中/失效covergroup
covergroup cg_cache_operation @(posedge clk);
option.name = "Cache_Operation";
cp_op_type: coverpoint {rd_req, wr_req} {
bins idle = {2'b00};
bins read = {2'b10};
bins write = {2'b01};
illegal_bins both = {2'b11};
}
cp_cache_result: coverpoint hit {
bins hit = {1};
bins miss = {0};
}
cp_cache_state: coverpoint cache_state {
bins invalid = {0};
bins shared = {1};
bins exclusive = {2};
bins modified = {3};
}
cp_burst_length: coverpoint burst_count {
bins single = {0};
bins burst_4 = {4};
bins burst_8 = {8};
bins burst_16 = {16};
bins other = default;
}
// 操作类型与命中/失效的交叉
cross_op_hit: cross cp_op_type, cp_cache_result {
bins read_hit = binsof(cp_op_type.read) && binsof(cp_cache_result.hit);
bins read_miss = binsof(cp_op_type.read) && binsof(cp_cache_result.miss);
bins write_hit = binsof(cp_op_type.write) && binsof(cp_cache_result.hit);
bins write_miss = binsof(cp_op_type.write) && binsof(cp_cache_result.miss);
}
// 缓存状态与操作类型的交叉
cross_state_op: cross cp_cache_state, cp_op_type {
// 关注从不同状态开始的读写
bins read_from_shared = binsof(cp_cache_state.shared) &&
binsof(cp_op_type.read);
bins write_to_exclusive = binsof(cp_cache_state.exclusive) &&
binsof(cp_op_type.write);
bins write_to_modified = binsof(cp_cache_state.modified) &&
binsof(cp_op_type.write);
}
// 突发传输与命中/失效的交叉
cross_burst_hit: cross cp_burst_length, cp_cache_result {
bins burst4_hit = binsof(cp_burst_length.burst_4) &&
binsof(cp_cache_result.hit);
bins burst8_miss = binsof(cp_burst_length.burst_8) &&
binsof(cp_cache_result.miss);
}
endgroup
// 数据模式covergroup
covergroup cg_data_pattern with function sample(bit[63:0] data, bit wr_op);
option.name = "Cache_Data_Patterns";
cp_data_pattern: coverpoint data {
// 常见数据模式
bins all_zeros = {64'h0};
bins all_ones = {64'hFFFF_FFFF_FFFF_FFFF};
bins walking_1 = {64'h0000_0000_0000_0001,
64'h0000_0000_0000_0002,
64'h0000_0000_0000_0004,
64'h0000_0000_0000_0008};
bins walking_0 = {64'hFFFF_FFFF_FFFF_FFFE,
64'hFFFF_FFFF_FFFF_FFFD,
64'hFFFF_FFFF_FFFF_FFFB,
64'hFFFF_FFFF_FFFF_FFF7};
bins alternating = {64'hAAAA_AAAA_AAAA_AAAA,
64'h5555_5555_5555_5555};
bins byte_boundary = {64'h0000_0000_0000_00FF,
64'h0000_0000_0000_FF00,
64'h0000_0000_00FF_0000};
}
cp_data_alignment: coverpoint data {
// 检查数据对齐情况
bins word_aligned = data with (data[31:0] == 0 || data[63:32] == 0);
bins dword_aligned = data with (data[31:0] == data[63:32]);
}
// 数据模式与操作类型的交叉(只在写操作时关注)
cross_pattern_op: cross cp_data_pattern, cp_data_alignment iff (wr_op) {
bins zeros_aligned = binsof(cp_data_pattern.all_zeros) &&
binsof(cp_data_alignment.word_aligned);
bins ones_unaligned = binsof(cp_data_pattern.all_ones) &&
!binsof(cp_data_alignment.word_aligned);
}
endgroup
// 构造函数
function new();
cg_address_space = new();
cg_cache_operation = new();
cg_data_pattern = new();
endfunction
// 采样方法
function void sample_address(bit[31:0] address);
cg_address_space.sample(address);
endfunction
function void sample_operation();
cg_cache_operation.sample();
endfunction
function void sample_data(bit[63:0] data, bit wr_op);
cg_data_pattern.sample(data, wr_op);
endfunction
// 覆盖率查询方法
function real get_coverage();
real total = 0;
total += cg_address_space.get_coverage() * 0.3;
total += cg_cache_operation.get_coverage() * 0.4;
total += cg_data_pattern.get_coverage() * 0.3;
return total;
endfunction
endclass
5.4 配置寄存器覆盖组
class config_register_coverage;
// 配置寄存器
bit [15:0] config_reg;
bit config_valid;
int config_write_count;
// 寄存器域定义
bit [1:0] mode = config_reg[1:0];
bit [2:0] speed = config_reg[4:2];
bit enable = config_reg[5];
bit [7:0] threshold = config_reg[13:6];
bit interrupt = config_reg[14];
bit reset = config_reg[15];
// 配置寄存器covergroup
covergroup cg_config_reg @(posedge clk iff (config_valid));
option.name = "Configuration_Register";
option.per_instance = 1;
option.at_least = 2; // 每个bin至少命中2次
// 各个域的覆盖点
cp_mode: coverpoint mode {
bins mode0 = {0}; // 低速模式
bins mode1 = {1}; // 中速模式
bins mode2 = {2}; // 高速模式
bins mode3 = {3}; // 自动模式
}
cp_speed: coverpoint speed {
bins speed0 = {0}; // 1x
bins speed1 = {1}; // 2x
bins speed2 = {2}; // 4x
bins speed3 = {3}; // 8x
bins speed4 = {4}; // 16x
bins speed5 = {5, 6, 7}; // 保留
}
cp_enable: coverpoint enable {
bins disabled = {0};
bins enabled = {1};
}
cp_threshold: coverpoint threshold {
bins zero = {0};
bins low = {[1:63]};
bins medium = {[64:127]};
bins high = {[128:191]};
bins max = {[192:255]};
}
cp_interrupt: coverpoint interrupt {
bins disabled = {0};
bins enabled = {1};
}
cp_reset: coverpoint reset {
bins normal = {0};
bins reset_active = {1};
}
// 域之间的交叉覆盖
cross_mode_speed: cross cp_mode, cp_speed {
// 检查模式和速度的组合是否合理
bins low_speed = binsof(cp_mode.mode0) &&
binsof(cp_speed.speed0);
bins high_speed = binsof(cp_mode.mode2) &&
binsof(cp_speed.speed4);
// 忽略不合理的组合
ignore_bins invalid =
(binsof(cp_mode.mode0) && binsof(cp_speed.speed4)) ||
(binsof(cp_mode.mode2) && binsof(cp_speed.speed0));
}
cross_enable_threshold: cross cp_enable, cp_threshold {
// 使能时关注阈值设置
bins enabled_low_thresh = binsof(cp_enable.enabled) &&
binsof(cp_threshold.low);
bins enabled_high_thresh = binsof(cp_enable.enabled) &&
binsof(cp_threshold.high);
}
// 多域交叉
cross_mode_speed_enable: cross cp_mode, cp_speed, cp_enable {
// 重点关注使能状态下的组合
bins enabled_combinations =
binsof(cp_enable.enabled) &&
(binsof(cp_mode.mode1) || binsof(cp_mode.mode2)) &&
(binsof(cp_speed.speed1) || binsof(cp_speed.speed2));
}
// 寄存器值序列覆盖
cp_reg_sequence: coverpoint config_reg {
// 检查特定的配置序列
bins power_on_config = (16'h0000 => 16'h0021 => 16'h0061);
bins mode_transition = (16'h0061 => 16'h00E1 => 16'h0161);
bins reset_sequence = (16'h8000 => 16'h0000 => 16'h0061);
}
// 配置写入次数覆盖
cp_write_count: coverpoint config_write_count {
bins first_write = {1};
bins few_writes = {[2:5]};
bins many_writes = {[6:20]};
bins excessive_writes = {[21:100]};
}
endgroup
// 非法配置检测covergroup
covergroup cg_illegal_config @(posedge clk);
option.name = "Illegal_Config_Detection";
// 非法配置模式
cp_illegal_combinations: coverpoint config_reg {
illegal_bins illegal_mode_speed =
{16'h0004, 16'h0005, 16'h0006, 16'h0007}; // 模式0+高速
illegal_bins illegal_threshold_enable =
{16'h0020, 16'h0060}; // 使能为0但中断使能
illegal_bins reserved_bits = config_reg with
(config_reg[3] == 1 || config_reg[12] == 1); // 保留位为1
}
// 配置验证序列
cp_config_sequence: coverpoint config_reg {
// 非法序列
illegal_bins illegal_sequence[] = (
16'h8000 => 16'h0061, // 复位后直接配置
16'h0061 => 16'h8000, // 配置后立即复位
16'h0000 => 16'h8000 // 从0直接跳转到复位
);
}
endgroup
// 构造函数
function new();
cg_config_reg = new();
cg_illegal_config = new();
endfunction
// 采样方法
function void sample_config();
cg_config_reg.sample();
cg_illegal_config.sample();
endfunction
// 覆盖率报告
function void report_coverage();
real reg_cov = cg_config_reg.get_coverage();
real illegal_cov = cg_illegal_config.get_coverage();
$display("=== Configuration Register Coverage ===");
$display("Legal config coverage: %0.2f%%", reg_cov);
$display("Illegal config coverage: %0.2f%%", illegal_cov);
$display("Overall coverage: %0.2f%%", (reg_cov + illegal_cov) / 2);
// 详细报告
if (reg_cov < 90) begin
$display("WARNING: Legal configuration coverage below 90%%");
cg_config_reg.get_inst_coverage(); // 获取详细覆盖率
end
endfunction
endclass
六、Covergroup高级特性
6.1 动态Covergroup创建
class dynamic_coverage;
// 动态covergroup数组
covergroup cg_array[int] with function sample(bit[31:0] data);
option.per_instance = 1;
coverpoint data {
bins ranges[] = {[0:255]};
}
endgroup
// 动态创建covergroup
function void create_cg(int id);
if (!cg_array.exists(id)) begin
cg_array[id] = new();
cg_array[id].option.comment = $sformatf("CG for ID %0d", id);
end
endfunction
// 动态采样
function void sample_data(int id, bit[31:0] data);
if (cg_array.exists(id)) begin
cg_array[id].sample(data);
end
endfunction
// 获取总体覆盖率
function real get_total_coverage();
real total = 0;
int count = 0;
foreach (cg_array[i]) begin
total += cg_array[i].get_coverage();
count++;
end
return (count > 0) ? total / count : 0;
endfunction
endclass
6.2 覆盖率控制和查询
module coverage_control;
covergroup cg with function sample(int data);
option.per_instance = 1;
option.get_inst_coverage = 1;
coverpoint data {
bins values[] = {[0:9]};
}
endgroup
cg cg_inst = new();
// 覆盖率控制
initial begin
// 开始收集覆盖率
cg_inst.start();
// 运行测试
#1000;
// 停止收集
cg_inst.stop();
// 查询覆盖率
$display("Instance coverage: %0.2f%%",
cg_inst.get_inst_coverage());
$display("Type coverage: %0.2f%%",
cg::get_coverage());
// 获取详细覆盖率
cg_inst.get_coverage_details();
// 重置覆盖率
if (cg_inst.get_inst_coverage() < 80) begin
$display("Coverage too low, resetting and retrying");
cg_inst.reset();
cg_inst.start();
end
end
endmodule
七、最佳实践和调试技巧
7.1 调试Covergroup
class coverage_debug;
covergroup debug_cg with function sample(bit[7:0] data, bit valid);
option.per_instance = 1;
option.comment = "Debug coverage group";
// 添加调试选项
option.debug = 1; // 启用调试信息
coverpoint data iff (valid) {
// 输出采样信息
bins low = {[0:127]} with
($display("Sampled low value: %h at %0t", data, $time));
bins high = {[128:255]} with
($display("Sampled high value: %h at %0t", data, $time));
}
// 覆盖率不足时报警
cross data, valid {
option.goal = 100;
bins valid_data = binsof(data) && binsof(valid) intersect {1} with
(if (coverage < 50)
$warning("Cross coverage low: %0.2f%%", coverage));
}
endgroup
// 覆盖率监控线程
task monitor_coverage();
real last_cov = 0;
forever begin
#1000;
real current_cov = debug_cg.get_inst_coverage();
if (current_cov - last_cov < 1.0) begin
$display("Coverage growth slow: %0.2f%% -> %0.2f%%",
last_cov, current_cov);
debug_cg.get_coverage_details(); // 打印详细报告
end
last_cov = current_cov;
end
endtask
endclass
7.2 性能优化
class optimized_coverage;
// 性能优化的covergroup
covergroup fast_cg @(posedge clk);
// 优化选项
option.per_instance = 0; // 共享实例减少内存
option.auto_bin_max = 32; // 限制自动bin数量
option.cross_num_print_missing = 1000; // 减少输出
// 减少覆盖点
coverpoint important_signal {
option.weight = 5; // 增加重要信号的权重
bins critical[] = {[0:15]};
}
// 减少交叉维度
cross important_signal, other_signal {
option.cross_auto_bin_max = 16; // 限制交叉bin数量
}
endgroup
// 条件采样减少开销
covergroup conditional_cg with function sample_when_needed();
option.sample_on = 0; // 禁用自动采样
coverpoint data {
// 只在特定条件下采样
bins values[] = {[0:255]};
}
endgroup
function void sample_conditionally(bit sample_en);
if (sample_en && $time > 1000) begin // 条件采样
conditional_cg.sample();
end
endfunction
endclass
八、总结
8.1 Covergroup的关键特性:
- 覆盖点(Coverpoints):收集变量值的覆盖率
- 仓(Bins):定义感兴趣的值或值范围
- 交叉覆盖(Cross Coverage):检查多个变量组合的覆盖率
- 选项(Options):控制采样行为、权重、目标等
- 条件覆盖:使用iff、with等条件
8.2 实际应用建议:
- 聚焦关键功能:优先覆盖设计的关键路径和功能
- 合理划分bins:避免过多或过少的bins
- 管理交叉覆盖:注意交叉爆炸问题,合理选择交叉维度
- 分层覆盖:从基本功能到复杂场景逐步覆盖
- 定期审查:定期检查覆盖率报告,指导测试开发
8.3 常见陷阱:
- 覆盖爆炸:过多的交叉覆盖导致性能问题
- 无效覆盖:覆盖了不重要的功能点
- 采样时机不当:在信号不稳定时采样
- 条件过于严格:导致某些场景永远无法覆盖
Covergroup是验证过程中确保功能覆盖完整性的重要工具,合理使用可以显著提高验证质量。
浙公网安备 33010602011771号