news 2026/2/28 7:08:40

SystemVerilog类与对象详解:小白指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SystemVerilog类与对象详解:小白指南

SystemVerilog类与对象详解:从零构建你的第一个验证模块


当硬件验证遇上“编程思维”

你有没有遇到过这样的场景?
写了一个APB读写测试,代码复制粘贴了十几遍,只因为地址和数据不同;想加个新字段到数据包里,结果五六个文件都得改;调试时发现某个信号异常,却不知道是哪个激励导致的……

传统的Verilog在面对复杂验证任务时,越来越像一把钝刀——能用,但效率低下。

而SystemVerilog的出现,就像给这把刀开了刃。它不仅支持更精确的RTL建模,更重要的是引入了面向对象编程(OOP)的思想。从此,我们不再只是“描述硬件”,而是可以“构建可复用、可扩展的验证平台”。

尤其当你开始接触UVM(Universal Verification Methodology),你会发现:整个UVM就是由一个个类拼接起来的积木世界。而这一切的起点,正是——类与对象

今天,我们就来揭开这个“小白一听就懵”的概念,用最接地气的方式讲清楚:

什么是类?什么是对象?它们怎么工作?为什么非学不可?


类:不是代码,是蓝图

你可以把它想象成一张“电路板设计图”

在SystemVerilog中,类(class)不是一个变量,也不是一段逻辑。它更像是一个模板、一张图纸。比如你要生产100块功能相同的开发板,不需要一块块手工焊,只需要一份PCB图纸 + 自动化产线。

类就是这张图纸。

我们来看一个典型的例子:网络通信中的数据包。

class Packet; local bit [31:0] src_addr; local bit [31:0] dst_addr; rand byte payload[]; int id; function new(int size = 8); payload = new[size]; id = $random % 1000; endfunction virtual function void display(); $display("Packet ID: %0d, Size: %0d", id, payload.size()); $display("Src: %h -> Dst: %h", src_addr, dst_addr); endfunction endclass

这段代码做了什么?

  • 定义了几个属性:源地址、目标地址、有效载荷、ID;
  • 使用local限制外部直接访问关键字段,保护内部状态;
  • 构造函数new()在创建实例时自动执行,初始化动态数组和ID;
  • 提供了一个公开的display()方法用于打印信息;
  • rand关键字表示该字段参与随机化,后续可通过randomize()自动生成合法值。

注意:到这里为止,还没有任何内存被分配。这个Packet类只是告诉编译器:“将来如果有人要创建Packet对象,请按这个结构来。”


对象:运行时的真实存在

如果说类是图纸,那么对象就是根据图纸造出来的实物

每个对象都有自己独立的一份属性副本。就像同一款手机生产了10万台,外观一样,但序列号、电量、存储内容各不相同。

在SystemVerilog中,对象必须通过new()动态创建,存储在堆(heap)上,由句柄(handle)引用。

看下面这段测试代码:

module test; initial begin Packet pkt1, pkt2; pkt1 = new(16); // 创建第一个对象,payload大小为16 pkt2 = new(32); // 创建第二个对象,payload大小为32 pkt1.src_addr = 32'hAABB_CCDD; // 错误!src_addr 是 local,无法直接访问 pkt1.set_src_addr(32'hAABB_CCDD); // 正确方式:通过方法设置 pkt2.set_src_addr(32'h1122_3344); pkt1.display(); pkt2.display(); end endmodule

这里有两个关键点需要特别注意:

1. 句柄 vs 对象 —— 引用类型的本质

pkt1 = new(16); pkt2 = new(32); pkt2 = pkt1; // 注意!这不是拷贝对象,而是让 pkt2 指向 pkt1 的对象

执行完最后一行后,pkt1pkt2指向的是同一个对象。此时修改pkt2.set_src_addr(...),其实是在改pkt1所指向的那个对象!

这就是所谓的“引用传递”——和C语言里的指针非常相似。

如果你想真正复制一个对象,必须手动实现克隆逻辑:

function Packet clone(); Packet c = new(this.payload.size()); c.src_addr = this.src_addr; c.dst_addr = this.dst_addr; foreach (c.payload[i]) c.payload[i] = this.payload[i]; return c; endfunction

否则,默认赋值永远只是“复制句柄”。

