news 2026/7/19 14:40:10

UVM 中 virtual 关键字的使用详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UVM 中 virtual 关键字的使用详解

UVM 中virtual关键字的使用详解

一、概述

在 UVM 验证环境中,virtual关键字出现在两个核心场景:

场景用法作用
虚接口virtual axi_if将 RTL 接口与验证组件解耦
虚方法virtual function/task实现多态(Polymorphism),支撑 UVM 的工厂机制和回调体系

这两个场景看似不同,但背后遵循同一个设计哲学:面向接口编程,依赖抽象而非具体实现


二、virtual interface— 虚接口

2.1 为什么需要虚接口

在 SystemVerilog 中,interface是一个硬件构造(可以包含modportclockingalways块等),而 UVM 组件是软件对象(class)。

// interface 是硬件,不是对象 interface axi_if(input clk, rst_n); logic [31:0] addr; logic [31:0] wdata; logic wr_en; // ... endinterface // UVM 组件是软件 class,不能直接"包含" interface class my_driver extends uvm_driver; // ❌ 不能这样声明 // axi_if vif; // axi_if 不是数据类型,不能用作 class 的成员 endclass

解决方案:使用virtual interface,它本质上是一个句柄,指向一个实际的 interface 实例,可以被赋值、传递、存储在 class 中。

class my_driver extends uvm_driver; virtual axi_if vif; // ✅ 正确:virtual interface 是一个句柄 endclass

2.2 没有virtual会怎样

// ❌ 错误代码 interface my_if; logic [31:0] data; logic valid; endinterface class bad_driver; my_if vif; // 编译错误:interface 不能作为 class 非虚成员 task drive(); vif.data <= 32'h1234; // 永远到不了这里 endtask endclass

编译错误Non-interface Port / Interface not allowed in class non-virtual member

2.3 实际含义对比

// 在 module 中(硬件世界) axi_if vif(); // 实例化一个 interface → 实际生成硬件连线 // 在 class 中(软件世界) virtual axi_if vif; // 声明一个句柄 → 指向已有的 interface 实例 // 不生成任何硬件,只是一个"指针"

2.4 虚接口的传递链路

┌─────────────────────────────────────────────────────────┐ │ tb_top (module) │ │ axi_if dut_if(); ← 硬件实例化 │ │ initial begin │ │ uvm_config_db#(virtual axi_if)::set(...); │ │ run_test(); │ │ end │ └─────────────────────────────────────────────────────────┘ │ │ uvm_config_db ▼ ┌─────────────────────────────────────────────────────────┐ │ my_driver (class) │ │ virtual axi_if vif; ← 软件句柄,指向 dut_if │ │ task run_phase(...); │ │ vif.addr <= tx.addr; ← 通过句柄驱动硬件 │ │ endtask │ └─────────────────────────────────────────────────────────┘

2.5 为什么必须用uvm_config_db传递

因为virtual interface是句柄,不能通过构造函数传递(function new的参数列表固定),UVM 也没有为每个组件预留set_interface方法,所以必须通过全局配置数据库传递:

// tb_top initial begin uvm_config_db#(virtual axi_if)::set(null, "uvm_test_top", "vif", dut_if); run_test(); end // test class my_test extends uvm_test; virtual axi_if vif; function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db#(virtual axi_if)::get(this, "", "vif", vif); uvm_config_db#(virtual axi_if)::set(this, "env.agent.driver", "vif", vif); endfunction endclass // driver class my_driver extends uvm_driver#(my_transaction); virtual axi_if vif; function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db#(virtual axi_if)::get(this, "", "vif", vif); // 获取句柄 endfunction endclass

2.6 虚接口的核心优势

优势说明
软硬件分离RTL 和验证环境可以独立开发、独立编译
接口切换更换 DUT 接口时只需修改tb_top和 driver 的信号驱动部分,组件结构无需变化
多接口支持同一个 driver 可以通过不同的虚接口句柄驱动多个实例
仿真加速通过virtual interfaceclocking块可以实现 cycle-level 驱动,与具体时序无关

2.7 多态接口的典型用法

// 定义统一的虚接口基类 virtual class virtual_if_base; pure virtual task reset(); pure virtual task drive_one_pkt(bit [31:0] data[]); endclass // 不同的 DUT 接口各自继承并实现 class axi_vif_wrapper extends virtual_if_base; virtual axi_if vif; task reset(); vif.rst_n <= 1'b0; repeat(10) @(posedge vif.clk); vif.rst_n <= 1'b1; endtask task drive_one_pkt(bit [31:0] data[]); foreach(data[i]) begin @(posedge vif.clk); vif.addr <= i; vif.wdata <= data[i]; vif.wr_en <= 1'b1; end endtask endclass

这样,driver 只需持有virtual_if_base句柄,而无需关心具体是 AXI 还是 AHB:

class my_driver extends uvm_driver; virtual_if_base vif_wrapper; // 面向抽象编程 task drive(); vif_wrapper.reset(); vif_wrapper.drive_one_pkt(tx.data); endtask endclass

三、virtual method— 虚方法

3.1 什么是虚方法


