2. UVM环境中的接口连接
任务目标
在UVM验证环境中,将DUT的信号级接口与UVM组件正确连接。核心问题是:UVM组件(driver、monitor)运行在仿真器调度域中,无法直接访问DUT的静态信号,必须通过virtual interface将物理接口的句柄传递到UVM组件中,实现驱动与采样。本案例将构建一个最小可运行的UVM环境,包含DUT、接口、driver、monitor及连接验证机制。
环境与依赖
- 仿真器:VCS、QuestaSim或任何支持SystemVerilog/UVM的仿真器
- UVM库:UVM-1.2或UVM-1800.2
- 编译方式:命令行编译,无需额外脚本
实现方案
采用最小完整实现:一个简单的DUT(仅含时钟和数据信号)、一个接口(含时钟生成)、一个driver(驱动数据)、一个monitor(采样数据)以及一个测试类(负责连接与验证)。连接的核心机制是:在test类中通过uvm_config_db#(virtual dut_if)::set()将接口句柄存入全局配置表,driver和monitor在build_phase中通过get()获取该句柄,从而完成连接。
关键完整代码
// 文件: dut_if.sv
interface dut_if(input logic clk);
logic rst_n;
logic [7:0] data_in;
logic [7:0] data_out;
logic valid;
endinterface
// 文件: dut.sv
module dut(dut_if dif);
always_ff @(posedge dif.clk or negedge dif.rst_n) begin
if (!dif.rst_n)
dif.data_out <= 8'h00;
else if (dif.valid)
dif.data_out <= dif.data_in;
end
endmodule
// 文件: uvm_env.sv
`include "uvm_macros.svh"
import uvm_pkg::*;
class my_driver extends uvm_driver #(uvm_sequence_item);
`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 found")
endfunction
task run_phase(uvm_phase phase);
vif.rst_n = 1'b0;
#100 vif.rst_n = 1'b1;
vif.valid = 1'b1;
for (int i = 0; i < 4; i++) begin
vif.data_in = i;
@(posedge vif.clk);
end
vif.valid = 1'b0;
endtask
endclass
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
virtual dut_if vif;
int sampled_data[$];
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 found")
endfunction
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.clk);
if (vif.valid) begin
sampled_data.push_back(vif.data_out);
`uvm_info("MON", $sformatf("Sampled data_out = %0d", vif.data_out), UVM_LOW)
end
end
endtask
endclass
class my_test extends uvm_test;
`uvm_component_utils(my_test)
my_driver driver;
my_monitor monitor;
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);
driver = my_driver::type_id::create("driver", this);
monitor = my_monitor::type_id::create("monitor", this);
if (!uvm_config_db#(virtual dut_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "virtual interface not found in test")
uvm_config_db#(virtual dut_if)::set(this, "driver", "vif", vif);
uvm_config_db#(virtual dut_if)::set(this, "monitor", "vif", vif);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
#500;
phase.drop_objection(this);
endtask
function void report_phase(uvm_phase phase);
if (monitor.sampled_data.size() == 4) begin
`uvm_info("TEST", "PASS: 4 data samples collected", UVM_LOW)
end else begin
`uvm_error("TEST", $sformatf("FAIL: expected 4 samples, got %0d", monitor.sampled_data.size()))
end
endfunction
endclass
// 文件: top.sv
module top;
logic clk;
initial clk = 0;
always #5 clk = ~clk;
dut_if dif(clk);
dut dut_inst(dif);
initial begin
uvm_config_db#(virtual dut_if)::set(null, "uvm_test_top", "vif", dif);
run_test("my_test");
end
endmodule
运行与验证
- 编译(以QuestaSim为例):
vlog -sv dut_if.sv dut.sv uvm_env.sv top.sv
vsim -c -do "run -all; quit" top - 通过条件:仿真结束时,报告阶段输出
PASS: 4 data samples collected,且无UVM_ERROR或UVM_FATAL。 - 连接正确性验证:driver驱动data_in值为0、1、2、3,monitor采样data_out应依次为0、1、2、3(DUT在valid=1时每个时钟上升沿将data_in锁存到data_out)。若monitor采样数量不为4或数据不匹配,说明接口连接或时序存在问题。
常见问题
- 接口句柄获取失败:检查
uvm_config_db的路径是否匹配。顶层使用null作为第一个参数,test中通过this设置,driver/monitor通过this获取,路径必须一致。 - 数据采样不完整:确认driver在valid拉高前已释放复位,且valid与data_in的时序满足DUT的建立保持要求。
- 仿真挂起:检查run_phase中objection的raise/drop是否配对,否则仿真不会自动结束。