SystemVerilog【六】功能覆盖率详解
📖 扩展学习资源:
文章目录
1. 功能覆盖率概述
1.1 什么是功能覆盖率
功能覆盖率(Functional Coverage)是SystemVerilog中用于衡量验证完整性的重要机制。与代码覆盖率不同,功能覆盖率关注的是设计功能点是否被充分验证,而不仅仅是代码是否被执行。
传统Verilog的局限性:
- 缺乏内建的覆盖率机制
- 需要手动编写复杂的统计代码
- 难以准确衡量验证完整性
- 覆盖率数据收集困难
SystemVerilog功能覆盖率的优势:
- 内建的covergroup和coverpoint语法
- 自动化的覆盖率数据收集
- 灵活的bins定义和交叉覆盖
- 与仿真工具集成的覆盖率报告
// 传统Verilog的覆盖率统计(复杂且易出错)
module verilog_coverage_example;
reg [7:0] data;
reg [1:0] mode;
// 手动统计变量
integer data_low_count = 0;
integer data_mid_count = 0;
integer data_high_count = 0;
integer mode_count[0:3];
integer total_samples = 0;
always @(posedge clk) begin
if (sample_enable) begin
total_samples = total_samples + 1;
// 手动分类统计
if (data < 8'h40)
data_low_count = data_low_count + 1;
else if (data < 8'h80)
data_mid_count = data_mid_count + 1;
else
data_high_count = data_high_count + 1;
mode_count[mode] = mode_count[mode] + 1;
end
end
// 手动计算覆盖率
task calculate_coverage;
real data_coverage, mode_coverage;
integer i;
$display("=== 手动覆盖率统计 ===");
$display("总采样数: %d", total_samples);
// 数据覆盖率
if (data_low_count > 0) $display("数据低段已覆盖");
if (data_mid_count > 0) $display("数据中段已覆盖");
if (data_high_count > 0) $display("数据高段已覆盖");
// 模式覆盖率
for (i = 0; i < 4; i++) begin
if (mode_count[i] > 0)
$display("模式%d已覆盖: %d次", i, mode_count[i]);
end
endtask
endmodule
// SystemVerilog功能覆盖率(简洁明了)
module systemverilog_coverage_example;
logic [7:0] data;
logic [1:0] mode;
logic clk, sample_enable;
// 定义覆盖率组
covergroup data_mode_cg @(posedge clk iff sample_enable);
// 数据覆盖点
data_cp: coverpoint data {
bins low = {[8'h00:8'h3F]};
bins mid = {[8'h40:8'h7F]};
bins high = {[8'h80:8'hFF]};
}
// 模式覆盖点
mode_cp: coverpoint mode {
bins mode0 = {2'b00};
bins mode1 = {2'b01};
bins mode2 = {2'b10};
bins mode3 = {2'b11};
}
// 交叉覆盖
data_mode_cross: cross data_cp, mode_cp;
endgroup
// 实例化覆盖率组
data_mode_cg cg_inst = new();
// 自动覆盖率报告
final begin
$display("=== SystemVerilog覆盖率报告 ===");
$display("总体覆盖率: %0.2f%%", cg_inst.get_coverage());
$display("数据覆盖率: %0.2f%%", cg_inst.data_cp.get_coverage());
$display("模式覆盖率: %0.2f%%", cg_inst.mode_cp.get_coverage());
$display("交叉覆盖率: %0.2f%%", cg_inst.data_mode_cross.get_coverage());
end
endmodule
1.2 如何在验证中使用功能覆盖率
功能覆盖率在验证中的主要用途:
- 验证完整性评估:确保所有功能点都被测试
- 测试用例指导:识别未覆盖的功能点,指导测试用例生成
- 回归测试优化:基于覆盖率数据优化测试套件
- 验证收敛判断:作为验证完成的重要指标
module verification_flow_example;
// 设计接口信号
logic clk, reset;
logic [7:0] addr, data;
logic read, write, ready;
logic [1:0] burst_type;
// 验证环境中的覆盖率定义
covergroup memory_transaction_cg @(posedge clk iff (read || write));
// 地址覆盖
addr_cp: coverpoint addr {
bins low_addr = {[8'h00:8'h3F]};
bins mid_addr = {[8'h40:8'h7F]};
bins high_addr = {[8'h80:8'hBF]};
bins special_addr = {8'hC0, 8'hFF};
}
// 数据覆盖
data_cp: coverpoint data {
bins zero = {8'h00};
bins low_data = {[8'h01:8'h7F]};
bins high_data = {[8'h80:8'hFE]};
bins all_ones = {8'hFF};
}
// 操作类型覆盖
operation_cp: coverpoint {read, write} {
bins read_op = {2'b10};
bins write_op = {2'b01};
illegal_bins invalid = {2'b00, 2'b11};
}
// 突发类型覆盖
burst_cp: coverpoint burst_type {
bins single = {2'b00};
bins incr = {2'b01};
bins wrap = {2'b10};
bins fixed = {2'b11};
}
// 关键交叉覆盖
addr_operation_cross: cross addr_cp, operation_cp {
ignore_bins ignore_special_read = binsof(addr_cp.special_addr) &&
binsof(operation_cp.read_op);
}
data_burst_cross: cross data_cp, burst_cp;
endgroup
memory_transaction_cg mem_cg = new();
// 验证任务:检查覆盖率并生成报告
task check_coverage_and_guide_testing;
real addr_cov, data_cov, op_cov, burst_cov, total_cov;
total_cov = mem_cg.get_coverage();
addr_cov = mem_cg.addr_cp.get_coverage();
data_cov = mem_cg.data_cp.get_coverage();
op_cov = mem_cg.operation_cp.get_coverage();
burst_cov = mem_cg.burst_cp.get_coverage();
$display("\n=== 覆盖率指导报告 ===");
$display("总体覆盖率: %0.2f%%", total_cov);
$display("地址覆盖率: %0.2f%%", addr_cov);
$display("数据覆盖率: %0.2f%%", data_cov);
$display("操作覆盖率: %0.2f%%", op_cov);
$display("突发覆盖率: %0.2f%%", burst_cov);
// 基于覆盖率指导测试
if (addr_cov < 90.0) begin
$display("建议:增加地址边界测试用例");
end
if (data_cov < 90.0) begin
$display("建议:增加特殊数据值测试");
end
if (total_cov >= 95.0) begin
$display("验证目标达成,可以考虑收敛");
end else begin
$display("需要继续测试,目标覆盖率: 95%%");
end
endtask
// 定期检查覆盖率
initial begin
repeat (1000) @(posedge clk);
check_coverage_and_guide_testing();
repeat (1000) @(posedge clk);
check_coverage_and_guide_testing();
$finish;
end
endmodule
2.2 covergroup参数
covergroup支持多种参数来控制覆盖率的行为和采样方式。
module covergroup_options_example;
logic clk, reset;
logic [7:0] addr, data;
logic read, write;
// 带选项的covergroup
covergroup options_cg @(posedge clk iff !reset);
// 全局选项设置
option.per_instance = 1; // 每个实例独立统计
option.goal = 95; // 目标覆盖率95%
option.name = "addr_data_coverage"; // 覆盖率组名称
option.comment = "地址和数据覆盖率分析"; // 注释
option.at_least = 2; // 每个bin至少命中2次
option.detect_overlap = 1; // 检测bin重叠
option.auto_bin_max = 64; // 自动bin的最大数量
// 地址覆盖点
addr_cp: coverpoint addr {
option.at_least = 3; // 覆盖点级别的选项
option.goal = 90;
bins low_addr = {[8'h00:8'h3F]};
bins mid_addr = {[8'h40:8'h7F]};
bins high_addr = {[8'h80:8'hBF]};
bins special = {8'hC0, 8'hFF};
}
// 数据覆盖点
data_cp: coverpoint data {
option.weight = 2; // 权重设置
option.goal = 100; // 目标100%覆盖
bins zero = {8'h00};
bins powers_of_2[] = {8'h01, 8'h02, 8'h04, 8'h08,
8'h10, 8'h20, 8'h40, 8'h80};
bins high_data = {[8'h81:8'hFE]};
bins all_ones = {8'hFF};
}
// 操作覆盖点
operation_cp: coverpoint {read, write} {
bins read_only = {2'b10};
bins write_only = {2'b01};
illegal_bins both = {2'b11};
ignore_bins idle = {2'b00};
}
// 交叉覆盖
addr_data_cross: cross addr_cp, data_cp {
option.goal = 80; // 交叉覆盖目标80%
option.at_least = 1; // 至少命中1次
// 忽略某些组合
ignore_bins ignore_special_zero =
binsof(addr_cp.special) && binsof(data_cp.zero);
}
endgroup
// 类型级别的covergroup选项
covergroup type_options_cg @(posedge clk);
type_option.goal = 85; // 类型级别目标
type_option.comment = "类型级别覆盖率";
type_option.strobe = 1; // 使用strobe采样
addr_cp: coverpoint addr {
bins addr_ranges[] = {[0:63], [64:127], [128:191], [192:255]};
}
endgroup
// 实例化不同选项的covergroup
options_cg opt_cg1 = new();
options_cg opt_cg2 = new();
type_options_cg type_cg = new();
// 动态选项修改
initial begin
// 运行时修改选项
opt_cg1.option.goal = 98; // 修改目标覆盖率
opt_cg1.addr_cp.option.at_least = 5; // 修改最小命中次数
// 不同实例可以有不同设置
opt_cg2.option.goal = 90;
opt_cg2.data_cp.option.weight = 3;
end
// 传统Verilog的等价实现(非常复杂)
typedef struct {
integer goal;
integer at_least;
integer weight;
string name;
string comment;
} verilog_option_t;
typedef struct {
integer count;
integer target_hits;
real weight;
bit covered;
} verilog_bin_t;
// 手动实现选项控制
verilog_option_t global_opts;
verilog_bin_t addr_bins[4]; // 4个地址bin
verilog_bin_t data_bins[4]; // 4个数据bin
integer total_samples;
initial begin
// 初始化选项
global_opts.goal = 95;
global_opts.at_least = 2;
global_opts.weight = 1;
global_opts.name = "manual_coverage";
// 初始化bins
for (int i = 0; i < 4; i++) begin
addr_bins[i].target_hits = global_opts.at_least;
addr_bins[i].weight = global_opts.weight;
data_bins[i].target_hits = global_opts.at_least;
data_bins[i].weight = global_opts.weight;
end
end
// 手动统计和选项控制
always @(posedge clk) begin
if (!reset && (read || write)) begin
total_samples++;
// 手动分类和计数
case (addr[7:6])
2'b00: addr_bins[0].count++;
2'b01: addr_bins[1].count++;
2'b10: addr_bins[2].count++;
2'b11: addr_bins[3].count++;
endcase
case (data[7:6])
2'b00: data_bins[0].count++;
2'b01: data_bins[1].count++;
2'b10: data_bins[2].count++;
2'b11: data_bins[3].count++;
endcase
end
end
// 手动计算覆盖率
task calculate_manual_coverage;
integer covered_bins;
real coverage_percent;
covered_bins = 0;
// 检查地址bins
for (int i = 0; i < 4; i++) begin
if (addr_bins[i].count >= addr_bins[i].target_hits) begin
addr_bins[i].covered = 1;
covered_bins++;
end
end
// 检查数据bins
for (int i = 0; i < 4; i++) begin
if (data_bins[i].count >= data_bins[i].target_hits) begin
data_bins[i].covered = 1;
covered_bins++;
end
end
coverage_percent = (covered_bins * 100.0) / 8.0; // 总共8个bins
$display("手动覆盖率计算: %0.2f%% (目标: %d%%)",
coverage_percent, global_opts.goal);
if (coverage_percent >= global_opts.goal) begin
$display("覆盖率目标已达成!");
end else begin
$display("还需要 %0.2f%% 才能达到目标",
global_opts.goal - coverage_percent);
end
endtask
// 测试序列
initial begin
reset = 1;
read = 0;
write = 0;
addr = 8'h00;
data = 8'h00;
#20 reset = 0;
// 生成测试数据
repeat (200) begin
@(posedge clk);
addr = $random;
data = $random;
read = $random % 2;
write = !read && ($random % 2);
end
// SystemVerilog覆盖率报告
$display("\n=== SystemVerilog选项控制覆盖率 ===");
$display("实例1覆盖率: %0.2f%% (目标: %d%%)",
opt_cg1.get_coverage(), opt_cg1.option.goal);
$display("实例2覆盖率: %0.2f%% (目标: %d%%)",
opt_cg2.get_coverage(), opt_cg2.option.goal);
$display("类型覆盖率: %0.2f%% (目标: %d%%)",
type_cg.get_coverage(), type_cg.type_option.goal);
// 详细的覆盖点报告
$display("\n地址覆盖点详情:");
$display(" 覆盖率: %0.2f%%, 目标: %d%%, 最小命中: %d",
opt_cg1.addr_cp.get_coverage(),
opt_cg1.addr_cp.option.goal,
opt_cg1.addr_cp.option.at_least);
$display("数据覆盖点详情:");
$display(" 覆盖率: %0.2f%%, 权重: %d",
opt_cg1.data_cp.get_coverage(),
opt_cg1.data_cp.option.weight);
// 传统方法的覆盖率计算
$display("\n=== 传统Verilog手动计算 ===");
calculate_manual_coverage();
$finish;
end
endmodule
2.3 covergroup采样
covergroup的采样控制决定了何时收集覆盖率数据。
module covergroup_sampling_example;
logic clk, reset;
logic [7:0] addr, data;
logic read, write, valid;
logic [1:0] state;
// 时钟边沿采样
covergroup clk_edge_cg @(posedge clk);
addr_cp: coverpoint addr;
data_cp: coverpoint data;
endgroup
// 条件采样
covergroup conditional_cg @(posedge clk iff valid);
addr_cp: coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
endgroup
// 复杂条件采样
covergroup complex_cond_cg @(posedge clk iff (valid && !reset && (read || write)));
operation_cp: coverpoint {read, write} {
bins read_op = {2'b10};
bins write_op = {2'b01};
}
addr_cp: coverpoint addr {
bins aligned = {[0:255]} iff (addr[1:0] == 2'b00);
bins unaligned = {[0:255]} iff (addr[1:0] != 2'b00);
}
endgroup
// 信号变化采样
covergroup signal_change_cg @(state);
state_cp: coverpoint state {
bins idle = {2'b00};
bins active = {2'b01};
bins busy = {2'b10};
bins error = {2'b11};
}
endgroup
// 多事件采样
covergroup multi_event_cg @(posedge clk, negedge reset);
reset_cp: coverpoint reset {
bins active = {1'b0};
bins inactive = {1'b1};
}
endgroup
// 手动采样控制
covergroup manual_cg;
addr_cp: coverpoint addr {
bins ranges[] = {[0:63], [64:127], [128:191], [192:255]};
}
data_cp: coverpoint data {
bins patterns[] = {8'h00, 8'hFF, 8'hAA, 8'h55};
}
endgroup
// 类中的采样控制
class sampling_control_class;
rand logic [7:0] addr;
rand logic [7:0] data;
rand logic read_write;
// 自动采样
covergroup auto_sample_cg;
addr_cp: coverpoint addr;
data_cp: coverpoint data;
rw_cp: coverpoint read_write;
endgroup
// 手动采样
covergroup manual_sample_cg;
addr_cp: coverpoint addr;
data_cp: coverpoint data;
endgroup
function new();
auto_sample_cg = new();
manual_sample_cg = new();
endfunction
// 条件采样函数
function void conditional_sample();
if (addr > 8'h80) begin
manual_sample_cg.sample();
end
endfunction
// 延迟采样
task delayed_sample();
#10; // 延迟10个时间单位
manual_sample_cg.sample();
endtask
endclass
// 实例化covergroup
clk_edge_cg clk_cg = new();
conditional_cg cond_cg = new();
complex_cond_cg complex_cg = new();
signal_change_cg sig_cg = new();
multi_event_cg multi_cg = new();
manual_cg manual_inst = new();
sampling_control_class sample_obj = new();
// 采样控制逻辑
logic sample_enable;
integer sample_counter;
// 周期性采样控制
always @(posedge clk) begin
if (!reset) begin
sample_counter++;
// 每10个周期采样一次
if (sample_counter % 10 == 0) begin
sample_enable = 1;
manual_inst.sample();
end else begin
sample_enable = 0;
end
end
end
// 条件采样控制
always @(posedge clk) begin
if (valid && (addr > 8'h80)) begin
// 高地址时进行额外采样
manual_inst.sample();
end
if (read && write) begin
// 错误条件时采样
$display("错误:同时读写,时间: %t", $time);
manual_inst.sample();
end
end
// 传统Verilog的采样控制(复杂)
typedef struct {
integer addr_samples[4]; // 4个地址范围
integer data_samples[4]; // 4个数据模式
integer total_samples;
integer conditional_samples;
} verilog_sampling_t;
verilog_sampling_t verilog_stats;
// 手动实现采样控制
always @(posedge clk) begin
if (!reset) begin
// 基本采样
verilog_stats.total_samples++;
// 条件采样
if (valid) begin
verilog_stats.conditional_samples++;
// 手动分类
case (addr[7:6])
2'b00: verilog_stats.addr_samples[0]++;
2'b01: verilog_stats.addr_samples[1]++;
2'b10: verilog_stats.addr_samples[2]++;
2'b11: verilog_stats.addr_samples[3]++;
endcase
// 数据模式检测
case (data)
8'h00: verilog_stats.data_samples[0]++;
8'hFF: verilog_stats.data_samples[1]++;
8'hAA: verilog_stats.data_samples[2]++;
8'h55: verilog_stats.data_samples[3]++;
endcase
end
end
end
// 状态变化检测(手动)
logic [1:0] prev_state;
always @(posedge clk) begin
if (state != prev_state) begin
$display("状态变化: %d -> %d, 时间: %t", prev_state, state, $time);
// 手动记录状态转换
end
prev_state = state;
end
// 测试序列
initial begin
reset = 1;
valid = 0;
read = 0;
write = 0;
addr = 8'h00;
data = 8'h00;
state = 2'b00;
sample_counter = 0;
#20 reset = 0;
// 测试不同的采样场景
repeat (50) begin
@(posedge clk);
// 随机生成信号
valid = $random % 2;
read = $random % 2;
write = $random % 2;
addr = $random;
data = $random;
// 状态机
if ($random % 10 == 0) begin
state = $random % 4;
end
// 手动采样示例
if ($random % 5 == 0) begin
manual_inst.sample();
end
// 类对象采样
sample_obj.addr = addr;
sample_obj.data = data;
sample_obj.read_write = read;
sample_obj.conditional_sample();
end
// 覆盖率报告
$display("\n=== SystemVerilog采样覆盖率报告 ===");
$display("时钟边沿采样: %0.2f%%", clk_cg.get_coverage());
$display("条件采样: %0.2f%%", cond_cg.get_coverage());
$display("复杂条件采样: %0.2f%%", complex_cg.get_coverage());
$display("信号变化采样: %0.2f%%", sig_cg.get_coverage());
$display("多事件采样: %0.2f%%", multi_cg.get_coverage());
$display("手动采样: %0.2f%%", manual_inst.get_coverage());
$display("类自动采样: %0.2f%%", sample_obj.auto_sample_cg.get_coverage());
$display("类手动采样: %0.2f%%", sample_obj.manual_sample_cg.get_coverage());
// 传统Verilog采样统计
$display("\n=== 传统Verilog采样统计 ===");
$display("总采样数: %d", verilog_stats.total_samples);
$display("条件采样数: %d", verilog_stats.conditional_samples);
$display("地址采样分布: [%d, %d, %d, %d]",
verilog_stats.addr_samples[0], verilog_stats.addr_samples[1],
verilog_stats.addr_samples[2], verilog_stats.addr_samples[3]);
$display("数据模式采样: [%d, %d, %d, %d]",
verilog_stats.data_samples[0], verilog_stats.data_samples[1],
verilog_stats.data_samples[2], verilog_stats.data_samples[3]);
$finish;
end
endmodule
3. coverpoint
3.1 coverpoint的定义
coverpoint是covergroup中的基本覆盖单元,用于定义对特定信号或表达式的覆盖要求。
module coverpoint_definition_example;
logic clk, reset;
logic [7:0] addr, data;
logic [3:0] cmd;
logic read, write, valid;
logic [1:0] burst_type;
covergroup coverpoint_examples_cg @(posedge clk iff !reset);
// 基本coverpoint
basic_addr_cp: coverpoint addr;
// 带标签的coverpoint
labeled_data_cp: coverpoint data {
bins zero = {8'h00};
bins non_zero = {[8'h01:8'hFF]};
}
// 表达式coverpoint
addr_high_cp: coverpoint addr[7:4] {
bins low_nibble = {[4'h0:4'h7]};
bins high_nibble = {[4'h8:4'hF]};
}
// 多信号组合coverpoint
operation_cp: coverpoint {read, write, valid} {
bins valid_read = {3'b101};
bins valid_write = {3'b011};
bins invalid_ops = {3'b111, 3'b001};
bins idle = {3'b000};
}
// 条件coverpoint
conditional_data_cp: coverpoint data iff (valid && (read || write)) {
bins data_ranges[] = {[0:63], [64:127], [128:191], [192:255]};
}
// 函数调用coverpoint
function logic [2:0] get_priority(logic [7:0] addr);
if (addr < 8'h40) return 3'b001; // 低优先级
else if (addr < 8'h80) return 3'b010; // 中优先级
else if (addr < 8'hC0) return 3'b100; // 高优先级
else return 3'b111; // 最高优先级
endfunction
priority_cp: coverpoint get_priority(addr) {
bins low_pri = {3'b001};
bins mid_pri = {3'b010};
bins high_pri = {3'b100};
bins max_pri = {3'b111};
}
// 枚举类型coverpoint
typedef enum logic [1:0] {
IDLE = 2'b00,
READ = 2'b01,
WRITE = 2'b10,
ERROR = 2'b11
} state_e;
state_e current_state;
state_cp: coverpoint current_state {
bins idle_state = {IDLE};
bins read_state = {READ};
bins write_state = {WRITE};
bins error_state = {ERROR};
}
// 位选择coverpoint
bit_pattern_cp: coverpoint {addr[7], addr[0]} {
bins pattern_00 = {2'b00};
bins pattern_01 = {2'b01};
bins pattern_10 = {2'b10};
bins pattern_11 = {2'b11};
}
// 算术表达式coverpoint
sum_cp: coverpoint (addr + data) {
bins low_sum = {[0:127]};
bins high_sum = {[128:511]};
}
// 逻辑表达式coverpoint
logic_cp: coverpoint (addr & data) {
bins zero_and = {8'h00};
bins partial_and = {[8'h01:8'hFE]};
bins full_and = {8'hFF};
}
endgroup
// 实例化covergroup
coverpoint_examples_cg cp_cg = new();
// 传统Verilog的等价实现(极其复杂)
typedef struct {
// 基本地址统计
integer addr_count[256];
// 数据分类统计
integer zero_data_count;
integer non_zero_data_count;
// 地址高位统计
integer low_nibble_count;
integer high_nibble_count;
// 操作组合统计
integer valid_read_count;
integer valid_write_count;
integer invalid_ops_count;
integer idle_count;
// 条件数据统计
integer cond_data_ranges[4];
// 优先级统计
integer priority_counts[4];
// 状态统计
integer state_counts[4];
// 位模式统计
integer bit_pattern_counts[4];
// 表达式统计
integer low_sum_count;
integer high_sum_count;
integer zero_and_count;
integer partial_and_count;
integer full_and_count;
integer total_samples;
} verilog_coverpoint_stats_t;
verilog_coverpoint_stats_t verilog_stats;
// 手动实现coverpoint功能
function logic [2:0] manual_get_priority(logic [7:0] addr);
if (addr < 8'h40) return 3'b001;
else if (addr < 8'h80) return 3'b010;
else if (addr < 8'hC0) return 3'b100;
else return 3'b111;
endfunction
always @(posedge clk) begin
if (!reset) begin
verilog_stats.total_samples++;
// 基本地址统计
verilog_stats.addr_count[addr]++;
// 数据分类
if (data == 8'h00)
verilog_stats.zero_data_count++;
else
verilog_stats.non_zero_data_count++;
// 地址高位
if (addr[7:4] <= 4'h7)
verilog_stats.low_nibble_count++;
else
verilog_stats.high_nibble_count++;
// 操作组合
case ({read, write, valid})
3'b101: verilog_stats.valid_read_count++;
3'b011: verilog_stats.valid_write_count++;
3'b111, 3'b001: verilog_stats.invalid_ops_count++;
3'b000: verilog_stats.idle_count++;
endcase
// 条件数据统计
if (valid && (read || write)) begin
case (data[7:6])
2'b00: verilog_stats.cond_data_ranges[0]++;
2'b01: verilog_stats.cond_data_ranges[1]++;
2'b10: verilog_stats.cond_data_ranges[2]++;
2'b11: verilog_stats.cond_data_ranges[3]++;
endcase
end
// 优先级统计
case (manual_get_priority(addr))
3'b001: verilog_stats.priority_counts[0]++;
3'b010: verilog_stats.priority_counts[1]++;
3'b100: verilog_stats.priority_counts[2]++;
3'b111: verilog_stats.priority_counts[3]++;
endcase
// 位模式统计
case ({addr[7], addr[0]})
2'b00: verilog_stats.bit_pattern_counts[0]++;
2'b01: verilog_stats.bit_pattern_counts[1]++;
2'b10: verilog_stats.bit_pattern_counts[2]++;
2'b11: verilog_stats.bit_pattern_counts[3]++;
endcase
// 算术表达式统计
if ((addr + data) <= 127)
verilog_stats.low_sum_count++;
else
verilog_stats.high_sum_count++;
// 逻辑表达式统计
case (addr & data)
8'h00: verilog_stats.zero_and_count++;
8'hFF: verilog_stats.full_and_count++;
default: verilog_stats.partial_and_count++;
endcase
end
end
// 测试序列
initial begin
reset = 1;
addr = 8'h00;
data = 8'h00;
cmd = 4'h0;
read = 0;
write = 0;
valid = 0;
burst_type = 2'b00;
cp_cg.current_state = cp_cg.IDLE;
#20 reset = 0;
// 生成各种测试场景
repeat (300) begin
@(posedge clk);
// 随机生成基本信号
addr = $random;
data = $random;
valid = $random % 2;
read = $random % 2;
write = !read && ($random % 2);
// 状态机转换
if ($random % 10 == 0) begin
case ($random % 4)
0: cp_cg.current_state = cp_cg.IDLE;
1: cp_cg.current_state = cp_cg.READ;
2: cp_cg.current_state = cp_cg.WRITE;
3: cp_cg.current_state = cp_cg.ERROR;
endcase
end
// 特殊测试场景
if ($random % 20 == 0) begin
// 边界值测试
addr = ($random % 2) ? 8'h00 : 8'hFF;
data = ($random % 2) ? 8'h00 : 8'hFF;
end
end
// SystemVerilog覆盖率报告
$display("\n=== SystemVerilog Coverpoint覆盖率报告 ===");
$display("总体覆盖率: %0.2f%%", cp_cg.get_coverage());
$display("基本地址覆盖率: %0.2f%%", cp_cg.basic_addr_cp.get_coverage());
$display("标签数据覆盖率: %0.2f%%", cp_cg.labeled_data_cp.get_coverage());
$display("地址高位覆盖率: %0.2f%%", cp_cg.addr_high_cp.get_coverage());
$display("操作组合覆盖率: %0.2f%%", cp_cg.operation_cp.get_coverage());
$display("条件数据覆盖率: %0.2f%%", cp_cg.conditional_data_cp.get_coverage());
$display("优先级覆盖率: %0.2f%%", cp_cg.priority_cp.get_coverage());
$display("状态覆盖率: %0.2f%%", cp_cg.state_cp.get_coverage());
$display("位模式覆盖率: %0.2f%%", cp_cg.bit_pattern_cp.get_coverage());
$display("算术表达式覆盖率: %0.2f%%", cp_cg.sum_cp.get_coverage());
$display("逻辑表达式覆盖率: %0.2f%%", cp_cg.logic_cp.get_coverage());
// 传统Verilog统计报告
$display("\n=== 传统Verilog Coverpoint统计 ===");
$display("总采样数: %d", verilog_stats.total_samples);
$display("零数据计数: %d", verilog_stats.zero_data_count);
$display("非零数据计数: %d", verilog_stats.non_zero_data_count);
$display("低位计数: %d", verilog_stats.low_nibble_count);
$display("高位计数: %d", verilog_stats.high_nibble_count);
$display("有效读计数: %d", verilog_stats.valid_read_count);
$display("有效写计数: %d", verilog_stats.valid_write_count);
$display("无效操作计数: %d", verilog_stats.invalid_ops_count);
$display("空闲计数: %d", verilog_stats.idle_count);
$finish;
end
endmodule
3.2 bins for value
bins用于定义coverpoint中的覆盖桶,将信号值分组进行覆盖率统计。
module bins_for_value_example;
logic clk, reset;
logic [7:0] addr, data;
logic [3:0] cmd;
logic read, write;
covergroup value_bins_cg @(posedge clk iff !reset);
// 单值bins
addr_single_cp: coverpoint addr {
bins zero = {8'h00};
bins max = {8'hFF};
bins mid = {8'h80};
}
// 多值bins
addr_multi_cp: coverpoint addr {
bins special_values = {8'h00, 8'h55, 8'hAA, 8'hFF};
bins power_of_2 = {8'h01, 8'h02, 8'h04, 8'h08, 8'h10, 8'h20, 8'h40, 8'h80};
}
// 范围bins
addr_range_cp: coverpoint addr {
bins low_range = {[8'h00:8'h3F]};
bins mid_range = {[8'h40:8'h7F]};
bins high_range = {[8'h80:8'hBF]};
bins top_range = {[8'hC0:8'hFF]};
}
// 自动bins数组
data_auto_cp: coverpoint data {
bins auto_bins[] = {[0:255]}; // 自动创建256个bins
}
// 限制自动bins数量
data_limited_cp: coverpoint data {
bins limited_bins[16] = {[0:255]}; // 创建16个bins,每个覆盖16个值
}
// 混合bins定义
cmd_mixed_cp: coverpoint cmd {
bins read_cmd = {4'h0, 4'h1}; // 读命令
bins write_cmd = {4'h2, 4'h3}; // 写命令
bins config_cmd = {[4'h4:4'h7]}; // 配置命令范围
bins debug_cmd = {4'h8, 4'h9, 4'hA}; // 调试命令
bins reserved[] = {[4'hB:4'hF]}; // 保留命令自动分组
}
// 条件bins
conditional_bins_cp: coverpoint data {
bins low_data = {[0:127]} iff (read);
bins high_data = {[128:255]} iff (write);
}
// 默认bins
default_bins_cp: coverpoint addr {
bins defined_values = {8'h00, 8'h55, 8'hAA, 8'hFF};
bins default; // 捕获所有其他值
}
// 表达式bins
expression_bins_cp: coverpoint (addr[7:4]) {
bins low_nibble = {[4'h0:4'h7]};
bins high_nibble = {[4'h8:4'hF]};
}
// 组合信号bins
combo_bins_cp: coverpoint {read, write} {
bins read_only = {2'b10};
bins write_only = {2'b01};
bins idle = {2'b00};
// 注意:{2'b11} 将在illegal_bins中定义
}
endgroup
// 高级bins示例
covergroup advanced_bins_cg @(posedge clk);
// 位模式bins
bit_pattern_cp: coverpoint addr {
bins all_zeros = {8'b00000000};
bins all_ones = {8'b11111111};
bins alternating1 = {8'b10101010};
bins alternating2 = {8'b01010101};
bins single_bit[] = {8'b00000001, 8'b00000010, 8'b00000100, 8'b00001000,
8'b00010000, 8'b00100000, 8'b01000000, 8'b10000000};
}
// 算术bins
arithmetic_cp: coverpoint (addr + data) {
bins sum_zero = {0};
bins sum_low = {[1:127]};
bins sum_mid = {[128:383]};
bins sum_high = {[384:510]};
bins sum_max = {511};
}
// 函数返回值bins
function logic [2:0] get_addr_type(logic [7:0] addr);
if (addr == 8'h00) return 3'b001; // NULL
else if (addr[0] == 1'b0) return 3'b010; // EVEN
else if (addr[7] == 1'b1) return 3'b100; // HIGH_ODD
else return 3'b011; // LOW_ODD
endfunction
addr_type_cp: coverpoint get_addr_type(addr) {
bins null_addr = {3'b001};
bins even_addr = {3'b010};
bins low_odd = {3'b011};
bins high_odd = {3'b100};
}
endgroup
// 实例化covergroup
value_bins_cg val_cg = new();
advanced_bins_cg adv_cg = new();
// 传统Verilog的等价实现(非常复杂)
typedef struct {
// 单值统计
integer zero_count, max_count, mid_count;
// 多值统计
integer special_values_count;
integer power_of_2_count;
// 范围统计
integer low_range_count, mid_range_count;
integer high_range_count, top_range_count;
// 自动bins模拟
integer auto_bins_count[256];
integer limited_bins_count[16];
// 混合bins统计
integer read_cmd_count, write_cmd_count;
integer config_cmd_count, debug_cmd_count;
integer reserved_cmd_count[5];
// 条件bins统计
integer low_data_read_count, high_data_write_count;
// 默认bins统计
integer defined_values_count, default_count;
// 表达式bins统计
integer low_nibble_count, high_nibble_count;
// 组合bins统计
integer read_only_count, write_only_count, idle_count;
// 高级bins统计
integer bit_pattern_counts[12]; // 各种位模式
integer arithmetic_counts[5]; // 算术结果分组
integer addr_type_counts[4]; // 地址类型
integer total_samples;
} verilog_bins_stats_t;
verilog_bins_stats_t verilog_stats;
// 手动实现bins功能
function logic [2:0] manual_get_addr_type(logic [7:0] addr);
if (addr == 8'h00) return 3'b001;
else if (addr[0] == 1'b0) return 3'b010;
else if (addr[7] == 1'b1) return 3'b100;
else return 3'b011;
endfunction
function bit is_power_of_2(logic [7:0] value);
case (value)
8'h01, 8'h02, 8'h04, 8'h08, 8'h10, 8'h20, 8'h40, 8'h80: return 1;
default: return 0;
endcase
endfunction
function bit is_special_value(logic [7:0] value);
case (value)
8'h00, 8'h55, 8'hAA, 8'hFF: return 1;
default: return 0;
endcase
endfunction
always @(posedge clk) begin
if (!reset) begin
verilog_stats.total_samples++;
// 单值bins统计
case (addr)
8'h00: verilog_stats.zero_count++;
8'hFF: verilog_stats.max_count++;
8'h80: verilog_stats.mid_count++;
endcase
// 多值bins统计
if (is_special_value(addr))
verilog_stats.special_values_count++;
if (is_power_of_2(addr))
verilog_stats.power_of_2_count++;
// 范围bins统计
if (addr >= 8'h00 && addr <= 8'h3F)
verilog_stats.low_range_count++;
else if (addr >= 8'h40 && addr <= 8'h7F)
verilog_stats.mid_range_count++;
else if (addr >= 8'h80 && addr <= 8'hBF)
verilog_stats.high_range_count++;
else if (addr >= 8'hC0 && addr <= 8'hFF)
verilog_stats.top_range_count++;
// 自动bins模拟
verilog_stats.auto_bins_count[addr]++;
verilog_stats.limited_bins_count[addr[7:4]]++;
// 混合bins统计
case (cmd)
4'h0, 4'h1: verilog_stats.read_cmd_count++;
4'h2, 4'h3: verilog_stats.write_cmd_count++;
4'h4, 4'h5, 4'h6, 4'h7: verilog_stats.config_cmd_count++;
4'h8, 4'h9, 4'hA: verilog_stats.debug_cmd_count++;
default: begin
if (cmd >= 4'hB && cmd <= 4'hF)
verilog_stats.reserved_cmd_count[cmd - 4'hB]++;
end
endcase
// 条件bins统计
if (read && data <= 127)
verilog_stats.low_data_read_count++;
if (write && data >= 128)
verilog_stats.high_data_write_count++;
// 默认bins统计
if (is_special_value(addr))
verilog_stats.defined_values_count++;
else
verilog_stats.default_count++;
// 表达式bins统计
if (addr[7:4] <= 4'h7)
verilog_stats.low_nibble_count++;
else
verilog_stats.high_nibble_count++;
// 组合bins统计
case ({read, write})
2'b10: verilog_stats.read_only_count++;
2'b01: verilog_stats.write_only_count++;
2'b00: verilog_stats.idle_count++;
endcase
// 位模式统计
case (addr)
8'b00000000: verilog_stats.bit_pattern_counts[0]++;
8'b11111111: verilog_stats.bit_pattern_counts[1]++;
8'b10101010: verilog_stats.bit_pattern_counts[2]++;
8'b01010101: verilog_stats.bit_pattern_counts[3]++;
8'b00000001: verilog_stats.bit_pattern_counts[4]++;
8'b00000010: verilog_stats.bit_pattern_counts[5]++;
8'b00000100: verilog_stats.bit_pattern_counts[6]++;
8'b00001000: verilog_stats.bit_pattern_counts[7]++;
8'b00010000: verilog_stats.bit_pattern_counts[8]++;
8'b00100000: verilog_stats.bit_pattern_counts[9]++;
8'b01000000: verilog_stats.bit_pattern_counts[10]++;
8'b10000000: verilog_stats.bit_pattern_counts[11]++;
endcase
// 算术bins统计
case (addr + data)
0: verilog_stats.arithmetic_counts[0]++;
1, 2, 3, 4, 5, 6, 7, 8, 9, 10: begin // 简化范围检查
if ((addr + data) >= 1 && (addr + data) <= 127)
verilog_stats.arithmetic_counts[1]++;
end
default: begin
if ((addr + data) >= 128 && (addr + data) <= 383)
verilog_stats.arithmetic_counts[2]++;
else if ((addr + data) >= 384 && (addr + data) <= 510)
verilog_stats.arithmetic_counts[3]++;
else if ((addr + data) == 511)
verilog_stats.arithmetic_counts[4]++;
end
endcase
// 地址类型统计
case (manual_get_addr_type(addr))
3'b001: verilog_stats.addr_type_counts[0]++;
3'b010: verilog_stats.addr_type_counts[1]++;
3'b011: verilog_stats.addr_type_counts[2]++;
3'b100: verilog_stats.addr_type_counts[3]++;
endcase
end
end
// 覆盖率计算函数
function real calculate_bin_coverage(integer bin_count, integer total_samples, integer min_hits);
if (total_samples == 0) return 0.0;
return (bin_count >= min_hits) ? 100.0 : 0.0;
endfunction
// 测试序列
initial begin
reset = 1;
addr = 8'h00;
data = 8'h00;
cmd = 4'h0;
read = 0;
write = 0;
#20 reset = 0;
// 系统性测试各种bins
$display("开始bins覆盖率测试...");
// 测试单值bins
repeat (10) begin
@(posedge clk);
addr = 8'h00; // 测试零值
end
repeat (10) begin
@(posedge clk);
addr = 8'hFF; // 测试最大值
end
repeat (10) begin
@(posedge clk);
addr = 8'h80; // 测试中间值
end
// 测试特殊值bins
foreach (val_cg.addr_multi_cp.special_values[i]) begin
repeat (5) begin
@(posedge clk);
case (i)
0: addr = 8'h00;
1: addr = 8'h55;
2: addr = 8'hAA;
3: addr = 8'hFF;
endcase
end
end
// 测试2的幂值
repeat (5) begin
@(posedge clk);
addr = 8'h01;
end
repeat (5) begin
@(posedge clk);
addr = 8'h02;
end
repeat (5) begin
@(posedge clk);
addr = 8'h04;
end
// 测试范围bins
repeat (20) begin
@(posedge clk);
addr = $random % 64; // 低范围
end
repeat (20) begin
@(posedge clk);
addr = 64 + ($random % 64); // 中范围
end
// 测试条件bins
repeat (30) begin
@(posedge clk);
read = 1;
write = 0;
data = $random % 128; // 低数据值
end
repeat (30) begin
@(posedge clk);
read = 0;
write = 1;
data = 128 + ($random % 128); // 高数据值
end
// 随机测试
repeat (200) begin
@(posedge clk);
addr = $random;
data = $random;
cmd = $random % 16;
read = $random % 2;
write = !read && ($random % 2);
end
// SystemVerilog覆盖率报告
$display("\n=== SystemVerilog Bins覆盖率报告 ===");
$display("总体覆盖率: %0.2f%%", val_cg.get_coverage());
$display("单值地址覆盖率: %0.2f%%", val_cg.addr_single_cp.get_coverage());
$display("多值地址覆盖率: %0.2f%%", val_cg.addr_multi_cp.get_coverage());
$display("范围地址覆盖率: %0.2f%%", val_cg.addr_range_cp.get_coverage());
$display("自动数据覆盖率: %0.2f%%", val_cg.data_auto_cp.get_coverage());
$display("限制数据覆盖率: %0.2f%%", val_cg.data_limited_cp.get_coverage());
$display("混合命令覆盖率: %0.2f%%", val_cg.cmd_mixed_cp.get_coverage());
$display("条件bins覆盖率: %0.2f%%", val_cg.conditional_bins_cp.get_coverage());
$display("默认bins覆盖率: %0.2f%%", val_cg.default_bins_cp.get_coverage());
$display("表达式bins覆盖率: %0.2f%%", val_cg.expression_bins_cp.get_coverage());
$display("组合bins覆盖率: %0.2f%%", val_cg.combo_bins_cp.get_coverage());
$display("\n高级bins覆盖率:");
$display("位模式覆盖率: %0.2f%%", adv_cg.bit_pattern_cp.get_coverage());
$display("算术覆盖率: %0.2f%%", adv_cg.arithmetic_cp.get_coverage());
$display("地址类型覆盖率: %0.2f%%", adv_cg.addr_type_cp.get_coverage());
// 传统Verilog统计报告
$display("\n=== 传统Verilog Bins统计 ===");
$display("总采样数: %d", verilog_stats.total_samples);
$display("零值计数: %d", verilog_stats.zero_count);
$display("最大值计数: %d", verilog_stats.max_count);
$display("中间值计数: %d", verilog_stats.mid_count);
$display("特殊值计数: %d", verilog_stats.special_values_count);
$display("2的幂计数: %d", verilog_stats.power_of_2_count);
$display("范围统计: [%d, %d, %d, %d]",
verilog_stats.low_range_count, verilog_stats.mid_range_count,
verilog_stats.high_range_count, verilog_stats.top_range_count);
$display("条件统计: 低数据读=%d, 高数据写=%d",
verilog_stats.low_data_read_count, verilog_stats.high_data_write_count);
$finish;
end
endmodule
3.3 bins for sequence
SystemVerilog支持序列bins,用于捕获信号值的时序变化模式。
module bins_for_sequence_example;
logic clk, reset;
logic [7:0] addr, data;
logic [1:0] state;
logic read, write, valid;
covergroup sequence_bins_cg @(posedge clk iff !reset);
// 基本序列bins
state_sequence_cp: coverpoint state {
bins idle_to_active = (2'b00 => 2'b01);
bins active_to_busy = (2'b01 => 2'b10);
bins busy_to_idle = (2'b10 => 2'b00);
bins error_recovery = (2'b11 => 2'b00);
}
// 多步序列bins
multi_step_cp: coverpoint state {
bins normal_flow = (2'b00 => 2'b01 => 2'b10 => 2'b00);
bins quick_cycle = (2'b00 => 2'b01 => 2'b00);
bins error_flow = (2'b00 => 2'b01 => 2'b11 => 2'b00);
}
// 重复序列bins
repeat_sequence_cp: coverpoint state {
bins stay_idle = (2'b00[*2:5]); // 保持idle状态2-5个周期
bins stay_active = (2'b01[*1:3]); // 保持active状态1-3个周期
bins stay_busy = (2'b10[*2:10]); // 保持busy状态2-10个周期
}
// 通配符序列bins
wildcard_sequence_cp: coverpoint addr[7:4] {
bins ascending = (4'h0 => 4'h1 => 4'h2 => 4'h3);
bins descending = (4'hF => 4'hE => 4'hD => 4'hC);
bins any_to_zero = ([4'h1:4'hF] => 4'h0);
bins zero_to_any = (4'h0 => [4'h1:4'hF]);
}
// 条件序列bins
conditional_sequence_cp: coverpoint data {
bins inc_sequence = ([0:254] => [1:255]) iff (valid);
bins dec_sequence = ([1:255] => [0:254]) iff (valid);
bins reset_sequence = ([1:255] => 0) iff (!valid);
}
// 复杂序列bins
complex_sequence_cp: coverpoint {read, write} {
bins read_burst = (2'b10[*3:8]); // 连续读3-8次
bins write_burst = (2'b01[*2:5]); // 连续写2-5次
bins rw_pattern = (2'b10 => 2'b01 => 2'b10 => 2'b01); // 读写交替
bins idle_active = (2'b00[*1:3] => 2'b10[*1:2] => 2'b00[*1:3]);
}
// 开放序列bins(goto重复)
goto_sequence_cp: coverpoint state {
bins any_path_to_error = ([2'b00:2'b10] [-> 1] => 2'b11);
bins recovery_attempts = (2'b11 [-> 1:5] => 2'b00);
}
endgroup
// 高级序列bins示例
covergroup advanced_sequence_cg @(posedge clk);
// 地址序列模式
addr_pattern_cp: coverpoint addr {
bins addr_increment = ([0:254] => [1:255]);
bins addr_decrement = ([1:255] => [0:254]);
bins addr_wrap = (8'hFF => 8'h00);
bins addr_jump = ([0:127] => [128:255]);
}
// 数据变化序列
data_change_cp: coverpoint data {
bins data_double = ([1:127] => [2:254]); // 近似翻倍
bins data_half = ([2:254] => [1:127]); // 近似减半
bins data_invert = (8'h00 => 8'hFF, 8'hFF => 8'h00);
}
// 状态机序列
state_machine_cp: coverpoint state {
bins full_cycle = (2'b00 => 2'b01 => 2'b10 => 2'b00);
bins error_entry = ([2'b00:2'b10] => 2'b11);
bins error_exit = (2'b11 => [2'b00:2'b10]);
bins loop_active = (2'b01[*2] => 2'b10[*2] => 2'b01[*2]);
}
endgroup
// 实例化covergroup
sequence_bins_cg seq_cg = new();
advanced_sequence_cg adv_seq_cg = new();
// 传统Verilog的序列检测(极其复杂)
typedef struct {
// 状态转换统计
integer idle_to_active_count;
integer active_to_busy_count;
integer busy_to_idle_count;
integer error_recovery_count;
// 多步序列统计
integer normal_flow_count;
integer quick_cycle_count;
integer error_flow_count;
// 重复序列统计
integer stay_idle_count;
integer stay_active_count;
integer stay_busy_count;
// 序列检测状态
integer idle_repeat_count;
integer active_repeat_count;
integer busy_repeat_count;
// 复杂序列状态机
typedef enum {
SEQ_IDLE,
SEQ_STEP1,
SEQ_STEP2,
SEQ_STEP3,
SEQ_COMPLETE
} sequence_state_e;
sequence_state_e normal_flow_state;
sequence_state_e quick_cycle_state;
sequence_state_e error_flow_state;
integer total_samples;
} verilog_sequence_stats_t;
verilog_sequence_stats_t verilog_stats;
// 前一个状态记录
logic [1:0] prev_state;
logic [7:0] prev_addr, prev_data;
logic prev_read, prev_write;
// 手动序列检测逻辑
always @(posedge clk) begin
if (reset) begin
prev_state = 2'b00;
prev_addr = 8'h00;
prev_data = 8'h00;
prev_read = 0;
prev_write = 0;
verilog_stats.idle_repeat_count = 0;
verilog_stats.active_repeat_count = 0;
verilog_stats.busy_repeat_count = 0;
verilog_stats.normal_flow_state = verilog_stats.SEQ_IDLE;
verilog_stats.quick_cycle_state = verilog_stats.SEQ_IDLE;
verilog_stats.error_flow_state = verilog_stats.SEQ_IDLE;
end else begin
verilog_stats.total_samples++;
// 基本状态转换检测
if (prev_state == 2'b00 && state == 2'b01)
verilog_stats.idle_to_active_count++;
else if (prev_state == 2'b01 && state == 2'b10)
verilog_stats.active_to_busy_count++;
else if (prev_state == 2'b10 && state == 2'b00)
verilog_stats.busy_to_idle_count++;
else if (prev_state == 2'b11 && state == 2'b00)
verilog_stats.error_recovery_count++;
// 重复状态检测
if (state == prev_state) begin
case (state)
2'b00: verilog_stats.idle_repeat_count++;
2'b01: verilog_stats.active_repeat_count++;
2'b10: verilog_stats.busy_repeat_count++;
endcase
end else begin
// 检查重复序列完成
if (prev_state == 2'b00 && verilog_stats.idle_repeat_count >= 2 && verilog_stats.idle_repeat_count <= 5)
verilog_stats.stay_idle_count++;
if (prev_state == 2'b01 && verilog_stats.active_repeat_count >= 1 && verilog_stats.active_repeat_count <= 3)
verilog_stats.stay_active_count++;
if (prev_state == 2'b10 && verilog_stats.busy_repeat_count >= 2 && verilog_stats.busy_repeat_count <= 10)
verilog_stats.stay_busy_count++;
// 重置计数器
verilog_stats.idle_repeat_count = 0;
verilog_stats.active_repeat_count = 0;
verilog_stats.busy_repeat_count = 0;
end
// 多步序列状态机 - 正常流程
case (verilog_stats.normal_flow_state)
verilog_stats.SEQ_IDLE: begin
if (state == 2'b00) verilog_stats.normal_flow_state = verilog_stats.SEQ_STEP1;
end
verilog_stats.SEQ_STEP1: begin
if (state == 2'b01) verilog_stats.normal_flow_state = verilog_stats.SEQ_STEP2;
else if (state != 2'b00) verilog_stats.normal_flow_state = verilog_stats.SEQ_IDLE;
end
verilog_stats.SEQ_STEP2: begin
if (state == 2'b10) verilog_stats.normal_flow_state = verilog_stats.SEQ_STEP3;
else verilog_stats.normal_flow_state = verilog_stats.SEQ_IDLE;
end
verilog_stats.SEQ_STEP3: begin
if (state == 2'b00) begin
verilog_stats.normal_flow_count++;
verilog_stats.normal_flow_state = verilog_stats.SEQ_STEP1;
end else begin
verilog_stats.normal_flow_state = verilog_stats.SEQ_IDLE;
end
end
endcase
// 多步序列状态机 - 快速循环
case (verilog_stats.quick_cycle_state)
verilog_stats.SEQ_IDLE: begin
if (state == 2'b00) verilog_stats.quick_cycle_state = verilog_stats.SEQ_STEP1;
end
verilog_stats.SEQ_STEP1: begin
if (state == 2'b01) verilog_stats.quick_cycle_state = verilog_stats.SEQ_STEP2;
else if (state != 2'b00) verilog_stats.quick_cycle_state = verilog_stats.SEQ_IDLE;
end
verilog_stats.SEQ_STEP2: begin
if (state == 2'b00) begin
verilog_stats.quick_cycle_count++;
verilog_stats.quick_cycle_state = verilog_stats.SEQ_STEP1;
end else begin
verilog_stats.quick_cycle_state = verilog_stats.SEQ_IDLE;
end
end
endcase
// 更新前一个状态
prev_state = state;
prev_addr = addr;
prev_data = data;
prev_read = read;
prev_write = write;
end
end
// 测试序列
initial begin
reset = 1;
state = 2'b00;
addr = 8'h00;
data = 8'h00;
read = 0;
write = 0;
valid = 0;
#20 reset = 0;
$display("开始序列bins测试...");
// 测试基本状态转换
@(posedge clk); state = 2'b00; // idle
@(posedge clk); state = 2'b01; // idle -> active
@(posedge clk); state = 2'b10; // active -> busy
@(posedge clk); state = 2'b00; // busy -> idle
// 测试错误恢复
@(posedge clk); state = 2'b01; // active
@(posedge clk); state = 2'b11; // error
@(posedge clk); state = 2'b00; // error -> idle
// 测试多步序列 - 正常流程
@(posedge clk); state = 2'b00; // idle
@(posedge clk); state = 2'b01; // active
@(posedge clk); state = 2'b10; // busy
@(posedge clk); state = 2'b00; // idle (完成正常流程)
// 测试快速循环
@(posedge clk); state = 2'b00; // idle
@(posedge clk); state = 2'b01; // active
@(posedge clk); state = 2'b00; // idle (完成快速循环)
// 测试重复序列
repeat (3) @(posedge clk) state = 2'b00; // 保持idle 3个周期
repeat (2) @(posedge clk) state = 2'b01; // 保持active 2个周期
repeat (4) @(posedge clk) state = 2'b10; // 保持busy 4个周期
@(posedge clk); state = 2'b00;
// 测试地址序列
valid = 1;
for (int i = 0; i < 10; i++) begin
@(posedge clk);
addr = i;
end
// 测试地址回绕
@(posedge clk); addr = 8'hFF;
@(posedge clk); addr = 8'h00;
// 测试数据序列
for (int i = 100; i > 90; i--) begin
@(posedge clk);
data = i;
end
// 测试读写序列
repeat (4) begin
@(posedge clk); read = 1; write = 0;
end
repeat (3) begin
@(posedge clk); read = 0; write = 1;
end
// 测试读写交替
repeat (2) begin
@(posedge clk); read = 1; write = 0;
@(posedge clk); read = 0; write = 1;
end
// 随机测试
repeat (100) begin
@(posedge clk);
state = $random % 4;
addr = $random;
data = $random;
read = $random % 2;
write = !read && ($random % 2);
valid = $random % 2;
end
// SystemVerilog覆盖率报告
$display("\n=== SystemVerilog序列Bins覆盖率报告 ===");
$display("总体覆盖率: %0.2f%%", seq_cg.get_coverage());
$display("状态序列覆盖率: %0.2f%%", seq_cg.state_sequence_cp.get_coverage());
$display("多步序列覆盖率: %0.2f%%", seq_cg.multi_step_cp.get_coverage());
$display("重复序列覆盖率: %0.2f%%", seq_cg.repeat_sequence_cp.get_coverage());
$display("通配符序列覆盖率: %0.2f%%", seq_cg.wildcard_sequence_cp.get_coverage());
$display("条件序列覆盖率: %0.2f%%", seq_cg.conditional_sequence_cp.get_coverage());
$display("复杂序列覆盖率: %0.2f%%", seq_cg.complex_sequence_cp.get_coverage());
$display("Goto序列覆盖率: %0.2f%%", seq_cg.goto_sequence_cp.get_coverage());
$display("\n高级序列覆盖率:");
$display("地址模式覆盖率: %0.2f%%", adv_seq_cg.addr_pattern_cp.get_coverage());
$display("数据变化覆盖率: %0.2f%%", adv_seq_cg.data_change_cp.get_coverage());
$display("状态机覆盖率: %0.2f%%", adv_seq_cg.state_machine_cp.get_coverage());
// 传统Verilog序列统计
$display("\n=== 传统Verilog序列统计 ===");
$display("总采样数: %d", verilog_stats.total_samples);
$display("状态转换统计:");
$display(" idle->active: %d", verilog_stats.idle_to_active_count);
$display(" active->busy: %d", verilog_stats.active_to_busy_count);
$display(" busy->idle: %d", verilog_stats.busy_to_idle_count);
$display(" error->idle: %d", verilog_stats.error_recovery_count);
$display("多步序列统计:");
$display(" 正常流程: %d", verilog_stats.normal_flow_count);
$display(" 快速循环: %d", verilog_stats.quick_cycle_count);
$display("重复序列统计:");
$display(" 保持idle: %d", verilog_stats.stay_idle_count);
$display(" 保持active: %d", verilog_stats.stay_active_count);
$display(" 保持busy: %d", verilog_stats.stay_busy_count);
$finish;
end
endmodule
3.4 wildcard、ignore_bins、illegal_bins
SystemVerilog提供了特殊的bins类型来处理通配符匹配、忽略特定值和标记非法值。
module wildcard_ignore_illegal_bins_example;
logic clk, reset;
logic [7:0] addr, data;
logic [3:0] opcode;
logic [1:0] mode;
logic valid, error;
covergroup special_bins_cg @(posedge clk iff !reset);
// 通配符bins - 使用?匹配任意位
addr_wildcard_cp: coverpoint addr {
wildcard bins addr_pattern_0 = {8'b0000????}; // 低4位任意,高4位为0000
wildcard bins addr_pattern_1 = {8'b0001????}; // 低4位任意,高4位为0001
wildcard bins addr_pattern_f = {8'b1111????}; // 低4位任意,高4位为1111
wildcard bins even_addr = {8'b???????0}; // 最低位为0(偶数)
wildcard bins odd_addr = {8'b???????1}; // 最低位为1(奇数)
}
// 通配符bins - 复杂模式
data_wildcard_cp: coverpoint data {
wildcard bins pattern_aa = {8'b10101010}; // 精确匹配
wildcard bins pattern_a? = {8'b1010????}; // 高4位匹配1010
wildcard bins pattern_?a = {8'b????1010}; // 低4位匹配1010
wildcard bins pattern_alt = {8'b?0?0?0?0}; // 交替模式
wildcard bins high_bit_set = {8'b1???????}; // 最高位为1
}
// ignore_bins - 忽略特定值,不参与覆盖率计算
opcode_ignore_cp: coverpoint opcode {
bins valid_opcodes = {[4'h0:4'h7]}; // 有效操作码
bins extended_opcodes = {4'h8, 4'h9, 4'hA}; // 扩展操作码
ignore_bins reserved = {4'hB, 4'hC}; // 保留操作码,忽略
ignore_bins debug_only = {4'hD}; // 仅调试用,忽略
ignore_bins future_use = {4'hE, 4'hF}; // 未来使用,忽略
}
// illegal_bins - 标记非法值,出现时报告错误
mode_illegal_cp: coverpoint mode {
bins normal_mode = {2'b00}; // 正常模式
bins test_mode = {2'b01}; // 测试模式
bins debug_mode = {2'b10}; // 调试模式
illegal_bins forbidden = {2'b11}; // 非法模式
}
// 组合使用通配符和ignore_bins
addr_data_combo_cp: coverpoint {addr[7:4], data[3:0]} {
wildcard bins high_addr_low_data = {8'b1111????};
wildcard bins low_addr_high_data = {8'b0000????};
bins specific_combo = {{4'h5}, {4'hA}};
ignore_bins test_patterns = {{4'hF}, {4'hF}}; // 测试模式,忽略
illegal_bins invalid_combo = {{4'h0}, {4'h0}}; // 无效组合
}
// 条件通配符bins
conditional_wildcard_cp: coverpoint addr {
wildcard bins valid_even = {8'b???????0} iff (valid);
wildcard bins valid_odd = {8'b???????1} iff (valid);
ignore_bins invalid_data = {[8'h00:8'hFF]} iff (!valid);
}
// 复杂通配符模式
complex_pattern_cp: coverpoint data {
wildcard bins nibble_match_0 = {8'b0000????};
wildcard bins nibble_match_1 = {8'b0001????};
wildcard bins nibble_match_2 = {8'b0010????};
wildcard bins nibble_match_3 = {8'b0011????};
wildcard bins high_nibbles = {8'b????0000, 8'b????0001, 8'b????0010, 8'b????0011};
ignore_bins test_nibbles = {8'b1110????, 8'b1111????};
illegal_bins error_patterns = {8'b????????} iff (error);
}
endgroup
// 高级特殊bins示例
covergroup advanced_special_cg @(posedge clk);
// 多层通配符
multi_wildcard_cp: coverpoint {addr[7:6], addr[1:0]} {
wildcard bins pattern_00 = {4'b00??};
wildcard bins pattern_01 = {4'b01??};
wildcard bins pattern_10 = {4'b10??};
wildcard bins pattern_11 = {4'b11??};
wildcard bins specific = {4'b?0?1, 4'b?1?0};
}
// 范围通配符
range_wildcard_cp: coverpoint addr {
wildcard bins low_range = {8'b000?????, 8'b001?????};
wildcard bins mid_range = {8'b010?????, 8'b011?????};
wildcard bins high_range = {8'b100?????, 8'b101?????};
wildcard bins top_range = {8'b110?????, 8'b111?????};
ignore_bins reserved_range = {[8'hF0:8'hFE]};
illegal_bins error_addr = {8'hFF};
}
// 状态相关的特殊bins
state_special_cp: coverpoint {valid, error, mode} {
bins normal_states = {{1'b1, 1'b0, 2'b00}, {1'b1, 1'b0, 2'b01}};
bins debug_states = {{1'b1, 1'b0, 2'b10}};
ignore_bins transitional = {{1'b0, 1'b0, 2'b??}};
illegal_bins error_states = {{1'b?, 1'b1, 2'b??}};
}
endgroup
// 实例化covergroup
special_bins_cg spec_cg = new();
advanced_special_cg adv_spec_cg = new();
// 传统Verilog的等价实现(极其复杂且容易出错)
typedef struct {
// 通配符模式统计
integer addr_pattern_counts[5]; // 各种地址模式
integer data_pattern_counts[5]; // 各种数据模式
// 有效操作码统计
integer valid_opcode_count;
integer extended_opcode_count;
// 注意:忽略的操作码不统计
// 模式统计(排除非法值)
integer normal_mode_count;
integer test_mode_count;
integer debug_mode_count;
integer illegal_mode_detected;
// 组合模式统计
integer combo_pattern_counts[3];
integer ignored_combo_count;
integer illegal_combo_detected;
// 条件模式统计
integer valid_even_count, valid_odd_count;
integer invalid_ignored_count;
// 复杂模式统计
integer nibble_counts[4];
integer high_nibble_counts[4];
integer test_nibble_ignored;
integer error_pattern_detected;
integer total_samples;
integer error_count;
} verilog_special_stats_t;
verilog_special_stats_t verilog_stats;
// 通配符匹配函数
function bit wildcard_match_8bit(logic [7:0] value, logic [7:0] pattern, logic [7:0] mask);
return ((value & mask) == (pattern & mask));
endfunction
function bit wildcard_match_4bit(logic [3:0] value, logic [3:0] pattern, logic [3:0] mask);
return ((value & mask) == (pattern & mask));
endfunction
// 手动实现特殊bins逻辑
always @(posedge clk) begin
if (reset) begin
verilog_stats = '{default: 0};
end else begin
verilog_stats.total_samples++;
// 地址通配符模式检测
if (wildcard_match_8bit(addr, 8'b00000000, 8'b11110000))
verilog_stats.addr_pattern_counts[0]++; // 0000????
else if (wildcard_match_8bit(addr, 8'b00010000, 8'b11110000))
verilog_stats.addr_pattern_counts[1]++; // 0001????
else if (wildcard_match_8bit(addr, 8'b11110000, 8'b11110000))
verilog_stats.addr_pattern_counts[2]++; // 1111????
if (wildcard_match_8bit(addr, 8'b00000000, 8'b00000001))
verilog_stats.addr_pattern_counts[3]++; // ???????0 (偶数)
else if (wildcard_match_8bit(addr, 8'b00000001, 8'b00000001))
verilog_stats.addr_pattern_counts[4]++; // ???????1 (奇数)
// 数据通配符模式检测
if (data == 8'b10101010)
verilog_stats.data_pattern_counts[0]++; // 精确匹配
else if (wildcard_match_8bit(data, 8'b10100000, 8'b11110000))
verilog_stats.data_pattern_counts[1]++; // 1010????
else if (wildcard_match_8bit(data, 8'b00001010, 8'b00001111))
verilog_stats.data_pattern_counts[2]++; // ????1010
else if (wildcard_match_8bit(data, 8'b00000000, 8'b01010101))
verilog_stats.data_pattern_counts[3]++; // ?0?0?0?0
else if (wildcard_match_8bit(data, 8'b10000000, 8'b10000000))
verilog_stats.data_pattern_counts[4]++; // 1???????
// 操作码统计(忽略特定值)
case (opcode)
4'h0, 4'h1, 4'h2, 4'h3, 4'h4, 4'h5, 4'h6, 4'h7:
verilog_stats.valid_opcode_count++;
4'h8, 4'h9, 4'hA:
verilog_stats.extended_opcode_count++;
4'hB, 4'hC, 4'hD, 4'hE, 4'hF:
; // 忽略这些值,不统计
endcase
// 模式统计(检测非法值)
case (mode)
2'b00: verilog_stats.normal_mode_count++;
2'b01: verilog_stats.test_mode_count++;
2'b10: verilog_stats.debug_mode_count++;
2'b11: begin
verilog_stats.illegal_mode_detected++;
verilog_stats.error_count++;
$error("检测到非法模式: %b", mode);
end
endcase
// 组合模式检测
case ({addr[7:4], data[3:0]})
{4'hF, 4'h0}, {4'hF, 4'h1}, {4'hF, 4'h2}, {4'hF, 4'h3},
{4'hF, 4'h4}, {4'hF, 4'h5}, {4'hF, 4'h6}, {4'hF, 4'h7},
{4'hF, 4'h8}, {4'hF, 4'h9}, {4'hF, 4'hA}, {4'hF, 4'hB},
{4'hF, 4'hC}, {4'hF, 4'hD}, {4'hF, 4'hE}, {4'hF, 4'hF}:
verilog_stats.combo_pattern_counts[0]++; // 高地址低数据
{4'h0, 4'h0}, {4'h0, 4'h1}, {4'h0, 4'h2}, {4'h0, 4'h3},
{4'h0, 4'h4}, {4'h0, 4'h5}, {4'h0, 4'h6}, {4'h0, 4'h7},
{4'h0, 4'h8}, {4'h0, 4'h9}, {4'h0, 4'hA}, {4'h0, 4'hB},
{4'h0, 4'hC}, {4'h0, 4'hD}, {4'h0, 4'hE}, {4'h0, 4'hF}:
verilog_stats.combo_pattern_counts[1]++; // 低地址高数据
{4'h5, 4'hA}:
verilog_stats.combo_pattern_counts[2]++; // 特定组合
{4'hF, 4'hF}:
verilog_stats.ignored_combo_count++; // 忽略的测试模式
{4'h0, 4'h0}: begin
verilog_stats.illegal_combo_detected++;
verilog_stats.error_count++;
$error("检测到非法组合: addr[7:4]=%h, data[3:0]=%h", addr[7:4], data[3:0]);
end
endcase
// 条件通配符检测
if (valid) begin
if (addr[0] == 1'b0)
verilog_stats.valid_even_count++;
else
verilog_stats.valid_odd_count++;
end else begin
verilog_stats.invalid_ignored_count++; // 无效时忽略
end
// 复杂模式检测
case (data[7:4])
4'h0: verilog_stats.nibble_counts[0]++;
4'h1: verilog_stats.nibble_counts[1]++;
4'h2: verilog_stats.nibble_counts[2]++;
4'h3: verilog_stats.nibble_counts[3]++;
4'hE, 4'hF: verilog_stats.test_nibble_ignored++; // 忽略测试模式
endcase
case (data[3:0])
4'h0: verilog_stats.high_nibble_counts[0]++;
4'h1: verilog_stats.high_nibble_counts[1]++;
4'h2: verilog_stats.high_nibble_counts[2]++;
4'h3: verilog_stats.high_nibble_counts[3]++;
endcase
// 错误模式检测
if (error) begin
verilog_stats.error_pattern_detected++;
verilog_stats.error_count++;
$error("检测到错误模式,数据值: %h", data);
end
end
end
// 覆盖率计算函数
function real calculate_wildcard_coverage(integer pattern_counts[], integer total_patterns);
integer hit_patterns = 0;
for (int i = 0; i < total_patterns; i++) begin
if (pattern_counts[i] > 0) hit_patterns++;
end
return (total_patterns > 0) ? (real'(hit_patterns) / real'(total_patterns) * 100.0) : 0.0;
endfunction
// 测试序列
initial begin
reset = 1;
addr = 8'h00;
data = 8'h00;
opcode = 4'h0;
mode = 2'b00;
valid = 1;
error = 0;
#20 reset = 0;
$display("开始特殊bins测试...");
// 测试通配符地址模式
$display("\n测试地址通配符模式:");
@(posedge clk); addr = 8'b00000101; // 0000???? 模式
@(posedge clk); addr = 8'b00010011; // 0001???? 模式
@(posedge clk); addr = 8'b11111000; // 1111???? 模式
@(posedge clk); addr = 8'b10101010; // 偶数地址
@(posedge clk); addr = 8'b10101011; // 奇数地址
// 测试数据通配符模式
$display("测试数据通配符模式:");
@(posedge clk); data = 8'b10101010; // 精确匹配
@(posedge clk); data = 8'b10100111; // 1010???? 模式
@(posedge clk); data = 8'b11111010; // ????1010 模式
@(posedge clk); data = 8'b00000000; // ?0?0?0?0 模式
@(posedge clk); data = 8'b10110101; // 1??????? 模式
// 测试有效操作码
$display("测试操作码(包含忽略值):");
for (int i = 0; i < 16; i++) begin
@(posedge clk);
opcode = i;
case (i)
4'hB, 4'hC, 4'hD, 4'hE, 4'hF:
$display(" 操作码 %h 被忽略", i);
default:
$display(" 操作码 %h 有效", i);
endcase
end
// 测试模式(包含非法值)
$display("测试模式(包含非法值):");
@(posedge clk); mode = 2'b00; // 正常模式
@(posedge clk); mode = 2'b01; // 测试模式
@(posedge clk); mode = 2'b10; // 调试模式
$display("尝试非法模式(应该报错):");
@(posedge clk); mode = 2'b11; // 非法模式
// 测试组合模式
$display("测试组合模式:");
@(posedge clk); addr = 8'hF5; data = 8'h3A; // 高地址任意数据
@(posedge clk); addr = 8'h05; data = 8'hFA; // 低地址任意数据
@(posedge clk); addr = 8'h5A; data = 8'hA5; // 特定组合
@(posedge clk); addr = 8'hFF; data = 8'hFF; // 忽略的测试模式
$display("尝试非法组合(应该报错):");
@(posedge clk); addr = 8'h00; data = 8'h00; // 非法组合
// 测试条件通配符
$display("测试条件通配符:");
valid = 1;
@(posedge clk); addr = 8'h42; // 有效偶数
@(posedge clk); addr = 8'h43; // 有效奇数
valid = 0;
@(posedge clk); addr = 8'h44; // 无效,应被忽略
@(posedge clk); addr = 8'h45; // 无效,应被忽略
// 测试复杂模式
$display("测试复杂模式:");
valid = 1;
@(posedge clk); data = 8'h00; // nibble 0
@(posedge clk); data = 8'h10; // nibble 1
@(posedge clk); data = 8'h20; // nibble 2
@(posedge clk); data = 8'h30; // nibble 3
@(posedge clk); data = 8'hE0; // 忽略的测试nibble
@(posedge clk); data = 8'hF0; // 忽略的测试nibble
// 测试错误模式
$display("测试错误模式(应该报错):");
error = 1;
@(posedge clk); data = 8'h55; // 错误状态下的任意数据
error = 0;
// 随机测试
$display("随机测试:");
repeat (100) begin
@(posedge clk);
addr = $random;
data = $random;
opcode = $random % 16;
mode = $random % 4;
valid = $random % 2;
error = ($random % 20) == 0; // 5%概率出现错误
end
// SystemVerilog覆盖率报告
$display("\n=== SystemVerilog特殊Bins覆盖率报告 ===");
$display("总体覆盖率: %0.2f%%", spec_cg.get_coverage());
$display("地址通配符覆盖率: %0.2f%%", spec_cg.addr_wildcard_cp.get_coverage());
$display("数据通配符覆盖率: %0.2f%%", spec_cg.data_wildcard_cp.get_coverage());
$display("操作码覆盖率(忽略保留值): %0.2f%%", spec_cg.opcode_ignore_cp.get_coverage());
$display("模式覆盖率(排除非法值): %0.2f%%", spec_cg.mode_illegal_cp.get_coverage());
$display("组合覆盖率: %0.2f%%", spec_cg.addr_data_combo_cp.get_coverage());
$display("条件通配符覆盖率: %0.2f%%", spec_cg.conditional_wildcard_cp.get_coverage());
$display("复杂模式覆盖率: %0.2f%%", spec_cg.complex_pattern_cp.get_coverage());
$display("\n高级特殊bins覆盖率:");
$display("多层通配符覆盖率: %0.2f%%", adv_spec_cg.multi_wildcard_cp.get_coverage());
$display("范围通配符覆盖率: %0.2f%%", adv_spec_cg.range_wildcard_cp.get_coverage());
$display("状态特殊覆盖率: %0.2f%%", adv_spec_cg.state_special_cp.get_coverage());
// 传统Verilog统计报告
$display("\n=== 传统Verilog特殊Bins统计 ===");
$display("总采样数: %d", verilog_stats.total_samples);
$display("错误计数: %d", verilog_stats.error_count);
$display("地址模式统计:");
$display(" 0000????: %d", verilog_stats.addr_pattern_counts[0]);
$display(" 0001????: %d", verilog_stats.addr_pattern_counts[1]);
$display(" 1111????: %d", verilog_stats.addr_pattern_counts[2]);
$display(" 偶数地址: %d", verilog_stats.addr_pattern_counts[3]);
$display(" 奇数地址: %d", verilog_stats.addr_pattern_counts[4]);
$display("数据模式统计:");
$display(" 精确匹配: %d", verilog_stats.data_pattern_counts[0]);
$display(" 1010????: %d", verilog_stats.data_pattern_counts[1]);
$display(" ????1010: %d", verilog_stats.data_pattern_counts[2]);
$display(" ?0?0?0?0: %d", verilog_stats.data_pattern_counts[3]);
$display(" 1???????: %d", verilog_stats.data_pattern_counts[4]);
$display("操作码统计(忽略保留值):");
$display(" 有效操作码: %d", verilog_stats.valid_opcode_count);
$display(" 扩展操作码: %d", verilog_stats.extended_opcode_count);
$display("模式统计:");
$display(" 正常模式: %d", verilog_stats.normal_mode_count);
$display(" 测试模式: %d", verilog_stats.test_mode_count);
$display(" 调试模式: %d", verilog_stats.debug_mode_count);
$display(" 非法模式检测: %d", verilog_stats.illegal_mode_detected);
$display("条件统计:");
$display(" 有效偶数: %d", verilog_stats.valid_even_count);
$display(" 有效奇数: %d", verilog_stats.valid_odd_count);
$display(" 无效忽略: %d", verilog_stats.invalid_ignored_count);
$display("复杂模式统计:");
$display(" Nibble统计: [%d, %d, %d, %d]",
verilog_stats.nibble_counts[0], verilog_stats.nibble_counts[1],
verilog_stats.nibble_counts[2], verilog_stats.nibble_counts[3]);
$display(" 测试nibble忽略: %d", verilog_stats.test_nibble_ignored);
$display(" 错误模式检测: %d", verilog_stats.error_pattern_detected);
// 覆盖率计算
$display("\n手动计算的覆盖率:");
$display("地址模式覆盖率: %0.2f%%", calculate_wildcard_coverage(verilog_stats.addr_pattern_counts, 5));
$display("数据模式覆盖率: %0.2f%%", calculate_wildcard_coverage(verilog_stats.data_pattern_counts, 5));
$finish;
end
endmodule
4 交叉覆盖率
交叉覆盖率(Cross Coverage)用于检测多个变量之间的组合覆盖情况,确保所有重要的变量组合都被测试到。
4.1 cross定义
module cross_coverage_example;
logic clk, reset;
logic [2:0] addr_type;
logic [1:0] data_size;
logic [1:0] burst_type;
logic read_write;
logic [3:0] priority;
logic valid, ready;
logic [7:0] data;
covergroup cross_basic_cg @(posedge clk iff !reset);
// 基本coverpoint定义
addr_type_cp: coverpoint addr_type {
bins sequential = {3'b000};
bins random = {3'b001};
bins fixed = {3'b010};
bins burst = {3'b011};
bins wrap = {3'b100};
bins reserved = {[3'b101:3'b111]};
}
data_size_cp: coverpoint data_size {
bins byte_size = {2'b00};
bins half_word = {2'b01};
bins word = {2'b10};
bins double_word = {2'b11};
}
burst_type_cp: coverpoint burst_type {
bins single = {2'b00};
bins incr = {2'b01};
bins wrap4 = {2'b10};
bins wrap8 = {2'b11};
}
rw_cp: coverpoint read_write {
bins read = {1'b0};
bins write = {1'b1};
}
// 基本交叉覆盖率 - 地址类型与数据大小
addr_data_cross: cross addr_type_cp, data_size_cp;
// 三变量交叉覆盖率
addr_data_burst_cross: cross addr_type_cp, data_size_cp, burst_type_cp;
// 四变量交叉覆盖率
full_transaction_cross: cross addr_type_cp, data_size_cp, burst_type_cp, rw_cp;
// 带条件的交叉覆盖率
valid_transaction_cross: cross addr_type_cp, data_size_cp, rw_cp iff (valid && ready);
endgroup
// 高级交叉覆盖率示例
covergroup cross_advanced_cg @(posedge clk);
// 优先级coverpoint
priority_cp: coverpoint priority {
bins low = {[4'h0:4'h3]};
bins medium = {[4'h4:4'h7]};
bins high = {[4'h8:4'hB]};
bins critical = {[4'hC:4'hF]};
}
// 数据模式coverpoint
data_pattern_cp: coverpoint data {
bins zero = {8'h00};
bins low_values = {[8'h01:8'h3F]};
bins mid_values = {[8'h40:8'hBF]};
bins high_values = {[8'hC0:8'hFE]};
bins max = {8'hFF};
}
// 状态coverpoint
state_cp: coverpoint {valid, ready} {
bins idle = {2'b00};
bins waiting = {2'b10};
bins ready_state = {2'b01};
bins active = {2'b11};
}
// 复杂交叉覆盖率
priority_data_cross: cross priority_cp, data_pattern_cp;
priority_state_cross: cross priority_cp, state_cp;
data_state_cross: cross data_pattern_cp, state_cp;
// 全面交叉覆盖率
complete_cross: cross priority_cp, data_pattern_cp, state_cp;
endgroup
// 实例化covergroup
cross_basic_cg basic_cross = new();
cross_advanced_cg advanced_cross = new();
// 传统Verilog的等价实现(非常复杂)
typedef struct {
// 二维交叉统计 - 地址类型 x 数据大小
integer addr_data_matrix[6][4]; // 6种地址类型 x 4种数据大小
// 三维交叉统计 - 地址类型 x 数据大小 x 突发类型
integer addr_data_burst_cube[6][4][4];
// 四维交叉统计 - 地址类型 x 数据大小 x 突发类型 x 读写
integer full_transaction_hypercube[6][4][4][2];
// 条件交叉统计
integer valid_transaction_matrix[6][4][2];
integer valid_condition_count;
// 高级交叉统计
integer priority_data_matrix[4][5]; // 4种优先级 x 5种数据模式
integer priority_state_matrix[4][4]; // 4种优先级 x 4种状态
integer data_state_matrix[5][4]; // 5种数据模式 x 4种状态
integer complete_cube[4][5][4]; // 完整三维交叉
// 统计计数器
integer total_samples;
integer valid_samples;
// 覆盖率统计
integer addr_data_hits;
integer addr_data_burst_hits;
integer full_transaction_hits;
integer valid_transaction_hits;
integer priority_data_hits;
integer priority_state_hits;
integer data_state_hits;
integer complete_hits;
} verilog_cross_stats_t;
verilog_cross_stats_t verilog_cross_stats;
// 映射函数
function integer map_addr_type(logic [2:0] addr);
case (addr)
3'b000: return 0; // sequential
3'b001: return 1; // random
3'b010: return 2; // fixed
3'b011: return 3; // burst
3'b100: return 4; // wrap
default: return 5; // reserved
endcase
endfunction
function integer map_data_size(logic [1:0] size);
return size; // 直接映射 0-3
endfunction
function integer map_burst_type(logic [1:0] burst);
return burst; // 直接映射 0-3
endfunction
function integer map_priority(logic [3:0] prio);
if (prio <= 4'h3) return 0; // low
else if (prio <= 4'h7) return 1; // medium
else if (prio <= 4'hB) return 2; // high
else return 3; // critical
endfunction
function integer map_data_pattern(logic [7:0] data_val);
if (data_val == 8'h00) return 0; // zero
else if (data_val <= 8'h3F) return 1; // low_values
else if (data_val <= 8'hBF) return 2; // mid_values
else if (data_val <= 8'hFE) return 3; // high_values
else return 4; // max (8'hFF)
endfunction
function integer map_state(logic valid_sig, logic ready_sig);
return {valid_sig, ready_sig}; // 直接映射到0-3
endfunction
// 手动实现交叉覆盖率统计
always @(posedge clk) begin
if (reset) begin
verilog_cross_stats = '{default: 0};
end else begin
automatic integer addr_idx = map_addr_type(addr_type);
automatic integer data_idx = map_data_size(data_size);
automatic integer burst_idx = map_burst_type(burst_type);
automatic integer rw_idx = read_write;
automatic integer prio_idx = map_priority(priority);
automatic integer pattern_idx = map_data_pattern(data);
automatic integer state_idx = map_state(valid, ready);
verilog_cross_stats.total_samples++;
// 二维交叉统计 - 地址类型 x 数据大小
if (verilog_cross_stats.addr_data_matrix[addr_idx][data_idx] == 0) begin
verilog_cross_stats.addr_data_hits++;
end
verilog_cross_stats.addr_data_matrix[addr_idx][data_idx]++;
// 三维交叉统计 - 地址类型 x 数据大小 x 突发类型
if (verilog_cross_stats.addr_data_burst_cube[addr_idx][data_idx][burst_idx] == 0) begin
verilog_cross_stats.addr_data_burst_hits++;
end
verilog_cross_stats.addr_data_burst_cube[addr_idx][data_idx][burst_idx]++;
// 四维交叉统计 - 完整事务
if (verilog_cross_stats.full_transaction_hypercube[addr_idx][data_idx][burst_idx][rw_idx] == 0) begin
verilog_cross_stats.full_transaction_hits++;
end
verilog_cross_stats.full_transaction_hypercube[addr_idx][data_idx][burst_idx][rw_idx]++;
// 条件交叉统计 - 仅在valid && ready时统计
if (valid && ready) begin
verilog_cross_stats.valid_condition_count++;
if (verilog_cross_stats.valid_transaction_matrix[addr_idx][data_idx][rw_idx] == 0) begin
verilog_cross_stats.valid_transaction_hits++;
end
verilog_cross_stats.valid_transaction_matrix[addr_idx][data_idx][rw_idx]++;
verilog_cross_stats.valid_samples++;
end
// 高级交叉统计 - 优先级 x 数据模式
if (verilog_cross_stats.priority_data_matrix[prio_idx][pattern_idx] == 0) begin
verilog_cross_stats.priority_data_hits++;
end
verilog_cross_stats.priority_data_matrix[prio_idx][pattern_idx]++;
// 优先级 x 状态
if (verilog_cross_stats.priority_state_matrix[prio_idx][state_idx] == 0) begin
verilog_cross_stats.priority_state_hits++;
end
verilog_cross_stats.priority_state_matrix[prio_idx][state_idx]++;
// 数据模式 x 状态
if (verilog_cross_stats.data_state_matrix[pattern_idx][state_idx] == 0) begin
verilog_cross_stats.data_state_hits++;
end
verilog_cross_stats.data_state_matrix[pattern_idx][state_idx]++;
// 完整三维交叉 - 优先级 x 数据模式 x 状态
if (verilog_cross_stats.complete_cube[prio_idx][pattern_idx][state_idx] == 0) begin
verilog_cross_stats.complete_hits++;
end
verilog_cross_stats.complete_cube[prio_idx][pattern_idx][state_idx]++;
end
end
// 覆盖率计算函数
function real calculate_cross_coverage(integer hits, integer total_bins);
return (total_bins > 0) ? (real'(hits) / real'(total_bins) * 100.0) : 0.0;
endfunction
// 测试序列
initial begin
reset = 1;
addr_type = 3'b000;
data_size = 2'b00;
burst_type = 2'b00;
read_write = 1'b0;
priority = 4'h0;
valid = 0;
ready = 0;
data = 8'h00;
#20 reset = 0;
$display("开始交叉覆盖率测试...");
// 系统性测试所有基本组合
$display("\n系统性测试地址类型和数据大小组合:");
for (int addr = 0; addr < 6; addr++) begin
for (int size = 0; size < 4; size++) begin
@(posedge clk);
addr_type = addr;
data_size = size;
burst_type = $random % 4;
read_write = $random % 2;
priority = $random % 16;
data = $random;
valid = 1;
ready = 1;
$display(" 测试组合: addr_type=%d, data_size=%d", addr, size);
end
end
// 测试三维交叉
$display("\n测试三维交叉覆盖率:");
repeat (50) begin
@(posedge clk);
addr_type = $random % 6;
data_size = $random % 4;
burst_type = $random % 4;
read_write = $random % 2;
priority = $random % 16;
data = $random;
valid = $random % 2;
ready = $random % 2;
end
// 测试条件交叉覆盖率
$display("\n测试条件交叉覆盖率(valid && ready):");
valid = 1;
ready = 1;
repeat (30) begin
@(posedge clk);
addr_type = $random % 6;
data_size = $random % 4;
read_write = $random % 2;
priority = $random % 16;
data = $random;
end
// 测试高级交叉组合
$display("\n测试高级交叉组合:");
for (int prio = 0; prio < 4; prio++) begin
for (int pattern = 0; pattern < 5; pattern++) begin
for (int state = 0; state < 4; state++) begin
@(posedge clk);
case (prio)
0: priority = $random % 4; // low
1: priority = 4 + ($random % 4); // medium
2: priority = 8 + ($random % 4); // high
3: priority = 12 + ($random % 4); // critical
endcase
case (pattern)
0: data = 8'h00; // zero
1: data = 1 + ($random % 63); // low_values
2: data = 64 + ($random % 128); // mid_values
3: data = 192 + ($random % 62); // high_values
4: data = 8'hFF; // max
endcase
{valid, ready} = state;
addr_type = $random % 6;
data_size = $random % 4;
burst_type = $random % 4;
read_write = $random % 2;
end
end
end
// 随机压力测试
$display("\n随机压力测试:");
repeat (200) begin
@(posedge clk);
addr_type = $random % 8; // 包含无效值
data_size = $random % 4;
burst_type = $random % 4;
read_write = $random % 2;
priority = $random % 16;
data = $random;
valid = $random % 2;
ready = $random % 2;
end
// SystemVerilog交叉覆盖率报告
$display("\n=== SystemVerilog交叉覆盖率报告 ===");
$display("基本交叉覆盖率组:");
$display(" 总体覆盖率: %0.2f%%", basic_cross.get_coverage());
$display(" 地址-数据交叉: %0.2f%%", basic_cross.addr_data_cross.get_coverage());
$display(" 地址-数据-突发交叉: %0.2f%%", basic_cross.addr_data_burst_cross.get_coverage());
$display(" 完整事务交叉: %0.2f%%", basic_cross.full_transaction_cross.get_coverage());
$display(" 有效事务交叉: %0.2f%%", basic_cross.valid_transaction_cross.get_coverage());
$display("\n高级交叉覆盖率组:");
$display(" 总体覆盖率: %0.2f%%", advanced_cross.get_coverage());
$display(" 优先级-数据交叉: %0.2f%%", advanced_cross.priority_data_cross.get_coverage());
$display(" 优先级-状态交叉: %0.2f%%", advanced_cross.priority_state_cross.get_coverage());
$display(" 数据-状态交叉: %0.2f%%", advanced_cross.data_state_cross.get_coverage());
$display(" 完整交叉: %0.2f%%", advanced_cross.complete_cross.get_coverage());
// 传统Verilog交叉统计报告
$display("\n=== 传统Verilog交叉统计 ===");
$display("总采样数: %d", verilog_cross_stats.total_samples);
$display("有效采样数: %d", verilog_cross_stats.valid_samples);
$display("\n交叉覆盖率统计:");
$display(" 地址-数据交叉命中: %d/24 (%.2f%%)",
verilog_cross_stats.addr_data_hits,
calculate_cross_coverage(verilog_cross_stats.addr_data_hits, 24));
$display(" 地址-数据-突发交叉命中: %d/96 (%.2f%%)",
verilog_cross_stats.addr_data_burst_hits,
calculate_cross_coverage(verilog_cross_stats.addr_data_burst_hits, 96));
$display(" 完整事务交叉命中: %d/192 (%.2f%%)",
verilog_cross_stats.full_transaction_hits,
calculate_cross_coverage(verilog_cross_stats.full_transaction_hits, 192));
$display(" 有效事务交叉命中: %d/48 (%.2f%%)",
verilog_cross_stats.valid_transaction_hits,
calculate_cross_coverage(verilog_cross_stats.valid_transaction_hits, 48));
$display(" 优先级-数据交叉命中: %d/20 (%.2f%%)",
verilog_cross_stats.priority_data_hits,
calculate_cross_coverage(verilog_cross_stats.priority_data_hits, 20));
$display(" 优先级-状态交叉命中: %d/16 (%.2f%%)",
verilog_cross_stats.priority_state_hits,
calculate_cross_coverage(verilog_cross_stats.priority_state_hits, 16));
$display(" 数据-状态交叉命中: %d/20 (%.2f%%)",
verilog_cross_stats.data_state_hits,
calculate_cross_coverage(verilog_cross_stats.data_state_hits, 20));
$display(" 完整三维交叉命中: %d/80 (%.2f%%)",
verilog_cross_stats.complete_hits,
calculate_cross_coverage(verilog_cross_stats.complete_hits, 80));
// 详细矩阵显示(部分)
$display("\n地址-数据交叉矩阵(前3x3):");
for (int i = 0; i < 3; i++) begin
$write(" [%d]: ", i);
for (int j = 0; j < 3; j++) begin
$write("%3d ", verilog_cross_stats.addr_data_matrix[i][j]);
end
$display("");
end
$finish;
end
endmodule
4.2 排除部分cross bin
在实际应用中,某些交叉组合可能是无效的或不需要测试的,SystemVerilog提供了多种方式来排除这些组合。
module cross_exclusion_example;
logic clk, reset;
logic [1:0] cmd_type;
logic [2:0] addr_mode;
logic [1:0] data_width;
logic [3:0] burst_len;
logic cache_enable;
logic [1:0] protection;
covergroup cross_exclude_cg @(posedge clk iff !reset);
// 命令类型coverpoint
cmd_cp: coverpoint cmd_type {
bins read = {2'b00};
bins write = {2'b01};
bins rmw = {2'b10}; // read-modify-write
bins reserved = {2'b11};
}
// 地址模式coverpoint
addr_cp: coverpoint addr_mode {
bins linear = {3'b000};
bins burst = {3'b001};
bins wrap = {3'b010};
bins fixed = {3'b011};
bins stream = {3'b100};
bins reserved1 = {3'b101};
bins reserved2 = {3'b110};
bins invalid = {3'b111};
}
// 数据宽度coverpoint
width_cp: coverpoint data_width {
bins byte_width = {2'b00};
bins half_word = {2'b01};
bins word = {2'b10};
bins double_word = {2'b11};
}
// 突发长度coverpoint
burst_cp: coverpoint burst_len {
bins single = {4'h1};
bins short_burst = {[4'h2:4'h4]};
bins medium_burst = {[4'h5:4'h8]};
bins long_burst = {[4'h9:4'hF]};
bins invalid = {4'h0};
}
// 缓存使能coverpoint
cache_cp: coverpoint cache_enable {
bins disabled = {1'b0};
bins enabled = {1'b1};
}
// 保护级别coverpoint
prot_cp: coverpoint protection {
bins user = {2'b00};
bins supervisor = {2'b01};
bins secure = {2'b10};
bins debug = {2'b11};
}
// 基本交叉覆盖率,排除无效组合
cmd_addr_cross: cross cmd_cp, addr_cp {
// 排除保留命令与所有地址模式的组合
ignore_bins reserved_cmd = binsof(cmd_cp.reserved);
// 排除无效地址模式与所有命令的组合
ignore_bins invalid_addr = binsof(addr_cp.invalid);
// 排除特定的无效组合
ignore_bins rmw_fixed = binsof(cmd_cp.rmw) && binsof(addr_cp.fixed);
ignore_bins rmw_stream = binsof(cmd_cp.rmw) && binsof(addr_cp.stream);
// 排除保留地址模式
ignore_bins reserved_addr = binsof(addr_cp.reserved1) || binsof(addr_cp.reserved2);
}
// 命令、地址、数据宽度三维交叉,带复杂排除条件
cmd_addr_width_cross: cross cmd_cp, addr_cp, width_cp {
// 排除所有包含保留/无效值的组合
ignore_bins invalid_combinations =
binsof(cmd_cp.reserved) ||
binsof(addr_cp.invalid) ||
binsof(addr_cp.reserved1) ||
binsof(addr_cp.reserved2);
// 排除特定的硬件限制组合
ignore_bins rmw_restrictions =
binsof(cmd_cp.rmw) && (
binsof(addr_cp.fixed) ||
binsof(addr_cp.stream) ||
binsof(width_cp.byte_width)
);
// 排除流模式的限制
ignore_bins stream_restrictions =
binsof(addr_cp.stream) && (
binsof(width_cp.byte_width) ||
binsof(width_cp.half_word)
);
// 排除包装模式的限制
ignore_bins wrap_restrictions =
binsof(addr_cp.wrap) && binsof(width_cp.double_word);
}
// 突发相关交叉,排除不合理的突发组合
addr_burst_cross: cross addr_cp, burst_cp {
// 排除无效突发长度
ignore_bins invalid_burst = binsof(burst_cp.invalid);
// 线性模式不支持长突发
ignore_bins linear_long =
binsof(addr_cp.linear) && binsof(burst_cp.long_burst);
// 固定模式只支持单次传输
ignore_bins fixed_multi =
binsof(addr_cp.fixed) && (
binsof(burst_cp.short_burst) ||
binsof(burst_cp.medium_burst) ||
binsof(burst_cp.long_burst)
);
// 包装模式有特定的突发长度限制
ignore_bins wrap_invalid =
binsof(addr_cp.wrap) && (
binsof(burst_cp.single) ||
binsof(burst_cp.long_burst)
);
// 流模式不支持短突发
ignore_bins stream_short =
binsof(addr_cp.stream) && binsof(burst_cp.short_burst);
}
// 缓存和保护级别交叉
cache_prot_cross: cross cache_cp, prot_cp {
// 调试模式下禁用缓存
ignore_bins debug_cached =
binsof(cache_cp.enabled) && binsof(prot_cp.debug);
}
// 复杂的四维交叉,带多重排除条件
complex_cross: cross cmd_cp, addr_cp, width_cp, cache_cp {
// 基本无效组合排除
ignore_bins basic_invalid =
binsof(cmd_cp.reserved) ||
binsof(addr_cp.invalid) ||
binsof(addr_cp.reserved1) ||
binsof(addr_cp.reserved2);
// RMW命令的限制
ignore_bins rmw_limits =
binsof(cmd_cp.rmw) && (
binsof(addr_cp.fixed) ||
binsof(addr_cp.stream) ||
(binsof(width_cp.byte_width) && binsof(cache_cp.enabled))
);
// 缓存相关限制
ignore_bins cache_limits =
binsof(cache_cp.enabled) && (
binsof(addr_cp.fixed) ||
(binsof(addr_cp.stream) && binsof(width_cp.byte_width))
);
// 特定的架构限制
ignore_bins arch_limits =
(binsof(addr_cp.wrap) && binsof(width_cp.double_word)) ||
(binsof(addr_cp.stream) && binsof(width_cp.half_word) && binsof(cache_cp.disabled));
}
// 使用条件表达式的排除
conditional_cross: cross cmd_cp, addr_cp, prot_cp {
// 使用iff条件排除某些组合
ignore_bins secure_restrictions =
binsof(prot_cp.secure) && (
binsof(addr_cp.stream) ||
binsof(cmd_cp.rmw)
) iff (cache_enable == 1'b0);
// 用户模式的限制
ignore_bins user_restrictions =
binsof(prot_cp.user) && (
binsof(addr_cp.reserved1) ||
binsof(addr_cp.reserved2)
);
}
endgroup
// 实例化covergroup
cross_exclude_cg exclude_cg = new();
// 传统Verilog实现(需要手动处理所有排除逻辑)
typedef struct {
// 有效组合计数器
integer cmd_addr_valid_combinations;
integer cmd_addr_width_valid_combinations;
integer addr_burst_valid_combinations;
integer cache_prot_valid_combinations;
integer complex_valid_combinations;
integer conditional_valid_combinations;
// 排除组合计数器
integer cmd_addr_excluded;
integer cmd_addr_width_excluded;
integer addr_burst_excluded;
integer cache_prot_excluded;
integer complex_excluded;
integer conditional_excluded;
// 总计数器
integer total_samples;
integer valid_samples;
// 具体的有效组合矩阵(简化版)
bit cmd_addr_matrix[4][8]; // 4命令 x 8地址模式
bit cmd_addr_width_cube[4][8][4]; // 4命令 x 8地址 x 4宽度
bit addr_burst_matrix[8][5]; // 8地址 x 5突发类型
bit cache_prot_matrix[2][4]; // 2缓存 x 4保护
} verilog_exclude_stats_t;
verilog_exclude_stats_t verilog_exclude_stats;
// 排除逻辑检查函数
function bit is_cmd_addr_valid(logic [1:0] cmd, logic [2:0] addr);
// 检查命令-地址组合是否有效
if (cmd == 2'b11) return 0; // 保留命令
if (addr == 3'b111) return 0; // 无效地址
if (addr == 3'b101 || addr == 3'b110) return 0; // 保留地址
if (cmd == 2'b10 && (addr == 3'b011 || addr == 3'b100)) return 0; // RMW限制
return 1;
endfunction
function bit is_cmd_addr_width_valid(logic [1:0] cmd, logic [2:0] addr, logic [1:0] width);
// 首先检查基本的命令-地址有效性
if (!is_cmd_addr_valid(cmd, addr)) return 0;
// RMW命令的宽度限制
if (cmd == 2'b10 && width == 2'b00) return 0; // RMW不支持字节宽度
// 流模式的宽度限制
if (addr == 3'b100 && (width == 2'b00 || width == 2'b01)) return 0;
// 包装模式的宽度限制
if (addr == 3'b010 && width == 2'b11) return 0;
return 1;
endfunction
function bit is_addr_burst_valid(logic [2:0] addr, logic [3:0] burst);
// 检查地址-突发组合是否有效
if (burst == 4'h0) return 0; // 无效突发长度
if (addr == 3'b111) return 0; // 无效地址
// 线性模式不支持长突发
if (addr == 3'b000 && burst >= 4'h9) return 0;
// 固定模式只支持单次传输
if (addr == 3'b011 && burst != 4'h1) return 0;
// 包装模式的突发限制
if (addr == 3'b010 && (burst == 4'h1 || burst >= 4'h9)) return 0;
// 流模式不支持短突发
if (addr == 3'b100 && (burst >= 4'h2 && burst <= 4'h4)) return 0;
return 1;
endfunction
function bit is_cache_prot_valid(logic cache, logic [1:0] prot);
// 调试模式下禁用缓存
if (prot == 2'b11 && cache == 1'b1) return 0;
return 1;
endfunction
function bit is_complex_valid(logic [1:0] cmd, logic [2:0] addr, logic [1:0] width, logic cache);
// 基本有效性检查
if (!is_cmd_addr_width_valid(cmd, addr, width)) return 0;
// RMW命令的缓存限制
if (cmd == 2'b10 && width == 2'b00 && cache == 1'b1) return 0;
// 缓存相关限制
if (cache == 1'b1) begin
if (addr == 3'b011) return 0; // 固定模式不支持缓存
if (addr == 3'b100 && width == 2'b00) return 0; // 流模式字节宽度不支持缓存
end
// 特定架构限制
if (addr == 3'b010 && width == 2'b11) return 0; // 包装模式双字限制
if (addr == 3'b100 && width == 2'b01 && cache == 1'b0) return 0; // 流模式半字无缓存限制
return 1;
endfunction
// 手动实现排除逻辑的统计
always @(posedge clk) begin
if (reset) begin
verilog_exclude_stats = '{default: 0};
end else begin
verilog_exclude_stats.total_samples++;
// 检查并统计各种交叉组合
if (is_cmd_addr_valid(cmd_type, addr_mode)) begin
if (!verilog_exclude_stats.cmd_addr_matrix[cmd_type][addr_mode]) begin
verilog_exclude_stats.cmd_addr_valid_combinations++;
verilog_exclude_stats.cmd_addr_matrix[cmd_type][addr_mode] = 1;
end
end else begin
verilog_exclude_stats.cmd_addr_excluded++;
end
if (is_cmd_addr_width_valid(cmd_type, addr_mode, data_width)) begin
if (!verilog_exclude_stats.cmd_addr_width_cube[cmd_type][addr_mode][data_width]) begin
verilog_exclude_stats.cmd_addr_width_valid_combinations++;
verilog_exclude_stats.cmd_addr_width_cube[cmd_type][addr_mode][data_width] = 1;
end
end else begin
verilog_exclude_stats.cmd_addr_width_excluded++;
end
if (is_addr_burst_valid(addr_mode, burst_len)) begin
// 映射突发长度到索引
automatic integer burst_idx;
if (burst_len == 4'h1) burst_idx = 0;
else if (burst_len >= 4'h2 && burst_len <= 4'h4) burst_idx = 1;
else if (burst_len >= 4'h5 && burst_len <= 4'h8) burst_idx = 2;
else if (burst_len >= 4'h9 && burst_len <= 4'hF) burst_idx = 3;
else burst_idx = 4; // invalid
if (burst_idx < 4 && !verilog_exclude_stats.addr_burst_matrix[addr_mode][burst_idx]) begin
verilog_exclude_stats.addr_burst_valid_combinations++;
verilog_exclude_stats.addr_burst_matrix[addr_mode][burst_idx] = 1;
end
end else begin
verilog_exclude_stats.addr_burst_excluded++;
end
if (is_cache_prot_valid(cache_enable, protection)) begin
if (!verilog_exclude_stats.cache_prot_matrix[cache_enable][protection]) begin
verilog_exclude_stats.cache_prot_valid_combinations++;
verilog_exclude_stats.cache_prot_matrix[cache_enable][protection] = 1;
end
end else begin
verilog_exclude_stats.cache_prot_excluded++;
end
if (is_complex_valid(cmd_type, addr_mode, data_width, cache_enable)) begin
verilog_exclude_stats.complex_valid_combinations++;
end else begin
verilog_exclude_stats.complex_excluded++;
end
// 条件排除逻辑
if (protection == 2'b10 && (addr_mode == 3'b100 || cmd_type == 2'b10) && cache_enable == 1'b0) begin
verilog_exclude_stats.conditional_excluded++;
end else if (protection == 2'b00 && (addr_mode == 3'b101 || addr_mode == 3'b110)) begin
verilog_exclude_stats.conditional_excluded++;
end else begin
verilog_exclude_stats.conditional_valid_combinations++;
end
verilog_exclude_stats.valid_samples++;
end
end
// 测试序列
initial begin
reset = 1;
cmd_type = 2'b00;
addr_mode = 3'b000;
data_width = 2'b00;
burst_len = 4'h1;
cache_enable = 1'b0;
protection = 2'b00;
#20 reset = 0;
$display("开始交叉排除测试...");
// 系统性测试所有可能的组合
$display("\n系统性测试所有命令-地址组合:");
for (int cmd = 0; cmd < 4; cmd++) begin
for (int addr = 0; addr < 8; addr++) begin
@(posedge clk);
cmd_type = cmd;
addr_mode = addr;
data_width = $random % 4;
burst_len = 1 + ($random % 15);
cache_enable = $random % 2;
protection = $random % 4;
if (is_cmd_addr_valid(cmd_type, addr_mode)) begin
$display(" 有效组合: cmd=%d, addr=%d", cmd, addr);
end else begin
$display(" 排除组合: cmd=%d, addr=%d", cmd, addr);
end
end
end
// 测试特定的排除场景
$display("\n测试特定排除场景:");
// 测试保留命令
@(posedge clk); cmd_type = 2'b11; addr_mode = 3'b000;
$display(" 保留命令测试: 应被排除");
// 测试无效地址
@(posedge clk); cmd_type = 2'b00; addr_mode = 3'b111;
$display(" 无效地址测试: 应被排除");
// 测试RMW限制
@(posedge clk); cmd_type = 2'b10; addr_mode = 3'b011; data_width = 2'b00;
$display(" RMW固定地址测试: 应被排除");
// 测试缓存-保护限制
@(posedge clk); cache_enable = 1'b1; protection = 2'b11;
$display(" 调试模式缓存测试: 应被排除");
// 随机测试
$display("\n随机测试:");
repeat (100) begin
@(posedge clk);
cmd_type = $random % 4;
addr_mode = $random % 8;
data_width = $random % 4;
burst_len = $random % 16;
cache_enable = $random % 2;
protection = $random % 4;
end
// SystemVerilog排除覆盖率报告
$display("\n=== SystemVerilog排除覆盖率报告 ===");
$display("总体覆盖率: %0.2f%%", exclude_cg.get_coverage());
$display("命令-地址交叉: %0.2f%%", exclude_cg.cmd_addr_cross.get_coverage());
$display("命令-地址-宽度交叉: %0.2f%%", exclude_cg.cmd_addr_width_cross.get_coverage());
$display("地址-突发交叉: %0.2f%%", exclude_cg.addr_burst_cross.get_coverage());
$display("缓存-保护交叉: %0.2f%%", exclude_cg.cache_prot_cross.get_coverage());
$display("复杂交叉: %0.2f%%", exclude_cg.complex_cross.get_coverage());
$display("条件交叉: %0.2f%%", exclude_cg.conditional_cross.get_coverage());
// 传统Verilog排除统计报告
$display("\n=== 传统Verilog排除统计 ===");
$display("总采样数: %d", verilog_exclude_stats.total_samples);
$display("有效采样数: %d", verilog_exclude_stats.valid_samples);
$display("\n有效组合统计:");
$display(" 命令-地址有效组合: %d", verilog_exclude_stats.cmd_addr_valid_combinations);
$display(" 命令-地址-宽度有效组合: %d", verilog_exclude_stats.cmd_addr_width_valid_combinations);
$display(" 地址-突发有效组合: %d", verilog_exclude_stats.addr_burst_valid_combinations);
$display(" 缓存-保护有效组合: %d", verilog_exclude_stats.cache_prot_valid_combinations);
$display(" 复杂有效组合: %d", verilog_exclude_stats.complex_valid_combinations);
$display(" 条件有效组合: %d", verilog_exclude_stats.conditional_valid_combinations);
$display("\n排除组合统计:");
$display(" 命令-地址排除: %d", verilog_exclude_stats.cmd_addr_excluded);
$display(" 命令-地址-宽度排除: %d", verilog_exclude_stats.cmd_addr_width_excluded);
$display(" 地址-突发排除: %d", verilog_exclude_stats.addr_burst_excluded);
$display(" 缓存-保护排除: %d", verilog_exclude_stats.cache_prot_excluded);
$display(" 复杂排除: %d", verilog_exclude_stats.complex_excluded);
$display(" 条件排除: %d", verilog_exclude_stats.conditional_excluded);
$finish;
end
endmodule
4.3 精细的交叉覆盖率指定
在复杂的验证场景中,我们需要对交叉覆盖率进行更精细的控制,包括指定特定的交叉组合、设置权重、定义自定义bins等。
module fine_grained_cross_example;
logic clk, reset;
logic [2:0] opcode;
logic [1:0] operand_type;
logic [3:0] register_id;
logic [1:0] addressing_mode;
logic [2:0] condition_code;
logic exception_enable;
logic [1:0] privilege_level;
logic [7:0] immediate_value;
covergroup fine_cross_cg @(posedge clk iff !reset);
// 操作码coverpoint
opcode_cp: coverpoint opcode {
bins arithmetic = {3'b000, 3'b001, 3'b010};
bins logical = {3'b011, 3'b100};
bins memory = {3'b101, 3'b110};
bins control = {3'b111};
}
// 操作数类型coverpoint
operand_cp: coverpoint operand_type {
bins register_reg = {2'b00};
bins register_imm = {2'b01};
bins memory_reg = {2'b10};
bins memory_imm = {2'b11};
}
// 寄存器ID coverpoint
reg_cp: coverpoint register_id {
bins low_regs = {[4'h0:4'h3]};
bins mid_regs = {[4'h4:4'h7]};
bins high_regs = {[4'h8:4'hB]};
bins special_regs = {[4'hC:4'hF]};
}
// 寻址模式coverpoint
addr_mode_cp: coverpoint addressing_mode {
bins direct = {2'b00};
bins indirect = {2'b01};
bins indexed = {2'b10};
bins relative = {2'b11};
}
// 条件码coverpoint
condition_cp: coverpoint condition_code {
bins always = {3'b000};
bins zero = {3'b001};
bins negative = {3'b010};
bins carry = {3'b011};
bins overflow = {3'b100};
bins parity = {3'b101};
bins greater = {3'b110};
bins less = {3'b111};
}
// 异常使能coverpoint
exception_cp: coverpoint exception_enable {
bins disabled = {1'b0};
bins enabled = {1'b1};
}
// 特权级别coverpoint
privilege_cp: coverpoint privilege_level {
bins user = {2'b00};
bins supervisor = {2'b01};
bins kernel = {2'b10};
bins hypervisor = {2'b11};
}
// 立即数值coverpoint
immediate_cp: coverpoint immediate_value {
bins zero = {8'h00};
bins small = {[8'h01:8'h0F]};
bins medium = {[8'h10:8'h7F]};
bins large = {[8'h80:8'hFE]};
bins max = {8'hFF};
}
// 基本的精细交叉覆盖率 - 指定特定bins组合
opcode_operand_cross: cross opcode_cp, operand_cp {
// 只关注特定的有意义组合
bins arithmetic_reg_reg = binsof(opcode_cp.arithmetic) && binsof(operand_cp.register_reg);
bins arithmetic_reg_imm = binsof(opcode_cp.arithmetic) && binsof(operand_cp.register_imm);
bins logical_reg_reg = binsof(opcode_cp.logical) && binsof(operand_cp.register_reg);
bins logical_reg_imm = binsof(opcode_cp.logical) && binsof(operand_cp.register_imm);
bins memory_mem_reg = binsof(opcode_cp.memory) && binsof(operand_cp.memory_reg);
bins memory_mem_imm = binsof(opcode_cp.memory) && binsof(operand_cp.memory_imm);
bins control_any = binsof(opcode_cp.control);
// 排除不合理的组合
ignore_bins invalid_memory_combinations =
binsof(opcode_cp.memory) && (
binsof(operand_cp.register_reg) ||
binsof(operand_cp.register_imm)
);
}
// 复杂的三维交叉 - 操作码、寄存器、寻址模式
opcode_reg_addr_cross: cross opcode_cp, reg_cp, addr_mode_cp {
// 算术操作的寄存器使用模式
bins arith_low_direct = binsof(opcode_cp.arithmetic) && binsof(reg_cp.low_regs) && binsof(addr_mode_cp.direct);
bins arith_mid_indirect = binsof(opcode_cp.arithmetic) && binsof(reg_cp.mid_regs) && binsof(addr_mode_cp.indirect);
bins arith_high_indexed = binsof(opcode_cp.arithmetic) && binsof(reg_cp.high_regs) && binsof(addr_mode_cp.indexed);
// 逻辑操作的寄存器使用模式
bins logic_any_direct = binsof(opcode_cp.logical) && binsof(addr_mode_cp.direct);
bins logic_special_any = binsof(opcode_cp.logical) && binsof(reg_cp.special_regs);
// 内存操作的特殊组合
bins mem_special_indirect = binsof(opcode_cp.memory) && binsof(reg_cp.special_regs) && binsof(addr_mode_cp.indirect);
bins mem_any_indexed = binsof(opcode_cp.memory) && binsof(addr_mode_cp.indexed);
bins mem_any_relative = binsof(opcode_cp.memory) && binsof(addr_mode_cp.relative);
// 控制操作的限制
bins control_special_direct = binsof(opcode_cp.control) && binsof(reg_cp.special_regs) && binsof(addr_mode_cp.direct);
// 排除不合理的组合
ignore_bins invalid_control = binsof(opcode_cp.control) && (
binsof(addr_mode_cp.indirect) ||
binsof(addr_mode_cp.indexed) ||
binsof(addr_mode_cp.relative)
);
}
// 条件执行的精细交叉
condition_privilege_cross: cross condition_cp, privilege_cp, exception_cp {
// 用户模式的条件限制
bins user_basic_conditions = binsof(privilege_cp.user) && (
binsof(condition_cp.always) ||
binsof(condition_cp.zero) ||
binsof(condition_cp.negative)
) && binsof(exception_cp.disabled);
// 管理员模式的扩展条件
bins supervisor_extended = binsof(privilege_cp.supervisor) && (
binsof(condition_cp.carry) ||
binsof(condition_cp.overflow) ||
binsof(condition_cp.parity)
);
// 内核模式的完整条件支持
bins kernel_full_conditions = binsof(privilege_cp.kernel) && (
binsof(condition_cp.greater) ||
binsof(condition_cp.less)
) && binsof(exception_cp.enabled);
// 虚拟化模式的特殊处理
bins hypervisor_special = binsof(privilege_cp.hypervisor) && binsof(exception_cp.enabled);
// 排除危险组合
ignore_bins dangerous_user = binsof(privilege_cp.user) && binsof(exception_cp.enabled) && (
binsof(condition_cp.carry) ||
binsof(condition_cp.overflow)
);
}
// 立即数相关的精细交叉
operand_immediate_cross: cross operand_cp, immediate_cp {
// 只有立即数操作数类型才关心立即数值
bins reg_imm_zero = binsof(operand_cp.register_imm) && binsof(immediate_cp.zero);
bins reg_imm_small = binsof(operand_cp.register_imm) && binsof(immediate_cp.small);
bins reg_imm_medium = binsof(operand_cp.register_imm) && binsof(immediate_cp.medium);
bins reg_imm_large = binsof(operand_cp.register_imm) && binsof(immediate_cp.large);
bins reg_imm_max = binsof(operand_cp.register_imm) && binsof(immediate_cp.max);
bins mem_imm_zero = binsof(operand_cp.memory_imm) && binsof(immediate_cp.zero);
bins mem_imm_small = binsof(operand_cp.memory_imm) && binsof(immediate_cp.small);
bins mem_imm_medium = binsof(operand_cp.memory_imm) && binsof(immediate_cp.medium);
bins mem_imm_large = binsof(operand_cp.memory_imm) && binsof(immediate_cp.large);
bins mem_imm_max = binsof(operand_cp.memory_imm) && binsof(immediate_cp.max);
// 排除非立即数操作数与立即数值的组合
ignore_bins non_immediate = (
binsof(operand_cp.register_reg) ||
binsof(operand_cp.memory_reg)
);
}
// 复杂的四维交叉 - 带权重的bins
complex_four_way_cross: cross opcode_cp, operand_cp, privilege_cp, exception_cp {
// 高优先级的关键组合
bins critical_kernel_arith = binsof(opcode_cp.arithmetic) &&
binsof(operand_cp.register_reg) &&
binsof(privilege_cp.kernel) &&
binsof(exception_cp.enabled) with (item.weight = 10);
bins critical_kernel_mem = binsof(opcode_cp.memory) &&
binsof(operand_cp.memory_reg) &&
binsof(privilege_cp.kernel) &&
binsof(exception_cp.enabled) with (item.weight = 10);
// 中等优先级的组合
bins medium_supervisor = binsof(privilege_cp.supervisor) &&
binsof(exception_cp.enabled) with (item.weight = 5);
// 低优先级的用户模式组合
bins low_user = binsof(privilege_cp.user) &&
binsof(exception_cp.disabled) with (item.weight = 1);
// 特殊的虚拟化组合
bins special_hypervisor = binsof(privilege_cp.hypervisor) with (item.weight = 8);
// 排除不支持的组合
ignore_bins unsupported = binsof(opcode_cp.control) &&
binsof(operand_cp.memory_imm);
}
// 使用表达式的精细交叉
expression_cross: cross opcode_cp, reg_cp {
// 使用表达式定义复杂的bins
bins arithmetic_even_regs = binsof(opcode_cp.arithmetic) &&
binsof(reg_cp) intersect {[4'h0:4'hF]} with
(register_id % 2 == 0);
bins logical_odd_regs = binsof(opcode_cp.logical) &&
binsof(reg_cp) intersect {[4'h0:4'hF]} with
(register_id % 2 == 1);
bins memory_power_of_two = binsof(opcode_cp.memory) &&
binsof(reg_cp) intersect {4'h1, 4'h2, 4'h4, 4'h8};
bins control_fibonacci = binsof(opcode_cp.control) &&
binsof(reg_cp) intersect {4'h1, 4'h1, 4'h2, 4'h3, 4'h5, 4'h8};
}
// 条件表达式的交叉
conditional_expression_cross: cross opcode_cp, immediate_cp {
// 只在特定条件下生效的bins
bins arith_small_imm = binsof(opcode_cp.arithmetic) &&
binsof(immediate_cp.small) iff
(operand_type == 2'b01); // register_imm
bins logic_zero_imm = binsof(opcode_cp.logical) &&
binsof(immediate_cp.zero) iff
(operand_type == 2'b01 && exception_enable == 1'b0);
bins mem_large_imm = binsof(opcode_cp.memory) &&
binsof(immediate_cp.large) iff
(operand_type == 2'b11 && privilege_level >= 2'b01);
}
endgroup
// 实例化covergroup
fine_cross_cg fine_cg = new();
// 传统Verilog的等价实现(极其复杂)
typedef struct {
// 精细交叉统计结构
integer opcode_operand_stats[7]; // 7个指定的bins
integer opcode_reg_addr_stats[9]; // 9个指定的bins
integer condition_privilege_stats[4]; // 4个主要bins组
integer operand_immediate_stats[10]; // 10个立即数相关bins
integer complex_four_way_stats[5]; // 5个四维交叉bins
integer expression_stats[4]; // 4个表达式bins
integer conditional_expression_stats[3]; // 3个条件表达式bins
// 权重统计
integer weighted_coverage_points;
integer total_weight;
// 条件统计
integer conditional_hits;
integer conditional_misses;
// 表达式评估计数
integer expression_evaluations;
integer expression_matches;
// 总体统计
integer total_samples;
integer valid_combinations;
} verilog_fine_stats_t;
verilog_fine_stats_t verilog_fine_stats;
// 复杂的映射和检查函数
function integer map_opcode_operand_bin(logic [2:0] op, logic [1:0] operand);
case ({op, operand})
{3'b000, 2'b00}, {3'b001, 2'b00}, {3'b010, 2'b00}: return 0; // arithmetic_reg_reg
{3'b000, 2'b01}, {3'b001, 2'b01}, {3'b010, 2'b01}: return 1; // arithmetic_reg_imm
{3'b011, 2'b00}, {3'b100, 2'b00}: return 2; // logical_reg_reg
{3'b011, 2'b01}, {3'b100, 2'b01}: return 3; // logical_reg_imm
{3'b101, 2'b10}, {3'b110, 2'b10}: return 4; // memory_mem_reg
{3'b101, 2'b11}, {3'b110, 2'b11}: return 5; // memory_mem_imm
{3'b111, 2'b00}, {3'b111, 2'b01}, {3'b111, 2'b10}, {3'b111, 2'b11}: return 6; // control_any
default: return -1; // invalid or ignored
endcase
endfunction
function bit is_valid_opcode_reg_addr(logic [2:0] op, logic [3:0] reg_id, logic [1:0] addr);
// 复杂的有效性检查逻辑
case (op)
3'b000, 3'b001, 3'b010: begin // arithmetic
if (reg_id <= 4'h3 && addr == 2'b00) return 1; // arith_low_direct
if (reg_id >= 4'h4 && reg_id <= 4'h7 && addr == 2'b01) return 1; // arith_mid_indirect
if (reg_id >= 4'h8 && reg_id <= 4'hB && addr == 2'b10) return 1; // arith_high_indexed
end
3'b011, 3'b100: begin // logical
if (addr == 2'b00) return 1; // logic_any_direct
if (reg_id >= 4'hC) return 1; // logic_special_any
end
3'b101, 3'b110: begin // memory
if (reg_id >= 4'hC && addr == 2'b01) return 1; // mem_special_indirect
if (addr == 2'b10 || addr == 2'b11) return 1; // mem_any_indexed/relative
end
3'b111: begin // control
if (reg_id >= 4'hC && addr == 2'b00) return 1; // control_special_direct
end
endcase
return 0;
endfunction
function integer get_bin_weight(logic [2:0] op, logic [1:0] operand, logic [1:0] priv, logic exc);
// 根据组合返回权重
if ((op == 3'b000 || op == 3'b001 || op == 3'b010) && operand == 2'b00 && priv == 2'b10 && exc == 1'b1)
return 10; // critical_kernel_arith
if ((op == 3'b101 || op == 3'b110) && operand == 2'b10 && priv == 2'b10 && exc == 1'b1)
return 10; // critical_kernel_mem
if (priv == 2'b01 && exc == 1'b1)
return 5; // medium_supervisor
if (priv == 2'b00 && exc == 1'b0)
return 1; // low_user
if (priv == 2'b11)
return 8; // special_hypervisor
return 1; // default weight
endfunction
function bit evaluate_expression_bin(logic [2:0] op, logic [3:0] reg_id);
case (op)
3'b000, 3'b001, 3'b010: return (reg_id % 2 == 0); // arithmetic_even_regs
3'b011, 3'b100: return (reg_id % 2 == 1); // logical_odd_regs
3'b101, 3'b110: return (reg_id == 4'h1 || reg_id == 4'h2 || reg_id == 4'h4 || reg_id == 4'h8); // memory_power_of_two
3'b111: return (reg_id == 4'h1 || reg_id == 4'h2 || reg_id == 4'h3 || reg_id == 4'h5 || reg_id == 4'h8); // control_fibonacci
endcase
return 0;
endfunction
function bit evaluate_conditional_expression(logic [2:0] op, logic [7:0] imm, logic [1:0] operand, logic exc, logic [1:0] priv);
case (op)
3'b000, 3'b001, 3'b010: return (operand == 2'b01 && imm >= 8'h01 && imm <= 8'h0F); // arith_small_imm
3'b011, 3'b100: return (operand == 2'b01 && exc == 1'b0 && imm == 8'h00); // logic_zero_imm
3'b101, 3'b110: return (operand == 2'b11 && priv >= 2'b01 && imm >= 8'h80 && imm <= 8'hFE); // mem_large_imm
endcase
return 0;
endfunction
// 手动实现精细交叉覆盖率统计
always @(posedge clk) begin
if (reset) begin
verilog_fine_stats = '{default: 0};
end else begin
automatic integer opcode_operand_bin = map_opcode_operand_bin(opcode, operand_type);
automatic integer current_weight = get_bin_weight(opcode, operand_type, privilege_level, exception_enable);
verilog_fine_stats.total_samples++;
// 操作码-操作数交叉统计
if (opcode_operand_bin >= 0) begin
verilog_fine_stats.opcode_operand_stats[opcode_operand_bin]++;
end
// 操作码-寄存器-地址模式交叉统计
if (is_valid_opcode_reg_addr(opcode, register_id, addressing_mode)) begin
// 简化的统计,实际应该有更详细的分类
case (opcode)
3'b000, 3'b001, 3'b010: verilog_fine_stats.opcode_reg_addr_stats[0]++; // arithmetic bins
3'b011, 3'b100: verilog_fine_stats.opcode_reg_addr_stats[1]++; // logical bins
3'b101, 3'b110: verilog_fine_stats.opcode_reg_addr_stats[2]++; // memory bins
3'b111: verilog_fine_stats.opcode_reg_addr_stats[3]++; // control bins
endcase
end
// 条件-特权级别交叉统计
case (privilege_level)
2'b00: if (!exception_enable) verilog_fine_stats.condition_privilege_stats[0]++; // user_basic
2'b01: if (exception_enable) verilog_fine_stats.condition_privilege_stats[1]++; // supervisor_extended
2'b10: if (exception_enable) verilog_fine_stats.condition_privilege_stats[2]++; // kernel_full
2'b11: if (exception_enable) verilog_fine_stats.condition_privilege_stats[3]++; // hypervisor_special
endcase
// 操作数-立即数交叉统计
if (operand_type == 2'b01 || operand_type == 2'b11) begin // 只有立即数操作数
case (immediate_value)
8'h00: verilog_fine_stats.operand_immediate_stats[0]++; // zero
8'h01: case(8'h0F) verilog_fine_stats.operand_immediate_stats[1]++; endcase // small
// ... 其他立即数范围的统计
endcase
end
// 权重统计
verilog_fine_stats.weighted_coverage_points += current_weight;
verilog_fine_stats.total_weight += current_weight;
// 表达式评估
verilog_fine_stats.expression_evaluations++;
if (evaluate_expression_bin(opcode, register_id)) begin
verilog_fine_stats.expression_matches++;
case (opcode)
3'b000, 3'b001, 3'b010: verilog_fine_stats.expression_stats[0]++; // arithmetic_even
3'b011, 3'b100: verilog_fine_stats.expression_stats[1]++; // logical_odd
3'b101, 3'b110: verilog_fine_stats.expression_stats[2]++; // memory_power_of_two
3'b111: verilog_fine_stats.expression_stats[3]++; // control_fibonacci
endcase
end
// 条件表达式评估
if (evaluate_conditional_expression(opcode, immediate_value, operand_type, exception_enable, privilege_level)) begin
verilog_fine_stats.conditional_hits++;
case (opcode)
3'b000, 3'b001, 3'b010: verilog_fine_stats.conditional_expression_stats[0]++; // arith_small_imm
3'b011, 3'b100: verilog_fine_stats.conditional_expression_stats[1]++; // logic_zero_imm
3'b101, 3'b110: verilog_fine_stats.conditional_expression_stats[2]++; // mem_large_imm
endcase
end else begin
verilog_fine_stats.conditional_misses++;
end
verilog_fine_stats.valid_combinations++;
end
end
// 测试序列
initial begin
reset = 1;
opcode = 3'b000;
operand_type = 2'b00;
register_id = 4'h0;
addressing_mode = 2'b00;
condition_code = 3'b000;
exception_enable = 1'b0;
privilege_level = 2'b00;
immediate_value = 8'h00;
#20 reset = 0;
$display("开始精细交叉覆盖率测试...");
// 系统性测试指定的bins组合
$display("\n测试操作码-操作数精细组合:");
// 算术-寄存器组合
@(posedge clk); opcode = 3'b000; operand_type = 2'b00;
$display(" 算术-寄存器组合");
// 算术-立即数组合
@(posedge clk); opcode = 3'b001; operand_type = 2'b01; immediate_value = 8'h05;
$display(" 算术-立即数组合");
// 逻辑-寄存器组合
@(posedge clk); opcode = 3'b011; operand_type = 2'b00;
$display(" 逻辑-寄存器组合");
// 内存-内存寄存器组合
@(posedge clk); opcode = 3'b101; operand_type = 2'b10;
$display(" 内存-内存寄存器组合");
// 控制指令组合
@(posedge clk); opcode = 3'b111; operand_type = 2'b00;
$display(" 控制指令组合");
// 测试权重相关的组合
$display("\n测试权重相关组合:");
// 高权重:内核模式算术操作
@(posedge clk);
opcode = 3'b000; operand_type = 2'b00; privilege_level = 2'b10; exception_enable = 1'b1;
$display(" 高权重:内核模式算术操作 (权重=10)");
// 高权重:内核模式内存操作
@(posedge clk);
opcode = 3'b101; operand_type = 2'b10; privilege_level = 2'b10; exception_enable = 1'b1;
$display(" 高权重:内核模式内存操作 (权重=10)");
// 中等权重:管理员模式
@(posedge clk);
privilege_level = 2'b01; exception_enable = 1'b1;
$display(" 中等权重:管理员模式 (权重=5)");
// 特殊权重:虚拟化模式
@(posedge clk);
privilege_level = 2'b11;
$display(" 特殊权重:虚拟化模式 (权重=8)");
// 测试表达式bins
$display("\n测试表达式bins:");
// 算术操作 + 偶数寄存器
for (int i = 0; i < 16; i += 2) begin
@(posedge clk);
opcode = 3'b000; register_id = i;
$display(" 算术操作 + 偶数寄存器: reg=%d", i);
end
// 逻辑操作 + 奇数寄存器
for (int i = 1; i < 16; i += 2) begin
@(posedge clk);
opcode = 3'b011; register_id = i;
$display(" 逻辑操作 + 奇数寄存器: reg=%d", i);
end
// 内存操作 + 2的幂寄存器
@(posedge clk); opcode = 3'b101; register_id = 4'h1;
@(posedge clk); opcode = 3'b101; register_id = 4'h2;
@(posedge clk); opcode = 3'b101; register_id = 4'h4;
@(posedge clk); opcode = 3'b101; register_id = 4'h8;
$display(" 内存操作 + 2的幂寄存器");
// 控制操作 + 斐波那契寄存器
@(posedge clk); opcode = 3'b111; register_id = 4'h1;
@(posedge clk); opcode = 3'b111; register_id = 4'h2;
@(posedge clk); opcode = 3'b111; register_id = 4'h3;
@(posedge clk); opcode = 3'b111; register_id = 4'h5;
@(posedge clk); opcode = 3'b111; register_id = 4'h8;
$display(" 控制操作 + 斐波那契寄存器");
// 测试条件表达式bins
$display("\n测试条件表达式bins:");
// 算术 + 小立即数 (条件:operand_type == register_imm)
@(posedge clk);
opcode = 3'b000; operand_type = 2'b01; immediate_value = 8'h05;
$display(" 算术 + 小立即数 (满足条件)");
// 逻辑 + 零立即数 (条件:operand_type == register_imm && exception_enable == 0)
@(posedge clk);
opcode = 3'b011; operand_type = 2'b01; immediate_value = 8'h00; exception_enable = 1'b0;
$display(" 逻辑 + 零立即数 (满足条件)");
// 内存 + 大立即数 (条件:operand_type == memory_imm && privilege_level >= supervisor)
@(posedge clk);
opcode = 3'b101; operand_type = 2'b11; immediate_value = 8'h90; privilege_level = 2'b01;
$display(" 内存 + 大立即数 (满足条件)");
// 随机压力测试
$display("\n随机压力测试:");
repeat (100) begin
@(posedge clk);
opcode = $random % 8;
operand_type = $random % 4;
register_id = $random % 16;
addressing_mode = $random % 4;
condition_code = $random % 8;
exception_enable = $random % 2;
privilege_level = $random % 4;
immediate_value = $random;
end
// SystemVerilog精细覆盖率报告
$display("\n=== SystemVerilog精细覆盖率报告 ===");
$display("总体覆盖率: %0.2f%%", fine_cg.get_coverage());
$display("操作码-操作数交叉: %0.2f%%", fine_cg.opcode_operand_cross.get_coverage());
$display("操作码-寄存器-地址交叉: %0.2f%%", fine_cg.opcode_reg_addr_cross.get_coverage());
$display("条件-特权级别交叉: %0.2f%%", fine_cg.condition_privilege_cross.get_coverage());
$display("操作数-立即数交叉: %0.2f%%", fine_cg.operand_immediate_cross.get_coverage());
$display("复杂四维交叉: %0.2f%%", fine_cg.complex_four_way_cross.get_coverage());
$display("表达式交叉: %0.2f%%", fine_cg.expression_cross.get_coverage());
$display("条件表达式交叉: %0.2f%%", fine_cg.conditional_expression_cross.get_coverage());
// 传统Verilog精细统计报告
$display("\n=== 传统Verilog精细统计 ===");
$display("总采样数: %d", verilog_fine_stats.total_samples);
$display("有效组合数: %d", verilog_fine_stats.valid_combinations);
$display("总权重: %d", verilog_fine_stats.total_weight);
$display("加权覆盖点: %d", verilog_fine_stats.weighted_coverage_points);
$display("\n表达式评估统计:");
$display(" 表达式评估次数: %d", verilog_fine_stats.expression_evaluations);
$display(" 表达式匹配次数: %d", verilog_fine_stats.expression_matches);
$display(" 表达式匹配率: %0.2f%%",
real'(verilog_fine_stats.expression_matches) / real'(verilog_fine_stats.expression_evaluations) * 100.0);
$display("\n条件表达式统计:");
$display(" 条件命中: %d", verilog_fine_stats.conditional_hits);
$display(" 条件未命中: %d", verilog_fine_stats.conditional_misses);
$display(" 条件命中率: %0.2f%%",
real'(verilog_fine_stats.conditional_hits) /
real'(verilog_fine_stats.conditional_hits + verilog_fine_stats.conditional_misses) * 100.0);
$display("\n操作码-操作数bins统计:");
for (int i = 0; i < 7; i++) begin
$display(" Bin[%d]: %d hits", i, verilog_fine_stats.opcode_operand_stats[i]);
end
$display("\n表达式bins统计:");
$display(" 算术偶数寄存器: %d", verilog_fine_stats.expression_stats[0]);
$display(" 逻辑奇数寄存器: %d", verilog_fine_stats.expression_stats[1]);
$display(" 内存2的幂寄存器: %d", verilog_fine_stats.expression_stats[2]);
$display(" 控制斐波那契寄存器: %d", verilog_fine_stats.expression_stats[3]);
$finish;
end
endmodule
5. 覆盖率选项
覆盖率选项允许我们精确控制覆盖率的收集行为,包括目标、权重、采样条件等。SystemVerilog提供了丰富的选项来定制覆盖率收集。
5.1 基本覆盖率选项
module coverage_options_example;
logic clk, reset;
logic [3:0] data;
logic [1:0] mode;
logic enable;
logic [7:0] address;
logic [15:0] value;
// 基本覆盖率选项示例
covergroup basic_options_cg @(posedge clk iff !reset);
// 全局选项设置
option.per_instance = 1; // 每个实例独立统计
option.goal = 95; // 覆盖率目标95%
option.name = "basic_coverage"; // 覆盖组名称
option.comment = "基本功能覆盖率收集"; // 注释
option.at_least = 2; // 每个bin至少命中2次
option.detect_overlap = 1; // 检测重叠的bins
option.auto_bin_max = 64; // 自动bin的最大数量
option.cross_num_print_missing = 10; // 打印缺失交叉的数量
// 数据coverpoint with options
data_cp: coverpoint data {
option.weight = 5; // 权重设置
option.goal = 100; // 个别目标
option.comment = "数据值覆盖";
option.at_least = 3; // 至少命中3次
bins low = {[4'h0:4'h3]};
bins mid = {[4'h4:4'h7]};
bins high = {[4'h8:4'hB]};
bins max = {[4'hC:4'hF]};
}
// 模式coverpoint with different options
mode_cp: coverpoint mode {
option.weight = 3;
option.goal = 90;
option.comment = "操作模式覆盖";
option.at_least = 1;
bins read_mode = {2'b00};
bins write_mode = {2'b01};
bins test_mode = {2'b10};
bins debug_mode = {2'b11};
}
// 使能coverpoint
enable_cp: coverpoint enable {
option.weight = 1;
option.comment = "使能信号覆盖";
bins disabled = {1'b0};
bins enabled = {1'b1};
}
// 地址coverpoint with auto bins
address_cp: coverpoint address {
option.auto_bin_max = 16; // 限制自动bin数量
option.weight = 4;
option.comment = "地址空间覆盖";
}
// 交叉覆盖with options
data_mode_cross: cross data_cp, mode_cp {
option.weight = 10; // 交叉覆盖权重
option.goal = 85; // 交叉覆盖目标
option.comment = "数据-模式交叉覆盖";
option.at_least = 2; // 交叉至少命中2次
// 忽略某些组合
ignore_bins invalid_combinations =
binsof(data_cp.max) && binsof(mode_cp.debug_mode);
}
endgroup
// 实例化covergroup
basic_options_cg basic_cg = new();
// 传统Verilog的等价实现
typedef struct {
// 基本统计
integer total_samples;
integer valid_samples;
// 权重相关
integer weighted_hits[4]; // 对应4个coverpoint的权重命中
integer total_weight;
real weighted_coverage;
// 目标相关
integer coverage_goals[4]; // 各coverpoint的目标
real actual_coverage[4]; // 实际覆盖率
bit goals_met[4]; // 目标是否达成
// at_least相关
integer min_hits_required[4]; // 最小命中次数要求
integer actual_hits[4][16]; // 实际命中次数(假设最多16个bins)
bit at_least_met[4][16]; // 最小命中是否满足
// 交叉覆盖统计
integer cross_total_combinations;
integer cross_hit_combinations;
integer cross_ignored_combinations;
real cross_coverage;
// 重叠检测
integer overlap_detected;
integer overlap_warnings;
// 自动bin统计
integer auto_bins_created;
integer auto_bin_max_limit;
// 实例统计
string instance_name;
integer per_instance_samples;
} verilog_options_stats_t;
verilog_options_stats_t verilog_options_stats;
// 权重计算函数
function real calculate_weighted_coverage();
integer total_weighted_hits = 0;
integer total_possible_weight = 0;
// 数据coverpoint: 权重5
total_weighted_hits += verilog_options_stats.weighted_hits[0] * 5;
total_possible_weight += 4 * 5; // 4个bins * 权重5
// 模式coverpoint: 权重3
total_weighted_hits += verilog_options_stats.weighted_hits[1] * 3;
total_possible_weight += 4 * 3; // 4个bins * 权重3
// 使能coverpoint: 权重1
total_weighted_hits += verilog_options_stats.weighted_hits[2] * 1;
total_possible_weight += 2 * 1; // 2个bins * 权重1
// 地址coverpoint: 权重4
total_weighted_hits += verilog_options_stats.weighted_hits[3] * 4;
total_possible_weight += 16 * 4; // 16个auto bins * 权重4
return real'(total_weighted_hits) / real'(total_possible_weight) * 100.0;
endfunction
// 目标检查函数
function void check_coverage_goals();
// 数据coverpoint目标检查 (100%)
verilog_options_stats.actual_coverage[0] = real'(verilog_options_stats.weighted_hits[0]) / 4.0 * 100.0;
verilog_options_stats.goals_met[0] = (verilog_options_stats.actual_coverage[0] >= 100.0);
// 模式coverpoint目标检查 (90%)
verilog_options_stats.actual_coverage[1] = real'(verilog_options_stats.weighted_hits[1]) / 4.0 * 100.0;
verilog_options_stats.goals_met[1] = (verilog_options_stats.actual_coverage[1] >= 90.0);
// 使能coverpoint目标检查 (95% - 全局默认)
verilog_options_stats.actual_coverage[2] = real'(verilog_options_stats.weighted_hits[2]) / 2.0 * 100.0;
verilog_options_stats.goals_met[2] = (verilog_options_stats.actual_coverage[2] >= 95.0);
// 地址coverpoint目标检查 (95% - 全局默认)
verilog_options_stats.actual_coverage[3] = real'(verilog_options_stats.weighted_hits[3]) / 16.0 * 100.0;
verilog_options_stats.goals_met[3] = (verilog_options_stats.actual_coverage[3] >= 95.0);
endfunction
// at_least检查函数
function void check_at_least_requirements();
// 数据coverpoint: at_least = 3
for (int i = 0; i < 4; i++) begin
verilog_options_stats.at_least_met[0][i] = (verilog_options_stats.actual_hits[0][i] >= 3);
end
// 模式coverpoint: at_least = 1
for (int i = 0; i < 4; i++) begin
verilog_options_stats.at_least_met[1][i] = (verilog_options_stats.actual_hits[1][i] >= 1);
end
// 使能coverpoint: at_least = 2 (全局默认)
for (int i = 0; i < 2; i++) begin
verilog_options_stats.at_least_met[2][i] = (verilog_options_stats.actual_hits[2][i] >= 2);
end
// 地址coverpoint: at_least = 2 (全局默认)
for (int i = 0; i < 16; i++) begin
verilog_options_stats.at_least_met[3][i] = (verilog_options_stats.actual_hits[3][i] >= 2);
end
endfunction
// 重叠检测函数
function bit detect_bin_overlap(logic [3:0] data_val);
// 简化的重叠检测逻辑
// 在实际实现中,这会更复杂
case (data_val)
4'h3: begin // 边界值,可能重叠
verilog_options_stats.overlap_detected++;
return 1;
end
4'h7: begin // 另一个边界值
verilog_options_stats.overlap_detected++;
return 1;
end
4'hB: begin // 第三个边界值
verilog_options_stats.overlap_detected++;
return 1;
end
default: return 0;
endcase
endfunction
// 自动bin管理函数
function void manage_auto_bins(logic [7:0] addr);
// 简化的自动bin管理
if (verilog_options_stats.auto_bins_created < verilog_options_stats.auto_bin_max_limit) begin
// 创建新的自动bin(简化逻辑)
verilog_options_stats.auto_bins_created++;
end
endfunction
// 交叉覆盖检查函数
function bit is_valid_cross_combination(logic [3:0] data_val, logic [1:0] mode_val);
// 检查是否为忽略的组合
if (data_val >= 4'hC && mode_val == 2'b11) begin // max data && debug_mode
verilog_options_stats.cross_ignored_combinations++;
return 0;
end
return 1;
endfunction
// 主要的覆盖率统计逻辑
always @(posedge clk) begin
if (reset) begin
verilog_options_stats = '{default: 0};
verilog_options_stats.instance_name = "basic_coverage_instance";
verilog_options_stats.auto_bin_max_limit = 16;
// 设置目标
verilog_options_stats.coverage_goals[0] = 100; // data
verilog_options_stats.coverage_goals[1] = 90; // mode
verilog_options_stats.coverage_goals[2] = 95; // enable
verilog_options_stats.coverage_goals[3] = 95; // address
// 设置最小命中要求
verilog_options_stats.min_hits_required[0] = 3; // data
verilog_options_stats.min_hits_required[1] = 1; // mode
verilog_options_stats.min_hits_required[2] = 2; // enable
verilog_options_stats.min_hits_required[3] = 2; // address
end else begin
verilog_options_stats.total_samples++;
verilog_options_stats.per_instance_samples++;
// 数据coverpoint统计
case (data)
4'h0, 4'h1, 4'h2, 4'h3: begin
verilog_options_stats.actual_hits[0][0]++; // low bin
if (verilog_options_stats.actual_hits[0][0] >= verilog_options_stats.min_hits_required[0])
verilog_options_stats.weighted_hits[0] = 1;
end
4'h4, 4'h5, 4'h6, 4'h7: begin
verilog_options_stats.actual_hits[0][1]++; // mid bin
if (verilog_options_stats.actual_hits[0][1] >= verilog_options_stats.min_hits_required[0])
verilog_options_stats.weighted_hits[0] = 2;
end
4'h8, 4'h9, 4'hA, 4'hB: begin
verilog_options_stats.actual_hits[0][2]++; // high bin
if (verilog_options_stats.actual_hits[0][2] >= verilog_options_stats.min_hits_required[0])
verilog_options_stats.weighted_hits[0] = 3;
end
4'hC, 4'hD, 4'hE, 4'hF: begin
verilog_options_stats.actual_hits[0][3]++; // max bin
if (verilog_options_stats.actual_hits[0][3] >= verilog_options_stats.min_hits_required[0])
verilog_options_stats.weighted_hits[0] = 4;
end
endcase
// 模式coverpoint统计
verilog_options_stats.actual_hits[1][mode]++;
if (verilog_options_stats.actual_hits[1][mode] >= verilog_options_stats.min_hits_required[1]) begin
case (mode)
2'b00: verilog_options_stats.weighted_hits[1] |= 1; // read_mode
2'b01: verilog_options_stats.weighted_hits[1] |= 2; // write_mode
2'b10: verilog_options_stats.weighted_hits[1] |= 4; // test_mode
2'b11: verilog_options_stats.weighted_hits[1] |= 8; // debug_mode
endcase
end
// 使能coverpoint统计
verilog_options_stats.actual_hits[2][enable]++;
if (verilog_options_stats.actual_hits[2][enable] >= verilog_options_stats.min_hits_required[2]) begin
verilog_options_stats.weighted_hits[2] |= (enable ? 2 : 1);
end
// 地址coverpoint统计(简化的自动bin)
automatic int addr_bin = address % 16; // 简化映射到16个bins
verilog_options_stats.actual_hits[3][addr_bin]++;
if (verilog_options_stats.actual_hits[3][addr_bin] >= verilog_options_stats.min_hits_required[3]) begin
verilog_options_stats.weighted_hits[3] |= (1 << addr_bin);
end
// 管理自动bins
manage_auto_bins(address);
// 重叠检测
if (detect_bin_overlap(data)) begin
verilog_options_stats.overlap_warnings++;
end
// 交叉覆盖统计
verilog_options_stats.cross_total_combinations++;
if (is_valid_cross_combination(data, mode)) begin
// 简化的交叉统计
verilog_options_stats.cross_hit_combinations++;
end
// 计算覆盖率
verilog_options_stats.weighted_coverage = calculate_weighted_coverage();
verilog_options_stats.cross_coverage =
real'(verilog_options_stats.cross_hit_combinations) /
real'(verilog_options_stats.cross_total_combinations - verilog_options_stats.cross_ignored_combinations) * 100.0;
// 检查目标和at_least要求
check_coverage_goals();
check_at_least_requirements();
verilog_options_stats.valid_samples++;
end
end
// 测试序列
initial begin
reset = 1;
data = 4'h0;
mode = 2'b00;
enable = 1'b0;
address = 8'h00;
value = 16'h0000;
#20 reset = 0;
$display("开始覆盖率选项测试...");
// 系统性测试各种选项
$display("\n测试基本覆盖率选项:");
// 测试权重不同的coverpoint
$display(" 测试权重设置:");
for (int i = 0; i < 16; i++) begin
@(posedge clk);
data = i;
mode = i % 4;
enable = i % 2;
address = i * 16;
$display(" 数据=%d, 模式=%d, 使能=%d, 地址=%d", data, mode, enable, address);
end
// 测试at_least要求
$display("\n 测试at_least要求:");
// 数据coverpoint需要至少3次命中
repeat (5) begin
@(posedge clk); data = 4'h0; // low bin
@(posedge clk); data = 4'h5; // mid bin
@(posedge clk); data = 4'hA; // high bin
@(posedge clk); data = 4'hF; // max bin
end
$display(" 完成数据bins的多次命中测试");
// 模式coverpoint只需要至少1次命中
@(posedge clk); mode = 2'b00; // read
@(posedge clk); mode = 2'b01; // write
@(posedge clk); mode = 2'b10; // test
@(posedge clk); mode = 2'b11; // debug
$display(" 完成模式bins的单次命中测试");
// 测试目标设置
$display("\n 测试覆盖率目标:");
// 确保数据coverpoint达到100%目标
for (int i = 0; i < 4; i++) begin
repeat (3) begin // 满足at_least=3的要求
@(posedge clk);
case (i)
0: data = 4'h1; // low
1: data = 4'h5; // mid
2: data = 4'h9; // high
3: data = 4'hD; // max
endcase
end
end
$display(" 数据coverpoint应达到100%目标");
// 测试交叉覆盖选项
$display("\n 测试交叉覆盖选项:");
for (int d = 0; d < 4; d++) begin
for (int m = 0; m < 4; m++) begin
// 跳过忽略的组合 (max data && debug mode)
if (d == 3 && m == 3) begin
$display(" 跳过忽略的组合: 最大数据 + 调试模式");
continue;
end
repeat (2) begin // 满足交叉的at_least=2要求
@(posedge clk);
case (d)
0: data = 4'h1; // low
1: data = 4'h5; // mid
2: data = 4'h9; // high
3: data = 4'hD; // max
endcase
mode = m;
end
end
end
$display(" 完成交叉覆盖测试");
// 测试重叠检测
$display("\n 测试重叠检测:");
@(posedge clk); data = 4'h3; // 边界值,可能触发重叠检测
@(posedge clk); data = 4'h7; // 另一个边界值
@(posedge clk); data = 4'hB; // 第三个边界值
$display(" 测试边界值的重叠检测");
// 测试自动bin限制
$display("\n 测试自动bin限制:");
for (int i = 0; i < 20; i++) begin // 超过auto_bin_max=16的限制
@(posedge clk);
address = i * 13; // 使用不同的地址模式
end
$display(" 测试自动bin数量限制");
// 随机压力测试
$display("\n随机压力测试:");
repeat (200) begin
@(posedge clk);
data = $random % 16;
mode = $random % 4;
enable = $random % 2;
address = $random;
value = $random;
end
// SystemVerilog覆盖率选项报告
$display("\n=== SystemVerilog覆盖率选项报告 ===");
$display("总体覆盖率: %0.2f%%", basic_cg.get_coverage());
$display("数据coverpoint覆盖率: %0.2f%% (目标: %0.2f%%)",
basic_cg.data_cp.get_coverage(), 100.0);
$display("模式coverpoint覆盖率: %0.2f%% (目标: %0.2f%%)",
basic_cg.mode_cp.get_coverage(), 90.0);
$display("使能coverpoint覆盖率: %0.2f%% (目标: %0.2f%%)",
basic_cg.enable_cp.get_coverage(), 95.0);
$display("地址coverpoint覆盖率: %0.2f%% (目标: %0.2f%%)",
basic_cg.address_cp.get_coverage(), 95.0);
$display("交叉覆盖率: %0.2f%% (目标: %0.2f%%)",
basic_cg.data_mode_cross.get_coverage(), 85.0);
// 传统Verilog选项统计报告
$display("\n=== 传统Verilog选项统计 ===");
$display("实例名称: %s", verilog_options_stats.instance_name);
$display("总采样数: %d", verilog_options_stats.total_samples);
$display("有效采样数: %d", verilog_options_stats.valid_samples);
$display("实例采样数: %d", verilog_options_stats.per_instance_samples);
$display("\n权重覆盖率统计:");
$display(" 加权覆盖率: %0.2f%%", verilog_options_stats.weighted_coverage);
$display(" 数据权重命中: %d (权重: 5)", verilog_options_stats.weighted_hits[0]);
$display(" 模式权重命中: %d (权重: 3)", verilog_options_stats.weighted_hits[1]);
$display(" 使能权重命中: %d (权重: 1)", verilog_options_stats.weighted_hits[2]);
$display(" 地址权重命中: %d (权重: 4)", verilog_options_stats.weighted_hits[3]);
$display("\n目标达成情况:");
$display(" 数据目标 (%d%%): %s (实际: %0.2f%%)",
verilog_options_stats.coverage_goals[0],
verilog_options_stats.goals_met[0] ? "达成" : "未达成",
verilog_options_stats.actual_coverage[0]);
$display(" 模式目标 (%d%%): %s (实际: %0.2f%%)",
verilog_options_stats.coverage_goals[1],
verilog_options_stats.goals_met[1] ? "达成" : "未达成",
verilog_options_stats.actual_coverage[1]);
$display(" 使能目标 (%d%%): %s (实际: %0.2f%%)",
verilog_options_stats.coverage_goals[2],
verilog_options_stats.goals_met[2] ? "达成" : "未达成",
verilog_options_stats.actual_coverage[2]);
$display(" 地址目标 (%d%%): %s (实际: %0.2f%%)",
verilog_options_stats.coverage_goals[3],
verilog_options_stats.goals_met[3] ? "达成" : "未达成",
verilog_options_stats.actual_coverage[3]);
$display("\n最小命中要求检查:");
for (int cp = 0; cp < 4; cp++) begin
$display(" Coverpoint %d (要求: %d次):", cp, verilog_options_stats.min_hits_required[cp]);
for (int bin = 0; bin < (cp == 3 ? 16 : (cp == 2 ? 2 : 4)); bin++) begin
$display(" Bin[%d]: %d次 %s", bin,
verilog_options_stats.actual_hits[cp][bin],
verilog_options_stats.at_least_met[cp][bin] ? "✓" : "✗");
end
end
$display("\n交叉覆盖统计:");
$display(" 总交叉组合: %d", verilog_options_stats.cross_total_combinations);
$display(" 命中交叉组合: %d", verilog_options_stats.cross_hit_combinations);
$display(" 忽略交叉组合: %d", verilog_options_stats.cross_ignored_combinations);
$display(" 交叉覆盖率: %0.2f%%", verilog_options_stats.cross_coverage);
$display("\n高级选项统计:");
$display(" 重叠检测次数: %d", verilog_options_stats.overlap_detected);
$display(" 重叠警告数: %d", verilog_options_stats.overlap_warnings);
$display(" 自动bin创建数: %d", verilog_options_stats.auto_bins_created);
$display(" 自动bin最大限制: %d", verilog_options_stats.auto_bin_max_limit);
$finish;
end
endmodule
6. 系统方法
SystemVerilog提供了丰富的系统方法来查询和控制覆盖率收集,这些方法使得覆盖率分析更加灵活和强大。
6.1 覆盖率查询方法
module coverage_system_methods_example;
logic clk, reset;
logic [3:0] data;
logic [1:0] mode;
logic enable;
logic [7:0] address;
// 覆盖组定义
covergroup system_methods_cg @(posedge clk iff !reset);
option.per_instance = 1;
option.name = "system_methods_coverage";
option.comment = "系统方法演示覆盖组";
data_cp: coverpoint data {
bins low = {[4'h0:4'h3]};
bins mid = {[4'h4:4'h7]};
bins high = {[4'h8:4'hB]};
bins max = {[4'hC:4'hF]};
}
mode_cp: coverpoint mode {
bins read_mode = {2'b00};
bins write_mode = {2'b01};
bins test_mode = {2'b10};
bins debug_mode = {2'b11};
}
enable_cp: coverpoint enable {
bins disabled = {1'b0};
bins enabled = {1'b1};
}
data_mode_cross: cross data_cp, mode_cp {
ignore_bins invalid = binsof(data_cp.max) && binsof(mode_cp.debug_mode);
}
endgroup
system_methods_cg sys_cg = new();
// 传统Verilog的等价实现
typedef struct {
// 基本统计信息
integer total_bins;
integer covered_bins;
integer total_samples;
real coverage_percentage;
// 各coverpoint的统计
integer data_total_bins;
integer data_covered_bins;
real data_coverage;
integer mode_total_bins;
integer mode_covered_bins;
real mode_coverage;
integer enable_total_bins;
integer enable_covered_bins;
real enable_coverage;
// 交叉覆盖统计
integer cross_total_bins;
integer cross_covered_bins;
real cross_coverage;
// 实例信息
string instance_name;
string type_name;
// 选项信息
integer per_instance_option;
string comment;
// bin命中统计
integer data_bin_hits[4]; // 4个data bins
integer mode_bin_hits[4]; // 4个mode bins
integer enable_bin_hits[2]; // 2个enable bins
integer cross_bin_hits[15]; // 16-1个交叉bins(排除1个忽略的)
// 状态信息
bit coverage_started;
bit coverage_stopped;
integer start_time;
integer stop_time;
} verilog_system_methods_t;
verilog_system_methods_t verilog_sys_methods;
// 获取总体覆盖率
function real get_total_coverage();
integer total_covered = verilog_sys_methods.data_covered_bins +
verilog_sys_methods.mode_covered_bins +
verilog_sys_methods.enable_covered_bins +
verilog_sys_methods.cross_covered_bins;
integer total_possible = verilog_sys_methods.data_total_bins +
verilog_sys_methods.mode_total_bins +
verilog_sys_methods.enable_total_bins +
verilog_sys_methods.cross_total_bins;
if (total_possible > 0)
return real'(total_covered) / real'(total_possible) * 100.0;
else
return 0.0;
endfunction
// 获取coverpoint覆盖率
function real get_coverpoint_coverage(string cp_name);
case (cp_name)
"data_cp": begin
if (verilog_sys_methods.data_total_bins > 0)
return real'(verilog_sys_methods.data_covered_bins) / real'(verilog_sys_methods.data_total_bins) * 100.0;
else
return 0.0;
end
"mode_cp": begin
if (verilog_sys_methods.mode_total_bins > 0)
return real'(verilog_sys_methods.mode_covered_bins) / real'(verilog_sys_methods.mode_total_bins) * 100.0;
else
return 0.0;
end
"enable_cp": begin
if (verilog_sys_methods.enable_total_bins > 0)
return real'(verilog_sys_methods.enable_covered_bins) / real'(verilog_sys_methods.enable_total_bins) * 100.0;
else
return 0.0;
end
default: return 0.0;
endcase
endfunction
// 获取交叉覆盖率
function real get_cross_coverage();
if (verilog_sys_methods.cross_total_bins > 0)
return real'(verilog_sys_methods.cross_covered_bins) / real'(verilog_sys_methods.cross_total_bins) * 100.0;
else
return 0.0;
endfunction
// 获取实例名称
function string get_instance_name();
return verilog_sys_methods.instance_name;
endfunction
// 获取类型名称
function string get_type_name();
return verilog_sys_methods.type_name;
endfunction
// 设置实例名称
function void set_instance_name(string name);
verilog_sys_methods.instance_name = name;
endfunction
// 开始覆盖率收集
function void start_coverage();
verilog_sys_methods.coverage_started = 1;
verilog_sys_methods.coverage_stopped = 0;
verilog_sys_methods.start_time = $time;
endfunction
// 停止覆盖率收集
function void stop_coverage();
verilog_sys_methods.coverage_stopped = 1;
verilog_sys_methods.stop_time = $time;
endfunction
// 获取选项值
function integer get_option_per_instance();
return verilog_sys_methods.per_instance_option;
endfunction
function string get_option_comment();
return verilog_sys_methods.comment;
endfunction
// 更新覆盖率统计
function void update_coverage_stats();
// 更新data coverpoint统计
verilog_sys_methods.data_covered_bins = 0;
for (int i = 0; i < 4; i++) begin
if (verilog_sys_methods.data_bin_hits[i] > 0)
verilog_sys_methods.data_covered_bins++;
end
verilog_sys_methods.data_coverage = get_coverpoint_coverage("data_cp");
// 更新mode coverpoint统计
verilog_sys_methods.mode_covered_bins = 0;
for (int i = 0; i < 4; i++) begin
if (verilog_sys_methods.mode_bin_hits[i] > 0)
verilog_sys_methods.mode_covered_bins++;
end
verilog_sys_methods.mode_coverage = get_coverpoint_coverage("mode_cp");
// 更新enable coverpoint统计
verilog_sys_methods.enable_covered_bins = 0;
for (int i = 0; i < 2; i++) begin
if (verilog_sys_methods.enable_bin_hits[i] > 0)
verilog_sys_methods.enable_covered_bins++;
end
verilog_sys_methods.enable_coverage = get_coverpoint_coverage("enable_cp");
// 更新交叉覆盖统计
verilog_sys_methods.cross_covered_bins = 0;
for (int i = 0; i < 15; i++) begin // 排除1个忽略的组合
if (verilog_sys_methods.cross_bin_hits[i] > 0)
verilog_sys_methods.cross_covered_bins++;
end
verilog_sys_methods.cross_coverage = get_cross_coverage();
// 更新总体统计
verilog_sys_methods.covered_bins = verilog_sys_methods.data_covered_bins +
verilog_sys_methods.mode_covered_bins +
verilog_sys_methods.enable_covered_bins +
verilog_sys_methods.cross_covered_bins;
verilog_sys_methods.coverage_percentage = get_total_coverage();
endfunction
// 主要的覆盖率收集逻辑
always @(posedge clk) begin
if (reset) begin
verilog_sys_methods = '{default: 0};
verilog_sys_methods.instance_name = "sys_methods_instance";
verilog_sys_methods.type_name = "system_methods_cg";
verilog_sys_methods.comment = "系统方法演示覆盖组";
verilog_sys_methods.per_instance_option = 1;
// 设置总bin数
verilog_sys_methods.data_total_bins = 4;
verilog_sys_methods.mode_total_bins = 4;
verilog_sys_methods.enable_total_bins = 2;
verilog_sys_methods.cross_total_bins = 15; // 16-1个忽略的
verilog_sys_methods.total_bins = 25; // 4+4+2+15
start_coverage();
end else if (!verilog_sys_methods.coverage_stopped) begin
verilog_sys_methods.total_samples++;
// 更新data coverpoint命中
case (data)
4'h0, 4'h1, 4'h2, 4'h3: verilog_sys_methods.data_bin_hits[0]++; // low
4'h4, 4'h5, 4'h6, 4'h7: verilog_sys_methods.data_bin_hits[1]++; // mid
4'h8, 4'h9, 4'hA, 4'hB: verilog_sys_methods.data_bin_hits[2]++; // high
4'hC, 4'hD, 4'hE, 4'hF: verilog_sys_methods.data_bin_hits[3]++; // max
endcase
// 更新mode coverpoint命中
verilog_sys_methods.mode_bin_hits[mode]++;
// 更新enable coverpoint命中
verilog_sys_methods.enable_bin_hits[enable]++;
// 更新交叉覆盖命中(简化逻辑)
if (!(data >= 4'hC && mode == 2'b11)) begin // 不是忽略的组合
automatic int data_bin_idx = (data < 4'h4) ? 0 : (data < 4'h8) ? 1 : (data < 4'hC) ? 2 : 3;
automatic int cross_idx = data_bin_idx * 4 + mode;
if (cross_idx < 15) // 确保不超出数组边界
verilog_sys_methods.cross_bin_hits[cross_idx]++;
end
// 更新统计信息
update_coverage_stats();
end
end
// 测试序列
initial begin
reset = 1;
data = 4'h0;
mode = 2'b00;
enable = 1'b0;
address = 8'h00;
#20 reset = 0;
$display("开始系统方法测试...");
// 基本系统方法测试
$display("\n=== 基本系统方法测试 ===");
// 初始状态查询
$display("初始覆盖率状态:");
$display(" 总体覆盖率: %0.2f%%", sys_cg.get_coverage());
$display(" 数据coverpoint覆盖率: %0.2f%%", sys_cg.data_cp.get_coverage());
$display(" 模式coverpoint覆盖率: %0.2f%%", sys_cg.mode_cp.get_coverage());
$display(" 使能coverpoint覆盖率: %0.2f%%", sys_cg.enable_cp.get_coverage());
$display(" 交叉覆盖率: %0.2f%%", sys_cg.data_mode_cross.get_coverage());
// 实例信息查询
$display("\n实例信息:");
$display(" 实例名称: %s", sys_cg.get_inst_name());
$display(" 类型名称: %s", sys_cg.get_type_name());
// 选项查询
$display("\n选项信息:");
$display(" per_instance选项: %d", sys_cg.get_option().per_instance);
$display(" name选项: %s", sys_cg.get_option().name);
$display(" comment选项: %s", sys_cg.get_option().comment);
// 系统性测试各种数据
$display("\n开始系统性测试...");
// 测试数据coverpoint的所有bins
$display(" 测试数据coverpoint:");
@(posedge clk); data = 4'h1; // low bin
$display(" 数据=0x%h, 覆盖率=%0.2f%%", data, sys_cg.data_cp.get_coverage());
@(posedge clk); data = 4'h5; // mid bin
$display(" 数据=0x%h, 覆盖率=%0.2f%%", data, sys_cg.data_cp.get_coverage());
@(posedge clk); data = 4'h9; // high bin
$display(" 数据=0x%h, 覆盖率=%0.2f%%", data, sys_cg.data_cp.get_coverage());
@(posedge clk); data = 4'hD; // max bin
$display(" 数据=0x%h, 覆盖率=%0.2f%%", data, sys_cg.data_cp.get_coverage());
// 测试模式coverpoint的所有bins
$display("\n 测试模式coverpoint:");
@(posedge clk); mode = 2'b00; // read
$display(" 模式=%b, 覆盖率=%0.2f%%", mode, sys_cg.mode_cp.get_coverage());
@(posedge clk); mode = 2'b01; // write
$display(" 模式=%b, 覆盖率=%0.2f%%", mode, sys_cg.mode_cp.get_coverage());
@(posedge clk); mode = 2'b10; // test
$display(" 模式=%b, 覆盖率=%0.2f%%", mode, sys_cg.mode_cp.get_coverage());
@(posedge clk); mode = 2'b11; // debug
$display(" 模式=%b, 覆盖率=%0.2f%%", mode, sys_cg.mode_cp.get_coverage());
// 测试使能coverpoint
$display("\n 测试使能coverpoint:");
@(posedge clk); enable = 1'b0; // disabled
$display(" 使能=%b, 覆盖率=%0.2f%%", enable, sys_cg.enable_cp.get_coverage());
@(posedge clk); enable = 1'b1; // enabled
$display(" 使能=%b, 覆盖率=%0.2f%%", enable, sys_cg.enable_cp.get_coverage());
// 测试交叉覆盖
$display("\n 测试交叉覆盖:");
for (int d = 0; d < 4; d++) begin
for (int m = 0; m < 4; m++) begin
// 跳过忽略的组合
if (d == 3 && m == 3) begin
$display(" 跳过忽略的组合: 数据bin=%d, 模式=%d", d, m);
continue;
end
@(posedge clk);
case (d)
0: data = 4'h1; // low
1: data = 4'h5; // mid
2: data = 4'h9; // high
3: data = 4'hD; // max
endcase
mode = m;
$display(" 数据bin=%d, 模式=%d, 交叉覆盖率=%0.2f%%",
d, m, sys_cg.data_mode_cross.get_coverage());
end
end
// 测试覆盖率控制方法
$display("\n=== 覆盖率控制方法测试 ===");
// 停止覆盖率收集
$display("停止覆盖率收集...");
sys_cg.stop();
// 在停止状态下继续采样(应该不影响覆盖率)
real coverage_before_stop = sys_cg.get_coverage();
@(posedge clk); data = 4'h0; mode = 2'b00; enable = 1'b0;
@(posedge clk); data = 4'h1; mode = 2'b01; enable = 1'b1;
real coverage_after_stop = sys_cg.get_coverage();
$display(" 停止前覆盖率: %0.2f%%", coverage_before_stop);
$display(" 停止后覆盖率: %0.2f%%", coverage_after_stop);
$display(" 覆盖率是否变化: %s", (coverage_before_stop == coverage_after_stop) ? "否" : "是");
// 重新开始覆盖率收集
$display("\n重新开始覆盖率收集...");
sys_cg.start();
// 继续采样
@(posedge clk); data = 4'h2; mode = 2'b10; enable = 1'b0;
real coverage_after_restart = sys_cg.get_coverage();
$display(" 重启后覆盖率: %0.2f%%", coverage_after_restart);
// 实例名称设置测试
$display("\n=== 实例名称设置测试 ===");
string original_name = sys_cg.get_inst_name();
$display("原始实例名称: %s", original_name);
sys_cg.set_inst_name("new_instance_name");
string new_name = sys_cg.get_inst_name();
$display("新实例名称: %s", new_name);
// 随机压力测试
$display("\n=== 随机压力测试 ===");
repeat (100) begin
@(posedge clk);
data = $random % 16;
mode = $random % 4;
enable = $random % 2;
end
// 最终覆盖率报告
$display("\n=== SystemVerilog系统方法最终报告 ===");
$display("总体覆盖率: %0.2f%%", sys_cg.get_coverage());
$display("数据coverpoint覆盖率: %0.2f%%", sys_cg.data_cp.get_coverage());
$display("模式coverpoint覆盖率: %0.2f%%", sys_cg.mode_cp.get_coverage());
$display("使能coverpoint覆盖率: %0.2f%%", sys_cg.enable_cp.get_coverage());
$display("交叉覆盖率: %0.2f%%", sys_cg.data_mode_cross.get_coverage());
$display("实例名称: %s", sys_cg.get_inst_name());
$display("类型名称: %s", sys_cg.get_type_name());
// 传统Verilog系统方法等价报告
$display("\n=== 传统Verilog系统方法等价报告 ===");
$display("总体覆盖率: %0.2f%%", get_total_coverage());
$display("数据coverpoint覆盖率: %0.2f%%", get_coverpoint_coverage("data_cp"));
$display("模式coverpoint覆盖率: %0.2f%%", get_coverpoint_coverage("mode_cp"));
$display("使能coverpoint覆盖率: %0.2f%%", get_coverpoint_coverage("enable_cp"));
$display("交叉覆盖率: %0.2f%%", get_cross_coverage());
$display("实例名称: %s", get_instance_name());
$display("类型名称: %s", get_type_name());
$display("总采样数: %d", verilog_sys_methods.total_samples);
$display("总bin数: %d", verilog_sys_methods.total_bins);
$display("已覆盖bin数: %d", verilog_sys_methods.covered_bins);
$display("覆盖率收集状态: %s", verilog_sys_methods.coverage_stopped ? "已停止" : "运行中");
$display("\n详细bin命中统计:");
$display(" 数据bins: [%d, %d, %d, %d]",
verilog_sys_methods.data_bin_hits[0], verilog_sys_methods.data_bin_hits[1],
verilog_sys_methods.data_bin_hits[2], verilog_sys_methods.data_bin_hits[3]);
$display(" 模式bins: [%d, %d, %d, %d]",
verilog_sys_methods.mode_bin_hits[0], verilog_sys_methods.mode_bin_hits[1],
verilog_sys_methods.mode_bin_hits[2], verilog_sys_methods.mode_bin_hits[3]);
$display(" 使能bins: [%d, %d]",
verilog_sys_methods.enable_bin_hits[0], verilog_sys_methods.enable_bin_hits[1]);
$finish;
end
endmodule
7. 计算方法
覆盖率的计算是功能覆盖率分析的核心,SystemVerilog提供了多种计算方法来评估验证的完整性。
7.1 基本覆盖率计算
module coverage_calculation_example;
logic clk, reset;
logic [3:0] data;
logic [1:0] mode;
logic enable;
logic [2:0] priority;
logic [7:0] address;
// 覆盖组定义
covergroup calculation_cg @(posedge clk iff !reset);
option.per_instance = 1;
option.name = "calculation_coverage";
option.comment = "覆盖率计算演示";
// 数据coverpoint - 4个bins
data_cp: coverpoint data {
option.weight = 4; // 权重为4
bins low = {[4'h0:4'h3]};
bins mid = {[4'h4:4'h7]};
bins high = {[4'h8:4'hB]};
bins max = {[4'hC:4'hF]};
}
// 模式coverpoint - 4个bins
mode_cp: coverpoint mode {
option.weight = 2; // 权重为2
bins read_mode = {2'b00};
bins write_mode = {2'b01};
bins test_mode = {2'b10};
bins debug_mode = {2'b11};
}
// 使能coverpoint - 2个bins
enable_cp: coverpoint enable {
option.weight = 1; // 权重为1
bins disabled = {1'b0};
bins enabled = {1'b1};
}
// 优先级coverpoint - 8个bins
priority_cp: coverpoint priority {
option.weight = 3; // 权重为3
bins prio0 = {3'b000};
bins prio1 = {3'b001};
bins prio2 = {3'b010};
bins prio3 = {3'b011};
bins prio4 = {3'b100};
bins prio5 = {3'b101};
bins prio6 = {3'b110};
bins prio7 = {3'b111};
}
// 交叉覆盖 - 数据和模式
data_mode_cross: cross data_cp, mode_cp {
option.weight = 5; // 权重为5
ignore_bins invalid = binsof(data_cp.max) && binsof(mode_cp.debug_mode);
}
// 三路交叉覆盖
data_mode_enable_cross: cross data_cp, mode_cp, enable_cp {
option.weight = 8; // 权重为8
ignore_bins invalid1 = binsof(data_cp.max) && binsof(mode_cp.debug_mode);
ignore_bins invalid2 = binsof(data_cp.low) && binsof(mode_cp.test_mode) && binsof(enable_cp.disabled);
}
endgroup
calculation_cg calc_cg = new();
// 传统Verilog的覆盖率计算实现
typedef struct {
// 基本bin统计
integer data_bins_total; // 4
integer data_bins_covered;
integer data_bin_hits[4];
real data_coverage;
integer data_weight; // 4
integer mode_bins_total; // 4
integer mode_bins_covered;
integer mode_bin_hits[4];
real mode_coverage;
integer mode_weight; // 2
integer enable_bins_total; // 2
integer enable_bins_covered;
integer enable_bin_hits[2];
real enable_coverage;
integer enable_weight; // 1
integer priority_bins_total; // 8
integer priority_bins_covered;
integer priority_bin_hits[8];
real priority_coverage;
integer priority_weight; // 3
// 交叉覆盖统计
integer data_mode_cross_total; // 15 (16-1忽略)
integer data_mode_cross_covered;
integer data_mode_cross_hits[15];
real data_mode_cross_coverage;
integer data_mode_cross_weight; // 5
integer triple_cross_total; // 29 (32-3忽略)
integer triple_cross_covered;
integer triple_cross_hits[29];
real triple_cross_coverage;
integer triple_cross_weight; // 8
// 总体统计
integer total_bins;
integer total_covered_bins;
real simple_coverage; // 简单覆盖率
real weighted_coverage; // 加权覆盖率
integer total_weight;
integer total_weighted_hits;
// 计算相关
integer total_samples;
real coverage_trend[100]; // 覆盖率趋势
integer trend_index;
} verilog_calculation_t;
verilog_calculation_t verilog_calc;
// 计算简单覆盖率(所有bins等权重)
function real calculate_simple_coverage();
return real'(verilog_calc.total_covered_bins) / real'(verilog_calc.total_bins) * 100.0;
endfunction
// 计算加权覆盖率
function real calculate_weighted_coverage();
integer total_possible_weight = 0;
integer actual_weighted_hits = 0;
// 数据coverpoint权重贡献
total_possible_weight += verilog_calc.data_bins_total * verilog_calc.data_weight;
actual_weighted_hits += verilog_calc.data_bins_covered * verilog_calc.data_weight;
// 模式coverpoint权重贡献
total_possible_weight += verilog_calc.mode_bins_total * verilog_calc.mode_weight;
actual_weighted_hits += verilog_calc.mode_bins_covered * verilog_calc.mode_weight;
// 使能coverpoint权重贡献
total_possible_weight += verilog_calc.enable_bins_total * verilog_calc.enable_weight;
actual_weighted_hits += verilog_calc.enable_bins_covered * verilog_calc.enable_weight;
// 优先级coverpoint权重贡献
total_possible_weight += verilog_calc.priority_bins_total * verilog_calc.priority_weight;
actual_weighted_hits += verilog_calc.priority_bins_covered * verilog_calc.priority_weight;
// 数据-模式交叉权重贡献
total_possible_weight += verilog_calc.data_mode_cross_total * verilog_calc.data_mode_cross_weight;
actual_weighted_hits += verilog_calc.data_mode_cross_covered * verilog_calc.data_mode_cross_weight;
// 三路交叉权重贡献
total_possible_weight += verilog_calc.triple_cross_total * verilog_calc.triple_cross_weight;
actual_weighted_hits += verilog_calc.triple_cross_covered * verilog_calc.triple_cross_weight;
if (total_possible_weight > 0)
return real'(actual_weighted_hits) / real'(total_possible_weight) * 100.0;
else
return 0.0;
endfunction
// 计算各coverpoint的覆盖率
function void calculate_individual_coverages();
// 数据coverpoint覆盖率
verilog_calc.data_bins_covered = 0;
for (int i = 0; i < 4; i++) begin
if (verilog_calc.data_bin_hits[i] > 0)
verilog_calc.data_bins_covered++;
end
verilog_calc.data_coverage = real'(verilog_calc.data_bins_covered) / real'(verilog_calc.data_bins_total) * 100.0;
// 模式coverpoint覆盖率
verilog_calc.mode_bins_covered = 0;
for (int i = 0; i < 4; i++) begin
if (verilog_calc.mode_bin_hits[i] > 0)
verilog_calc.mode_bins_covered++;
end
verilog_calc.mode_coverage = real'(verilog_calc.mode_bins_covered) / real'(verilog_calc.mode_bins_total) * 100.0;
// 使能coverpoint覆盖率
verilog_calc.enable_bins_covered = 0;
for (int i = 0; i < 2; i++) begin
if (verilog_calc.enable_bin_hits[i] > 0)
verilog_calc.enable_bins_covered++;
end
verilog_calc.enable_coverage = real'(verilog_calc.enable_bins_covered) / real'(verilog_calc.enable_bins_total) * 100.0;
// 优先级coverpoint覆盖率
verilog_calc.priority_bins_covered = 0;
for (int i = 0; i < 8; i++) begin
if (verilog_calc.priority_bin_hits[i] > 0)
verilog_calc.priority_bins_covered++;
end
verilog_calc.priority_coverage = real'(verilog_calc.priority_bins_covered) / real'(verilog_calc.priority_bins_total) * 100.0;
// 数据-模式交叉覆盖率
verilog_calc.data_mode_cross_covered = 0;
for (int i = 0; i < 15; i++) begin
if (verilog_calc.data_mode_cross_hits[i] > 0)
verilog_calc.data_mode_cross_covered++;
end
verilog_calc.data_mode_cross_coverage = real'(verilog_calc.data_mode_cross_covered) / real'(verilog_calc.data_mode_cross_total) * 100.0;
// 三路交叉覆盖率
verilog_calc.triple_cross_covered = 0;
for (int i = 0; i < 29; i++) begin
if (verilog_calc.triple_cross_hits[i] > 0)
verilog_calc.triple_cross_covered++;
end
verilog_calc.triple_cross_coverage = real'(verilog_calc.triple_cross_covered) / real'(verilog_calc.triple_cross_total) * 100.0;
endfunction
// 更新覆盖率趋势
function void update_coverage_trend();
if (verilog_calc.trend_index < 100) begin
verilog_calc.coverage_trend[verilog_calc.trend_index] = verilog_calc.weighted_coverage;
verilog_calc.trend_index++;
end else begin
// 滑动窗口
for (int i = 0; i < 99; i++) begin
verilog_calc.coverage_trend[i] = verilog_calc.coverage_trend[i+1];
end
verilog_calc.coverage_trend[99] = verilog_calc.weighted_coverage;
end
endfunction
// 获取交叉覆盖索引(简化映射)
function integer get_data_mode_cross_index(logic [3:0] data_val, logic [1:0] mode_val);
integer data_bin = (data_val < 4'h4) ? 0 : (data_val < 4'h8) ? 1 : (data_val < 4'hC) ? 2 : 3;
// 跳过忽略的组合 (max data && debug mode)
if (data_bin == 3 && mode_val == 2'b11)
return -1; // 无效索引
integer cross_idx = data_bin * 4 + mode_val;
// 调整索引以跳过忽略的组合
if (cross_idx >= 15) cross_idx = cross_idx - 1;
return cross_idx;
endfunction
function integer get_triple_cross_index(logic [3:0] data_val, logic [1:0] mode_val, logic enable_val);
integer data_bin = (data_val < 4'h4) ? 0 : (data_val < 4'h8) ? 1 : (data_val < 4'hC) ? 2 : 3;
// 检查忽略的组合
if ((data_bin == 3 && mode_val == 2'b11) || // max data && debug mode
(data_bin == 0 && mode_val == 2'b10 && enable_val == 1'b0)) // low data && test mode && disabled
return -1;
integer cross_idx = data_bin * 8 + mode_val * 2 + enable_val;
// 简化的索引调整
if (cross_idx >= 29) cross_idx = cross_idx - 3;
return cross_idx;
endfunction
// 主要的覆盖率收集和计算逻辑
always @(posedge clk) begin
if (reset) begin
verilog_calc = '{default: 0};
// 初始化bin总数和权重
verilog_calc.data_bins_total = 4;
verilog_calc.data_weight = 4;
verilog_calc.mode_bins_total = 4;
verilog_calc.mode_weight = 2;
verilog_calc.enable_bins_total = 2;
verilog_calc.enable_weight = 1;
verilog_calc.priority_bins_total = 8;
verilog_calc.priority_weight = 3;
verilog_calc.data_mode_cross_total = 15;
verilog_calc.data_mode_cross_weight = 5;
verilog_calc.triple_cross_total = 29;
verilog_calc.triple_cross_weight = 8;
verilog_calc.total_bins = 4 + 4 + 2 + 8 + 15 + 29; // 62
verilog_calc.total_weight = 4 + 2 + 1 + 3 + 5 + 8; // 23
end else begin
verilog_calc.total_samples++;
// 更新各coverpoint的bin命中
// 数据bins
case (data)
4'h0, 4'h1, 4'h2, 4'h3: verilog_calc.data_bin_hits[0]++; // low
4'h4, 4'h5, 4'h6, 4'h7: verilog_calc.data_bin_hits[1]++; // mid
4'h8, 4'h9, 4'hA, 4'hB: verilog_calc.data_bin_hits[2]++; // high
4'hC, 4'hD, 4'hE, 4'hF: verilog_calc.data_bin_hits[3]++; // max
endcase
// 模式bins
verilog_calc.mode_bin_hits[mode]++;
// 使能bins
verilog_calc.enable_bin_hits[enable]++;
// 优先级bins
verilog_calc.priority_bin_hits[priority]++;
// 数据-模式交叉bins
automatic integer dm_cross_idx = get_data_mode_cross_index(data, mode);
if (dm_cross_idx >= 0 && dm_cross_idx < 15)
verilog_calc.data_mode_cross_hits[dm_cross_idx]++;
// 三路交叉bins
automatic integer triple_cross_idx = get_triple_cross_index(data, mode, enable);
if (triple_cross_idx >= 0 && triple_cross_idx < 29)
verilog_calc.triple_cross_hits[triple_cross_idx]++;
// 计算各种覆盖率
calculate_individual_coverages();
// 更新总体统计
verilog_calc.total_covered_bins = verilog_calc.data_bins_covered +
verilog_calc.mode_bins_covered +
verilog_calc.enable_bins_covered +
verilog_calc.priority_bins_covered +
verilog_calc.data_mode_cross_covered +
verilog_calc.triple_cross_covered;
verilog_calc.simple_coverage = calculate_simple_coverage();
verilog_calc.weighted_coverage = calculate_weighted_coverage();
// 更新覆盖率趋势
update_coverage_trend();
end
end
// 测试序列
initial begin
reset = 1;
data = 4'h0;
mode = 2'b00;
enable = 1'b0;
priority = 3'b000;
address = 8'h00;
#20 reset = 0;
$display("开始覆盖率计算测试...");
// 初始状态
$display("\n=== 初始覆盖率状态 ===");
$display("SystemVerilog总体覆盖率: %0.2f%%", calc_cg.get_coverage());
$display("传统Verilog简单覆盖率: %0.2f%%", calculate_simple_coverage());
$display("传统Verilog加权覆盖率: %0.2f%%", calculate_weighted_coverage());
// 系统性测试以观察覆盖率变化
$display("\n=== 系统性覆盖率测试 ===");
// 阶段1:测试数据coverpoint
$display("\n阶段1:测试数据coverpoint");
for (int i = 0; i < 4; i++) begin
@(posedge clk);
case (i)
0: data = 4'h1; // low
1: data = 4'h5; // mid
2: data = 4'h9; // high
3: data = 4'hD; // max
endcase
$display(" 数据=0x%h, SV总体=%0.2f%%, 数据CP=%0.2f%%, Verilog简单=%0.2f%%, Verilog加权=%0.2f%%",
data, calc_cg.get_coverage(), calc_cg.data_cp.get_coverage(),
calculate_simple_coverage(), calculate_weighted_coverage());
end
// 阶段2:测试模式coverpoint
$display("\n阶段2:测试模式coverpoint");
for (int i = 0; i < 4; i++) begin
@(posedge clk);
mode = i;
$display(" 模式=%d, SV总体=%0.2f%%, 模式CP=%0.2f%%, Verilog简单=%0.2f%%, Verilog加权=%0.2f%%",
mode, calc_cg.get_coverage(), calc_cg.mode_cp.get_coverage(),
calculate_simple_coverage(), calculate_weighted_coverage());
end
// 阶段3:测试使能coverpoint
$display("\n阶段3:测试使能coverpoint");
for (int i = 0; i < 2; i++) begin
@(posedge clk);
enable = i;
$display(" 使能=%d, SV总体=%0.2f%%, 使能CP=%0.2f%%, Verilog简单=%0.2f%%, Verilog加权=%0.2f%%",
enable, calc_cg.get_coverage(), calc_cg.enable_cp.get_coverage(),
calculate_simple_coverage(), calculate_weighted_coverage());
end
// 阶段4:测试优先级coverpoint
$display("\n阶段4:测试优先级coverpoint");
for (int i = 0; i < 8; i++) begin
@(posedge clk);
priority = i;
$display(" 优先级=%d, SV总体=%0.2f%%, 优先级CP=%0.2f%%, Verilog简单=%0.2f%%, Verilog加权=%0.2f%%",
priority, calc_cg.get_coverage(), calc_cg.priority_cp.get_coverage(),
calculate_simple_coverage(), calculate_weighted_coverage());
end
// 阶段5:测试交叉覆盖
$display("\n阶段5:测试数据-模式交叉覆盖");
for (int d = 0; d < 4; d++) begin
for (int m = 0; m < 4; m++) begin
// 跳过忽略的组合
if (d == 3 && m == 3) continue;
@(posedge clk);
case (d)
0: data = 4'h1; // low
1: data = 4'h5; // mid
2: data = 4'h9; // high
3: data = 4'hD; // max
endcase
mode = m;
$display(" 数据bin=%d, 模式=%d, SV交叉=%0.2f%%, Verilog简单=%0.2f%%, Verilog加权=%0.2f%%",
d, m, calc_cg.data_mode_cross.get_coverage(),
calculate_simple_coverage(), calculate_weighted_coverage());
end
end
// 阶段6:测试三路交叉覆盖
$display("\n阶段6:测试三路交叉覆盖(部分)");
for (int d = 0; d < 2; d++) begin // 只测试部分组合
for (int m = 0; m < 2; m++) begin
for (int e = 0; e < 2; e++) begin
// 跳过忽略的组合
if (d == 0 && m == 2 && e == 0) continue; // low && test && disabled
@(posedge clk);
case (d)
0: data = 4'h1; // low
1: data = 4'h5; // mid
endcase
mode = m;
enable = e;
$display(" 数据bin=%d, 模式=%d, 使能=%d, SV三路交叉=%0.2f%%, Verilog简单=%0.2f%%, Verilog加权=%0.2f%%",
d, m, e, calc_cg.data_mode_enable_cross.get_coverage(),
calculate_simple_coverage(), calculate_weighted_coverage());
end
end
end
// 随机压力测试
$display("\n=== 随机压力测试 ===");
repeat (200) begin
@(posedge clk);
data = $random % 16;
mode = $random % 4;
enable = $random % 2;
priority = $random % 8;
address = $random;
end
// 最终覆盖率计算报告
$display("\n=== SystemVerilog覆盖率计算最终报告 ===");
$display("总体覆盖率: %0.2f%%", calc_cg.get_coverage());
$display("数据coverpoint覆盖率: %0.2f%% (权重: %d)", calc_cg.data_cp.get_coverage(), 4);
$display("模式coverpoint覆盖率: %0.2f%% (权重: %d)", calc_cg.mode_cp.get_coverage(), 2);
$display("使能coverpoint覆盖率: %0.2f%% (权重: %d)", calc_cg.enable_cp.get_coverage(), 1);
$display("优先级coverpoint覆盖率: %0.2f%% (权重: %d)", calc_cg.priority_cp.get_coverage(), 3);
$display("数据-模式交叉覆盖率: %0.2f%% (权重: %d)", calc_cg.data_mode_cross.get_coverage(), 5);
$display("三路交叉覆盖率: %0.2f%% (权重: %d)", calc_cg.data_mode_enable_cross.get_coverage(), 8);
// 传统Verilog覆盖率计算报告
$display("\n=== 传统Verilog覆盖率计算报告 ===");
$display("简单覆盖率: %0.2f%% (%d/%d bins)",
verilog_calc.simple_coverage, verilog_calc.total_covered_bins, verilog_calc.total_bins);
$display("加权覆盖率: %0.2f%% (总权重: %d)",
verilog_calc.weighted_coverage, verilog_calc.total_weight);
$display("\n详细coverpoint统计:");
$display(" 数据CP: %0.2f%% (%d/%d bins, 权重: %d)",
verilog_calc.data_coverage, verilog_calc.data_bins_covered, verilog_calc.data_bins_total, verilog_calc.data_weight);
$display(" 模式CP: %0.2f%% (%d/%d bins, 权重: %d)",
verilog_calc.mode_coverage, verilog_calc.mode_bins_covered, verilog_calc.mode_bins_total, verilog_calc.mode_weight);
$display(" 使能CP: %0.2f%% (%d/%d bins, 权重: %d)",
verilog_calc.enable_coverage, verilog_calc.enable_bins_covered, verilog_calc.enable_bins_total, verilog_calc.enable_weight);
$display(" 优先级CP: %0.2f%% (%d/%d bins, 权重: %d)",
verilog_calc.priority_coverage, verilog_calc.priority_bins_covered, verilog_calc.priority_bins_total, verilog_calc.priority_weight);
$display(" 数据-模式交叉: %0.2f%% (%d/%d bins, 权重: %d)",
verilog_calc.data_mode_cross_coverage, verilog_calc.data_mode_cross_covered, verilog_calc.data_mode_cross_total, verilog_calc.data_mode_cross_weight);
$display(" 三路交叉: %0.2f%% (%d/%d bins, 权重: %d)",
verilog_calc.triple_cross_coverage, verilog_calc.triple_cross_covered, verilog_calc.triple_cross_total, verilog_calc.triple_cross_weight);
$display("\n计算统计信息:");
$display(" 总采样数: %d", verilog_calc.total_samples);
$display(" 覆盖率趋势记录数: %d", verilog_calc.trend_index > 100 ? 100 : verilog_calc.trend_index);
if (verilog_calc.trend_index > 10) begin
$display(" 最近10次覆盖率趋势:");
for (int i = (verilog_calc.trend_index > 100 ? 90 : verilog_calc.trend_index-10);
i < (verilog_calc.trend_index > 100 ? 100 : verilog_calc.trend_index); i++) begin
$display(" [%d]: %0.2f%%", i, verilog_calc.coverage_trend[i]);
end
end
$finish;
end
endmodule
8. SystemVerilog与传统Verilog的对比
通过前面章节的详细介绍,我们可以清楚地看到SystemVerilog在功能覆盖率方面相比传统Verilog的巨大优势。
8.1 语法简洁性对比
SystemVerilog的优势:
- 声明式语法:使用
covergroup
、coverpoint
、bins
等关键字直接声明覆盖点 - 自动化管理:自动处理覆盖率统计、计算和报告
- 内建支持:原生支持交叉覆盖、条件覆盖、权重设置等高级特性
- 简洁表达:复杂的覆盖需求可以用几行代码表达
传统Verilog的局限:
- 手动实现:需要手动编写大量的统计逻辑和数据结构
- 复杂维护:随着覆盖点增加,代码复杂度呈指数增长
- 易出错:手动计算覆盖率容易出现逻辑错误
- 可读性差:大量的统计代码掩盖了设计意图
8.2 功能完整性对比
功能特性 | SystemVerilog | 传统Verilog |
---|---|---|
基本覆盖点 | ✅ 原生支持 | ❌ 需手动实现 |
交叉覆盖 | ✅ 内建支持 | ❌ 复杂手动逻辑 |
条件覆盖 | ✅ iff 关键字 |
❌ 需额外判断逻辑 |
权重设置 | ✅ option.weight |
❌ 需手动加权计算 |
忽略bins | ✅ ignore_bins |
❌ 需手动排除逻辑 |
非法bins | ✅ illegal_bins |
❌ 需手动检查逻辑 |
通配符匹配 | ✅ wildcard bins |
❌ 需复杂位操作 |
序列覆盖 | ✅ 转换bins | ❌ 需状态机实现 |
系统方法 | ✅ 丰富的查询API | ❌ 需自定义函数 |
覆盖率计算 | ✅ 自动计算 | ❌ 需手动实现算法 |
实例管理 | ✅ 自动管理 | ❌ 需手动跟踪 |
采样控制 | ✅ 灵活的采样机制 | ❌ 需手动控制逻辑 |
8.3 开发效率对比
SystemVerilog:
// 10行代码实现复杂的功能覆盖率
covergroup complex_cg @(posedge clk);
data_cp: coverpoint data {
bins low = {[0:31]};
bins high = {[32:63]};
}
mode_cp: coverpoint mode;
cross_coverage: cross data_cp, mode_cp {
ignore_bins invalid = binsof(data_cp.high) && binsof(mode_cp) intersect {3};
}
endgroup
传统Verilog:
// 需要200+行代码实现相同功能
// 包括:数据结构定义、统计逻辑、计算函数、
// 交叉覆盖映射、忽略逻辑、报告生成等
8.4 维护性对比
SystemVerilog的优势:
- 自文档化:覆盖意图直接体现在代码中
- 易于修改:添加新覆盖点只需几行代码
- 自动一致性:工具自动保证统计逻辑的正确性
- 标准化:统一的语法和语义
传统Verilog的问题:
- 维护困难:修改覆盖需求需要同时修改多处代码
- 一致性风险:手动逻辑容易出现不一致
- 文档分离:覆盖意图与实现代码分离
- 非标准化:不同项目可能有不同的实现方式
8.5 性能对比
SystemVerilog:
- 优化编译:工具可以对覆盖率收集进行优化
- 内存效率:自动优化内存使用
- 运行时优化:智能采样和统计
传统Verilog:
- 手动优化:需要手动优化性能
- 内存开销:可能存在内存浪费
- 运行时负担:复杂的统计逻辑影响仿真性能
8.6 工具支持对比
SystemVerilog:
- EDA工具原生支持:主流仿真器都支持SystemVerilog覆盖率
- 自动报告生成:工具自动生成详细的覆盖率报告
- 图形化界面:可视化覆盖率分析
- 数据库集成:覆盖率数据可以导入专业分析工具
传统Verilog:
- 有限工具支持:需要额外的脚本和工具处理
- 手动报告:需要自己编写报告生成逻辑
- 分析困难:缺乏标准化的分析接口
- 集成复杂:与其他工具集成需要额外工作
8.7 学习曲线对比
SystemVerilog:
- 概念清晰:覆盖率概念直接映射到语法
- 渐进学习:可以从简单覆盖点开始逐步学习
- 丰富资源:大量的文档和示例
- 标准化:IEEE标准保证一致性
传统Verilog:
- 概念复杂:需要理解底层实现细节
- 经验依赖:需要大量实践经验
- 资源分散:缺乏统一的最佳实践
- 项目特定:不同项目可能有不同的实现方式
8.8 实际应用建议
推荐使用SystemVerilog的场景:
- 新项目开发
- 复杂的验证需求
- 需要详细覆盖率分析的项目
- 团队协作的大型项目
- 需要与现代EDA工具集成的项目
可能继续使用传统Verilog的场景:
- 遗留项目维护(已有大量Verilog覆盖率代码)
- 工具链限制(不支持SystemVerilog)
- 简单的覆盖需求
- 特殊的性能要求(需要精确控制)
9. 总结
SystemVerilog的功能覆盖率是现代硬件验证的重要工具,它通过以下特性大大提升了验证效率:
9.1 核心优势
- 声明式语法:直观表达覆盖意图
- 自动化管理:减少手动编码工作
- 丰富特性:支持复杂的覆盖需求
- 工具集成:与现代EDA工具无缝集成
- 标准化:IEEE标准保证一致性和可移植性
9.2 关键概念回顾
- covergroup:覆盖组,组织相关的覆盖点
- coverpoint:覆盖点,定义需要监控的信号或表达式
- bins:覆盖桶,定义值的分组和统计
- cross:交叉覆盖,监控多个变量的组合
- 选项系统:灵活配置覆盖行为
- 系统方法:查询和控制覆盖率收集
9.3 最佳实践
- 合理规划覆盖点:避免过度覆盖和覆盖不足
- 使用分层覆盖:从模块级到系统级的分层覆盖策略
- 利用交叉覆盖:关注关键的变量组合
- 设置合理权重:突出重要的覆盖点
- 定期分析报告:及时发现覆盖盲点
- 与断言结合:功能覆盖率与断言验证相辅相成
9.4 发展趋势
随着硬件设计复杂度的不断增加,SystemVerilog功能覆盖率将继续发展:
- 智能覆盖率:AI辅助的覆盖点生成和分析
- 形式化集成:与形式化验证方法的深度集成
- 云端分析:基于云计算的大规模覆盖率分析
- 实时反馈:仿真过程中的实时覆盖率指导
9.5 结语
SystemVerilog功能覆盖率作为现代硬件验证的基石,不仅提供了强大的技术能力,更重要的是改变了验证工程师的思维方式。从传统的"测试驱动"转向"覆盖率驱动",从手动统计转向自动化分析,这些变化使得验证工作更加科学、高效和可靠。
掌握SystemVerilog功能覆盖率,不仅是技术技能的提升,更是验证思维的升级。它帮助我们更好地理解设计、发现问题、提升质量,是每个硬件验证工程师必须掌握的核心技能。
通过本章的学习,读者应该能够:
- 理解功能覆盖率的基本概念和重要性
- 掌握covergroup和coverpoint的定义和使用
- 熟练使用各种类型的bins
- 理解和应用交叉覆盖
- 配置和使用覆盖率选项
- 利用系统方法进行覆盖率查询和控制
- 理解覆盖率的计算方法
- 认识SystemVerilog相对于传统Verilog的优势
这些知识将为后续的高级验证技术学习奠定坚实的基础。
10. 综合示例:FIFO验证中的功能覆盖率
下面通过一个完整的FIFO验证例子来展示功能覆盖率的实际应用:
10.1 FIFO设计模块
// FIFO设计模块
module fifo #(
parameter DATA_WIDTH = 8,
parameter DEPTH = 16
) (
input logic clk,
input logic rst_n,
input logic wr_en,
input logic rd_en,
input logic [DATA_WIDTH-1:0] wr_data,
output logic [DATA_WIDTH-1:0] rd_data,
output logic full,
output logic empty,
output logic [$clog2(DEPTH):0] count
);
// FIFO实现代码...
endmodule
10.2 完整的功能覆盖率验证
// FIFO验证模块,包含完整的功能覆盖率
module fifo_tb;
// 参数定义
parameter DATA_WIDTH = 8;
parameter DEPTH = 16;
parameter CLK_PERIOD = 10;
// 信号声明
logic clk;
logic rst_n;
logic wr_en;
logic rd_en;
logic [DATA_WIDTH-1:0] wr_data;
logic [DATA_WIDTH-1:0] rd_data;
logic full;
logic empty;
logic [$clog2(DEPTH):0] count;
// 时钟生成
initial begin
clk = 0;
forever #(CLK_PERIOD/2) clk = ~clk;
end
// DUT实例化
fifo #(
.DATA_WIDTH(DATA_WIDTH),
.DEPTH(DEPTH)
) dut (
.clk(clk),
.rst_n(rst_n),
.wr_en(wr_en),
.rd_en(rd_en),
.wr_data(wr_data),
.rd_data(rd_data),
.full(full),
.empty(empty),
.count(count)
);
// ========================================
// 功能覆盖率定义
// ========================================
// 1. 基本操作覆盖率
covergroup basic_ops_cg @(posedge clk);
option.per_instance = 1;
option.name = "basic_operations";
option.comment = "FIFO基本操作覆盖率";
// 写使能覆盖
wr_enable_cp: coverpoint wr_en {
bins write_active = {1};
bins write_inactive = {0};
}
// 读使能覆盖
rd_enable_cp: coverpoint rd_en {
bins read_active = {1};
bins read_inactive = {0};
}
// 同时读写操作覆盖
rw_operation_cp: cross wr_enable_cp, rd_enable_cp {
bins idle = binsof(wr_enable_cp.write_inactive) &&
binsof(rd_enable_cp.read_inactive);
bins write_only = binsof(wr_enable_cp.write_active) &&
binsof(rd_enable_cp.read_inactive);
bins read_only = binsof(wr_enable_cp.write_inactive) &&
binsof(rd_enable_cp.read_active);
bins simultaneous = binsof(wr_enable_cp.write_active) &&
binsof(rd_enable_cp.read_active);
}
endgroup
// 2. FIFO状态覆盖率
covergroup fifo_state_cg @(posedge clk);
option.per_instance = 1;
option.name = "fifo_states";
option.comment = "FIFO状态转换覆盖率";
// 空满状态覆盖
empty_cp: coverpoint empty {
bins empty_state = {1};
bins not_empty = {0};
}
full_cp: coverpoint full {
bins full_state = {1};
bins not_full = {0};
}
// FIFO计数覆盖
count_cp: coverpoint count {
bins empty_count = {0};
bins low_fill = {[1:DEPTH/4]};
bins mid_fill = {[DEPTH/4+1:3*DEPTH/4]};
bins high_fill = {[3*DEPTH/4+1:DEPTH-1]};
bins full_count = {DEPTH};
}
// 状态组合覆盖
state_combination: cross empty_cp, full_cp {
bins normal_states = binsof(empty_cp.not_empty) &&
binsof(full_cp.not_full);
bins empty_state = binsof(empty_cp.empty_state) &&
binsof(full_cp.not_full);
bins full_state = binsof(empty_cp.not_empty) &&
binsof(full_cp.full_state);
// 排除不可能的状态
illegal_bins impossible = binsof(empty_cp.empty_state) &&
binsof(full_cp.full_state);
}
endgroup
// 3. 数据模式覆盖率
covergroup data_pattern_cg @(posedge clk iff wr_en);
option.per_instance = 1;
option.name = "data_patterns";
option.comment = "写入数据模式覆盖率";
// 数据值覆盖
data_value_cp: coverpoint wr_data {
bins zero = {8'h00};
bins all_ones = {8'hFF};
bins low_values = {[8'h01:8'h7F]};
bins high_values = {[8'h80:8'hFE]};
bins alternating_01 = {8'h55};
bins alternating_10 = {8'hAA};
}
// 数据位模式覆盖
bit_pattern_cp: coverpoint wr_data {
wildcard bins pattern_0x0x = {8'b0?0?0?0?};
wildcard bins pattern_1x1x = {8'b1?1?1?1?};
wildcard bins pattern_x01x = {8'b?01??01?};
wildcard bins pattern_x10x = {8'b?10??10?};
}
endgroup
// 4. 边界条件覆盖率
covergroup boundary_cg @(posedge clk);
option.per_instance = 1;
option.name = "boundary_conditions";
option.comment = "边界条件覆盖率";
// 写操作边界条件
write_boundary_cp: coverpoint {wr_en, full} {
bins normal_write = {2'b10}; // 写使能且非满
bins blocked_write = {2'b11}; // 写使能但已满
bins no_write_empty = {2'b00}; // 不写且非满
bins no_write_full = {2'b01}; // 不写且满
}
// 读操作边界条件
read_boundary_cp: coverpoint {rd_en, empty} {
bins normal_read = {2'b10}; // 读使能且非空
bins blocked_read = {2'b11}; // 读使能但已空
bins no_read_full = {2'b00}; // 不读且非空
bins no_read_empty = {2'b01}; // 不读且空
}
// 边界转换覆盖
boundary_transition: cross write_boundary_cp, read_boundary_cp {
// 关注关键的边界组合
bins fill_to_full = binsof(write_boundary_cp.normal_write) &&
binsof(read_boundary_cp.no_read_full);
bins empty_to_fill = binsof(write_boundary_cp.normal_write) &&
binsof(read_boundary_cp.blocked_read);
bins drain_to_empty = binsof(write_boundary_cp.no_write_empty) &&
binsof(read_boundary_cp.normal_read);
bins full_to_drain = binsof(write_boundary_cp.blocked_write) &&
binsof(read_boundary_cp.normal_read);
}
endgroup
// 5. 序列操作覆盖率
covergroup sequence_cg @(posedge clk);
option.per_instance = 1;
option.name = "operation_sequences";
option.comment = "操作序列覆盖率";
// 连续写操作序列
write_sequence_cp: coverpoint wr_en {
bins single_write = (0 => 1 => 0);
bins double_write = (0 => 1 [*2] => 0);
bins triple_write = (0 => 1 [*3] => 0);
bins long_write = (0 => 1 [*4:8] => 0);
bins continuous_write = (1 [*9:$]);
}
// 连续读操作序列
read_sequence_cp: coverpoint rd_en {
bins single_read = (0 => 1 => 0);
bins double_read = (0 => 1 [*2] => 0);
bins triple_read = (0 => 1 [*3] => 0);
bins long_read = (0 => 1 [*4:8] => 0);
bins continuous_read = (1 [*9:$]);
}
// 填充-排空序列
fill_drain_cp: coverpoint {wr_en, rd_en} {
bins fill_phase = (2'b00 => 2'b10 [*1:$] => 2'b00);
bins drain_phase = (2'b00 => 2'b01 [*1:$] => 2'b00);
bins alternating = (2'b10 => 2'b01) [*3:$];
bins simultaneous = (2'b11 [*2:$]);
}
endgroup
// 6. 性能相关覆盖率
covergroup performance_cg @(posedge clk);
option.per_instance = 1;
option.name = "performance_metrics";
option.comment = "性能指标覆盖率";
// 吞吐量覆盖(连续操作周期数)
throughput_cp: coverpoint {wr_en && !full, rd_en && !empty} {
bins no_throughput = {2'b00};
bins write_only = {2'b10};
bins read_only = {2'b01};
bins full_throughput = {2'b11};
option.weight = 2; // 提高吞吐量覆盖的权重
}
// 利用率覆盖
utilization_cp: coverpoint count {
bins underutilized = {[0:DEPTH/8]};
bins low_util = {[DEPTH/8+1:DEPTH/4]};
bins medium_util = {[DEPTH/4+1:3*DEPTH/4]};
bins high_util = {[3*DEPTH/4+1:DEPTH]};
}
endgroup
// 7. 错误条件覆盖率
covergroup error_cg @(posedge clk);
option.per_instance = 1;
option.name = "error_conditions";
option.comment = "错误条件覆盖率";
// 溢出尝试
overflow_attempt_cp: coverpoint {wr_en, full} {
bins normal_write = {2'b10};
bins overflow_attempt = {2'b11};
bins no_write = {2'b0?};
}
// 下溢尝试
underflow_attempt_cp: coverpoint {rd_en, empty} {
bins normal_read = {2'b10};
bins underflow_attempt = {2'b11};
bins no_read = {2'b0?};
}
// 错误组合
error_combination: cross overflow_attempt_cp, underflow_attempt_cp {
bins both_errors = binsof(overflow_attempt_cp.overflow_attempt) &&
binsof(underflow_attempt_cp.underflow_attempt);
bins overflow_only = binsof(overflow_attempt_cp.overflow_attempt) &&
binsof(underflow_attempt_cp.normal_read);
bins underflow_only = binsof(overflow_attempt_cp.normal_write) &&
binsof(underflow_attempt_cp.underflow_attempt);
}
endgroup
// ========================================
// 覆盖组实例化和控制
// ========================================
// 实例化所有覆盖组
basic_ops_cg basic_ops_inst;
fifo_state_cg fifo_state_inst;
data_pattern_cg data_pattern_inst;
boundary_cg boundary_inst;
sequence_cg sequence_inst;
performance_cg performance_inst;
error_cg error_inst;
// 初始化覆盖组
initial begin
basic_ops_inst = new();
fifo_state_inst = new();
data_pattern_inst = new();
boundary_inst = new();
sequence_inst = new();
performance_inst = new();
error_inst = new();
// 设置覆盖组选项
basic_ops_inst.set_inst_name("basic_ops_coverage");
fifo_state_inst.set_inst_name("fifo_state_coverage");
data_pattern_inst.set_inst_name("data_pattern_coverage");
boundary_inst.set_inst_name("boundary_coverage");
sequence_inst.set_inst_name("sequence_coverage");
performance_inst.set_inst_name("performance_coverage");
error_inst.set_inst_name("error_coverage");
end
// ========================================
// 测试激励生成
// ========================================
// 随机测试任务
task automatic random_test(int num_cycles);
for (int i = 0; i < num_cycles; i++) begin
@(posedge clk);
wr_en <= $random() % 2;
rd_en <= $random() % 2;
wr_data <= $random();
end
endtask
// 定向测试任务
task automatic directed_test();
// 测试填满FIFO
repeat(DEPTH + 2) begin
@(posedge clk);
wr_en <= 1;
rd_en <= 0;
wr_data <= $random();
end
// 测试排空FIFO
repeat(DEPTH + 2) begin
@(posedge clk);
wr_en <= 0;
rd_en <= 1;
end
// 测试同时读写
repeat(20) begin
@(posedge clk);
wr_en <= 1;
rd_en <= 1;
wr_data <= $random();
end
endtask
// ========================================
// 覆盖率监控和报告
// ========================================
// 覆盖率监控任务
task automatic monitor_coverage();
real total_coverage;
real individual_coverage;
forever begin
#1000; // 每1000个时间单位检查一次
// 计算总体覆盖率
total_coverage = ($get_coverage() +
basic_ops_inst.get_coverage() +
fifo_state_inst.get_coverage() +
data_pattern_inst.get_coverage() +
boundary_inst.get_coverage() +
sequence_inst.get_coverage() +
performance_inst.get_coverage() +
error_inst.get_coverage()) / 8.0;
$display("[%0t] 总体覆盖率: %0.2f%%", $time, total_coverage);
// 显示各个覆盖组的覆盖率
$display(" 基本操作覆盖率: %0.2f%%", basic_ops_inst.get_coverage());
$display(" FIFO状态覆盖率: %0.2f%%", fifo_state_inst.get_coverage());
$display(" 数据模式覆盖率: %0.2f%%", data_pattern_inst.get_coverage());
$display(" 边界条件覆盖率: %0.2f%%", boundary_inst.get_coverage());
$display(" 序列操作覆盖率: %0.2f%%", sequence_inst.get_coverage());
$display(" 性能指标覆盖率: %0.2f%%", performance_inst.get_coverage());
$display(" 错误条件覆盖率: %0.2f%%", error_inst.get_coverage());
// 如果达到目标覆盖率,结束仿真
if (total_coverage >= 95.0) begin
$display("\n[%0t] 达到目标覆盖率 95%%, 仿真结束", $time);
$finish;
end
end
endtask
// ========================================
// 主测试流程
// ========================================
initial begin
// 初始化
rst_n = 0;
wr_en = 0;
rd_en = 0;
wr_data = 0;
// 复位
repeat(5) @(posedge clk);
rst_n = 1;
// 启动覆盖率监控
fork
monitor_coverage();
join_none
// 执行测试
$display("[%0t] 开始FIFO功能覆盖率测试", $time);
// 定向测试
$display("[%0t] 执行定向测试", $time);
directed_test();
// 随机测试
$display("[%0t] 执行随机测试", $time);
random_test(5000);
// 等待覆盖率收集完成
repeat(100) @(posedge clk);
// 最终覆盖率报告
$display("\n========== 最终覆盖率报告 ==========");
$display("基本操作覆盖率: %0.2f%%", basic_ops_inst.get_coverage());
$display("FIFO状态覆盖率: %0.2f%%", fifo_state_inst.get_coverage());
$display("数据模式覆盖率: %0.2f%%", data_pattern_inst.get_coverage());
$display("边界条件覆盖率: %0.2f%%", boundary_inst.get_coverage());
$display("序列操作覆盖率: %0.2f%%", sequence_inst.get_coverage());
$display("性能指标覆盖率: %0.2f%%", performance_inst.get_coverage());
$display("错误条件覆盖率: %0.2f%%", error_inst.get_coverage());
$finish;
end
endmodule
10.3 传统Verilog等价实现对比
如果使用传统Verilog实现相同的功能覆盖率,需要大量的手动代码:
// 传统Verilog需要手动实现所有统计逻辑
module fifo_coverage_verilog;
// 需要定义大量的计数器和统计变量
integer write_active_count, write_inactive_count;
integer read_active_count, read_inactive_count;
integer idle_count, write_only_count, read_only_count, simultaneous_count;
integer empty_state_count, full_state_count;
integer zero_data_count, all_ones_count;
// ... 数百个统计变量
// 需要手动编写所有的统计逻辑
always @(posedge clk) begin
if (wr_en) write_active_count <= write_active_count + 1;
else write_inactive_count <= write_inactive_count + 1;
if (rd_en) read_active_count <= read_active_count + 1;
else read_inactive_count <= read_inactive_count + 1;
case ({wr_en, rd_en})
2'b00: idle_count <= idle_count + 1;
2'b10: write_only_count <= write_only_count + 1;
2'b01: read_only_count <= read_only_count + 1;
2'b11: simultaneous_count <= simultaneous_count + 1;
endcase
// ... 数百行类似的统计代码
end
// 需要手动编写覆盖率计算函数
function real calculate_coverage;
// 复杂的覆盖率计算逻辑
// ... 数十行计算代码
endfunction
// 需要手动编写报告生成逻辑
task generate_coverage_report;
// ... 数十行报告生成代码
endtask
endmodule
10.4 示例总结
这个FIFO验证示例展示了SystemVerilog功能覆盖率的强大能力:
- 全面覆盖:从基本操作到复杂序列,从正常情况到错误条件
- 分层组织:不同类型的覆盖点分组管理
- 自动化:自动统计、计算和报告
- 灵活配置:通过选项控制覆盖行为
- 实时监控:动态查询覆盖率进度
- 简洁表达:复杂的覆盖需求用简洁的语法表达