!!!
virtual function/virtual task是 SystemVerilog OOP 的多态机制。声明为virtual的方法,在通过基类句柄调用时,实际执行的是对象实际类型的方法。


3.2 没有virtual会怎样

// 基类 class packet extends uvm_object; function void my_print(); // ❌ 非虚函数 $display("This is a packet"); endfunction endclass // 派生类 class eth_packet extends packet; function void my_print(); // 同名函数,但不是 override $display("This is an eth_packet"); endfunction endclass // 测试 packet p; eth_packet e; e = new(); p = e; // 基类句柄指向派生类对象 p.my_print(); // 输出:"This is a packet" ❌ 调用了基类版本! e.my_print(); // 输出:"This is an eth_packet"

没有virtual,多态失效。基类句柄永远只能调用基类的方法,无法调用派生类的重写版本。

3.3 加上virtual

class packet extends uvm_object; virtual function void my_print(); // ✅ 虚函数 $display("This is a packet"); endfunction endclass class eth_packet extends packet; virtual function void my_print(); // ✅ override $display("This is an eth_packet"); endfunction endclass packet p; eth_packet e; e = new(); p = e; // 基类句柄指向派生类对象 p.my_print(); // 输出:"This is an eth_packet" ✅ 多态正确工作!

3.4 UVM 中必须用virtual的关键方法

UVM 的整个框架依赖多态,以下方法是 UVM 架构正常运行的基石:

(1) Phase 方法
class uvm_component; virtual function void build_phase(uvm_phase phase); // ✅ 必须 virtual virtual function void connect_phase(uvm_phase phase); virtual task run_phase(uvm_phase phase); virtual function void report_phase(uvm_phase phase); // ... endclass class my_test extends uvm_test; virtual function void build_phase(uvm_phase phase); // ✅ override super.build_phase(phase); // 用户代码 endfunction endclass

UVM 内核通过uvm_component句柄调用 phase 方法:

// UVM 内部(伪代码) uvm_component comp_list[$]; foreach(comp_list[i]) comp_list[i].build_phase(phase); // 如果没有 virtual,永远调用 uvm_component::build_phase // 用户写的派生类重写版本永远不会被执行
(2)do_*field automation 方法
class uvm_object; virtual function void do_copy(uvm_object rhs); virtual function bit do_compare(uvm_object rhs, uvm_comparer comparer); virtual function void do_print(uvm_printer printer); virtual function void do_pack(uvm_packer packer); // ... endclass // 用户重写这些 do_* 来实现自定义行为 class my_packet extends uvm_object; virtual function void do_print(uvm_printer printer); super.do_print(printer); printer.print_field("crc", crc, 32, UVM_HEX); endfunction endclass
(3)convert2string
class uvm_object; virtual function string convert2string(); endclass class my_transaction extends uvm_sequence_item; virtual function string convert2string(); return $sformatf("addr=%0h data=%0h", addr, data); endfunction endclass

3.5 如果不加virtual,UVM 会直接崩溃

考虑 UVM 的print()实现:

// UVM 内部 function void uvm_object::print(uvm_printer printer = null); // ... do_print(printer); // ✅ do_print 是 virtual → 调用实际类型的版本 // ... endfunction

如果do_print不是virtual,那么无论对象是什么类型,print()都只会调用uvm_object::do_print,打印出空内容。用户重写的do_print永远不会被执行。


四、virtual class— 虚类(抽象类)

4.1 什么是虚类

virtual class(也称为抽象类)是不能被直接实例化的类,只能作为基类使用。它通常包含pure virtual method(纯虚方法),强制派生类实现。

4.2 在 UVM 中的使用

// ❌ 错误:virtual class 不能被实例化 // packet p = packet::type_id::create("p"); // 编译错误 // 定义抽象基类 virtual class packet_base extends uvm_sequence_item; pure virtual function bit [31:0] get_addr(); pure virtual function bit [31:0] get_data(); endclass // 具体的派生类必须实现所有 pure virtual 方法 class axi_packet extends packet_base; rand bit [31:0] addr; rand bit [31:0] data; virtual function bit [31:0] get_addr(); return addr; endfunction virtual function bit [31:0] get_data(); return data; endfunction endclass class ahb_packet extends packet_base; rand bit [31:0] addr; rand bit [31:0] wdata; virtual function bit [31:0] get_addr(); return addr; endfunction virtual function bit [31:0] get_data(); return wdata; // AHB 用 wdata 表示写数据 endfunction endclass

这样,monitor / scoreboard 就可以面向抽象编程:

class generic_monitor extends uvm_monitor; packet_base pkt; task run_phase(uvm_phase phase); forever begin // 收集到的数据,无论什么协议,都通过抽象接口读取 $display("addr=%0h data=%0h", pkt.get_addr(), pkt.get_data()); end endtask endclass

4.3 UVM 中的预定义虚类

UVM 大量使用了虚类来定义接口:

虚类说明
uvm_object所有 UVO 数据对象的基类(虽然不是virtual class,但不可直接实例化使用)
uvm_component所有 UVM 组件的基类
uvm_sequence_item所有 sequence item 的基类
uvm_sequence_baseuvm_sequence的基类
uvm_monitor所有 monitor 的基类
uvm_subscriber所有 subscriber(如 scoreboard)的基类

