SV中面向对象编程基础

 

验证为什么需要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完成类的导入,使得新的环境可以识别出该类,否则类会躺在包这个盒子里不被外部识别。

上一篇:IC验证养成记-从入行到跑路


下一篇:Docker 中如何安装配置 Nginx