UVM 方法学

UVM 入门:它到底解决了什么问题?

从零理解 UVM 为何存在、testbench 的分层结构,以及 uvm_component 的基本写法。

在没有统一方法学之前,不同团队、不同项目的 SystemVerilog testbench 往往各写各的:有的用类封装驱动逻辑,有的直接在 initial 块里堆代码。结果是 testbench 难以复用、难以维护,新人接手成本很高。

UVM(Universal Verification Methodology) 是建立在 SystemVerilog 面向对象特性之上的一套标准化验证方法学,它规定了:

Testbench 的典型分层

一个典型的 UVM testbench 层级大致如下:

uvm_test
  └── uvm_env
        ├── uvm_agent
        │     ├── uvm_sequencer   (产生/管理事务)
        │     ├── uvm_driver      (把事务转换为引脚级信号,驱动 DUT)
        │     └── uvm_monitor     (从引脚采样,还原成事务)
        └── uvm_scoreboard        (比对预期结果与实际结果)

uvm_agent 通常包含 sequencer、driver、monitor 三件套,可以配置为 active(既驱动又监测)或 passive(只监测,常用于总线一侧无需驱动的场景)。

一个最简单的 uvm_component

UVM 中几乎所有结构性部件都继承自 uvm_component。下面是一个只打印日志的最简驱动骨架:

class simple_driver extends uvm_driver #(my_transaction);
  `uvm_component_utils(simple_driver)
 
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
 
  task run_phase(uvm_phase phase);
    forever begin
      my_transaction tr;
      seq_item_port.get_next_item(tr);
      `uvm_info("DRV", $sformatf("driving transaction: %s", tr.convert2string()), UVM_MEDIUM)
      // 此处将 tr 中的字段转换为对 DUT 引脚的实际驱动
      seq_item_port.item_done();
    end
  endtask
endclass

几个关键点:

uvm_component 与 uvm_object 的区别

一个典型的 uvm_agent 通常包含哪三个核心子组件?

以下哪一项是 uvm_component 与 uvm_object 的关键区别?