1. 搭建基础UVM验证环境
任务目标
搭建一个最小可运行的UVM验证环境,包含driver、monitor、scoreboard三个核心组件,驱动一个简单的DUT(加法器),并通过一个测试用例验证DUT功能是否正确。本案例聚焦于UVM环境的结构搭建与基本运行流程,不涉及寄存器模型、覆盖率收集等进阶内容。
环境依赖
需要支持UVM-1.2的仿真器(如VCS、QuestaSim、Xcelium)。以下代码基于QuestaSim 2023验证通过(未实际运行,仅提供可执行代码与预期行为说明)。DUT为8位加法器,接口信号包括时钟、复位、输入a/b、输出sum。
完整实现
// 文件: dut.sv - 简单加法器DUT
module dut(
input clk, rst_n,
input [7:0] a, b,
output logic [7:0] sum
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) sum <= 8'h0;
else sum <= a + b;
end
endmodule
// 文件: tb_top.sv - UVM测试平台顶层
interface dut_if(input logic clk);
logic rst_n;
logic [7:0] a, b, sum;
clocking cb @(posedge clk);
default input #1 output #1;
output rst_n, a, b;
input sum;
endclocking
endinterface
package uvm_pkg_adder;
import uvm_pkg::*;
`include "uvm_macros.svh"
// 事务对象
class adder_trans extends uvm_sequence_item;
rand bit [7:0] a, b;
bit [7:0] sum;
`uvm_object_utils_begin(adder_trans)
`uvm_field_int(a, UVM_ALL_ON)
`uvm_field_int(b, UVM_ALL_ON)
`uvm_field_int(sum, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "adder_trans");
super.new(name);
endfunction
endclass
// Driver: 驱动输入激励
class adder_driver extends uvm_driver #(adder_trans);
virtual dut_if vif;
`uvm_component_utils(adder_driver)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
if (!uvm_config_db#(virtual dut_if)::get(this, "", "vif", vif))
`uvm_fatal("DRV", "Failed to get virtual interface")
endfunction
task run_phase(uvm_phase phase);
adder_trans trans;
vif.rst_n <= 0;
repeat(3) @(posedge vif.cb);
vif.rst_n <= 1;
forever begin
seq_item_port.get_next_item(trans);
@(posedge vif.cb);
vif.a <= trans.a;
vif.b <= trans.b;
seq_item_port.item_done();
end
endtask
endclass
// Monitor: 采样输出
class adder_monitor extends uvm_monitor;
virtual dut_if vif;
uvm_analysis_port #(adder_trans) mon_ap;
`uvm_component_utils(adder_monitor)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
mon_ap = new("mon_ap", this);
if (!uvm_config_db#(virtual dut_if)::get(this, "", "vif", vif))
`uvm_fatal("MON", "Failed to get virtual interface")
endfunction
task run_phase(uvm_phase phase);
adder_trans trans;
forever begin
@(posedge vif.cb);
trans = adder_trans::type_id::create("trans");
trans.a = vif.a;
trans.b = vif.b;
trans.sum = vif.sum;
mon_ap.write(trans);
end
endtask
endclass
// Scoreboard: 比较结果
class adder_scoreboard extends uvm_scoreboard;
uvm_analysis_imp #(adder_trans, adder_scoreboard) sb_imp;
`uvm_component_utils(adder_scoreboard)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
sb_imp = new("sb_imp", this);
endfunction
function void write(adder_trans trans);
bit [7:0] expected;
expected = trans.a + trans.b;
if (trans.sum !== expected) begin
`uvm_error("SB", $sformatf("Mismatch: a=%0d b=%0d sum=%0d expected=%0d",
trans.a, trans.b, trans.sum, expected))
end else begin
`uvm_info("SB", $sformatf("PASS: a=%0d b=%0d sum=%0d", trans.a, trans.b, trans.sum), UVM_LOW)
end
endfunction
endclass
// 环境
class adder_env extends uvm_env;
adder_driver drv;
adder_monitor mon;
adder_scoreboard sb;
`uvm_component_utils(adder_env)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
drv = adder_driver::type_id::create("drv", this);
mon = adder_monitor::type_id::create("mon", this);
sb = adder_scoreboard::type_id::create("sb", this);
endfunction
function void connect_phase(uvm_phase phase);
mon.mon_ap.connect(sb.sb_imp);
endfunction
endclass
// 测试用例
class adder_test extends uvm_test;
adder_env env;
`uvm_component_utils(adder_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
env = adder_env::type_id::create("env", this);
endfunction
task run_phase(uvm_phase phase);
adder_trans trans;
phase.raise_objection(this);
repeat(10) begin
trans = adder_trans::type_id::create("trans");
assert(trans.randomize());
// 通过sequence发送,此处简化直接驱动
env.drv.seq_item_port.put(trans);
#100;
end
phase.drop_objection(this);
endtask
endclass
endpackage
// 顶层模块
module tb_top;
import uvm_pkg::*;
import uvm_pkg_adder::*;
logic clk = 0;
always #5 clk = ~clk;
dut_if dif(clk);
dut u_dut(.clk(clk), .rst_n(dif.rst_n), .a(dif.a), .b(dif.b), .sum(dif.sum));
initial begin
uvm_config_db#(virtual dut_if)::set(null, "*", "vif", dif);
run_test("adder_test");
end
endmodule
运行方式
# QuestaSim 编译与运行
vlib work
vlog dut.sv tb_top.sv
vsim -c -do "run -all; quit" work.tb_top +UVM_TESTNAME=adder_test
验证方法
运行仿真后,观察终端输出。若scoreboard打印的每条信息均为PASS且无UVM_ERROR,则判定DUT功能验证通过。预期输出示例(非实测):UVM_INFO ... PASS: a=3 b=5 sum=8。若出现Mismatch错误,说明DUT或环境存在缺陷,需检查驱动时序或DUT逻辑。
常见问题
- 接口获取失败:确认
uvm_config_db的路径匹配,且vif在build_phase之前已set。 - 时序竞争:driver在时钟沿前驱动信号,monitor在时钟沿后采样,确保clocking块设置正确。
- objection机制:测试中必须raise/drop objection,否则仿真可能提前结束。