本文已参与「新人创作礼」活动,一起开启掘金创作之路!
1、概述
class 是一种类型,包括数据和对这些数据进行操作的子例程(函数和任务)。class的数据称为class properties,其子例程称为methods;都是这个class的成员。class properties 和 methods一起定义了某种对象(object)的内容和功能。
数据包可能是一个对象,可能有一个命令字段、一个地址、一个序列号、一个时间戳和一个数据包负载。此外,还有很多事情可以用数据包来完成:初始化数据包、设置命令、读取数据包的状态或检查序列号。
每个数据包都是不同的,但作为一个类,数据包具有可以在定义中捕获的固有属性。
class Packet ;
//数据或类属性
bit [3:0] command;
bit [40:0] address;
bit [4:0] master_id;
integer time_requested;
integer time_issued;
integer status;
typedef enum { ERR_OVERFLOW= 10, ERR_UNDERFLOW = 1123} PCKT_TYPE;
const integer buffer_size = 100;
const integer header_size;
// 初始化
function new();
command = 4'd0;
address = 41'b0;
master_id = 5'bx;
header_size = 10;
endfunction
// methods
// 公共访问入口点
task clean();
command = 0; address = 0; master_id = 5'bx;
endtask
task issue_request( int delay );
// 向总线发送请求
endtask
function integer current_status();
current_status = status;
endfunction
endclass
面向对象的类扩展允许动态创建和销毁对象,类实例或对象可以通过object handle来传递。
对象可以声明为带有方向input、output、inout或ref的参数;在每种情况下,复制的参数都是object handle,而不是对象的内容。
2、对象(类实例)
类定义了一种数据类型,对象是该类的实例。
使用对象时,首先声明该类类型的变量(包含object handle),然后创建该类的对象(使用新函数)并将其分配给变量。
Packet p; // 声明类数据包的一个变量
p = new; //将变量初始化为类数据包的新分配对象
变量p被称为持有类数据包对象的object handle(对象句柄)。
未初始化的object handle 默认设置为特殊值null,通过将handle与null进行比较,可以检测到未初始化的对象。
class obj_example;
...
endclass
task task1(integer a, obj_example myexample);
if (myexample == null) myexample = new;
endtask
任务task1检查对象是否已初始化,如果对象未初始化,将通过new命令创建一个新对象。
通过空(null)object handle 访问非静态成员或虚拟方法是非法的;通过空(null)对象非法访问的结果不确定,实现可能会发出错误。
SystemVerilog对象使用 object handle 引用,C指针和SystemVerilog object handle之间有一些区别,见表8-1。C指针为程序员提供了大量使用指针的自由。
| 运算 | C 指针 | SV object handle | SV chandle |
|---|---|---|---|
| 算术运算(如递增) | 允许 | 不允许 | 不允许 |
| 对于任意数据类型 | 允许 | 不允许 | 不允许 |
| 空时取消引用 | 错误 | 错误 | 不允许 |
| Casting | 允许 | 限制 | 不允许 |
| 对数据类型地址的赋值 | 允许 | 不允许 | 不允许 |
| 未引用的对象被垃圾收集 | 不会 | 会 | 不会 |
| 默认值 | 未定义 | null | null |
| 对于类型 | C++ | 允许 | 不允许 |
管理SystemVerilog object handle要比C指针使用的规则要严格得多。例如,C指针可以递增,SystemVerilog object handle不能递增。
只有以下运算符对object handle有效:
- 相等(==),与另一个类对象或null不相等(!=)(要比较的一个对象必须与另一个对象兼容)。
- 大小写相等(==)、与另一个类对象或null的大小写不相等(!==)(与== 和!= 的语义相同)。
- 条件运算符。
- 类对象的赋值,其类数据类型与目标类对象的赋值兼容。
- 赋值为空。
3、对象属性和对象参数数据
类属性的数据类型没有限制,对象的类属性可以通过使用实例名限定类属性名来使用。前面的示例中,数据包对象p的属性如下:
Packet p = new;
int var1;
p.command = INIT;
p.address = $random;
packet_time = p.time_requested;
var1 = p.buffer_size;
类枚举名称除了可以使用类作用域解析运算符访问外,还可以通过使用实例名称限定类枚举名称来访问。
initial $display (p.ERR_OVERFLOW);
还可以通过使用实例名称限定类值参数或局部值参数名称来访问对象的参数数据值,这不是常数表达式,不允许使用object handle访问数据类型。例如:
class vector #(parameter width = 7, type T = int);
endclass
vector #(3) v = new;
initial $display (vector #(3)::T'(3.45)); // Typecasting
initial $display ((v.T)'(3.45)); //ILLEGAL
initial $display (v.width);
4、对象方法
可以使用与访问类属性相同的语法访问对象方法:
Packet p = new;
status = p.current_status();
前面的状态分配不能按如下方式写入:
status = current_status(p);
面向对象编程的重点是对象,在本例中是数据包,而不是函数调用。 对象是自包含的,有自己的方法来处理自己的属性。对象不需要作为参数传递给current_status(),类的属性对该类的方法是自由、广泛可用的,但每个方法只访问与其对象(即其实例)相关联的属性。
声明为class类型一部分的方法的生存期应是自动的,声明具有静态生存期的类方法是非法的。