2. 内存管理:有GC,但别太依赖

SystemVerilog仿真器具备垃圾回收机制(Garbage Collection),当一个对象没有任何句柄指向它时,系统会在适当时机自动释放其内存。

但这并不意味着你可以肆意创建对象而不关心生命周期。

常见陷阱:
- 忘记清空容器中的句柄,导致对象一直被引用,无法回收;
- 循环引用(A持有B,B又持有A),GC可能无法正确识别;
- 长时间仿真实例大量对象,造成内存暴涨。

建议做法:
- 显式将不再使用的句柄设为null
- 在大型测试中监控对象数量变化;
- 复杂结构使用弱引用(weak)或定期清理策略。


封装、继承、多态:OOP三大法宝如何落地?

✅ 封装:把细节藏起来,暴露接口

前面用了local来隐藏src_addr,这是封装的第一步。

更好的做法是提供get/set接口,并加入校验逻辑:

function void set_src_addr(bit [31:0] addr); if (addr == 0) begin $warning("Invalid source address: zero not allowed"); return; end src_addr = addr; endfunction

这样即使别人误操作,也不会破坏对象状态一致性。


✅ 继承:站在巨人的肩膀上

假设现在要定义一个更高级的数据包PriorityPacket,除了普通字段外,还带优先级标记。

我们可以让它继承自Packet

