Quick Start
- 步骤1:安装支持SystemVerilog的仿真器(如QuestaSim 2024.3+、VCS 2025.06+、Xsim 2025.2+),并确认已包含UVM库(1.2或2.0)。
- 步骤2:创建工程目录结构:
tb/(顶层测试台)、uvm/(UVM环境)、rtl/(待测设计)、scripts/(编译运行脚本)。 - 步骤3:编写最简UVM测试台:包含一个top模块,实例化DUT,生成时钟与复位,通过
run_test()启动UVM。 - 步骤4:编写UVM环境:
my_env继承uvm_env,内部包含my_agent(含driver、monitor、sequencer)和my_scoreboard。 - 步骤5:编写一个简单测试用例
my_test继承uvm_test,在build_phase中创建环境并设置配置对象。 - 步骤6:编写编译脚本(如Makefile或Tcl),编译所有源文件(DUT、UVM库、测试台),运行仿真并观察终端输出“UVM test passed”或波形中信号正确。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T | DUT为典型FPGA设计,用于验证UVM环境通用性 | Intel Cyclone V、Lattice ECP5 |
| EDA版本 | QuestaSim 2024.3 / VCS 2025.06 | 支持UVM 1.2/2.0,SystemVerilog 2017 | Xsim 2025.2(功能受限,建议仅用于基本测试) |
| 仿真器 | QuestaSim(含UVM库) | 内置UVM 1.2,无需额外安装 | VCS(需指定-uvm开关)、IUS |
| 时钟/复位 | 100 MHz时钟,异步低电平复位 | DUT典型接口,UVM环境中使用uvm_clock或直接生成 | 可改为同步复位,但UVM环境需相应调整 |
| 接口依赖 | AXI4-Lite或APB(简单控制总线) | UVM agent需实现对应协议驱动 | 自定义握手协议(需自行编写driver) |
| 约束文件 | 无(纯仿真) | UVM验证环境不涉及时序约束 | 若需后仿,需SDC文件 |
目标与验收标准
- 功能点:UVM环境能正确驱动DUT输入、监测输出、与参考模型比对,发现至少一个故意注入的错误(如数据位反转)。
- 性能指标:仿真速度不低于1 kHz(即每毫秒仿真时间≤1秒墙钟时间,以典型DUT规模为准)。
- 资源/Fmax:不适用(纯仿真)。
- 关键波形/日志:终端输出“UVM_INFO @ 1000 ns: [my_test] Test passed”;波形中DUT输出与预期一致。
实施步骤
阶段1:工程结构搭建
- 创建目录:
rtl/放DUT(如counter.v),tb/放top.sv,uvm/放UVM组件,scripts/放编译脚本。 - 编写
top.sv:实例化DUT,生成时钟(50%占空比,周期10 ns),生成复位(0–100 ns低电平,之后高电平),调用run_test()。 - 编写编译脚本(Makefile):
compile: vlog -work work rtl/*.v uvm/*.sv tb/*.svrun: vsim -c -do "run -all; quit" work.top
阶段2:关键UVM模块编写
// uvm/my_env.sv
class my_env extends uvm_env;
`uvm_component_utils(my_env)
my_agent agent;
my_scoreboard sb;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent = my_agent::type_id::create("agent", this);
sb = my_scoreboard::type_id::create("sb", this);
endfunction
function void connect_phase(uvm_phase phase);
agent.monitor.ap.connect(sb.analysis_export);
endfunction
endclass逐行说明
- 第1行:定义类
my_env,继承自uvm_env,是UVM环境容器。 - 第2行:使用UVM工厂注册宏,使该类可被工厂创建和覆盖。
- 第3-4行:声明agent和scoreboard句柄。
- 第5-7行:构造函数,调用父类构造函数。
- 第9-12行:
build_phase中创建子组件,使用type_id::create方法(工厂方式)。 - 第14-16行:
connect_phase中将monitor的分析端口连接到scoreboard的analysis_export,实现数据传递。
// uvm/my_agent.sv
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
my_driver driver;
my_monitor monitor;
my_sequencer sequencer;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driver = my_driver::type_id::create("driver", this);
monitor = my_monitor::type_id::create("monitor", this);
sequencer = my_sequencer::type_id::create("sequencer", this);
endfunction
function void connect_phase(uvm_phase phase);
driver.seq_item_port.connect(sequencer.seq_item_export);
endfunction
endclass逐行说明
- 第1行:定义agent类,继承自
uvm_agent。 - 第2行:工厂注册。
- 第3-5行:声明driver、monitor、sequencer。
- 第7-9行:构造函数。
- 第11-16行:在
build_phase中创建三个子组件。 - 第18-20行:在
connect_phase中将driver的seq_item_port连接到sequencer的seq_item_export,实现sequence与driver通信。
// uvm/my_driver.sv
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
virtual dut_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db #(virtual dut_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "Virtual interface not set");
endfunction
task run_phase(uvm_phase phase);
my_transaction tx;
forever begin
seq_item_port.get_next_item(tx);
drive_transaction(tx);
seq_item_port.item_done();
end
endtask
task drive_transaction(my_transaction tx);
@(posedge vif.clk);
vif.data_in <= tx.data;
vif.valid <= 1;
@(posedge vif.clk);
vif.valid <= 0;
endtask
endclass逐行说明
- 第1行:定义参数化driver,参数为事务类型
my_transaction。 - 第2行:工厂注册。
- 第3行:声明虚拟接口句柄,用于驱动DUT信号。
- 第5-7行:构造函数。
- 第9-12行:在
build_phase中通过uvm_config_db获取虚拟接口,若未设置则报fatal错误。 - 第14-21行:
run_phase中循环从sequencer获取事务,调用drive_transaction驱动,完成后通知item_done。 - 第23-29行:
drive_transaction任务,在时钟上升沿驱动数据和控制信号。
阶段3:时序/CDC/约束
- 时序:纯仿真无需时序约束,但DUT接口时序需与driver/monitor对齐。建议在
top.sv中生成时钟和复位时使用always #5 clk = ~clk;(周期10 ns)。 - CDC:若DUT包含跨时钟域逻辑,UVM环境中需使用
uvm_clock或uvm_sync组件处理。简单情况下,在monitor中采样时使用@(posedge vif.clk)即可。 - 约束:无(纯仿真)。
阶段4:验证与调试
- 编写测试用例
my_test:在build_phase中设置虚拟接口到config_db,创建环境,在run_phase中启动sequence。 - 运行仿真:使用
vsim -c -do "run -all; quit" work.top,观察UVM_INFO和UVM_ERROR输出。 - 常见坑1:虚拟接口未设置 → 检查
top.sv中是否在initial块中调用uvm_config_db #(virtual dut_if)::set(...)。 - 常见坑2:UVM库版本不匹配 → 确保仿真器使用UVM 1.2(默认),若使用UVM 2.0需添加
-uvmhome UVM_2_0开关。
阶段5:上板(可选)
- UVM环境通常不直接上板,但可通过生成测试向量文件(如.hex)供FPGA上板测试。在UVM中编写
write_to_file函数,将事务序列写入文件。 - 上板后使用逻辑分析仪(如ChipScope)比对波形与UVM仿真波形。
原理与设计说明
为什么使用UVM而不是传统Verilog测试台? UVM提供了可重用的组件层次(env→agent→driver/monitor/sequencer)、工厂模式(便于覆盖和配置)、以及基于事务的通信(sequence→driver),使得大型验证项目可维护性大幅提升。对于FPGA开发,UVM尤其适用于复杂IP核(如DDR控制器、PCIe)的验证。
关键trade-off:
- 资源 vs Fmax:不适用(纯仿真)。
- 吞吐 vs 延迟:UVM的sequence-driver机制引入少量仿真时间开销(通常<5%),但换来可扩展性。若需高吞吐仿真,可使用
uvm_analysis_port的阻塞与非阻塞模式。 - 易用性 vs 可移植性:UVM 1.2是业界标准,可移植到任何支持SystemVerilog的仿真器;但学习曲线较陡。建议团队至少有一人熟悉UVM架构。
验证与结果
| 指标 | 测量值(示例) | 测量条件 |
|---|---|---|
| 仿真速度 | 1.2 kHz(每毫秒仿真耗时0.83秒墙钟) | QuestaSim 2024.3,DUT为16位计数器,UVM环境含driver+monitor+scoreboard,运行10000个事务 |
| 资源(仿真器内存) | ~150 MB | 同上 |
| 功能覆盖率 | 95%(边界值测试) | 使用UVM功能覆盖率收集,测试用例覆盖所有输入组合 |
故障排查(Troubleshooting)
- 现象:仿真器报告“Unknown identifier ‘uvm_*’” → 原因:未编译UVM库或未正确包含。检查编译命令是否添加
-uvm(VCS)或vlog -uvm(Questa)。 - 现象:
run_test()不执行任何UVM组件 → 原因:测试用例未通过工厂注册或未在命令行指定。检查top.sv中run_test("my_test")或命令行+UVM_TESTNAME=my_test。 - 现象:driver中
get_next_item一直阻塞 → 原因:sequencer未收到sequence请求。检查sequence是否在test的run_phase中启动(seq.start(sequencer))。 - 现象:scoreboard未收到事务 → 原因:monitor的
analysis_port未连接。检查connect_phase中monitor.ap.connect(sb.analysis_export)。 - 现象:仿真速度极慢(<100 Hz) → 原因:UVM中使用了大量
$display或波形记录。减少日志输出,关闭波形记录(-novopt或-noassertdebug)。 - 现象:UVM_FATAL: “Virtual interface not set” → 原因:
uvm_config_db中未设置接口。检查top.sv中set调用是否在run_test之前。 - 现象:波形中DUT信号为X或Z → 原因:驱动时序错误或未初始化。检查driver中是否在时钟沿后赋值,且复位期间驱动已知值。
- 现象:UVM_ERROR: “Sequence item already got” → 原因:driver中调用了两次
get_next_item而未调用item_done。确保每次get_next_item后都有item_done。 - 现象:编译错误“Multiple definitions of my_transaction” → 原因:文件被重复编译。检查编译脚本中是否多次包含同一文件。
- 现象:仿真结果与预期不符 → 原因:参考模型(scoreboard)实现有误。单独测试参考模型,或使用断言(assertion)检查关键时序。
- 现象:UVM 2.0与1.2宏冲突 → 原因:混用了不同版本的UVM库。确保所有源文件使用同一版本,并设置正确的
-uvmhome。 - 现象:仿真器崩溃(Segmentation fault) → 原因:UVM组件中指针错误或无限循环。检查
run_phase中是否有forever循环但缺少退出条件。
扩展与下一步
- 参数化环境:使用UVM配置对象(
uvm_config_db)传递参数,如总线宽度、时钟频率,使环境可重用于不同DUT。 - 带宽提升:对于高速接口(如AXI4-Stream),使用UVM的
uvm_tlm_fifo实现非阻塞传输,提高仿真吞吐。 - 跨平台:将UVM环境与Vivado的Xsim集成,通过Tcl脚本实现一键编译运行。注意Xsim对UVM 2.0支持有限,建议使用UVM 1.2。
- 加入断言与覆盖:在DUT内部嵌入SystemVerilog断言(SVA),在UVM环境中收集功能覆盖率,生成覆盖率报告。
- 形式验证:将UVM环境中的关键属性(如握手协议)提取为形式验证断言,使用JasperGold或VC Formal进行静态验证。
- 回归测试自动化:编写Python脚本,自动运行多个测试用例,比对日志与预期结果,生成回归报告。
参考与信息来源
- Accellera UVM 1.2 Class Reference (2020)
- Mentor Graphics QuestaSim User’s Manual (2024)
- Synopsys VCS UVM User Guide (2025)
- Xilinx Vivado Design Suite User Guide: Logic Simulation (UG900, 2025)
- “UVM for FPGA Designers” – DVCon 2025 Proceedings
- IEEE 1800-2017 SystemVerilog Standard
技术附录
术语表
- UVM:Universal Verification Methodology,通用验证方法学。
- DUT:Design Under Test,待测设计。
- Agent:UVM中封装driver、monitor、sequencer的容器。
- Scoreboard:UVM中用于比对DUT输出与参考模型的组件。
- Sequence:UVM中定义事务序列的类。
- Factory:UVM的工厂模式,支持组件创建和覆盖。
- Config DB:UVM配置数据库,用于传递参数和接口。




