一、类和对象的概述
在IC验证中,为什么要用OOP(面向对象编程)呢?
我们知道,验证结构分为以下四个部分:
- 激励生成器(stimulate)
- 驱动器(driver)
- 监测器(monitor)
- 比较器(checker)
验证环境不同组件其功能和所需要的处理的数据内容是不相同的,因为在不同的环境可能会使用到同一种类型的组件,所以使用OOP的话,会使得验证工作更加方便。
创建第一个transaction类
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
在class中,不能定义硬件类型的变量,例如wire 与 reg 都是不可以的,c软件世界中只能定义软件变量。(动态的)在class中所有的变量都默认是动态的,与module不同,在module中所有的变量默认是静态的。那么,接口作为软硬件之间的唯一 媒介,其指针是可以传递到class中的,class可以通过接口来获得硬件世界的信号!
二、一些概念
- Class类:基本模块包含成员变量和方法。在Verilog的module中也可以这样,但是module是硬件,而class是软件的。
- Object对象:类的实例。
- Handle句柄:其实就是用来指向对象的指针。
- Property属性:就是类的变量
- Method方法:在类中的function与task。
但是在类中不能存在initial与always
三、创建对象
注意,Verilog的例化与SV中class的例化的区别!!!!!!
- Verilog的例化是静态的,在编译链接的时候完成,SV class是动态的,在仿真开始后,才能创建。
- Verilog中是没有句柄的概念的,只能通过层次化的索引方式。而SV class是通过句柄空压机将对象的指针赋予其他句柄。
Transaction tr;//声明变量
tr = new();//创建对象
- 创建对象是开辟了新的内存空间,用来存放新的成员变量和方法的
- 与C++一样,SV也可以定义自己的构造函数,其格式如下
function new(); // new()在定义的时候不需要有返回值
addr = 3;
foreach (data[i])
data[i] = 5;
endfunction
- 在写构造函数的时候,也可以自己添加多个参数,作为初始化的值。如下:
class Transaction;
bit [31:0] addr, crc, data[8];
function new(logic [31:0] a = 3, d = 5);
addr = a;
foreach (data[i])
data[i] = d;
endfunction
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(10);
end
在这种情况下,addr的值就会成为传递的参数10,而不是默认的3,但是data里面的值,则都是默认参数的值5。
四、句柄的使用
- 看如下代码,来理解句柄的使用
Transaction t1, t2; //声明句柄t1,t2
t1 = new(); //例化对象,将其句柄赋予t1
t2 = t1; //将t1的值赋给t2,即就是t1与t2指向同一个对象
t1 = new(); //例化第二个对象,并将其句柄赋予t1
如下列图:
当执行到t2 = t1; 这条语句的时候,其存储情况如上图所示
当执行完的时候,其结果如上图所示
四、销毁对象
- 在SV中,不会像C和C++一样需要自己时刻关注内存空间,并且防止内存泄漏的情况。在SV中,不需要自己去写析构函数,SV与Java和Python一样,是自动回收空间。
- 在SV中,当一个对象,在整个程序中没有任何一个句柄(及就是该对象类型的指针)指向这个对象的时候,那么,这个对象所在的空间就被free掉了。
Transaction t1, t2; //声明句柄t1,t2
t1 = new();
t2 = new();
t1 = t2; // 将t2赋值予t1,t1和t2指向同一对象,t1之前指向的对象被释放
t2 = null;// 将t2的值赋为空
t1.addr = 32'h42; //可以使用句柄来使用对象中的成员变量或者方法
t1.display();
五、静态变量以及静态方法
静态变量
- 使用关键字static
- 静态变量是在编译阶段就存在了,贯穿于整个仿真阶段
- 如果在类中声明的静态变量,则可以使用类名直接去引用该变量,甚至不需要创建对象。class::var。当然通过例化的对象来引用也是可以的。
- 类中的静态变量,是所有该类型的对象所共享的。不管例化多少对象,都共享这一个静态变量。
class Transaction;
static int count = 0;
int id;
bit [31:0] addr, crc, data[8];
function new(logic [31:0] a = 3, d = 5);
id = count++;
addr = a;
foreach (data[i])
data[i] = d;
endfunction
function void display;
$display("Transaction: %h", addr);
endfunction:display
function void calc_crc;
crc = addr^data.xor;
endfunction:calc_crc
endclass:Transaction
Tramsaction tr1, tr2;
initial begin
tr1 = new(); // 1st instance, id = 0, count = 1
tr2 = new(); // 2nd instance, id = 1, count = 2
$display("Second id=%d, count=%d", tr2.id, tr2.count);
end
静态方法
- 在class中,其方法默认也是动态的,但是加上static就成了静态方法
- 静态方法内部也可以声明并使用动态变量,但是不能使用在该方法外部声明的动态变量。因为在调用静态方法的时候,可能并没有创建具体的对象,也因此没有为动态成员变量开辟空间,所以在静态方法中,使用类的动态成员变量是禁止的。
- 静态方法也可以使用静态变量。