1 类和对象的概述
类是将相同的个体抽象出来的描述方式,对象是实体,具有相同属性和功能的对象就属于同一类。
类的定义核心就是属性声明和方法定义,所以类是数据和方法的自洽体,既可以保存数据又可以处理数据,这是类与结构体在数据保存上最重要区别。
面向对象OOP的基本概念(三要素:封装、继承、多态)
验证为什么要用OOP:
原因:验证环境中不同组件其功能和所需处理的数据内容是不相同的;
不同环境的同一类型的组件其所具备的额功能和数据内容是相似的。
基于以上两点,验证世界的各个组件角色分明、功能独立、与面向对象编程的要素十分符合。
创建对象(实例说明,):
实例

总结oop概念要素:
class类:基本模块包含了成员变量和方法,是一个软件“盒子”;
object对象:类的实例,“软件”例化;
handle句柄:指向对象的指针,通过句柄可以索引对象的变量和方法;
property属性:在类中声明的存储数据的变量;Method方法:类中function和task
句柄的使用
◆当句柄指向一个对象时, SystemVerilog不会释放该对象的内存空间(对象的创建)
◆如果没有句柄指向个对象, Systemverilog将释放该对象的内存空间(对象的销毁)
◆将句柄设置为null,将手动释放所有的句柄(销毁所有)
◆可以通过句柄来使用对象中的成员变量和方法
实例:
Transaction t1, t2; //声明两个句柄
t1 = new();//创建一个对象,并将t1指向该对象
t2 = new();//创建一个对象,并将t2指向该对象
t1 = t2; //将t2的值赋予t1,这时t1和t2指向同一对象,t1之前指向的对象被释放
t1.display();//调用对象的成员方法
t1.addr = 32'h42;//给对象成员变量赋值
t2 = null; //t2指针悬空,很危险;但t1仍指向该对象
对象的复制(深复制和浅复制)
shallow copy浅复制:new复制创建了一个新的对象,原对象的值被盲目的抄写到目的对象中,但如果类中包含了一个指向另一个类的句柄,那么只有最高一级的对象被new操作符复制,下层的对象不会被复制,举例说明

(在实例中,class B中例化了class A的一个对象,在使用new复制的时候A对象并没有被复制,b1 b2里的a指向的是同一个A对象,所以使用b2句柄修改j会影响到b1里面的j)
{注意:b2 = new b1 和 b2=b1的区别 b2 = new b1 分配两个内存空间 ,但内容一样;
b2=b1,为句柄赋值,两个句柄都指向同一个内存空间}
Deep copy 深复制 :当使用深复制时,所有的变量都会被复制,包括下层的对象。
Transaction b1,b2;//声明两个句柄b1,b2
b1=new; //给b1分配内存空间
b2=new; //给b2分配内存空间
b2.copy(b1); //深复制,其中一个改变,不会影响另外一个

