一键导入
uvvm-verification
UVM 验证方法论技能 - 从验证计划到功能覆盖率,完整 UVM 验证平台开发
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
UVM 验证方法论技能 - 从验证计划到功能覆盖率,完整 UVM 验证平台开发
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
物理设计全流程技能 - 从floorplan到GDSII,完整数字芯片物理设计指导
芯片架构设计技能 - 从需求定义到模块划分,完整指导芯片架构开发
EDA工具脚本开发技能 - TCL/Python/Makefile 自动化流程最佳实践
功能安全分析技能 - FMEDA 分析、安全机制设计、ISO 26262 符合
静态时序分析技能 - 时序诊断、收敛优化、签收标准
功耗分析和优化技能 - 功耗计算、IR降分析、电迁移分析、低功耗优化
基于 SOC 职业分类
| name | uvvm-verification |
| description | UVM 验证方法论技能 - 从验证计划到功能覆盖率,完整 UVM 验证平台开发 |
| description.zh | UVM 验证方法论技能 - 从验证计划到功能覆盖率,完整 UVM 验证平台开发 |
| origin | ICER |
| categories | verification |
当用户需要开发 UVM 验证平台时,启用此技能。遵循 IEEE 标准 UVM,一步一步完成验证开发,从验证计划到功能覆盖率。
| 覆盖率类型 | 目标值 | 说明 |
|---|---|---|
| 行覆盖率 | ≥ 95% | 所有语句至少执行一次 |
| 条件覆盖率 | ≥ 90% | 所有条件分支至少执行一次 |
| 翻转覆盖率 | ≥ 85% | 所有寄存器位翻转 |
| FSM 覆盖率 | 100% | 所有状态和转换 |
| 功能覆盖率 | 100% | 所有功能点覆盖 |
| 设计复杂度 | 代码行数 | 验证时间 | 验证人员 |
|---|---|---|---|
| 小型模块 | < 1K 行 | 2-4 周 | 1 人 |
| 中型模块 | 1K-10K 行 | 1-3 月 | 1-2 人 |
| 大型模块 | 10K-50K 行 | 3-6 月 | 2-4 人 |
| 子系统 | 50K-200K 行 | 6-12 月 | 4-8 人 |
| SoC | > 200K 行 | 12-24 月 | 8-20 人 |
| 设计阶段 | 测试用例数 | 回归时间 | 频率 |
|---|---|---|---|
| 功能开发 | 100-500 | < 1 小时 | 每次提交 |
| 功能完成 | 500-2000 | 1-4 小时 | 每日 |
| 回归阶段 | 2000-10000 | 4-24 小时 | 每周 |
| 流片前 | > 10000 | > 24 小时 | 每周 |
| 阶段 | Bug/KLOC | 说明 |
|---|---|---|
| 功能开发 | 5-10 | 正常 |
| 功能完成 | 1-3 | 逐步减少 |
| 回归阶段 | 0.1-0.5 | 大部分已修复 |
| 流片前 | < 0.01 | 几乎无新 Bug |
诊断步骤:
1. 分析覆盖率报告
2. 识别未覆盖的代码/功能
3. 检查测试用例
4. 检查约束
判断标准:
| 覆盖率 | 判断 | 行动 |
|---|---|---|
| > 95% | 良好 | 继续 |
| 90-95% | 接近 | 补充测试用例 |
| 80-90% | 不足 | 检查约束 |
| < 80% | 严重 | 重新评估验证策略 |
根本原因定位:
| 根本原因 | 诊断特征 | 解决方案 |
|---|---|---|
| 约束过紧 | 随机值不发散 | 放宽约束 |
| 测试用例不足 | 功能点未覆盖 | 补充测试用例 |
| 死代码 | 无法覆盖 | 检查 RTL |
| 边界条件遗漏 | 边界未覆盖 | 添加边界测试 |
诊断步骤:
1. 检查仿真时间
2. 查看波形
3. 检查 UVM phase
4. 检查 objection
根本原因定位:
| 根本原因 | 诊断特征 | 解决方案 |
|---|---|---|
| 死锁 | 等待信号不变化 | 检查握手协议 |
| 无限循环 | 时间不推进 | 检查循环条件 |
| Objection 泄漏 | phase 不结束 | 检查 objection |
| 事件未触发 | 等待条件不满足 | 检查事件触发 |
诊断步骤:
1. 分析错误日志
2. 分类错误类型
3. 定位错误模块
4. 分析错误原因
根本原因定位:
| 根本原因 | 诊断特征 | 解决方案 |
|---|---|---|
| 规格理解错误 | 测试用例错误 | 重新理解规格 |
| RTL Bug | 波形不正确 | 修复 RTL |
| 测试平台错误 | 预期值错误 | 修复测试平台 |
| 时序问题 | 竞争条件 | 修复时序 |
诊断步骤:
1. 分析仿真时间分布
2. 检查日志输出
3. 检查覆盖率收集
4. 检查波形转储
根本原因定位:
| 根本原因 | 诊断特征 | 解决方案 |
|---|---|---|
| 日志过多 | 打印时间占比高 | 减少日志 |
| 波形转储 | 磁盘 I/O 高 | 仅转储关键信号 |
| 覆盖率开销 | 内存占用高 | 优化覆盖率 |
| 设计效率低 | 仿真时间长 | 优化模型 |
验证开始前必须制定验证计划,明确验证范围和目标。
| 内容 | 说明 |
|---|---|
| DUT 功能描述 | 简要说明 DUT 要实现的功能 |
| 功能点列表 | 所有需要验证的功能点,枚举 |
| 测试类型 | 定向测试/随机测试/约束随机 |
| 覆盖率目标 | 代码覆盖率目标,功能覆盖率目标 |
| 收敛标准 | 达到什么标准验证完成 |
对于一个异步 FIFO,功能点包括:
代码覆盖率:
- 行覆盖率 >= 90%
- 条件覆盖率 >= 80%
- 翻转覆盖率 >= 85%
- FSM 覆盖率 = 100%
功能覆盖率:
- 所有功能点 100% 覆盖
根据验证计划制定验证方案。
标准 UVM 验证平台结构:
testbench/
├── tb_top.sv # 测试平台顶层(例化DUT和接口)
├── interfaces/ # DUT接口定义
│ └── bus_if.sv
├── agents/ # agent (driver + monitor + sequencer)
│ ├── driver.sv
│ ├── monitor.sv
│ ├── sequencer.sv
│ └── agent.sv
├── sequences/ # 激励序列
│ ├── base_seq.sv
│ ├── read_seq.sv
│ ├── write_seq.sv
│ └── stress_seq.sv
├── models/ # 参考模型
│ └── ref_model.sv
├── scoreboards/ # 结果比较
│ └── scoreboard.sv
├── coverage/ # 功能覆盖率收集
│ └── coverage.sv
├── tests/ # 测试用例
│ ├── base_test.sv
│ ├── sanity_test.sv
│ └── stress_test.sv
└── env.sv # 环境顶层
这个结构分工清晰,方便复用。
每个功能点对应一个测试点,明确测试什么,怎么测试。
测试点 ID: TP-001
测试点名称: 异步FIFO满状态测试
测试点描述: 当FIFO满了,写请求应该被忽略,满信号应该正确输出
测试方法: 连续写直到满,检查满信号,检查数据正确
优先级: P1
覆盖分组: 边界条件
每个测试点对应功能覆盖率覆盖点:
covergroup fifo_cg {
option.per_instance = 1;
empty: coverpoint fifo_empty {
bins empty = {1};
bins not_empty = {0};
}
full: coverpoint fifo_full {
bins full = {1};
bins not_full = {0};
}
half_full: coverpoint fifo_counter {
bins low[] = {[0:DEPTH/2-1], [DEPTH/2+1:DEPTH-1]};
bins mid = {DEPTH/2};
}
}
验证环境搭建需要按顺序完成:接口 → 事务 → Driver → Monitor → Agent → Env → Test。
做什么: 定义 DUT 与验证环境的连接接口。
接口编写模板:
interface dut_if #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst_n
);
// 时钟和复位
logic clk;
logic rst_n;
// 输入信号
logic [ADDR_WIDTH-1:0] addr;
logic [DATA_WIDTH-1:0] wdata;
logic wr_en;
logic rd_en;
// 输出信号
logic [DATA_WIDTH-1:0] rdata;
logic ready;
logic error;
// 时钟块(用于同步)
clocking cb @(posedge clk);
default input #1ns output #1ns;
output addr, wdata, wr_en, rd_en;
input rdata, ready, error;
endclocking
// modport for DUT
modport dut (
input clk, rst_n, addr, wdata, wr_en, rd_en,
output rdata, ready, error
);
// modport for Driver
modport driver (
clocking cb,
output rst_n
);
// modport for Monitor
modport monitor (
clocking cb
);
endinterface
接口编写检查清单:
做什么: 定义验证环境中传递的数据结构。
事务编写模板:
class dut_trans extends uvm_sequence_item;
`uvm_object_utils(dut_trans)
// 随机化字段
rand bit [31:0] addr;
rand bit [31:0] data;
rand bit wr_en;
rand bit rd_en;
// 约束
constraint c_addr_align {
addr[1:0] == 2'b00; // 4字节对齐
}
constraint c_valid_trans {
wr_en != rd_en; // 读写互斥
}
// 覆盖点
covergroup trans_cg;
cp_addr: coverpoint addr {
bins low = {[0:32'h0000FFFF]};
bins mid = {[32'h00010000:32'hFFFF0000]};
bins high = {[32'hFFFF0001:32'hFFFFFFFF]};
}
cp_wr: coverpoint wr_en;
cp_rd: coverpoint rd_en;
endgroup
function new(string name = "dut_trans");
super.new(name);
trans_cg = new();
endfunction
function void post_randomize();
trans_cg.sample();
endfunction
// 复制、比较、打印方法
virtual function void do_copy(uvm_object rhs);
dut_trans rhs_;
super.do_copy(rhs);
$cast(rhs_, rhs);
addr = rhs_.addr;
data = rhs_.data;
wr_en = rhs_.wr_en;
rd_en = rhs_.rd_en;
endfunction
virtual function bit do_compare(uvm_object rhs, uvm_comparer comparer);
dut_trans rhs_;
bit same = super.do_compare(rhs, comparer);
$cast(rhs_, rhs);
same = (addr == rhs_.addr) &&
(data == rhs_.data) &&
(wr_en == rhs_.wr_en) &&
(rd_en == rhs_.rd_en);
return same;
endfunction
virtual function string convert2string();
return $sformatf("addr=0x%8h, data=0x%8h, wr=%b, rd=%b",
addr, data, wr_en, rd_en);
endfunction
endclass
事务编写检查清单:
做什么: 将事务转换为 DUT 接口信号。
Driver 编写模板:
class dut_driver extends uvm_driver #(dut_trans);
`uvm_component_utils(dut_driver)
virtual dut_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual dut_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("build_phase", "Failed to get vif")
end
endfunction
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive_trans(req);
seq_item_port.item_done();
end
endtask
virtual task drive_trans(dut_trans trans);
// 等待时钟沿
@(vif.cb);
// 驱动信号
vif.cb.addr <= trans.addr;
vif.cb.data <= trans.data;
vif.cb.wr_en <= trans.wr_en;
vif.cb.rd_en <= trans.rd_en;
// 等待响应
wait(vif.cb.ready);
`uvm_info("drive_trans", trans.convert2string(), UVM_HIGH)
endtask
endclass
Driver 编写检查清单:
做什么: 观察接口信号,转换为事务。
Monitor 编写模板:
class dut_monitor extends uvm_monitor;
`uvm_component_utils(dut_monitor)
virtual dut_if vif;
uvm_analysis_port #(dut_trans) ap;
function new(string name, uvm_component parent);
super.new(name, parent);
ap = new("ap", this);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual dut_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("build_phase", "Failed to get vif")
end
endfunction
virtual task run_phase(uvm_phase phase);
forever begin
collect_trans();
end
endtask
virtual task collect_trans();
dut_trans trans;
// 等待有效信号
wait(vif.cb.ready);
// 采样信号
trans = dut_trans::type_id::create("trans");
trans.addr = vif.cb.addr;
trans.data = vif.cb.data;
trans.wr_en = vif.cb.wr_en;
trans.rd_en = vif.cb.rd_en;
// 发送到 analysis port
ap.write(trans);
`uvm_info("collect_trans", trans.convert2string(), UVM_HIGH)
endtask
endclass
Monitor 编写检查清单:
做什么: 封装 Driver、Monitor、Sequencer。
Agent 编写模板:
class dut_agent extends uvm_agent;
`uvm_component_utils(dut_agent)
dut_driver driver;
dut_sequencer sequencer;
dut_monitor monitor;
virtual dut_if vif;
// 配置:ACTIVE 或 PASSIVE
uvm_active_passive_enum is_active = UVM_ACTIVE;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 获取配置
if (!uvm_config_db#(virtual dut_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("build_phase", "Failed to get vif")
end
// 创建 Monitor(总是创建)
monitor = dut_monitor::type_id::create("monitor", this);
uvm_config_db#(virtual dut_if)::set(this, "monitor", "vif", vif);
// 根据 is_active 决定是否创建 Driver 和 Sequencer
if (is_active == UVM_ACTIVE) begin
driver = dut_driver::type_id::create("driver", this);
sequencer = dut_sequencer::type_id::create("sequencer", this);
uvm_config_db#(virtual dut_if)::set(this, "driver", "vif", vif);
end
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// 连接 Driver 和 Sequencer
if (is_active == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction
endclass
Agent 编写检查清单:
做什么: 组装所有组件。
Environment 编写模板:
class dut_env extends uvm_env;
`uvm_component_utils(dut_env)
dut_agent agent;
dut_scoreboard scoreboard;
dut_coverage coverage;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent = dut_agent::type_id::create("agent", this);
scoreboard = dut_scoreboard::type_id::create("scoreboard", this);
coverage = dut_coverage::type_id::create("coverage", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// Monitor → Scoreboard
agent.monitor.ap.connect(scoreboard.analysis_export);
// Monitor → Coverage
agent.monitor.ap.connect(coverage.analysis_export);
endfunction
endclass
Environment 编写检查清单:
做什么: 定义测试场景,配置验证环境。
Test 编写模板:
class dut_base_test extends uvm_test;
`uvm_component_utils(dut_base_test)
dut_env env;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = dut_env::type_id::create("env", this);
// 配置
uvm_config_db#(uvm_active_passive_enum)::set(
this, "env.agent", "is_active", UVM_ACTIVE
);
endfunction
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
// 打印拓扑
uvm_top.print_topology();
endfunction
virtual task run_phase(uvm_phase phase);
dut_default_seq seq;
phase.raise_objection(this, "Starting test");
seq = dut_default_seq::type_id::create("seq");
seq.start(env.agent.sequencer);
phase.drop_objection(this, "Test finished");
endtask
endclass
Test 编写检查清单:
在 coverage 组件收集:
class fifo_coverage extends uvm_component;
`uvm_component_utils(fifio_coverage)
uvm_analysis_export #(fifo_trans) analysis_export;
covergroup fifo_function_cg;
// 所有功能点覆盖
endgroup
function new(string name, uvm_component parent);
super.new(name, parent);
analysis_export = new("analysis_export", this);
fifo_function_cg = new();
endfunction
function void write(fifo_trans t);
fifo_function_cg.sample();
endfunction
endclass
所有组件和事务都必须工厂注册:
// 组件注册
`uvm_component_utils(my_agent)
// 事务注册
`uvm_object_utils(my_transaction)
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_object_utils_end
遵循 UVM phase 顺序:
| Phase | 用途 |
|---|---|
| build_phase | 创建所有子组件 |
| connect_phase | 连接 TLM 端口 |
| end_of_elaboration | 最后调整,打印配置 |
| run_phase | 运行测试,产生激励 |
| extract_phase | 提取覆盖率和结果 |
| check_phase | 检查结果正确性 |
| report_phase | 打印报告统计 |
✅ 永远:每个 phase 必须调用 super.phase()
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase); // 必须调用父类
// 你的代码...
endfunction
使用 uvm_config_db 传递配置,不要全局变量:
// 发送配置:在 test
uvm_config_db#(virtual fifo_if)::set(this, "*env.agent*", "fifo_vif", fifo_if);
// 获取配置:在 agent
if (!uvm_config_db#(virtual fifo_if)::get(this, "", "fifo_vif", vif)) begin
`uvm_fatal(get_full_name(), "Failed to get fifo_vif from config db");
end
验证平台完成检查:
super.phase()uvm_config_db 传递验证环境搭建完成后,需要进行调试确保正确性。
常见编译错误:
| 错误类型 | 原因 | 解决方法 |
|---|---|---|
| 未定义类型 | 缺少 include | 添加 ``include "file.sv"` |
| 工厂注册缺失 | 缺少 uvm_object_utils | 添加工厂注册宏 |
| 端口不匹配 | TLM 端口类型错误 | 检查 uvm_analysis_port vs uvm_analysis_export |
| 参数不匹配 | 参数化类型错误 | 检查 #(TRANS) 参数 |
检查连接正确性:
// 在 end_of_elaboration_phase 打印拓扑
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction
检查项:
调试方法:
// 设置日志级别
set_report_verbosity_level_hier(UVM_HIGH);
// 设置特定组件日志
env.agent.driver.set_report_verbosity_level(UVM_DEBUG);
# VCS
fsdbDumpfile("wave.fsdb");
fsdbDumpvars(0, tb_top);
# Xcelium
probe -create tb_top -depth all
// 检查 objection 状态
virtual function void phase_ready_to_end(uvm_phase phase);
if (phase.get_name() == "run") begin
`uvm_info("objection", "Run phase ending", UVM_LOW)
end
endfunction
| 问题现象 | 可能原因 | 诊断方法 | 解决方法 |
|---|---|---|---|
| 仿真立即结束 | Objection 未 raise | 检查 run_phase | 添加 phase.raise_objection |
| 仿真挂起 | Objection 未 drop | 检查 objection 计数 | 确保 drop_objection 被调用 |
| 无激励产生 | Sequence 未启动 | 检查 sequencer 连接 | 检查 seq.start(env.agent.sequencer) |
| 数据不正确 | Monitor 采样时机错误 | 查看波形 | 使用 clocking block |
| 覆盖率不更新 | 覆盖点未 sample | 检查 sample 调用 | 添加 trans_cg.sample() |
| 回归类型 | 测试用例数 | 运行频率 | 预期时间 |
|---|---|---|---|
| Smoke 测试 | 10-50 | 每次提交 | < 10 分钟 |
| Nightly 测试 | 100-500 | 每晚 | 1-4 小时 |
| 完整回归 | 500-2000 | 每周 | 4-24 小时 |
| Tapeout 测试 | > 2000 | 流片前 | > 24 小时 |
测试用例分类:
// 使用 UVM test 进行分类
class smoke_test extends dut_base_test;
// 快速冒烟测试
endclass
class feature_test extends dut_base_test;
// 功能测试
endclass
class regression_test extends dut_base_test;
// 回归测试
endclass
class stress_test extends dut_base_test;
// 压力测试
endclass
运行命令:
# VCS
vcs -sverilog -ntb_opts uvm \
+UVM_TESTNAME=smoke_test \
+UVM_VERBOSITY=UVM_HIGH \
-l run.log
# Xcelium
xrun -sv -uvm \
+UVM_TESTNAME=feature_test \
+UVM_VERBOSITY=UVM_MEDIUM \
-log run.log
合并多个测试的覆盖率:
# VCS
urg -dir simv.vdb -dir test1.vdb -dir test2.vdb \
-report coverage_report
# Xcelium
imc -exec coverage_merge.tcl
覆盖率合并脚本示例:
# coverage_merge.tcl
open_database test1.ucd
merge_database test2.ucd test3.ucd test4.ucd
report_coverage -totals -by_instance
exit
验证完成条件(全部满足才算收敛):
| 检查项 | 标准 | 当前状态 |
|---|---|---|
| 所有测试用例通过 | 100% | [ ] |
| 行覆盖率 | ≥ 95% | [ ] |
| 条件覆盖率 | ≥ 90% | [ ] |
| 翻转覆盖率 | ≥ 85% | [ ] |
| FSM 覆盖率 | 100% | [ ] |
| 功能覆盖率 | 100% | [ ] |
| 所有 Bug 已修复 | 无 open Bug | [ ] |
| 回归测试稳定 | 连续 3 天无新 Bug | [ ] |
覆盖率缺口分析:
1. 运行所有测试用例
2. 合并覆盖率报告
3. 识别未覆盖代码/功能
4. 分析原因
5. 添加测试用例覆盖缺口
覆盖率缺口原因分析:
| 缺口类型 | 可能原因 | 解决方法 |
|---|---|---|
| 死代码 | 不可达代码 | 检查 RTL,可能删除 |
| 边界条件 | 测试用例未覆盖 | 添加边界测试 |
| 异常路径 | 约束过紧 | 放宽约束 |
| 功能遗漏 | 测试计划不完整 | 补充测试计划 |
Bug 趋势指标:
| 指标 | 定义 | 收敛标准 |
|---|---|---|
| Bug 发现率 | 每周新发现 Bug 数 | 持续下降 |
| Bug 修复率 | 已修复 Bug / 总 Bug | > 95% |
| Bug 重开率 | 重开 Bug / 已修复 Bug | < 5% |
| Bug 密度 | Bug 数 / KLOC | < 0.1 |
Bug 趋势图:
Bug 数量
^
| 新发现 Bug
| *
| * *
| * *
| * * ← 期望趋势
|* *
+-------------------> 时间
签收前检查:
| 错误 | 后果 | 修复 |
|---|---|---|
忘记 super.phase() | 父类不执行,组件没创建 | 添加 super.phase() |
| 忘记 objection | 仿真立即结束 | 添加 raise_objection / drop_objection |
在 build_phase 之前 get | 配置还没设置,get 失败 | 在 build_phase 里面 get |
| driver 里面做结果检查 | 破坏分层,难以复用 | 结果检查移到 scoreboard |
| 不检查 get 成功失败 | 失败了还继续,难 debug | 失败 uvm_fatal |
❌ 上帝 sequence:一个 sequence 做完所有事情包括结果检查 → 拆分 sequence 和 scoreboard ❌ 硬编码配置 → 不能配置不同位宽 → 参数化配置 ❌ 嵌套太深 → 超过 5 层嵌套,调试困难 → 合理分层 ❌ 不收集覆盖率 → 认为测试通过就是完成 → 必须达到覆盖率目标 ❌ 日志太多 → 日志文件几 G,打不开 → 合理控制日志级别
verification-engineer 代理进行验证开发code-reviewer 代理进行代码审查assertion-based-verification 技能