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

运行与验证

  1. 编译(以QuestaSim为例):
    vlog -sv dut_if.sv dut.sv uvm_env.sv top.sv
    vsim -c -do "run -all; quit" top
  2. 通过条件:仿真结束时,报告阶段输出PASS: 4 data samples collected,且无UVM_ERROR或UVM_FATAL。
  3. 连接正确性验证: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是否配对,否则仿真不会自动结束。