verilog 面向对象编程基础 本节汇总 verilog(SV)面向对象编程(OOP)的核心概念,面向 UVM 学习或工程使用的入门读者。
说明:本文代码片段相互独立,便于单独阅读与复制。
1. 动物类与对象
class 用来定义类,new() 用来创建对象。
类变量保存“句柄”,对象实例分配在堆上。
flowchart TD
H1["栈区 句柄 c1"] -->|指向| O1["堆区 对象 Cat#1"]
H2["栈区 句柄 c2"] -->|指向| O2["堆区 对象 Cat#2"]
O1 -->|动态类型| C["类 Cat"]
O2 -->|动态类型| C
C -->|继承自| B["类 Animal"]
B --> M1["方法 eat"]
C --> M2["重写 eat"]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Animal; string name; function new (string name = "Animal" ); this .name = name; endfunction function void eat(); $display ("%s eats" , name); endfunction endclass class Cat extends Animal; function new (string name = "Cat" ); super .new (name); endfunction function void eat(); $display ("%s eats fish" , name); endfunction endclass Cat c1; Cat c2; initial begin c1 = new ("Mimi" ); c2 = new ("Lulu" ); c1.eat (); c2.eat (); end
2. 成员:属性与方法
属性:类中的变量。
方法:function 或 task,可访问类属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Animal; int age; function void eat(); $display ("Animal eats" ); endfunction endclass class Cat extends Animal; string color; function void eat(); $display ("Cat eats fish" ); endfunction endclass
3. 构造函数与 this
构造函数名字固定为 new。
this 指向当前对象,避免名字冲突。
flowchart TD
H["栈区 句柄 c"] -->|指向| O["堆区 对象 Cat"]
O -->|动态类型| C["类 Cat"]
C --> N["构造函数 new"]
C --> F["字段 age"]
O -->|this 指向自身| T["this"]
T -->|写入 age| F
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Animal; string name; function new (string name = "Animal" ); this .name = name; endfunction function void eat(); $display ("%s eats" , name); endfunction endclass class Cat extends Animal; int age; function new (string name = "Cat" , int age = 1 ); super .new (name); this .age = age; endfunction function void eat(); $display ("%s eats fish" , name); endfunction endclass
4. 继承与重写
子类 extends 父类(Cat 继承 Animal)。
可重写父类方法,必要时调用 super。
flowchart TD
H["栈区 句柄 h: Animal"] -->|指向| O["堆区 对象: Cat"]
O -->|动态类型| C["子类: Cat"]
O -->|继承自| B["父类: Animal"]
C --> M["子类方法: eat"]
M -->|调用 super.eat| S["父类方法: eat"]
1 2 3 4 5 6 7 8 9 10 11 12 class Animal; function void eat(); $display ("Animal eats" ); endfunction endclass class Cat extends Animal; function void eat(); super .eat (); $display ("Cat eats fish" ); endfunction endclass
5. 多态与虚方法(详细) 多态的核心是“父类句柄指向子类对象”,方法的实际执行版本由运行时对象类型决定。
5.x 父类句柄指向子类实例对象分析 核心要点
句柄类型决定“可访问的接口范围”,对象真实类型决定“实际执行的实现”。
只有父类方法声明为 virtual,才能在子类中动态绑定。
父类句柄无法直接访问子类新增成员(除非进行类型转换)。
典型示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Animal; string name; virtual function void eat(); $display ("%s eats" , name); endfunction endclass class Cat extends Animal; string toy; function void eat(); $display ("%s eats fish" , name); endfunction endclass Animal h; initial begin h = new Cat(); h.name = "Mimi" ; h.eat (); end
必要时的类型转换
1 2 3 4 Cat m; if ($cast (m, h)) begin m.toy = "ball" ; end
flowchart TD
H["父类句柄 h: Animal"] -->|$cast| M["子类句柄 m: Cat"]
H -->|失败| F["转换失败"]
M -->|成功| U["可访问 Cat 专有成员"]
$cast 转换示例(Dog)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Animal; string name; function new (string name = "Generic" ); this .name = name; endfunction function void eat(); $display ("%s eats" , name); endfunction endclass class Dog extends Animal; function void fetch(); $display ("Dog fetches" ); endfunction endclass Animal animal = new ("Generic" ); Dog dog; if ($cast (dog, animal)) begin dog.fetch (); end else begin $display ("Not a Dog instance" ); end
flowchart TD
H["栈区 父动物类句柄 Animal"] -->|指向| O["堆区 对象 Cat"]
O -->|动态类型| C["子类 Cat"]
H -->|调用 eat| R["运行时绑定"]
R --> E["执行 Cat.eat"]
flowchart LR
H1["栈区 父动物类句柄 a1"] -->|指向| O1["堆区 对象 Cat"]
H2["栈区 子猫类句柄 c1"] -->|指向| O1
flowchart LR
H1["栈区 子猫类句柄 c"] -->|new| O1["堆区 对象 Cat"]
H2["栈区 父动物类句柄 a"] -->|赋值指向| O1
H3["栈区 父动物类句柄 b"] -->|new| O2["堆区 对象 Animal"]
O1 --> R1["a.eat 触发子猫类 eat"]
O2 --> R2["b.eat 触发父动物类 eat"]
5.1 基本多态示例
父类方法需要 virtual,才能在子类中实现动态绑定。
子类方法签名必须一致(参数、返回值匹配)。
1 2 3 4 5 Animal a; initial begin a = new Cat(); a.eat (); end
5.2 多态调用链与 super
子类可以调用 super 保留父类行为,再追加特性。
1 2 3 4 5 6 7 8 9 10 11 12 class Animal; virtual function void eat(); $display ("Animal starts eating" ); endfunction endclass class Cat extends Animal; function void eat(); super .eat (); $display ("Cat eats fish" ); endfunction endclass
5.3 多态数组:统一管理不同子类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Animal; virtual function void eat(); $display ("Animal eats" ); endfunction endclass class Cat extends Animal; function void eat(); $display ("Cat eats fish" ); endfunction endclass class Kitten extends Cat; function void eat(); $display ("Kitten eats small fish" ); endfunction endclass Animal zoo[$]; initial begin zoo.push_back (new Cat()); zoo.push_back (new Kitten()); foreach (zoo[i]) begin zoo[i].eat (); end end
5.4 动态绑定 vs 静态绑定
动态绑定:virtual + 父类句柄,运行时根据对象真实类型选择实现。
静态绑定:子类句柄直接调用,编译期就确定目标实现。
用途与场景
动态绑定适合“统一接口 + 不同实现”的场景,例如 UVM 中 driver/monitor 的基类句柄统一调度不同协议实现。
静态绑定适合“已知具体类型”的场景,例如调试/性能路径中直接调用子类方法,逻辑清晰、开销更低。
对比示例
flowchart LR
D1["动态绑定: 父动物类句柄 a"] -->|指向| O1["堆区 对象 Cat"]
D1 -->|调用 eat| R1["运行时绑定"]
R1 --> E1["执行 Cat.eat"]
S1["静态绑定: 子猫类句柄 c"] -->|指向| O2["堆区 对象 Cat"]
S1 -->|调用 eat| E2["编译期确定: Cat.eat"]
1 2 3 4 5 6 7 8 9 10 11 12 Animal a; Cat c; initial begin a = new Cat(); a.eat (); c = new (); c.eat (); end
5.5 多态常见注意点
父类方法未声明 virtual 时,子类同名方法不会多态触发。
方法签名不一致会导致“隐藏”而非“重写”。
多态配合抽象类与纯虚方法更清晰地约束接口。
5.6 父类方法非 virtual 的情况 现象
父类方法不是 virtual 时,父类句柄调用会固定到父类实现。
子类同名方法只是“隐藏”,不会触发多态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Animal; function void eat(); $display ("Animal eats" ); endfunction endclass class Cat extends Animal; function void eat(); $display ("Cat eats fish" ); endfunction endclass Animal a; initial begin a = new Cat(); a.eat (); end
如何实现多态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Animal; virtual function void eat(); $display ("Animal eats" ); endfunction endclass class Cat extends Animal; function void eat(); $display ("Cat eats fish" ); endfunction endclass Animal a; initial begin a = new Cat(); a.eat (); end
6. 抽象类与纯虚方法
抽象类不能直接 new。
纯虚方法必须由子类实现。
1 2 3 4 5 6 7 8 9 virtual class AnimalBase; pure virtual function void eat(); endclass class Cat extends AnimalBase; function void eat(); $display ("Cat eats fish" ); endfunction endclass
7. 访问控制:local / protected
local:仅类内部可见(子类也不可见)。
protected:类内部与子类可见。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Animal; local int secret; protected int age; function void eat(); $display ("Animal eats" ); endfunction endclass class Cat extends Animal; function void eat(); $display ("Cat eats fish" ); endfunction endclass
8. 句柄赋值与对象拷贝
句柄赋值只复制“指针”,不是对象内容。
需要深拷贝时自定义 copy()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Animal; string name; function void eat(); $display ("%s eats" , name); endfunction endclass class Cat extends Animal; string color; function Cat copy(); Cat c = new (); c.name = this .name ; c.color = this .color ; return c; endfunction function void eat(); $display ("%s eats fish" , name); endfunction endclass
9. 参数化类 1 2 3 4 5 6 class CatCage #(type T = Cat) ; T q[$]; function void push(T v); q.push_back (v); endfunction endclass