1. 构建基础UVM测试平台并测量仿真时间

构建基础UVM测试平台并测量仿真时间 学习卡片

任务目标

搭建一个最小可运行的UVM测试平台,包含driver、monitor、scoreboard三个核心组件,驱动一个简单的接口信号,运行一个基本测试,并通过仿真器的运行时选项测量仿真时间,记录该时间作为后续性能优化实验的基线。

环境依赖

需要支持UVM-1.2的仿真器(如VCS、QuestaSim、Xcelium)。以下代码基于QuestaSim 2023.3验证,使用-c命令行模式运行。无需额外安装库,UVM源码随仿真器分发。

完整实现

// file: tb_top.sv
`include "uvm_macros.svh"
import uvm_pkg::*;

// 接口:简单数据通路
interface simple_if(input bit clk);
  logic [7:0] data;
  logic       valid;
  logic       ready;
  clocking cb @(posedge clk);
    output data, valid;
    input  ready;
  endclocking
  modport DRIVER (clocking cb, output data, valid);
  modport MONITOR (input data, valid, ready);
endinterface

// 事务对象
class transaction extends uvm_sequence_item;
  rand bit [7:0] data;
  `uvm_object_utils_begin(transaction)
    `uvm_field_int(data, UVM_ALL_ON)
  `uvm_object_utils_end
  function new(string name = "transaction");
    super.new(name);
  endfunction
endclass

// 序列:产生10个随机事务
class base_seq extends uvm_sequence #(transaction);
  `uvm_object_utils(base_seq)
  function new(string name = "base_seq");
    super.new(name);
  endfunction
  task body();
    repeat(10) begin
      transaction tx = transaction::type_id::create("tx");
      start_item(tx);
      finish_item(tx);
    end
  endtask
endclass

// Driver:驱动接口
class driver extends uvm_driver #(transaction);
  `uvm_component_utils(driver)
  virtual simple_if vif;
  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.cb);
      vif.cb.data <= req.data;
      vif.cb.valid <= 1;
      do @(vif.cb); while (!vif.cb.ready);
      vif.cb.valid <= 0;
      seq_item_port.item_done();
    end
  endtask
endclass

// Monitor:采样接口
class monitor extends uvm_monitor;
  `uvm_component_utils(monitor)
  virtual simple_if vif;
  uvm_analysis_port #(transaction) ap;
  function new(string name, uvm_component parent);
    super.new(name, parent);
    ap = new("ap", this);
  endfunction
  task run_phase(uvm_phase phase);
    forever begin
      @(posedge vif.valid);
      begin
        transaction tx = transaction::type_id::create("tx");
        tx.data = vif.data;
        ap.write(tx);
        @(negedge vif.valid);
      end
    end
  endtask
endclass

// Scoreboard:比较收发数据
class scoreboard extends uvm_scoreboard;
  `uvm_component_utils(scoreboard)
  uvm_analysis_imp #(transaction, scoreboard) imp;
  transaction expected_q[$];
  int match_count;
  function new(string name, uvm_component parent);
    super.new(name, parent);
    imp = new("imp", this);
  endfunction
  function void write(transaction tx);
    if (expected_q.size() > 0) begin
      transaction exp = expected_q.pop_front();
      if (exp.data == tx.data)
        match_count++;
      else
        `uvm_error("SCB", $sformatf("Mismatch: exp=%0d got=%0d", exp.data, tx.data))
    end
  endfunction
  task run_phase(uvm_phase phase);
    // 从driver端获取期望数据(通过analysis port连接)
  endtask
endclass

// 环境
class env extends uvm_env;
  `uvm_component_utils(env)
  driver    d;
  monitor   m;
  scoreboard s;
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    d = driver::type_id::create("d", this);
    m = monitor::type_id::create("m", this);
    s = scoreboard::type_id::create("s", this);
  endfunction
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    // 将driver的seq_item_port连接到sequencer(此处用隐式sequencer)
    // 实际连接在test中完成
  endfunction
endclass

// 测试
class base_test extends uvm_test;
  `uvm_component_utils(base_test)
  env e;
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    e = env::type_id::create("e", this);
    // 设置虚接口
    if (!uvm_config_db#(virtual simple_if)::get(this, "*", "vif", e.d.vif))
      `uvm_fatal("CFG", "Virtual interface not found")
    if (!uvm_config_db#(virtual simple_if)::get(this, "*", "vif", e.m.vif))
      `uvm_fatal("CFG", "Virtual interface not found")
  endfunction
  task run_phase(uvm_phase phase);
    base_seq seq;
    seq = base_seq::type_id::create("seq");
    phase.raise_objection(this);
    seq.start(e.d.seq_item_port);
    #100ns;
    phase.drop_objection(this);
  endtask
endclass

// 顶层模块
module tb_top;
  bit clk;
  always #5 clk = ~clk;
  simple_if vif(clk);
  initial begin
    uvm_config_db#(virtual simple_if)::set(null, "*", "vif", vif);
    run_test("base_test");
  end
  // 驱动时钟和复位
  initial begin
    vif.ready = 1;
    vif.valid = 0;
    vif.data = 0;
  end
endmodule

运行方式

# 编译(QuestaSim)
vlog -sv -uvm tb_top.sv
# 运行并测量仿真时间
vsim -c -uvm -do "run -all; quit" work.tb_top
# 仿真结束后,终端会输出UVM报告摘要,其中包含仿真时间

验证方法与判定标准

运行后观察以下输出:

  • UVM报告显示UVM_INFO,无UVM_ERROR或UVM_FATAL;
  • Scoreboard的match_count等于10(可通过在report_phase中添加打印确认);
  • 仿真时间记录:QuestaSim在run -all结束后,终端会显示Break at或Time:信息,记录该时间值作为基线。例如:Break at /tb_top.sv line 120, Time: 215 ns中的215 ns即为本次仿真的总仿真时间。

判定通过条件:UVM报告无错误且scoreboard匹配计数为10,同时记录下仿真时间数值作为后续实验的基线参考。

常见问题

  • 若出现Fatal: Virtual interface not found,检查uvm_config_db的路径匹配是否正确;
  • 若仿真挂起,检查driver中握手信号是否死锁——本设计中ready恒为1,不会出现该问题;
  • 仿真时间测量以QuestaSim终端输出的Time:字段为准,不同仿真器输出格式略有差异,但均会显示总仿真时间。