五、pure virtual— 纯虚方法

5.1 定义

class abstract_base; pure virtual function void must_implement(); pure virtual task must_implement_task(); endclass
  • pure virtual方法没有实现体
  • 包含pure virtual方法的类必须是virtual class
  • 派生类必须实现所有pure virtual方法(否则编译错误)
  • 用于定义"契约"或"接口规范"

5.2 使用场景:定义验证组件接口规范

virtual class driver_base extends uvm_driver#(uvm_sequence_item); // 所有 driver 必须实现的接口 pure virtual task reset_dut(); pure virtual task drive_item(uvm_sequence_item item); pure virtual function void check_protocol(); endclass // 具体实现 class axi_driver extends driver_base; virtual task reset_dut(); // AXI 复位时序 endtask virtual task drive_item(uvm_sequence_item item); // AXI 驱动逻辑 endtask virtual function void check_protocol(); // AXI 协议检查 endfunction endclass

六、完整对比总结

6.1 三种virtual用法的对比

类型语法作用不用的后果
virtual interfacevirtual axi_if vif将 interface 作为句柄传入 class编译错误,完全不能用
virtual function/taskvirtual function void foo()实现方法多态多态失效,基类句柄调不到派生类方法,UVM 的 phase / print 等机制全部崩溃
virtual classvirtual class foo定义抽象基类,不能被实例化可以被实例化,失去了抽象约束的意义
pure virtual methodpure virtual function foo()强制派生类实现派生类可以"忘记"实现,编译不报错但运行出错

6.2 UVM 的生命线

UVM 的核心机制全部依赖virtual

UVM 核心机制 依赖的 virtual ───────────────────────────────────────── Phase 自动调度 virtual function build_phase() Factory 创建 virtual function create() Field automation virtual function do_print/do_copy/do_compare() Callback 机制 virtual function pre/post_* Report 机制 virtual function convert2string() Sequence 执行 virtual task body() Interface 传递 virtual interface

任何一个去掉virtual,UVM 的相应功能就会完全失效。

6.3 一句话总结

// 没有 virtual: // class 中不能使用 interface(编译失败) // 基类句柄无法调用派生类方法(多态失效) // UVM 的 phase、factory、field automation 全部无法工作 // // 加上 virtual: // 硬件接口可以通过句柄传递到软件 class 中 // 基类句柄→派生类方法(多态完美工作) // UVM 的所有核心机制正常运行

virtual是 UVM 的基石。没有它,UVM 就不存在了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/19 14:39:17

TI F2838x DCSM安全模块实战:寄存器配置与内存隔离指南

1. 项目概述与DCSM核心价值在工业控制、汽车电子和高端嵌入式应用里&#xff0c;代码和数据的安全隔离已经不是“锦上添花”&#xff0c;而是“生死攸关”的底线要求。想象一下&#xff0c;你的电机控制算法核心代码如果被恶意篡改&#xff0c;或者关键的生产参数在运行时被非法…

作者头像 李华
网站建设 2026/7/19 14:38:35

深入解析TMS320F2838x I2C驱动:从寄存器到Driverlib函数映射与实战

1. 项目概述与核心价值在嵌入式开发&#xff0c;尤其是工业控制、电机驱动和数字电源这些对实时性和可靠性要求极高的领域&#xff0c;与外部传感器、存储芯片或从设备通信是家常便饭。I2C总线因其简洁的两线制&#xff08;SDA数据线、SCL时钟线&#xff09;和软件可寻址的多主…

作者头像 李华
网站建设 2026/7/19 14:36:59

缠论量化框架chan.py:技术实现与实战应用深度解析

缠论量化框架chan.py&#xff1a;技术实现与实战应用深度解析 【免费下载链接】chan.py 开放式的缠论python实现框架&#xff0c;支持形态学/动力学买卖点分析计算&#xff0c;多级别K线联立&#xff0c;区间套策略&#xff0c;可视化绘图&#xff0c;多种数据接入&#xff0c;…

作者头像 李华
网站建设 2026/7/19 14:36:49

TMS320F28003x ERAD模块实战:硬件断点、事件计数器与CRC的嵌入式调试利器

1. 项目概述与ERAD模块核心价值在嵌入式实时控制系统的开发深水区&#xff0c;尤其是面对TMS320F28003x这类高性能C2000微控制器时&#xff0c;我们常常会遇到一个棘手的矛盾&#xff1a;如何在不干扰CPU实时任务执行的前提下&#xff0c;对系统内部的运行状态进行“透视”和“…

作者头像 李华
网站建设 2026/7/19 14:35:42

2026年聚四氟乙烯厂家排行榜前十名品牌有哪些?

核心导读&#xff1a; 国内聚四氟乙烯&#xff08;PTFE&#xff09;品牌排名需区分原材料生产商与制品加工商两大维度。本文综合全球产能数据与中国氟硅协会调研结果&#xff0c;按序列号盘点国内十大代表性PTFE品牌&#xff0c;涵盖产能规模、技术实力与核心产品&#xff0c;帮…

作者头像 李华