SystemVerilog 第5章 面向对象编程基础
5.1 概述
5.2 考虑名词而非动词
5.3 Class(类)
类的定义
class Transaction;
//属性声明
bit [31:0] addr;
//方法创建
function void display;
$display();
endfunction
endclass: Transaction
- 变量命名参考
类:大写字母开头,不含下划线(Transaction)
常数:全大写(SIZE)
变量:全小写
5.4 类定义的位置
- program、module、package中或这些块之外
- 类可以再program和module中使用
5.5 OOP术语
- class:对象+方法
- object:对象
- handle:句柄
- property:属性
- method:方法(function&task)
- prototype:原型(块头:名字,返回类型,参数列表)
5.6 创建对象
- class和module联系和区别
– 相同点:
①属性和方法的载体
②可以逐层例化得到复杂层次结构
– 不同点:
①module静态,再编译时就分配好空间;class动态,仿真随用随分配
②封装性:module公开;class可以设定保护(protected/local)
③继承性:module不继承,class可以
④module的例化名和空间是绑定的(即便是automatic变量名也是和空间绑定的);对象的句柄和空间不绑定;不用了可以回收
5.6.1 没有消息就是好消息
- 声明&例化
Transaction tr; //声明句柄
tr = new(); //例化对象
- 刚声明完时候句柄为null,直到调用new()
5.6.2 new函数(构造函数)
- new()例化对象的步骤
①new()根据句柄类型分配对应的空间
②为变量初始化默认值
③返回对象的句柄 - new()也是class的方法,可以在new()函数中行使方法的功能(修改属性值,从外部传入参数等)
5.6.3 将声明和创建分开
- 避免声明句柄的时候就调用new()
5.6.4 new()和new[]
- new() 为对象(属性+方法)开辟空间
- new[n] 为数组开辟大小为n的空间
5.6.5 为对象创建句柄
- 句柄传递:仅仅是指向了相同的对象(可以把" : "看作“→”)
Transaction t1,t2;
t1 = new();
t2 = t1; //t1和t2都指向了相同对象
5.7 对象回收(deallocation)
- 当句柄指向一对象时,对象不可回收;当没有句柄引用时,对象自动回收
- t = null; //回收t指向的对象
- SV的句柄只能指向一种类型
5.8 使用对象
- 用 . 来引用属性和方法
t.addr;
t.display();
5.9 静态变量
- 对象中的变量默认是动态的局部变量
- 声明静态变量:
static int count=0;
5.9.1 class的静态变量
- class中定义的静态变量,其初始化语句只执行一次;而动态变量每次执行都会重新创建并初始化
5.9.2 静态变量的访问
- ① . 引用:与属性/方法相同
- ②静态独有作用域符号 ::(Transaction::count)
5.9.3 静态变量的初始化
- 静态变量通常应在声明的时候初始化
5.9.4 静态方法
- 定义和调用
//定义
static function void display();
...
endfunction
//调用
object.display();
Transaction::display();
- 静态方法不可以调用动态变量(编译时候动态变量不存在)
5.10 类的方法
- class的属性和方法默认都是动态的(automatic)
5.11 类外部定义方法
- class定义尽量在一页内写完
- 定义格式: prototype在内,代码在外
class Transaction;
bit [31:0] addr;
extern function void display();
endclass
function void Transaction::display();
...
endfunction
5.12 作用域
- 作用域即一个代码块的范围:module,program,function,task,class或begin-end
- 变量可以使用相对作用域(相对于当前块)和绝对作用域$root
- SV调用一个变量名时,先在当前作用域中寻找,接着在上一级作用域中寻找,直到找到为止
- class应该定义在program和module之外的package中
- this 是当前类的指针
5.13 类的嵌套
- 格式:一定要在new()中例化嵌套的类
class Transaction;
bit [31:0] addr;
Statistics stats; //声明嵌套类的句柄
function new(); //Transaction类构造函数
stats = new(); //stats实例化
endfunction
endclass
5.13.1 类做多大
5.13.2 编译顺序
- 一定要先定义类,然后再调用。解决编译顺序方法有二:
①将class的定义放在调用前,或定义在package中先编译
②在块首使用typedef创建临时class名:typedef class Statistics;
5.14 SV动态对象
5.14.1 将对象传递给方法
- 如果直接传递句柄名到方法中,则传递的是句柄的副本,而非原始句柄,更不是对象本身
- 下面代码中执行完后句柄 t 仍然为null,而方法中创建了一个和 t 同类型的局部句柄 tr 及对象
//操作对象的方法
function void create(Transaction tr);
tr=new()
endfunction
//句柄传递
Transaction t;
intial begin
create(t);
end
5.14.2 在任务中修改句柄
- 在方法中修改句柄本身,不会对方法外的对象句柄产生任何影响,因为方法中的句柄是一个句柄的副本
- 若要想直接对句柄本身操作且不影响所指对象,需要使用ref参数将句柄以指针方式传递
function void create(ref Transaction tr);
...
endfunction
5.14.3 在程序中修改对象
- 发送对象时需要为每个对象进行实例化
5.14.4 句柄数组
- 存放句柄的数组,每一个元素都指向一个对象
5.15 对象的复制
5.15.1 new的浅复制
- new操作可以复制现有对象的所有变量和方法
handle2=new handle1; - 浅复制只能复制当前对象,对象中的嵌套对象不能复制(只传递句柄,没有调用new()函数)
5.15.2 copy函数的简复制
5.15.3 copy函数的深复制
- 实现深复制则需要为层次结构中每一个类增加一个copy函数
- copy函数返回值为所在类的句柄。步骤:函数体调用new()实例一个对象;将当前类中所有属性值复制给新建的copy对象
- copy函数的定义和使用
//嵌套class定义
class Statistics;
time startT,stopT;
...
function Statistics copy();
copy = new();
copy.startT = startT;
copy.stopT = stopT;
endfunction
endclass
//copy function
class Transaction;
bit [31:0] addr,crc,data[8];
Statistics stats;
static int count = 0;
int id;
function new();
stats = new();
id = count++;
endfunction
function Transaction copy;
copy = new();
copy.addr = addr;
copy.crc = crc;
copy.data = data;
copy.stats = stats.copy;
id = count++;
endfunction
endclass
//深复制
program top;
Transaction src,dst;
initial begin
src = new();
src.stats.startT = 42;
dst = src.copy();
dst.stats.startT = 96;
$display(src.stats.startT);
end
endprogram
5.15.4 流操作符打包对象
- 编写pack和unpack函数
function void pack(ref byte bytes[40]);
bytes = {>>{addr,crc,data}};
endfunction
function void unpack(ref byte bytes[40]);
{>>{addr,crc,data}} = bytes;
endfunction
5.16 公有和私有
5.17 题外话
5.18 搭建测试平台
- 分层测试平台的各个部分都是类,test处在最高层的program中,从而例化layered environment。功能覆盖(function coverage)定义可以放在layered environment类的内部或外部