2. 编写第一个UVM测试用例
任务目标
本案例将编写一个最小可运行的UVM测试用例:通过UVM验证环境向一个简单的DUT(组合逻辑加法器)发送一组激励,并自动检查输出是否正确。完成本案例后,你将理解UVM测试用例从启动到结束的完整执行流程。
环境依赖
需要支持UVM-1.2的仿真器(如VCS、QuestaSim、Xcelium)。以下代码基于标准UVM库编写,无需额外安装包。
DUT设计(加法器)
// adder.v - 被测设计:8位加法器
module adder(
input [7:0] a,
input [7:0] b,
output [8:0] sum
);
assign sum = a + b;
endmodule
UVM验证环境与测试用例
// test_adder.sv - 完整UVM测试用例
`include "uvm_macros.svh"
import uvm_pkg::*;
// 1. 事务对象:定义激励与期望值
class adder_trans extends uvm_sequence_item;
rand bit [7:0] a;
rand bit [7:0] b;
bit [8:0] expected_sum;
`uvm_object_utils_begin(adder_trans)
`uvm_field_int(a, UVM_ALL_ON)
`uvm_field_int(b, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "adder_trans");
super.new(name);
endfunction
// 约束:限制随机范围便于人工核对
constraint c_range { a inside {[0:15]}; b inside {[0:15]}; }
endclass
// 2. 驱动器:将事务驱动到DUT接口
class adder_driver extends uvm_driver #(adder_trans);
virtual adder_if vif;
`uvm_component_utils(adder_driver)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
vif.a = req.a;
vif.b = req.b;
#10; // 等待DUT输出稳定
seq_item_port.item_done();
end
endtask
endclass
// 3. 监视器:采样输出并发送给计分板
class adder_monitor extends uvm_monitor;
virtual adder_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);
mon_ap = new("mon_ap", this);
endfunction
task run_phase(uvm_phase phase);
adder_trans trans;
forever begin
@(posedge vif.clk);
trans = adder_trans::type_id::create("trans");
trans.a = vif.a;
trans.b = vif.b;
trans.expected_sum = vif.a + vif.b; // 期望值由DUT输入直接计算
mon_ap.write(trans);
end
endtask
endclass
// 4. 计分板:比较期望值与DUT实际输出
class adder_scoreboard extends uvm_scoreboard;
`uvm_component_utils(adder_scoreboard)
uvm_analysis_imp #(adder_trans, adder_scoreboard) sb_imp;
function new(string name, uvm_component parent);
super.new(name, parent);
sb_imp = new("sb_imp", this);
endfunction
function void write(adder_trans trans);
if (trans.expected_sum !== vif.sum) begin
`uvm_error("SCB", $sformatf("Mismatch! a=%0d b=%0d expected=%0d actual=%0d",
trans.a, trans.b, trans.expected_sum, vif.sum))
end
else begin
`uvm_info("SCB", $sformatf("PASS: a=%0d b=%0d sum=%0d", trans.a, trans.b, vif.sum), UVM_LOW)
end
endfunction
endclass
// 5. 环境:连接各组件
class adder_env extends uvm_env;
adder_driver drv;
adder_monitor mon;
adder_scoreboard scb;
`uvm_component_utils(adder_env)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = adder_driver::type_id::create("drv", this);
mon = adder_monitor::type_id::create("mon", this);
scb = adder_scoreboard::type_id::create("scb", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
mon.mon_ap.connect(scb.sb_imp);
endfunction
endclass
// 6. 序列:生成一组激励
class adder_seq extends uvm_sequence #(adder_trans);
`uvm_object_utils(adder_seq)
function new(string name = "adder_seq");
super.new(name);
endfunction
task body();
repeat (5) begin
adder_trans trans = adder_trans::type_id::create("trans");
start_item(trans);
assert(trans.randomize());
finish_item(trans);
end
endtask
endclass
// 7. 测试用例:顶层入口
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);
super.build_phase(phase);
env = adder_env::type_id::create("env", this);
endfunction
task run_phase(uvm_phase phase);
adder_seq seq = adder_seq::type_id::create("seq");
phase.raise_objection(this);
seq.start(env.drv.seq_item_port);
#100; // 等待最后一个事务完成
phase.drop_objection(this);
endtask
endclass
// 8. 顶层模块:连接接口并启动UVM
module tb_top;
logic clk = 0;
always #5 clk = ~clk;
adder_if vif(clk);
adder dut(.a(vif.a), .b(vif.b), .sum(vif.sum));
initial begin
uvm_config_db#(virtual adder_if)::set(null, "uvm_test_top.env.*", "vif", vif);
run_test("adder_test");
end
endmodule
接口定义
// adder_if.sv - 接口定义
interface adder_if(input logic clk);
logic [7:0] a;
logic [7:0] b;
logic [8:0] sum;
endinterface
运行方式
# 使用QuestaSim运行
vlib work
vlog adder.sv adder_if.sv test_adder.sv
vsim -c work.tb_top -do "run -all; quit"
验证方法与通过标准
运行仿真后,观察UVM报告。通过标准为:
- UVM_INFO消息中显示5条
PASS记录,每条包含a、b和sum值; - UVM_ERROR计数为0,即没有出现
Mismatch错误; - 仿真正常结束,UVM报告显示
UVM_ERROR : 0。
执行流程说明
UVM测试的执行遵循以下固定流程:
run_test("adder_test")创建测试实例并执行build_phase,此时构建环境组件;- 进入
run_phase后,测试用例通过raise_objection阻止仿真退出; - 启动序列,序列随机生成事务并通过
start_item/finish_item发送给驱动器; - 驱动器将事务驱动到DUT接口,监视器采样输入输出并发送给计分板;
- 计分板比较期望值与实际输出,打印PASS或ERROR;
- 所有激励发送完毕后,测试用例调用
drop_objection,仿真结束。
本案例中计分板直接使用DUT输入计算期望值,因此能自动验证加法器功能。若需验证更复杂的DUT,可在事务中增加约束或参考模型来计算期望值。