class PriorityPacket extends Packet; rand bit [2:0] priority; constraint c_priority { priority < 3; // 仅允许低/中/高三个等级 } virtual function void display(); super.display(); // 调用父类方法 $display("Priority: %0d", priority); endfunction endclass

关键特性:
- 自动获得父类所有属性和方法;
- 可添加新成员(如priority);
- 可重写虚方法(virtual function)实现定制行为;
- 支持向上转型:Packet p = new PriorityPacket(...);合法。

这种机制极大提升了代码复用性。例如UVM中所有的事务类都继承自uvm_sequence_item,驱动器统一处理基类句柄即可兼容各种协议类型。


✅ 多态:一次调用,多种行为

考虑以下场景:

Packet p1 = new(); Packet p2 = new PriorityPacket(); p1.display(); // 输出普通信息 p2.display(); // 虽然类型是Packet,但实际调用的是PriorityPacket::display()

这就是运行时多态的力量。只要方法声明为virtual,调用时就会根据对象的实际类型决定执行哪段代码。

应用场景:
- 统一处理不同类型的数据包;
- 实现回调机制(callback);
- 构建插件式架构(如UVM phase机制)。


实战小技巧:如何写出健壮的类?

1. 合理使用访问控制

修饰符可见范围
local仅本类内部
protected本类及子类
默认(无)全局可访问(慎用!)

建议原则:尽可能使用local,通过方法暴露可控接口


2. 善用约束块实现受控随机化

constraint c_payload { payload.size() inside {[4:256]}; // 包长在4~256字节之间 foreach (payload[i]) payload[i] != 8'hFF; // 数据不能全为FF }

然后只需调用:

pkt.randomize(); pkt.display(); // 每次生成都不一样的合法数据包

这对于覆盖边界条件、压力测试极为有用。


3. 构造函数参数设计要有弹性

不要强迫用户传一堆参数:

function new(int size = 8, bit [31:0] s_addr = 0, d_addr = 0); payload = new[size]; src_addr = s_addr; dst_addr = d_addr; endfunction

默认值 + 可选参数 = 更友好的API。


在UVM中,它是怎么跑起来的?

回到现实工程中。你在UVM里写的每一个组件,本质上都是一个类的实例:

class my_test extends uvm_test; my_env env; virtual function void build_phase(uvm_phase phase); env = my_env::type_id::create("env", this); endfunction endclass

这里的create并非直接调用new(),而是通过工厂机制(factory)创建对象。这意味着你可以在不改代码的情况下替换组件实现——比如用一个带错误注入功能的driver代替正常driver。

而这套灵活机制的背后,依然是“类与对象”这套基础模型在支撑。


新手常踩的坑 & 解决方案

问题现象原因分析解决办法
显示的信息总是最后一个对象的值多个句柄指向同一对象确保每次new()创建新实例
randomize()不生效忘记声明rand或约束冲突检查字段是否标记rand,打印constraint_mode()
子类方法没被调用父类方法未声明为virtual所有多态方法必须加virtual
内存占用越来越高句柄未置空,对象无法回收定期检查并清除无效引用

记住一句话:类是静态的模板,对象是动态的生命体。理解这一点,你就迈过了最关键的一道门槛。


结语:掌握它,才能驾驭复杂的验证世界

你现在可能觉得,“不就是封装个数据嘛,Verilog也能做”。但请设想一下:

  • 如果你要验证一个PCIe设备,涉及 thousands of transactions;
  • 每个transaction有不同的类型、长度、QoS等级;
  • 还要支持随机错误注入、延迟模拟、覆盖率收集……

不用类?那你只能靠全局变量+任务+重复代码硬扛。

而用了类之后呢?

  • 每种事务是一个类;
  • 驱动器、监视器、记分板各自封装;
  • 测试用例只需组合不同的对象序列;
  • 整个平台清晰、解耦、易维护。

这才是现代验证方法学的核心竞争力。

所以,别再把SystemVerilog当成“高级Verilog”了。
把它当作一门真正的编程语言来对待,学会用类去组织逻辑,用对象去表达行为

当你能熟练写出第一个属于自己的transaction类,并成功在测试中发送出去时,恭喜你——
你已经踏上了通往专业验证工程师的道路。

如果你在实现过程中遇到了其他挑战,欢迎在评论区分享讨论。

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

SeedVR-3B:通用视频修复的扩散Transformer新突破

SeedVR-3B&#xff1a;通用视频修复的扩散Transformer新突破 【免费下载链接】SeedVR-3B 项目地址: https://ai.gitcode.com/hf_mirrors/ByteDance-Seed/SeedVR-3B 导语 字节跳动最新发布的SeedVR-3B模型&#xff0c;采用创新的扩散Transformer架构&#xff0c;突破传…

作者头像 李华
网站建设 2026/2/12 16:48:46

透明度报告发布:公开模型训练数据来源信息

VibeVoice-WEB-UI&#xff1a;如何让AI讲出一场90分钟的自然对话&#xff1f; 在播客创作者圈子里&#xff0c;一个老生常谈的问题是&#xff1a;如何低成本制作高质量、多角色、富有情绪张力的长篇语音内容&#xff1f;传统文本转语音&#xff08;TTS&#xff09;工具虽然能“…

作者头像 李华
网站建设 2026/2/24 16:47:47

DeepSeek-V3.1双模式AI:智能工具调用与极速响应体验

DeepSeek-V3.1双模式AI&#xff1a;智能工具调用与极速响应体验 【免费下载链接】DeepSeek-V3.1-Base-BF16 项目地址: https://ai.gitcode.com/hf_mirrors/unsloth/DeepSeek-V3.1-Base-BF16 导语 DeepSeek-V3.1正式发布&#xff0c;作为一款支持思考模式与非思考模式的…

作者头像 李华
网站建设 2026/2/25 15:03:26

GLM-4-9B-Chat-1M重磅登场:1M上下文超长文本处理新体验

GLM-4-9B-Chat-1M重磅登场&#xff1a;1M上下文超长文本处理新体验 【免费下载链接】glm-4-9b-chat-1m 项目地址: https://ai.gitcode.com/zai-org/glm-4-9b-chat-1m 导语&#xff1a;智谱AI正式发布GLM-4系列开源模型的最新成员——GLM-4-9B-Chat-1M&#xff0c;将上下…

作者头像 李华
网站建设 2026/2/26 1:16:11

Granite-4.0-Micro:3B小模型解锁12种语言能力

Granite-4.0-Micro&#xff1a;3B小模型解锁12种语言能力 【免费下载链接】granite-4.0-micro-GGUF 项目地址: https://ai.gitcode.com/hf_mirrors/unsloth/granite-4.0-micro-GGUF IBM最新发布的Granite-4.0-Micro模型以30亿参数规模实现了多语言处理与企业级功能&…

作者头像 李华
网站建设 2026/2/25 18:51:03

CVAT与AI结合:如何用智能标注提升开发效率

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个基于CVAT的AI辅助标注系统&#xff0c;支持以下功能&#xff1a;1. 自动检测图像中的物体并生成初始标注框&#xff1b;2. 提供智能修正建议&#xff0c;减少人工调整时间…

作者头像 李华