1. 类和对象概述
- class类:包含成员变量和方法,是软件盒子
- object对象:类的实例
- handle句柄(指针):用来指向对象的指针
- property属性(变量):在类中声明的存储数据的变量
- method方法:类中使用task或function定义方法,处理自身或外部传入的数据
2. 声明类并创建对象
class Transaction;
bit [31: 0] addr, crc, data[8];
function void display;
$display ("Transaction: %h", addr);
endfunction: display
function void calc_crc;
crc = addr ^ data.xor;
endfunction: calc_crc
endclass: Transaction
initial begin
Transaction tr; // 声明句柄
tr = new(); // 创建对象
end
- 创建对象,开辟新的内存空间,用来存放新的成员变量和方法
- 创建对象时,会调用构建函数(可自定义),不需要返回值,隐式的返回例化后的对象指针
- 创建对象的动作时动态的,可以在仿真过程中发生
3. 句柄的传递
Transaction t1, t2;
t1 = new();
t2 = t1;
t1 = new();
4. 对象的销毁
- 没有句柄指向的对象就会被销毁,空间被收回
- SV不能回收一个被句柄引用的对象,即同一个对象被多个句柄指向,必须清除所有句柄才能销毁对象
5. 句柄的使用
-
句柄可以用来创建多个对象,也可以前后指向不同对象
Transaction t1, t2; t1 = new(); t2 = t1; t1 = new(); t2 = null; // 将t2赋值为‘空’,即不指向任何对象,此时指针‘悬空’,悬空的指针很‘危险’
-
可以通过句柄来使用对象中的成员变量或者成员方法
Transaction t; t = new(); t.addr = 32'h42; t.display();// 调用对象的成员方法
6. 静态变量
-
class中声明的变量默认为动态变量,声明周期始于对象创建,终于对象销毁
-
static声明class中变量为静态,声明周期始于编译阶段,贯穿于整个仿真阶段
-
静态变量var的引用:class::car、object.var
-
类的所有对象共享一个静态变量
class Transaction; static itn count = 0; int id; function new (); id = count++; endfunction endclass: Transaction initial begin Transaction t1, t2; t1 = new(); // id = 0, count = 1 t2 = new(); // id = 1, count = 2 $display("Second id = %d, count = %0d", t2.id, t2.count); // 同 Transaction::count end
7. 静态方法
- class中声明的方法默认是动态的,可以用static修改为静态方法
- 静态方法内可以声明并使用动态变量,但是不能使用类的动态成员变量
在调用静态方法时,可能并没有创建具体的对象,也因此没有为动态成员变量开辟空间,因此在静态方法中使用类的动态成员变量是禁止的,可能会造成内存泄漏,但是静态方法可以使用类的静态变量,因为静态方法和静态变量在编译阶段就已经分配好了空间。
class Transaction;
static Congif cfg;
static int count = 0;
int id;
// 通过静态方法来操作静态变量
static function void display_statics();
$display("Transaction cfg.mode = %s, count = %0d", cdf.mode.name(), count);
endfunction
endclass: Transaction
initial begin
Congif cfg;
Transaction::cfg = cfg; // 静态变量赋值
Transaction::display_statics(); // 调用静态方法
end
8. 验证为什么需要OOP面向对象
- 验证环境的不同组件其功能和所需要处理的数据内容是不相同的
- 不同环境的同一类型的组件其所具备的功能和数据内容是相似的
- 基于以上两点,验证世界的各个组件角色明确、功能分立,使用面向对象编程于验证世界的构建原则十分符合
9. 一些差别对比
9.1. Verilog的例化和SV class例化的差别:
- 两者共同点在于使用想用的模板来创建内存实例
- 不同点在于Verilog的例化是静态的,即在编译链接时玩车个,而SV class例化是动态的,可以在任意时间点发生,使得类的例化方式更加灵活和节省空间
- Verilog中没有句柄的概念,即只能通过层次化的索引方式A.B.sigX,而SV class通过句柄可以将对象的指针赋予其他句柄,使得操作更加灵活
9.2. 类与结构体的异同
- 二者本身都可以定义数据成员
- 类变量在声明之后,需要构造函数才会创建对象实体,而struct在变量声明时就开辟内存
- 类除了可以声明数据变量,还可以声明方法,而struct不行
- 根本上看,struct是一种数据结构,而class包含了数据成员以及针对这些成员们操作的方法
9.3. 类与模块的异同
- 从数据和方法定义而言,二者都可以作为封闭的容器来定义和存储
- 从例化来看,模块必须在仿真一开始就确定是否应该被例化,类的变量在仿真的任何时段都可以被构造(开辟内存)创建新对象。硬件部分必须在仿真一开始就确定下来,即module和其内部过程块、变量都应该是静态的;而软甲部分,即类的部分可以在仿真任何阶段声明并创建出新的对象
- 从封装性来看,模块内的变量和方法是对外部公共(public)开放的,而类可以根据需要确定外部访问的权限是否是默认的公共类型、或者受保护类型(protected)还是私有类型(local)
- 从继承性来看,模块没有任何的继承性,即无法在原有module基础上进行新module功能的扩展,而继承性是类的一大特点