验证为什么需要OOP(面向对象编程)?
验证环境不同组件及其功能和所需要处理的数据内容是不相同的,但是不同环境的同一类型的组件所具备的功能和数据内容是相似的,所以验证世界的各个组件角色明确,功能分立,使用面向对象编程与验证世界的构建原则十分符合。
激励生成器:生成激励内容
驱动器:将激励以时序形式发送到DUT
监测器:监测信号并记录数据
比较器:比较数据
OOP术语
(1)类(class):是软件盒子,包含变量和子程序的基本构建块。Verilog中对应的是模块(module),是硬件盒子。
(2)对象(object):类的一个实例。Verilog中module可以例化,SV中class也可以例化。
(3)句柄(handle):用来指向对象的指针。在Verilog中可以通过层次化的索引来找到结构中的设计实例,而在SV的对象索引时,需要通过句柄索引对象的变量和方法。
(4)属性(property):在类中声明的存储数据的变量
(5)方法(method):处理数据的方法,如task,function
将类视为房子的蓝图,则对象是一个实际的房子,而句柄就像是房子的地址。
定制构造函数
Transaction tr; //声明句柄
tr=new(); //创建对象
构建函数new()是系统预定义的函数,不需要指定返回值,函数会隐式地返回例化后的对象指针。构造函数除了分配内存之外,它还初始化变量。在默认情况之下,它将变量设置成默认数值——二值变量为0,四值变量为X。你也可以自定义new函数将默认值设置成你想要的数值。类似于python中的__init__函数。你不能简单地在类的构造函数中初始化静态变量,因为每一个对象都会调用构造函数,你可以在类的定义中初始化。
class Transaction;
logic[31:0]addr,crc,data[8];
function new(logic[31:0]a=3,d=5);
addr=a;
foreach(data[i])
data[i]=d;
endfunction
endclass
对象的解除分配
垃圾回收是一种自动释放不再被引用的对象的过程。SV分辨对象不再被引用的方法是记住指向它句柄的数量,当最后一个句柄不再引用某个对象时,SV就释放该对象的空间。
类的成员
一个类的功能应该尽可能简单,不应该承担过多的责任,也不应该承担不符合它的职责。类作为载体,不会将成员变量直接暴露给外部,通过public,protected,local关键词来设置成员变量方法的外部权限访问。
默认类型是public,子类和外部均可以访问成员。
protected:只有该类和子类可以访问成员,外部是无法访问的
local:只有该类可以访问,子类外部都不行访问
module和class的异同
(1)数据和方法定义方面:两者都使用相同的模板来创建实例,都可以作为封闭的容器来存储数据和方法。
(2)例化方面:module例化是静态的,在编译链接时就完成;而SV中class的例化是动态的,可以在仿真的任何阶段声明并动态创建新的对象,这也使类的例化方式更加灵活而节省空间。
(3)封装性方面:模块内的变量和方法对外部都是开放的,而类可以根据需要来确定外部访问的权限:pubilc,protected,local
(4)继承性方面:模块没有继承性而言
类与结构体的异同
(1)二者都可以定义数据成员
(2)类变量在声明之后,需要构造函数才构建对象实体,而struct在声明变量时已经开辟内存
(3)类不仅可以声明数据变量成员,而且可以声明方法,这是struct做不到的
(4)从根本上说,struct是一种数据结构,而class包含数据成员以及针对这些成员的操作方法
this
class A;
string oname;
function new(string oname);
this.oname=oname;
endfuction
endclass
当你使用一个变量名时,SV会在当前作用域寻找,接着在上一级作用域上寻找,直到找到该变量为止。但是你在类的很深层的作用域想引用类一级的对象时,就可以采用this。this表示调用的成员是当前类的成员,而非同名的局部变量。
包的使用
SV提供了一个在多个module,interface,program之*享parameter,data,task,function,class等方法,通过包(package)来实现。这么做的好处是将一族相关的类组织在了单一命名空间下,可以通过package来解决归属问题。
package regs_pkg;
`include "stimulator.sv"
`include"monitor.sv"
`include"checker.sv"
`include"env.sv"
endpackage
重名的类归属到不同的package编译,不会发生冲突,只需注明是哪一个package
module A;
regs_pkg::monitor mon1=new();
arb_pkg::monitor mon2=new();
endmodule
将类封装到一个包之中,那么它就不在其他地方编译,这么做的好处是之后对类的引用更加方便。包中可以定义类,静态方法和静态变量,使用`include来完成类在包中的封装,即“平铺”在包中,按照顺序完成包和各个类的有序编译。使用类可以通过import完成类的导入,使得新的环境可以识别出该类,否则类会躺在包这个盒子里不被外部识别。