【IC验证】systemverilog_类
systemverilog_类
- 一.概述
- 1.定义
- 2.术语
- 3.模块、结构体、类的区别
- 二.类的使用
- 1.语法
- 2.构造函数
- 3.静态与动态
- 4.this
- 5.`类的拷贝`
- 6.注意点
- 三.类的三大特性
- 1.概述
- 2.封装
- 3.继承
- (1)语法
- (2)例子
- a 例子1
- b 例子2
- c `例子3`
- (3)注意
- 4.多态
- (1)例子
- $.特殊注意点
- (1)代码执行时刻1( `待确定 `)
- (2)代码执行时刻2
- (3)类对象的创建与释放
- (4)静态与动态
- (5)类的队列
- (6)类的传参
- (7)super和this
一.概述
1.定义
类(class)是一种可以包含数据和方法(function,task)的类型。
2.术语
对象(object):类在例化后的实例;
句柄(handle):指向对象的指针,注意例化名即为句柄;
原型(prototype):程序的声明部分,包含程序名、返回类型和参数列表;
null:当仿真时,句柄的状态是null,表示句柄没有指向任何实例(为空);
3.模块、结构体、类的区别
(1)结构体和类
结构体只包含数据,类包含数据和方法;
(2)模块和类
模块只用实例化,且必须在过程块外实例化;
类需要先声明,再实例化;
类的声明既可以在模块和类中也可以在过程块和方法中,类的实例化既可以在模块和类中在声明时同时进行也可以在过程块和方法中;
模块中的成员默认是静态的,类中的成员默认是动态的;
(3)例子
//class
class Myclass1;int data1;
endclass
//module
module Mymodule1;int data1;
endmodule
//struct
typedef struct {int data1;
} Mstruct1;module tb;Myclass1 c1 = new();Myclass1 c2;initial beginMyclass1 c3 = new();c2 = new();$display("print");endMymodule1 m1();//***** error *******// initial begin// Mymodule1 m2();// end//Mstruct1 s1;initial beginMstruct1 s2;end
endmodule
二.类的使用
1.语法
(1)定义语法
class 类名;//成员变量...//构造函数function new(参数列表);...endfunction//方法function ... ();...endfunctiontask ... ();...tndtask
endclass
(2)例化语法
声明和例化可以一步完成:
类名 句柄名 = new();
声明和例化可以分布完成:
类名 句柄名;句柄名 = new();
2.构造函数
A 说明
类在定义时,需要定义构造函数,如果未定义系统会自动定义一个空的构造函数;
类在实例化时,会按照构造函数,创建对象并分配存储空间;
B语法
function new(参数列表);...endfunction
C 注意
构造函数没有返回值(唯一一个默认没有返回值的函数)
;
3.静态与动态
静态与动态是systemverilog中一个非常重要的概念,也是systemverilog与verilog一个非常重要的区别(扩展),这个概念单独讲解。
链接: link
4.this
作用:
用来索引当前类的对象的成员(变量、参数、方法);
注意:
this只能用在类的非静态成员,约束和覆盖组中;
方法中的变量,查找原则是就近原则;
所以方法内部的变量查找的顺序是:方法内部定义的变量或者参数->类中定义的变量->全局变量,最先找到即对应;
例子:
class Myclass2;int data1 = 1;function new(int data1 = 2);$display("this.data1 = %0d",this.data1);$display("data1 = %0d",data1);endfunction
endclass
module tb2;Myclass2 c2;initial beginc2 = new();end
endmodule
结果:
说明
构造函数中的参数data1和类中的成员data1同时存在,this指向的是类中的成员data1,data1指向的是构造函数中的data1;
5.类的拷贝
(1)概述
类的拷贝分为三种:句柄拷贝、浅拷贝和深拷贝;
(2)准备知识
使用类(例Myclass)时,需要先声明一个句柄(c1),句柄初始值为空(null);
类在例化后会创建对象并分配对象的存储空间,句柄的值就是对象存储空间的起始地址,即句柄指向对象的存储空间;
(3)句柄拷贝
说明:
将句柄的地址值赋值给新的句柄,两个句柄指向同一个对象,不会创建新对象;
总结:新旧句柄指向同一对象
;
语法:
新句柄名 = 旧句柄名;
例子:
class Myclass3;int data1 = 1;
endclass
module tb3;initial beginMyclass3 c3_1,c3_2;c3_1 = new();c3_2 = c3_1;c3_1.data1 = 5;$display("c3_1.data1 = %0d",c3_2.data1);end
endmodule
结果:
(4)浅拷贝
说明:
新的句柄会指向新的对象,新对象各成员的值是原对象各成员值拷贝过来的,包括内部句柄的地址值
也一样(即指向同一个对象);
总结:新旧对象内部句柄指向同一对象
;
语法:
新句柄名 = new 旧句柄名;
例子:
class myclass_B;int c = 2;
endclass
class myclass_1;int a = 1;myclass_B b;function new();b = new();endfunction
endclass
module tb4;myclass_1 c1,c2;initial beginc1 = new();c2 = new c1;c1.a = 3;$display("c4b2.data4b = %0d",c2.a);c1.b.c = 4;$display("c4b2.c4a.data4a = %0d",c2.b.c);end
endmodule
结果:
(5)深拷贝
说明:
新的句柄会指向新的对象,新对象各成员的值是原对象各成员值拷贝过来的,但是句柄的地址值是不同的(即指向一个新的相同对象);
总结:新旧对象内部句柄指向不同对象
;
注意:
systemverilog中没有深拷贝的内部语法,需要自己写深拷贝函数进行实现。
语法:见例子
例子:
结果:
6.注意点
(1)静态成员和方法的存储空间
类中的静态成员和方法,并不是和实例化对象一块存储的,而是单独分配的存储空间;
三.类的三大特性
1.概述
类的三大特性是封装、继承和多态;
2.封装
(1)概念
将相关联的成员和方法进行打包,仅提供接口和使用方法,而不提供内部实现的细节;
(2)优点
保证代码的安全性,代码易于使用和维护。
(3)语法
成员不加修饰:成员可以在当前类的内部、当前类的外部和子类中索引;
成员用local修饰:成员仅可以在当前类的内部进行使用;
成员用protected修饰:成员仅可以在当前类的内部和子类中索引;
3.继承
(1)语法
class 子类名 extebds 父类名;endclass
(2)例子
a 例子1
说明:
子类会继承父类的所有成员
代码:
class packet;integer i = 1;function new();i = 2;endfunctionendclassclass linkedpacked extends packet;endclassmodule tb;initial begin : toppacket p = new();linkedpacked lp= new();$display("p.i = %0d",p.i);$display("lp.i = %0d",lp.i);end
endmodule
结果:
b 例子2
说明:
当子类中定义了和父类中的同名成员,子类和父类中的这个成员同时存在,子类中直接索引的是子类中的这个成员,可以在子类中用super关键字索引到父类中的这个成员;
代码:
class packet;integer i = 1;function new();i = 2;endfunctionendclassclass linkedpacked extends packet;integer i = 3;
endclassmodule tb;initial begin : toppacket p = new();linkedpacked lp= new();end
endmodule
结果:
c 例子3
说明:
当子类和父类同时定义了构造函数时,在运行子类的构造函数时,会默认先运行父类的构造函数再运行子类的构造函数;
代码:
class packet3;integer i = 1;function new();i = 2;endfunction
endclass
class linkedpacket3 extends packet3;function new();i = 3;endfunction
endclassmodule tb3;initial beginlinkedpacket3 lp3 = new();$display("lp3.i = %0d",lp3.i);end
endmodule
结果:
先执行i=1,再执行父类构造函数i=2,再执行子类构造函数i=3;
注意:
只有构造函数会,其他函数不会
;
只有父类构造函数不需要传参时才行,当父类构造函数需要传参时,需要手动调用super.new(参数);
(3)注意
a建议子类的成员变量和父类同名;
4.多态
(1)例子
a 例子1
说明:
子类句柄可以赋值给父类句柄,父类句柄也仅能访问这个子类的父类空间;
代码:
class packet;int i = 1;function new();i = 2;endfunction
endclassclass linpacket extends packet;int i = 3;function new();i = 4;endfunction
endclassmodule tb;initial beginpacket p;linpacket lp;lp = new();p = lp;$display("i = %0d",p.i);end
endmodule
结果:
b 例子2
说明:
父类句柄不可以赋值给子类句柄,编译会报错。
代码:
class packet;int i = 1;function new();i = 2;endfunction
endclassclass linpacket extends packet;int i = 3;function new();i = 4;endfunction
endclassmodule tb;initial beginpacket p;linpacket lp;p = new();lp = p;end
endmodule
结果:
特殊用法:(自行延伸)
$cast()
$.特殊注意点
(1)代码执行时刻1( 待确定
)
定义:
静态成员的创建和初始化在编译时刻,动态成员的创建和初始化是在仿真执行时刻。
例子:
class packet_c1;function new();endfunction
endclassmodule tb;reg [31:0] a =0; packet_c1 c1_1 = new();
endmodule
结果:
编译后的成员变量:(静态变量)
仿真一个单位之间后的成员变量:
说明:
tb中的成员a是静态的,所以声明和初始化都在编译时执行;
tb中声明的类c1_1也是静态的,所以在编译时刻执行;
类的实例化是动态操作,所以是在仿真开始后执行。
(2)代码执行时刻2
定义
模块中代码的执行顺序是,在编译阶段会执行成员变量声明语句(同时初始化会执行初始化语句)和initial过程块的声明的成员变量语句(同时初始化会执行初始化语句),仿真开始后才会执行后续操作语句(比如变量声明和赋值语句分开,赋值语句仿真开始后才会执行)。
例子:
module tb;reg [31:0] a =0; reg [31:0] b;packet_c1 c1_1 = new();initial begin : proc_1b = 1;end
endmodule
结果:
编译后的成员变量:(静态变量)
仿真一个单位之间后的成员变量:
(3)类对象的创建与释放
定义
在软件编程时,对象使用前需要先创建对象,在对象使用后为避免占用过多空间需要将对象释放掉;
在systemverilog中,通过调用构造函数实例化创建对象,对象释放是系统自动实现的当没有任何句柄指向该对象时对象会被自动释放掉。
(4)静态与动态
模块中的成员一般默认是静态的,但是类的实例化一定是动态的,类中的成员默认是动态的;
(5)类的队列
说明:
除了数组以外,类也可以创建队列,队列是由事务句柄构成;
例子:
lass class1;int data1 = 1;
endclassmodule tb;class1 c1[$];initial beginfor(int i = 0;i <= 5;i++)beginc1[i] = new();endend
endmodule
结果:
(6)类的传参
说明:
除了数组以外,类也可以作为参数传递,参数传递本质是句柄拷贝;
例子:
class trans;bit [3:0] src;bit [3:0] dst;bit [7:0] data [];function set_mem(bit [3:0] src,bit [3:0] dst,bit [7:0] data []);this.src = src;this.dst = dst;this.data = data;endfunction
endclassclass store_tran;trans mt[$];function void get_tran(trans tran1);mt.push_back(tran1);endfunction
endclassmodule tb;store_tran st;initial begin : proc1trans p;st = new();p = new();p.set_mem(1,3,'{8'haa,8'hbb});st.get_tran(p);p = new();p.set_mem(2,4,'{8'hcc,8'hdd});st.get_tran(p);end
endmodule
结果:
(7)super和this
super和this关键字仅能在类的内部使用。