Quick Start
本指南帮助您在30分钟内搭建一个可运行的UVM验证环境,并完成对FPGA RTL模块的仿真验证。以下是最短路径步骤:
- 步骤1:安装支持UVM-1.2或IEEE 1800.2-2023的仿真器(如QuestaSim 2024.3、VCS 2025.06、Xsim 2025.2)。
- 步骤2:创建工程目录结构(src/rtl, src/tb, scripts, sim)。
- 步骤3:编写一个简单的DUT(例如FIFO或加法器),保存为.sv文件。
- 步骤4:编写UVM testbench顶层,包含interface、transaction、driver、monitor、scoreboard、test。
- 步骤5:编写仿真脚本(Makefile或Tcl),编译所有源文件并运行仿真。
- 步骤6:观察仿真日志,确认testbench启动、sequence发送、scoreboard比对通过。
- 步骤7:修改DUT代码引入一个故意错误,验证scoreboard能捕获并报告失败。
- 步骤8:清理临时文件,将工程纳入版本控制(Git)。
验收点:运行脚本后终端显示“# UVM_INFO @ 0: reporter [RNTST] Running test my_test”和“# UVM_INFO @ 1000: reporter [SCOREBOARD] PASSED: 10 transactions”。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 (xc7a35t) | FPGA验证环境与器件无关,但约束与时序需匹配目标器件 | Intel Cyclone V, Lattice ECP5 |
| EDA版本 | QuestaSim 2024.3 | 原生支持UVM-1.2,编译速度快 | VCS 2025.06, Xsim 2025.2, Riviera-PRO 2024.10 |
| 仿真器 | QuestaSim (vsim) | 用于UVM testbench仿真 | VCS, Xsim, Icarus Verilog (仅支持部分UVM) |
| 时钟/复位 | 100 MHz时钟,异步低有效复位 | DUT与testbench共享时钟 | 50 MHz或其他频率 |
| 接口依赖 | AXI4-Stream或简单Valid-Ready | 便于UVM driver/monitor建模 | APB, AHB-Lite |
| 约束文件 | 无(仿真阶段) | 综合与实现时才需要XDC/SDC | — |
| 操作系统 | Ubuntu 22.04 LTS 或 Windows 11 | 仿真器均支持 | CentOS 7, macOS (有限支持) |
目标与验收标准
完成本指南后,您应能:
- 功能点:UVM testbench能对DUT进行定向与随机激励,自动比对结果。
- 性能指标:仿真速度不低于1000个事务/秒(取决于DUT复杂度)。
- 资源/Fmax:不适用(仿真阶段)。
- 关键波形/日志:仿真日志包含UVM_INFO、UVM_ERROR、UVM_FATAL信息;波形文件(.vcd或.wlf)可查看信号变化。
验收方法:运行完整回归测试,scoreboard报告“PASSED: 1000 transactions, 0 errors”。故意在DUT中插入一个错误,重新仿真应报告“FAILED: 1 error detected”。
实施步骤
阶段1:工程结构
创建以下目录结构:
uvm_fpga_demo/
├── src/
│ ├── rtl/
│ │ └── simple_fifo.sv
│ └── tb/
│ ├── fifo_if.sv
│ ├── fifo_transaction.sv
│ ├── fifo_driver.sv
│ ├── fifo_monitor.sv
│ ├── fifo_scoreboard.sv
│ ├── fifo_env.sv
│ ├── fifo_test.sv
│ └── top_tb.sv
├── scripts/
│ └── run_sim.tcl
└── sim/
└── (仿真输出目录)逐行说明
- 第1行:工程根目录,建议与Git仓库同名。
- 第2行:源代码根目录。
- 第3-4行:RTL设计文件存放位置,simple_fifo.sv是待测模块。
- 第5-12行:UVM testbench文件,每个组件一个文件,便于管理。
- 第13行:仿真脚本目录。
- 第14行:仿真输出目录,编译与运行中间文件放在这里。
阶段2:关键模块
以下是一个简单的同步FIFO DUT(深度16,数据宽度8位):
// src/rtl/simple_fifo.sv
module simple_fifo #(
parameter DEPTH = 16,
parameter WIDTH = 8
)(
input logic clk,
input logic rst_n,
input logic wr_en,
input logic [WIDTH-1:0] wr_data,
output logic full,
input logic rd_en,
output logic [WIDTH-1:0] rd_data,
output logic empty
);
logic [WIDTH-1:0] mem [0:DEPTH-1];
logic [$clog2(DEPTH):0] wr_ptr, rd_ptr;
logic [$clog2(DEPTH):0] count;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr_ptr <= 0;
rd_ptr <= 0;
count <= 0;
end else begin
if (wr_en && !full) begin
mem[wr_ptr] <= wr_data;
wr_ptr <= wr_ptr + 1;
count <= count + 1;
end
if (rd_en && !empty) begin
rd_data <= mem[rd_ptr];
rd_ptr <= rd_ptr + 1;
count <= count - 1;
end
end
end
assign full = (count == DEPTH);
assign empty = (count == 0);
endmodule逐行说明
- 第1行:模块声明,参数化深度和宽度。
- 第2-3行:参数默认值,可在实例化时覆盖。
- 第4-11行:端口列表,包括时钟、复位、写使能、写数据、满标志、读使能、读数据、空标志。
- 第12行:内部存储器,二维数组。
- 第13行:读写指针,位宽为$clog2(DEPTH)+1,用于判断满/空。
- 第14行:计数器,记录当前存储的数据个数。
- 第15行:时序逻辑块,敏感列表为时钟上升沿或复位下降沿。
- 第16-19行:复位逻辑,清零所有指针和计数器。
- 第20-25行:写操作,当wr_en有效且非满时写入数据,指针递增,计数器加1。
- 第26-31行:读操作,当rd_en有效且非空时读出数据,指针递增,计数器减1。
- 第33-34行:组合逻辑赋值满和空标志。
接下来是UVM testbench组件。先定义接口(interface):
// src/tb/fifo_if.sv
interface fifo_if (input logic clk, input logic rst_n);
logic wr_en;
logic [7:0] wr_data;
logic full;
logic rd_en;
logic [7:0] rd_data;
logic empty;
modport DUT (input clk, rst_n, wr_en, wr_data, rd_en,
output full, rd_data, empty);
modport TB (output wr_en, wr_data, rd_en,
input full, rd_data, empty);
endinterface逐行说明
- 第1行:接口声明,包含时钟和复位作为参数。
- 第2-7行:信号声明,与DUT端口对应。
- 第9-10行:DUT modport,方向为DUT视角(输入wr_en等,输出full等)。
- 第11-12行:TB modport,方向为testbench视角(输出wr_en等,输入full等)。
事务类(transaction):
// src/tb/fifo_transaction.sv
class fifo_transaction extends uvm_sequence_item;
rand logic wr_en;
rand logic [7:0] wr_data;
rand logic rd_en;
constraint valid_c {
wr_en dist {0 := 30, 1 := 70}; // 70%概率写
rd_en dist {0 := 30, 1 := 70}; // 70%概率读
}
`uvm_object_utils_begin(fifo_transaction)
`uvm_field_int(wr_en, UVM_ALL_ON)
`uvm_field_int(wr_data, UVM_ALL_ON)
`uvm_field_int(rd_en, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "fifo_transaction");
super.new(name);
endfunction
endclass逐行说明
- 第1行:类声明,继承自uvm_sequence_item。
- 第2-4行:随机变量,对应接口信号。
- 第6-9行:约束,写使能和读使能各有70%概率为1,模拟随机读写。
- 第11-15行:UVM自动化宏,注册字段以便于复制、比较、打印。
- 第17-19行:构造函数。
Driver组件:
// src/tb/fifo_driver.sv
class fifo_driver extends uvm_driver #(fifo_transaction);
`uvm_component_utils(fifo_driver)
virtual fifo_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 fifo_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "Virtual interface not set")
endfunction
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive_transaction(req);
seq_item_port.item_done();
end
endtask
task drive_transaction(fifo_transaction tr);
@(posedge vif.clk);
vif.wr_en <= tr.wr_en;
vif.wr_data <= tr.wr_data;
vif.rd_en <= tr.rd_en;
endtask
endclass逐行说明
- 第1行:类声明,参数化为fifo_transaction。
- 第2行:UVM组件注册宏。
- 第4行:虚拟接口句柄,用于驱动DUT信号。
- 第6-8行:构造函数。
- 第10-13行:build_phase,从config_db获取虚拟接口,若失败则报fatal。
- 第15-20行:run_phase,循环从sequence获取事务并驱动,每次驱动后通知item_done。
- 第22-27行:驱动任务,在时钟上升沿后给接口赋值。
Monitor组件:
// src/tb/fifo_monitor.sv
class fifo_monitor extends uvm_monitor;
`uvm_component_utils(fifo_monitor)
virtual fifo_if vif;
uvm_analysis_port #(fifo_transaction) mon_ap;
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 fifo_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "Virtual interface not set")
mon_ap = new("mon_ap", this);
endfunction
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.clk);
if (vif.wr_en || vif.rd_en) begin
tr = fifo_transaction::type_id::create("tr");
tr.wr_en = vif.wr_en;
tr.wr_data = vif.wr_data;
tr.rd_en = vif.rd_en;
mon_ap.write(tr);
end
end
endtask
fifo_transaction tr;
endclass逐行说明
- 第1行:类声明,继承自uvm_monitor。
- 第4行:虚拟接口句柄。
- 第5行:分析端口,用于发送监测到的事务。
- 第10-14行:build_phase,获取接口并创建分析端口。
- 第16-26行:run_phase,每个时钟上升沿采样,若wr_en或rd_en有效则创建事务对象并写入分析端口。
- 第28行:内部事务对象句柄。
Scoreboard组件(简化版,仅计数):
// src/tb/fifo_scoreboard.sv
class fifo_scoreboard extends uvm_scoreboard;
`uvm_component_utils(fifo_scoreboard)
uvm_analysis_imp #(fifo_transaction, fifo_scoreboard) mon_export;
int pass_count, fail_count;
function new(string name, uvm_component parent);
super.new(name, parent);
mon_export = new("mon_export", this);
endfunction
function void write(fifo_transaction tr);
// 简化:仅检查是否同时写和读(合法),否则计数
if (tr.wr_en && tr.rd_en) begin
pass_count++;
`uvm_info("SCOREBOARD", $sformatf("PASS: wr=%0d rd=%0d", tr.wr_en, tr.rd_en), UVM_LOW)
end else begin
fail_count++;
`uvm_error("SCOREBOARD", $sformatf("FAIL: wr=%0d rd=%0d", tr.wr_en, tr.rd_en))
end
endfunction
function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("SCOREBOARD", $sformatf("PASSED: %0d, FAILED: %0d", pass_count, fail_count), UVM_LOW)
endfunction
endclass逐行说明
- 第1行:类声明,继承自uvm_scoreboard。
- 第4行:分析实现端口,接收monitor发送的事务。
- 第5行:通过和失败计数。
- 第9-11行:构造函数,创建分析实现端口。
- 第13-21行:write函数,每次收到事务时被调用,这里简单判断wr_en和rd_en同时有效则通过,否则失败。实际应比对预期输出。
- 第23-26行:report_phase,仿真结束时打印统计信息。
Env组件:
// src/tb/fifo_env.sv
class fifo_env extends uvm_env;
`uvm_component_utils(fifo_env)
fifo_driver driver;
fifo_monitor monitor;
fifo_scoreboard scoreboard;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driver = fifo_driver::type_id::create("driver", this);
monitor = fifo_monitor::type_id::create("monitor", this);
scoreboard = fifo_scoreboard::type_id::create("scoreboard", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
monitor.mon_ap.connect(scoreboard.mon_export);
endfunction
endclass逐行说明
- 第1行:类声明,继承自uvm_env。
- 第4-6行:子组件句柄。
- 第10-12行:构造函数。
- 第14-18行:build_phase,创建所有子组件。
- 第20-23行:connect_phase,连接monitor的分析端口到scoreboard的导出。
Test组件:
// src/tb/fifo_test.sv
class fifo_test extends uvm_test;
`uvm_component_utils(fifo_test)
fifo_env env;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = fifo_env::type_id::create("env", this);
endfunction
task run_phase(uvm_phase phase);
fifo_sequence seq;
phase.raise_objection(this);
seq = fifo_sequence::type_id::create("seq");
seq.start(env.driver.seq_item_port);
phase.drop_objection(this);
endtask
endclass逐行说明
- 第1行:类声明,继承自uvm_test。
- 第4行:env句柄。
- 第8-10行:构造函数。
- 第12-15行:build_phase,创建env。 <!-- /