静态变量和静态方法
静态变量
1、class内部声明的变量默认为动态变量,其生命周期始于对象创建,终于对象销毁
2、使用static声明class内部的变量时,其为静态变量。其生命周期始于编译阶段,贯穿于整个 仿真阶段
3、 静态变量的引用:使用类名直接引用(Transaction::count)或者通过例化对象引用(tr.count)
静态方法:静态方法内可以声明并使用动态变量,但不能使用类的动态成员变量
2 类的成员
(概述、protected和local、定义成员方法、类的封装)
概述
类是成员变量和成员方法的载体,一个类的功能应当尽可能的简单,不应承担过多的职责,更不应承担不符合它的职责,这在设计模式中称为单一职责(Single Responsibility Principle)
类作为载体,也具备天生的闭合属性,即将其属性和方法封装在内部,不直接暴露给外部。通过protected和local关键词来设置成员变量和方法的外部访问权限。所以封装属性在设计模式中称为开放封闭原则。
类中的成员默认是public,子类和外部均可访问。
但如果指明了类型为protected,那么只有该类或者子类可以访问;如果访问类型为local,那么只有该类可以访问成员,子类和外部均不可访问。
class clock;
local bit is_summer = 0;
local bit nclock = 6;
function int get_clock();
...
endfunction
endclass
clock ck;
initial begin
ck = new();
$display("now time is %0d",ck.get_clock);//成功访问
ck.set_summer(1);
$display("now time is %0d",ck.nclock); //报错,外部句柄不能访问local变量
end
类的封装
类和结构体的异同:
两者都可定义数据成员
类在声明之后,需要构造函数new才能实例化对象,而struct在声明后就已经开辟内存
类中可以声明数据变量和方法(保存和处理数据),但struct不能含有方法(只能存储数据)
类和module的异同
从数据和方法定义来说,两者都可作为封闭的容器来定义和存储数据
从例化来看,模块必须在仿真一开始就确定是否应该被例化,其内部变量都是静态的;而类可以在仿真的任何阶段创建新的对象(开辟内存),其内部变量为动态的.
从封装性来看,模块内部的方法和变量对外部都是开放的;而类可以根据需要来确定外部访问权限为public/protected/local
从继承性来看,模块没有任何继承性可言,而继承正是类的一大特点。
类的迷思
可以在哪里定义类:module interface program package
可以在类中再声明类的成员吗:可以,类也是一种数据载体
this是什么: this.x表明所调用的成员为当前类的成员,而非同名的局部变量或形式参数
类的编译顺序:先编译基本类,再编译高级类。
3 类的继承
(概述、实例说明、成员覆盖)
概述
- 继承的特点是可以在在原来的类的属性基础上增加新的属性
- 把通用型的代码同一编写在一个基类(父类)中(base class) ,在派生类(子类)中添加新的属性。
- 优点:可以重用前期项目中经过验证和检查的bug少的类;不会破坏原有的代码架构;
实例


class basic_test; //父类
int def = 100;//成员变量赋予默认值
int fin;
task test(stm_ini ini);
$display("basic_test::test");
endtask
function new(int val);
...
endfunction
endclass
class test_wr extends basic_test;//子类wr
function new()
super.new(def);//子类函数在定义new函数时,应先调用父类函数的new函数 super.new()
$display("test_wr::new");
endfunction
task test(stm_ini ini);
super.test(ini);
$display ("test_wr::test");
...
endtask
endclass
class test_rd extends basic_test;//子类rd
function new()
super.new(def);
$display("test_rd::new");
endfunction
task test(stm_ini ini);
super.test(ini);
$display ("test_rd::test");
...
endtask
endclass
this和super: 子类中使用this.xxx是先访问子类有没有xxx的变量或者方法,而super.xxx直接访问父类的变量和方法,不会访问子类。如果没有super和this 来指示作用域,则依照从近到远的原则来引用变量(函数内部的局部变量=>当前类的成员变量=>父类或更底层的类中变量)
句柄的使用
句柄可以作为形式参数通过方法来完成对象指针的传递
也可以在方法内部首先完成修改,再由外部使用
在程序执行时,可以在任何时刻为句柄创建新的对象,并将新的指针赋值给句柄
4 包的使用
(包的意义、包的定义、包和库的区分 、包的命名规则、包的使用)
包的意义
可以将不同模块的验证环境放入不同 的package当中,这样就可以通过package来解决类的归属问题
包的定义
实例说明
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)
包的命名规则与使用
module mcdf_tb;
import regs_pkg::*; //import完成包中所有类的导入
import arb_pkg::*;
regs_pkg::regs_mon mon1 = new();
arb_pkg::arb_mon mon2 = new();
endmodule
- package的名称应独一无二,其内部的类也应尽可能独一无二
- 通过include关键词完成类在包中的封装,要注意编译的前后顺序来放置各个’include类文件
- 通过import完成包中所有类或者某个类的导入,否则类将不会被识别
————————————————
版权声明:本文为CSDN博主「持续学习_ing」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_59670134/article/details/119190972
本文介绍了面向对象编程(OOP)的基本概念,包括类和对象的定义、封装、继承、多态等核心特性。同时探讨了类与结构体的区别,以及如何在SystemVerilog中实现对象的创建、复制和管理。
4900

被折叠的 条评论
为什么被折叠?



