[Systemverilog学习笔记] class
文章目录
一、Class 基础知识
class的定义是什么?
class是一种用户自定义的数据结构,一个OOP()的构造结构,可以封装对应数据并产生对数据的任务和方法;
亦可以表述:包含变量和子程序的基本构造块;
class中有什么?
封装的数据类型和操作数据类型的任务和函数。
存在以下常用名称:
属性 (property) :一个类中的数据类型;
方法 (Method):操作数据的任务和函数;
构造函数 (constructor) : 类中定义的new()函数,在类创建时自动调用;
句柄 (handle) : 指向对象的指针 ;
对象 (Object) : 一个创建的某个类的实例,需要使用new()函数进行创建,否则值为null;
如何定义一个类,并且使用它?
class Myclass; //class name
bit [2:0] header; //property
bit encode;
bit [2:0] mode;
bit [7:0] data;
bit stop;
// constructor
function new(bit [2:0] header = 3'h1, bit [2:0] mode = 5);
this.header = header;
this.encode = 0 ;
this.mode = mode ;
this.stop = 1 ;
endfunction : new
//Method
function display();
$display("Header = 0x%0h,Encode = 0x%0b,Mode = 0x%0h,Stop = 0x%0b",
this.header,this.encode,this.mode,this.stop);
endfunction : display
endclass : Myclass
module tb_top ();
Myclass pkt0,pkt1;
initial begin
pkt0 =new(); //use default parameter
pkt0.display();
pkt1 =new(3'h2,2h'h3);
pkt1.display();
end
endmodule
代码分析:
1、new函数–构造函数
类中定义的new() 即自定义构造函数,当tb_top模块中例化一个对象的时候,会为该对象申请一个新的内存块来保存对象的变量,大小为类中所定义的属性大小之和,然后初始化变量,当使用系统new函数(class中未自定义new)时,会将变量设置为默认值;当使用自定义new函数时,可以初始化默认值成我们想要的数值;
new函数会返回一个指向类的对象的句柄,其类型就是类本身,所以不能有返回值;
Sytemverilog 会优先调用赋值操作符左边的句柄类型,如下所示
class Transcation;
//...
endclass : Transcation
class Driver;
Transcation tr;
function new();
tr =new(); //会调用Transcation类中的new函数
endfunction : new
endclass : Driver
new() 和new[]的区别:
new(pram1,…,pramn) : 仅创建一个对象;
new[pram]:创建了一个含有pram个类的类数组,如下所示
module tb_top ();
Myclass pkt0[3];
initial begin
for (int i = 0; i < 3; i ++) begin
pkt0[i] =new(); //use default parameter
pkt0[i].display(); //通过class_name.method_name 调用对应方法
end
end
endmodule
2、this 关键词和作用域
this
this: 时一个预定义的对象句柄,指的是调用所使用方法的对象;被用于指代类性质,参数和当前实例的方法。只能在非静态方法、约束和覆盖组中使用
class Packet;
bit [31:0] addr;
function new(bit [31:0] addr);
//类变量名addr = 局部变量addr
this.addr = addr; //将方法中参数的值赋值给本地对象
endfunction : new
endclass : Packet
当存在歧义时,需要使用this关键词来指定对方法中类成员的访问
作用域
作用域是一个代码块,例如一个module、process、class、task、function 、begin-end块。
foreach和for循环会自动创建一个块,其下标变量可以作为该循环作用域的局部变量来声明和创建;
当使用一个变量名时,Systemverilog首先会在当前作用域内寻找,接着在上一级作用域内寻找,直到找到改变量
3、Super 关键词
是什么? :在"子类"中使用Super关键词来引用“基类”的属性和方法;如果“父类”中的属性和方法被子类重写,则必须要使用super关键词来对其进行访问;
Super只能使用在派生自父类的子类中
父类 (parent class) :也称作为基类
如上图中所示,父类就是Class B_C,子类从父类Class B_C中进行拓展生成,使用如下格式
class Myclass;
bit [2:0] header;
bit encode;
bit [2:0] mode;
bit [7:0] data;
bit stop;
function new(bit [2:0] header = 3'h1, bit [2:0] mode = 5);
this.header = header;
this.encode = 0 ;
this.mode = mode ;
this.stop = 1 ;
endfunction : new
function display();
$display("Header = 0x%0h,Encode = 0x%0b,Mode = 0x%0h,Stop = 0x%0b",
this.header,this.encode,this.mode,this.stop);
endfunction : display
endclass : Myclass
class extclass extends Myclass;
function display();
super.display();
$display("[child]Header = 0x%0h,Encode = 0x%0b,Mode = 0x%0h,Stop = 0x%0b",
header,encode,mode,stop);
endfunction : display
endclass : extclass
module tb ();
Myclass p;
extclass extp;
initial begin
ep = new();
p =new();
extp.display();
end
endmodule
4、静态变量和全局变量
静态变量
静态变量: 在类中创建的静态变量将被这个类的所偶有实例所共享,并且它的使用范围仅限于这个类。即不管创建了多少个对象,这个类中的静态对象只存在一个,可以理解为静态变量保存在类中而非对象;
静态变量通常在声明时进行初始化,并且可能需要另一个静态变量来作为标志,标识原始变量已被初始化
一般使用方法如下所示
static variable_type name;
class Transcation;
static int count = 0; 、
int id = 0;
function new();
id = count++;
endfunction : new
endclass : Transcation
Transcation t1,t2;
initial begin
t1 = new();
t2 = new();
$display("second id = %0d,count =%0d",t2.id,t2.count);
//可以通过使用类名+ "::"操作符号来引用静态句柄
$display("%d Transcation were created",Transcation::count); //引用静态句柄
end
静态方法
静态方法:与静态变量一样,我们也可以使用 static 修饰方法,称为静态方法或类方法
静态方法遵循所有类的作用域和访问规则,但唯一的区别时它调用它时,它可以没有对应的实例,静态方法中可以直接调用同类中的静态成员,但不能直接调用非静态成员。静态方法也不能加virtual
关键词。同时在调用静态函数时,需要通过作用域运算符来进行::
静态初始化块只在类加载时执行,且只会执行一次,同时静态初始化块只能给静态变量赋值,不能初始化普通的成员变量
class Transcation;
static int count = 0; //静态变量
int id = 0;
function new();
id = count++;
endfunction : new
static function display_static();
$display("Transaction count = %0d",count);
endfunction : display_static
static function display_static_err(); //在静态函数中调用非静态成员会导致编译错误
$display("Transaction count = %0d",id);
endfunction : display_static_err
endclass : Transcation
Transcation t1,t2;
initial begin
t1 = new();
t2 = new();
$display("second id = %0d,count =%0d",t2.id,t2.count);
//可以通过使用类名+ "::"操作符号来引用静态句柄
$display("%d Transcation were created",Transcation::count); //引用静态句柄
Transcation::display_static(); //引用静态函数
end
编译顺序-typedef
当编译类时,这个类中包含一个尚未定义的类。声明这个被包含的类的句柄会引起错误,编译器会在此类的后面去找这个尚未定义类的定义,此时需要使用typedef 语句对类名进行声明
typedef class call_class;
class Transaction;
call_class name;
...;
endclass
class call_class;
...;
endclass
typedef也可以和参数化对象一起使用
typedef XYZ;
module top;
XYZ #(8'h3f,real) xyz0;
XYZ #(.ADDR(8'h(60),.T(real)) xyz1;
class XYZ #(parameter ADDR = 8'h00, type T = int);
endclass
4、Inheritance -继承
继承是类中的一个概念,可以通过拓展来得到另外一个类,并且可以通过句柄获取基类中的所有方法和属性,可以让我们在不改变基类的情况下在新的类中添加新的属性和方法;
如上代码所示,使用如下格式声明新类对基类进行继承
class extclass extends base_class_name;
...
endclass : extclass
5、polymorphism - 多态
是什么 : 多态指调用相同的名字和方法,得到的结果是不同的
virtual
父类中的方法可以声明为virtual,在子类中可以声明相同名称不同功能的函数,将父类中的函数覆盖掉,但此种使用方法下两个类的返回值类型和参数的原型应保持不变
虚方法可参考如下文章
https://blog.csdn.net/liujingyu_1205/article/details/81563010
https://blog.csdn.net/immeatea_aun/article/details/89216857
6、类的参数化
参数化类可以在实例化时实现不同的数组大小和数据类型;
同时也可将数据类型作为参数进行传递,
class stack#(type T = int);
T item;
function T add_a(T a);
return item + a;
endfunction : add_a
//表示此函数在外部实现
extern function T add_b(T b);
endclass : stack
function T add_b(T b);
return item - b;
endfunction : add_b
module tb;
stack st;
stack #(bit[3:0]) bs;
stack #(real) rs;
stack #(float) fs;
initial begin
st =new();
bs =new();
rs =new();
st.item = -456;
$display("st.item = %0d",st.add(10));
bs.item = 8'hA1;
$display("st.item = %0d",st.add(10));
rs.item = 3.14;
$display("st.item = %0.2f",st.add(10));
fs.item = 12.34;
$display("fs.item = %0.2f",st.add(10));
end
endmodule
7、extern和local
extern
语法
class <name_of_class> #(<parameters>)
class Trans #(addr = 32);
<name_of_class> #(<parameters>) <name_of_inst>;
Trans #(.addr(16)) obj;
local
被声明为local的成员仅可用于同一类的方法中,同时不能被子类访问。但是访问local成员的非本地方法可以被子类继承和覆盖