验证为什么要用OOP:
原因:验证环境中不同组件其功能和所需处理的数据内容是不相同的;
不同环境的同一类型的组件其所具备的额功能和数据内容是相似的。
基于以上两点,验证世界的各个组件角色分明、功能独立、与面向对象编程的要素十分符合。
封装
类和结构体的异同:
两者都可定义数据成员
类在声明之后,需要构造函数new才能实例化对象,而struct在声明后就已经开辟内存
类中可以声明数据变量和方法(保存和处理数据),但struct不能含有方法(只能存储数据)
类和module的异同
从数据和方法定义来说,两者都可作为封闭的容器来定义和存储数据
从例化来看,模块必须在仿真一开始就确定是否应该被例化,其内部变量都是静态的;而类可以在仿真的任何阶段创建新的对象(开辟内存),其内部变量为动态的.
从封装性来看,模块内部的方法和变量对外部都是开放的;而类可以根据需要来确定外部访问权限为public/protected/local
从继承性来看,模块没有任何继承性可言,而继承正是类的一大特点。
类的位置
可以在哪里定义类:module interface program package
可以在类中再声明类的成员吗:可以,类也是一种数据载体
this是什么: this.x表明所调用的成员为当前类的成员,而非同名的局部变量或形式参数
类的编译顺序:先编译基本类,再编译高级类。
继承
概述
继承的特点是可以在在原来的类的属性基础上增加新的属性
把通用型的代码同一编写在一个基类(父类)中(base class) ,在派生类(子类)中添加新的属性。
优点:可以重用前期项目中经过验证和检查的bug少的类;不会破坏原有的代码架构;
访问:this和super: 子类中使用this.xxx是先访问子类有没有xxx的变量或者方法,而super.xxx直接访问父类的变量和方法,不会访问子类。如果没有super和this 来指示作用域,则依照从近到远的原则来引用变量(函数内部的局部变量=>当前类的成员变量=>父类或更底层的类中变量)
Package的使用
包的意义
可以将不同模块的验证环境放入不同 的package当中,这样就可以通过package来解决类的归属问题
包的定义
package的名称应独一无二,其内部的类也应尽可能独一无二
通过include关键词完成类在包中的封装,要注意编译的前后顺序来放置各个’include类文件
package regs_pkg; //regs_pkg的包
'include "stimulator"
'include "monitor.sv"
'include "chker.sv"
'include "env.sv"
endpackage
package arb_pkg;
'include "stimulator"
'include "monitor.sv"
'include "chker.sv"
'include "env.sv"
endpakage
module mcdf_tb;
regs_pkg::monitor mon1 = new();
arb_pkg::monitor mon2 = new();
访问
我们可以通过不同名称的package来管理同名的类,再通过域名索引“::”操作符来指出使用哪个package中的类。
从这个例子中,package这个容器可以对类名做一个隔离作用。具体来说就是将软件(类,类型,方法等)封装在不同命名空间中,以此来与全局的命名空间进行隔离。
包和库的区分
library是编译的产物,既可以容纳硬件类型(module interface program),也可容纳软件类型(类,方法,package)
通过import完成包中所有类或者某个类的导入,否则类将不会被识别
module mcdf_tb;
import regs_pkg::*; //import完成包中所有类的导入
import arb_pkg::*;
regs_pkg::regs_mon mon1 = new();
arb_pkg::arb_mon mon2 = new();
endmodule
多态
对于同一种指令,针对不同的对象产生不同的行为。
分为静态多态和动态多态:
静态多态发生在编译时 --- 函数重载、运算符重载、模板;
动态多态发生在运行时 --- 虚函数