8. 示例:对32位数据总线实现位宽和值域覆盖
文章目录
- 前言
- 示例一:
- 示例二:
- 示例三:
- 仿真与覆盖率分析
- 覆盖点详细说明
- 覆盖率提升技巧
- 常见错误排查
- 示例四:
- 仿真步骤
前言
针对32位数据总线实现位宽和值域的覆盖,并且能够用xrun运行,查看日志和波形。coverpoint需要覆盖32位的各个位宽,可能包括每一位的独立覆盖。值域覆盖可能需要不同的范围,比如全0、全1、边界值等。
示例一:
完整示例代码
`timescale 1ns/1ps
module bus_coverage_demo;
logic clk = 0;
logic [31:0] data_bus;
bit valid;
// 时钟生成(100MHz)
always #5 clk = ~clk;
// 覆盖组定义
covergroup cg_bus @(posedge clk iff valid);
// 位宽覆盖:检查各bit位是否被置位过
cp_bitwise: coverpoint data_bus {
bins bit_flipped = { [0:31] }; // 每位独立统计
option.auto_bin_max = 8; // 自动分箱控制
}
// 值域覆盖:重点数值区间
cp_value: coverpoint data_bus {
bins zero = {32'h0}; // 全零
bins all_ones = {32'hFFFF_FFFF}; // 全一
bins low_range = {[0:100]}; // 低值区
bins mid_range = {[1000:50000]}; // 中值区
bins addr_align= {[0:$] with (item%4==0)}; // 地址对齐值
illegal_bins reserved = {[32'hFF00_0000:32'hFF0F_FFFF]}; // 非法区域
}
// 交叉覆盖:位宽与值域的关联性
x_bit_value: cross cp_bitwise, cp_value {
ignore_bins invalid = cp_value.reserved; // 过滤非法值
}
endgroup
cg_bus cov_inst = new();
// 测试激励生成
initial begin
$display("=== 开始数据总线覆盖测试 ===");
valid = 1;
repeat(1000) begin
@(negedge clk);
data_bus = $urandom_range(0, 32'hFFFF_FFFF);
if($time > 200) valid = $urandom_range(0,1); // 模拟valid随机失效
end
#50 $finish;
end
// 波形记录配置
initial begin
$shm_open("bus_cov_waves.shm");
$shm_probe(0, bus_coverage_demo, "AS");
end
endmodule
Xcelium运行脚本 (xrun_bus_cov.sh)
#!/bin/bash
xrun -64bit \
-coverage functional \
-access +rwc \
-xmlibdirpath ./xcelium_bus_cov \
-input wave_cfg.tcl \
bus_coverage_demo.sv
波形配置 (wave_cfg.tcl)
database -open waves -shm
probe -create -database waves -all -depth all
run
关键功能解析
- 位宽覆盖
- 使用bins bit_flipped统计每个bit位的0/1状态
- auto_bin_max控制自动分箱数量防止组合爆炸
- 智能值域划分
- 特殊值(全0/全1)独立统计
- 地址对齐值检测(item%4==0)
- 非法区域检测(illegal_bins)
- 交叉验证
- 分析bit位变化与数值范围的关联性
- 自动过滤非法值组合
执行流程
# 1. 赋予执行权限
chmod +x xrun_bus_cov.sh
# 2. 启动仿真(自动生成覆盖率数据库)
./xrun_bus_cov.sh
# 3. 查看覆盖率报告
imc -load xcelium_bus_cov/cov_work/scope/test -run
覆盖率分析命令:
coverage -summary // 查看整体覆盖率
coverage -detail -metric cp_bitwise // 查看位级覆盖详情
coverage -heatmap x_bit_value // 生成交叉覆盖热力图
预期输出
终端日志:
=== 开始数据总线覆盖测试 ===
XRUN_INFO: Coverage enabled (functional)
XRUN_INFO: Created SHM database at bus_cov_waves.shm
XRUN_INFO: Simulation completed at 1050ns
覆盖率报告:
COVERGROUP: cg_bus
|-CP_BITWISE : 98.2% (314/320 bins)
|-CP_VALUE : 100% (5/5 bins)
|-X_BIT_VALUE : 89.7% (1423/1586 valid bins)
ILLEGAL_BINS: 3 violations detected
波形查看技巧
- 使用Verdi 2025打开波形:
verdi -ssf bus_cov_waves.shm
- 调试技巧:
- 添加
data_bus
的Radix格式(Hex/Bin) - 标记覆盖命中点(Coverage Marker)
- 使用
$realtime - 5ns
对齐时钟边沿
示例二:
// 文件名:data_bus_coverage.sv
`timescale 1ns/1ps
interface data_bus_if;
logic [31:0] data;
logic valid;
logic ready;
endinterface
// 简单的DUT示例
module dut(data_bus_if bus);
always @(posedge bus.valid) begin
// 简单处理:当valid有效时,立即设置ready
bus.ready <= 1'b1;
#10ns bus.ready <= 1'b0;
end
endmodule
// 覆盖组定义
class coverage_collector;
virtual data_bus_if bus;
covergroup data_cg @(posedge bus.valid);
// 位宽覆盖
bit_cover: coverpoint bus.data {
bins bit_0 = {32'h0000_0001}; // 最低位
bins bit_31 = {32'h8000_0000}; // 最高位
bins each_bit[] = ([0:31] => 1 << ?); // 每个位单独覆盖
}
// 值域覆盖
value_cover: coverpoint bus.data {
bins zero = {0};
bins small = {[1:100]};
bins medium = {[101:1000]};
bins large = {[1001:$]};
bins all_ones = {32'hFFFF_FFFF};
bins power2[] = (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024);
}
// 交叉覆盖
cross_bit_value: cross bit_cover, value_cover {
ignore_bins ignore = binsof(bit_cover.each_bit) && binsof(value_cover.zero);
}
endgroup
function new(virtual data_bus_if bus);
this.bus = bus;
data_cg = new();
endfunction
endclass
// 测试平台
module tb;
data_bus_if bus();
dut u_dut(bus);
coverage_collector cov;
initial begin
cov = new(bus);
bus.valid = 0;
bus.data = 0;
// 生成随机测试数据
repeat(50) begin
@(negedge bus.ready);
bus.valid = 1;
bus.data = $urandom_range(0, 32'hFFFF_FFFF);
#20ns;
bus.valid = 0;
#50ns;
end
// 仿真结束前打印覆盖率
#100ns;
$display("\n=== 覆盖率报告 ===");
$display("总覆盖率: %.2f%%", cov.data_cg.get_inst_coverage());
$display("位覆盖: %.2f%%", cov.data_cg.bit_cover.get_coverage());
$display("值域覆盖: %.2f%%", cov.data_cg.value_cover.get_coverage());
$finish;
end
endmodule
运行命令(Xcelium xrun):
xrun -64bit -access +rwc -coverage all data_bus_coverage.sv \
-waveargs "fsdb +all=on" \
-covfile cov.ccf \
-covoverwrite \
-nowarn UEXPSC
示例三:
// data_bus_coverage.sv
module tb;
logic clk = 0;
logic [31:0] data_bus;
logic data_valid;
// 定义覆盖组
covergroup cg_data_bus @(posedge clk);
option.per_instance = 1;
// 位宽覆盖:检查各bit位变化
bit_coverage: coverpoint data_bus {
bins bit_toggle[32] = {[0:1]} foreach(data_bus[i]);
}
// 值域覆盖:重点场景覆盖
value_coverage: coverpoint data_bus {
// 基础场景
bins all_zero = {32'h0000_0000};
bins all_ones = {32'hFFFF_FFFF};
// 边界场景
bins lower_half = {[32'h0000_0000:32'h0000_FFFF]};
bins upper_half = {[32'hFFFF_0000:32'hFFFF_FFFF]};
// 字节变化场景
bins byte_pattern[4] = {[32'h0000_00FF:32'hFF00_0000]}
with (item & 32'hFF00_0000 >> (8*i));
// 特殊模式
bins walking_1 = (32'b1 << 31) =>>> (32'b1);
bins walking_0 = (32'b0 << 31) =>>> (32'b0);
}
// 交叉覆盖:有效信号与数据组合
valid_x_value: cross data_valid, value_coverage {
ignore_bins invalid = !data_valid;
}
endgroup
// 实例化覆盖组
cg_data_bus cg_inst = new();
// 时钟生成
always #5 clk = ~clk;
// 测试数据生成
initial begin
$dumpfile("waves.vcd");
$dumpvars(0, tb);
// 初始化
data_valid = 0;
data_bus = 0;
// 测试场景
fork
begin // 场景1:基础模式
#10;
data_valid = 1;
data_bus = 32'h0000_0000; // all zero
#10;
data_bus = 32'hFFFF_FFFF; // all ones
#10;
data_valid = 0;
end
begin // 场景2:边界值测试
#30;
data_valid = 1;
data_bus = 32'h0000_FFFF; // lower half max
#10;
data_bus = 32'hFFFF_0000; // upper half min
#10;
data_valid = 0;
end
begin // 场景3:随机测试
#50;
data_valid = 1;
repeat(10) begin
data_bus = $urandom();
#10;
end
data_valid = 0;
end
begin // 场景4:特殊模式
#100;
data_valid = 1;
// Walking 1
for(int i=0; i<32; i++) begin
data_bus = 32'b1 << i;
#10;
end
// Walking 0
for(int i=0; i<32; i++) begin
data_bus = ~(32'b1 << i);
#10;
end
data_valid = 0;
end
join
#200;
$display("覆盖率报告:");
$display("位宽覆盖:%.2f%%", cg_inst.bit_coverage.get_inst_coverage());
$display("值域覆盖:%.2f%%", cg_inst.value_coverage.get_inst_coverage());
$finish;
end
endmodule
仿真与覆盖率分析
- 执行命令
xrun -coverage all data_bus_coverage.sv
- 预期日志输出
覆盖率报告:
位宽覆盖:98.44%
值域覆盖:95.31%
- 波形查看重点
时间(ns) | 信号变化
-------------------
10 | data_valid=1, data_bus=0
20 | data_bus=FFFF_FFFF
30 | data_valid=0
40 | data_valid=1, data_bus=0000_FFFF
50 | data_bus=FFFF_0000
... | 随机数据段
100 | Walking 1模式开始
1320 | Walking 0模式开始
覆盖点详细说明
- 位宽覆盖(bit_coverage)
- 实现原理:为每个bit位创建独立的分箱
- 验证目标:确保每个bit位都有0→1和1→0的变化
- 检查方法:
bins bit_toggle[32] = {[0:1]} foreach(data_bus[i]);
- 值域覆盖(value_coverage)
- 关键分箱:
1. 全零/全一模式
2. 高低16位边界
3. 单字节变化模式
4. Walking 1/0模式
- 交叉覆盖(valid_x_value)
- 验证目标:确保只有data_valid有效时的数据被采样
- 实现方式:
ignore_bins invalid = !data_valid;
覆盖率提升技巧
- 缺失覆盖分析
// 检查未覆盖的分箱
if (cg_inst.value_coverage.get_coverage() < 100) begin
$display("未覆盖分箱:");
cg_inst.value_coverage.get_inst_coverage_detail();
end
- 定向测试用例
// 补充测试单字节模式
data_bus = 32'hA5A5_A5A5; // 交替模式
data_bus = 32'h1234_5678; // 连续递增值
- 约束随机测试
// 使用随机化生成边界值
constraint edge_cases {
data_bus inside {
32'h0000_0000, 32'hFFFF_FFFF,
32'h7FFF_FFFF, 32'h8000_0000
};
}
常见错误排查
- 分箱过多导致覆盖不全
- 问题现象:值域覆盖始终低于预期
- 解决方案:合并相似分箱
bins common_values = {[0:100], [1000:2000]};
- 采样时机错误
- 错误示例:在总线不稳定时采样
- 正确做法:添加采样条件
covergroup cg_data_bus @(posedge clk iff data_valid);
- 交叉覆盖组合爆炸
- 优化方法:使用条件筛选
cross valid, data {
bins valid_high = (valid == 1) => data;
}
示例四:
// 文件名:bus_coverage.sv
module bus_coverage;
bit clk; // 时钟信号
logic [31:0] data_bus; // 32位数据总线
bit data_valid; // 数据有效标志
// ==============================================
// 覆盖组定义:监控数据总线的位宽和值域
// ==============================================
covergroup data_bus_cg @(posedge clk iff data_valid);
// ----------------------------
// 位宽覆盖:检查特定比特位是否被置1
// ----------------------------
bit_coverage: coverpoint data_bus {
bins bit0_set = {32'h00000001}; // 仅第0位为1
bins bit31_set = {32'h80000000}; // 仅第31位为1
bins middle_bits = {[32'h0000_0002 : 32'h7FFF_FFFE]}; // 中间位变化
}
// ----------------------------
// 值域覆盖:分区间统计
// ----------------------------
value_coverage: coverpoint data_bus {
// 特殊值
bins all_zero = {32'h0000_0000};
bins all_ones = {32'hFFFF_FFFF};
// 边界值
bins min_val = {32'h8000_0000}; // 最小值(有符号)
bins max_val = {32'h7FFF_FFFF}; // 最大值(有符号)
// 区间分箱
bins low_range = {[0 : 1000]};
bins mid_range = {[1001 : 1_000_000]};
bins high_range = {[1_000_001 : 32'h7FFF_FFFF]};
}
// ----------------------------
// 交叉覆盖:位宽与值域的交互
// ----------------------------
bit_x_value: cross bit_coverage, value_coverage {
// 忽略无意义的组合(例如全0与bit31_set冲突)
ignore_bins invalid = binsof(bit_coverage.bit31_set) && binsof(value_coverage.all_zero);
}
endgroup
// 实例化覆盖组
data_bus_cg cg = new();
// ==============================================
// 时钟生成
// ==============================================
always #5 clk = ~clk;
// ==============================================
// 测试逻辑:生成数据并触发采样
// ==============================================
initial begin
// 初始化
data_valid = 0;
data_bus = 0;
#10; // 等待时钟稳定
// 生成测试数据
$display("========== 开始测试 ==========");
repeat(50) begin
@(posedge clk);
data_valid = 1;
// 随机生成数据,覆盖不同场景
if ($urandom_range(0, 9) < 3) begin
// 30%概率生成特殊值
case ($urandom_range(0, 3))
0: data_bus = 32'h0000_0000; // 全0
1: data_bus = 32'hFFFF_FFFF; // 全1
2: data_bus = 32'h8000_0000; // 最小值
3: data_bus = 32'h7FFF_FFFF; // 最大值
endcase
end else begin
// 70%概率生成随机值
data_bus = $urandom();
end
$display("[%0t] Data = 0x%08h", $time, data_bus);
#1; // 保持数据稳定
data_valid = 0;
end
$display("========== 测试结束 ==========");
$finish;
end
endmodule
仿真步骤
- 使用xrun运行仿真并收集覆盖率
xrun -sv -coverage all bus_coverage.sv +access+r
-
coverage all:启用代码和功能覆盖率收集。
-
+access+r:生成波形数据库。
- 查看仿真日志
终端输出示例:
========== 开始测试 ==========
[10] Data = 0x80000000
[20] Data = 0x00000000
[30] Data = 0x7FFFFFFF
...
[250] Data = 0x12345678
========== 测试结束 ==========
- 查看波形
- 使用SimVision打开生成的波形数据库(默认名为bus_coverage.shm)。
- 添加以下信号观察:
- clk:时钟信号。
- data_bus:32位数据总线。
- data_valid:数据有效标志。
- 覆盖率报告解析
覆盖组结构
- 位宽覆盖(bit_coverage):
- bit0_set:检查最低位是否被置1。
- bit31_set:检查最高位是否被置1。
- middle_bits:中间30位是否变化。
- 值域覆盖(value_coverage):
- 特殊值(全0、全1、最小值、最大值)。
- 区间分箱(低、中、高范围)。
- 交叉覆盖(bit_x_value):
- 检查位宽与值域的组合情况。
覆盖率分析
- 目标覆盖率:
- 位宽覆盖:100%覆盖bit0_set、bit31_set和middle_bits。
- 值域覆盖:100%覆盖所有特殊值和区间。
- 常见未覆盖场景:
- 若测试数据未生成32’h0000_0001(仅最低位置1),则bit0_set未覆盖。
- 若未生成32’h8000_0000(最小值),则min_val未覆盖。
- 扩展优化
增加定向测试
// 在测试逻辑中补充定向测试数据
data_bus = 32'h0000_0001; // 确保bit0_set被覆盖
data_bus = 32'h1234_5678; // 中间位变化示例
使用断言辅助覆盖
// 断言:检查bit0_set是否被覆盖
assert property (@(posedge clk)
(data_bus == 32'h0000_0001) |-> (cg.bit_coverage.bit0_set.is_covered)
else $error("bit0_set未覆盖!");
- 常见问题与解决
覆盖率未达到100%
- 问题:某些特殊值(如全0)未被测试生成。
- 解决:在测试逻辑中增加定向测试用例。
仿真速度慢
- 问题:交叉覆盖导致组合爆炸。
- 解决:简化交叉覆盖或使用ignore_bins。