1. 搭建基础UVM验证环境

搭建基础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,否则仿真可能提前结束。