SV -- Class 类
SV -- Class 类
0. 基础
定义: class name;
实例化: .new()
类中可以包含function, task
class sv_class;
//class properties
int x;
//method-1
task set(int i);
x = i;
endtask
//method-2
function int get();
return x;
endfunction
endclass
- 当类内的成员函数的输入变量跟类内的成员变量同名时,会有歧义,可以使用this.来指定类的成员变量
1. static
可以指定类的成员变量或函数任务为静态。
多个类的实例共享静态变量。
class packet;
//class properties
byte packet_id;
//static property to keep track of number of pkt's created
static byte no_of_pkts_created;
//constructor
function new();
//incrementing pkt count on creating an object
no_of_pkts_created++;
packet_id = no_of_pkts_created;
endfunction
//method to display class prperties
function void display();
$display("--------------------------------------");
$display("\t packet_id = %0d",packet_id);
$display("--------------------------------------");
endfunction
endclass
module static_properties;
packet pkt[3];
initial begin
foreach(pkt[i]) begin
pkt[i] = new();
pkt[i].display();
end
end
endmodule
输出:
--------------------------------------
packet_id = 1
--------------------------------------
--------------------------------------
packet_id = 2
--------------------------------------
--------------------------------------
packet_id = 3
--------------------------------------
注意:
- 如果是静态function,则调用的变量也需要是静态的。
- 静态的变量和function可以直接通过句柄访问,无需实例化
2. shallow copy
语法:
packet pkt_1;
pkt_1 = new();
packet pkt_2;
pkt_2 = new pkt_1; //shallow copy
浅复制会复制所有类成员,开辟新的存储空间。但类中的类(内部类)不会被复制,而是会共享同一个内存空间:
相比于类的赋值:
类的赋值只是将句柄指向实例,所以内存空间是共享的,改变其中一个会作用到另一个身上。而浅赋值则不会。
3. deep copy
相比于浅复制,深复制会复制类的所有成员,包括内部类。但是需要为所有内部类都添加深复制方法:
示例代码:
//-- class ---
class address_range;
bit [31:0] start_address;
bit [31:0] end_address ;
function new();
start_address = 10;
end_address = 50;
endfunction
//copy method
function address_range copy;
copy = new();
copy.start_address = this.start_address;
copy.end_address = this.end_address;
return copy;
endfunction
endclass
//-- class ---
class packet;
//class properties
bit [31:0] addr;
bit [31:0] data;
address_range ar; //class handle
//constructor
function new();
addr = 32'h10;
data = 32'hFF;
ar = new(); //creating object
endfunction
//method to display class prperties
function void display();
$display("---------------------------------------------------------");
$display("\t addr = %0h",addr);
$display("\t data = %0h",data);
$display("\t start_address = %0d",ar.start_address);
$display("\t end_address = %0d",ar.end_address);
$display("---------------------------------------------------------");
endfunction
//copy method
function packet copy();
copy = new();
copy.addr = this.addr;
copy.data = this.data;
copy.ar = ar.copy;//calling copy function of tr
return copy;
endfunction
endclass
// -- module ---
module class_assignment;
packet pkt_1;
packet pkt_2;
initial begin
pkt_1 = new(); //creating pkt_1 object
$display("\t**** calling pkt_1 display ****");
pkt_1.display();
pkt_2 = new(); //creating pkt_2 object
$display("\t**** calling pkt_2 display ****");
pkt_2.display();
pkt_2 = pkt_1.copy(); //calling copy method
//changing values with pkt_2 handle
pkt_2.addr = 32'h68;
pkt_2.ar.start_address = 60;
pkt_2.ar.end_address = 80;
$display("\t**** calling pkt_1 display after changing pkt_2 properties ****");
pkt_1.display();
$display("\t**** calling pkt_2 display after changing pkt_2 properties ****");
pkt_2.display();
end
endmodule
输出:
**** calling pkt_1 display ****
---------------------------------------------------------
addr = 10
data = ff
start_address = 10
end_address = 50
---------------------------------------------------------
**** calling pkt_2 display ****
---------------------------------------------------------
addr = 10
data = ff
start_address = 10
end_address = 50
---------------------------------------------------------
**** calling pkt_1 display after changing pkt_2 properties ****
---------------------------------------------------------
addr = 10
data = ff
start_address = 10
end_address = 50
---------------------------------------------------------
**** calling pkt_2 display after changing pkt_2 properties ****
---------------------------------------------------------
addr = 68
data = ff
start_address = 60
end_address = 80
---------------------------------------------------------
5. parameterized class
类似参数化的module.
//---- class ----
class packet #(parameter int ADDR_WIDTH = 32,DATA_WIDTH = 32);
bit [ADDR_WIDTH-1:0] address;
bit [DATA_WIDTH-1:0] data ;
function new();
address = 10;
data = 20;
endfunction
endclass
实例化的时候packet #(32,64) pkt;
也可以将一个变量类型作为class的paramter传入:
class packet #(parameter type T = int);
T address;
T data ;
function new();
address = 10;
data = 20;
endfunction
endclass
实例化的时候packet #(bit [31:0]) pkt;
6. 继承
子类继承父类的所有成员变量和方法。子类中可以使用父类定义的变量和方法。
class parent_class;
bit [31:0] addr;
endclass
class child_class extends parent_class;
bit [31:0] data;
endclass
module inheritence;
initial begin
child_class c = new();
c.addr = 10;
c.data = 20;
$display("Value of addr = %0d data = %0d",c.addr,c.data);
end
endmodule
7. 多态(polymorphism)
父类的句柄指向子类实例时,此时调用该句柄的方法实际上只会调用父类的方法,除非使用virtual关键字。将父类中的方法定义为virtual,则指向子类的父类句柄就会根据所指向的子类调用子类方法,这一特性表现为超类句柄的多态。
// base class
class base_class;
virtual function void display();
$display("Inside base class");
endfunction
endclass
// extended class 1
class ext_class_1 extends base_class;
function void display();
$display("Inside extended class 1");
endfunction
endclass
// extended class 2
class ext_class_2 extends base_class;
function void display();
$display("Inside extended class 2");
endfunction
endclass
// extended class 3
class ext_class_3 extends base_class;
function void display();
$display("Inside extended class 3");
endfunction
endclass
// module
module class_polymorphism;
initial begin
//declare and create extended class
ext_class_1 ec_1 = new();
ext_class_2 ec_2 = new();
ext_class_3 ec_3 = new();
//base class handle
base_class b_c[3];
//assigning extended class to base class
b_c[0] = ec_1;
b_c[1] = ec_2;
b_c[2] = ec_3;
//accessing extended class methods using base class handle
b_c[0].display();
b_c[1].display();
b_c[2].display();
end
endmodule
输出:
Inside base class 1
Inside base class 2
Inside base class 3
上面例子中使用了基类句柄指向派生类,此时调用display方法则是使用指向的派生类中的方法。
同样,直接调用派生类的display方法也可以得到正确结果,但是这只是体现了子类对父类函数的修改,并不是多态的概念。
8. 修改成员函数
上面多态的例子其实已经提到了,如果希望子类有独特的自己的父类同名方法,只需要在子类中重新定义该方法,这样实例化子类后调用的方法即为子类重定义的方法。无论父类中该方法是否使用virtual关键字。
所以这里再次强调下,多态只是针对父类句柄而言的。当这类句柄配合virtual关键字后,指向不同的子类可以调用不同的方法。
9. super
如果在子类中重定义了父类发成员函数或变量,此时还想使用父类的成员函数,则使用super.function实现
10. casting
主要分为静态转换和动态转换:
- 静态cast:
i_a = int'(2.1 * 3.2);
- 动态cast:
- 动态类型转换用于将超类指针(引用)安全地转换为类层次结构中的子类指针(引用)
- 动态转换在运行过程中检查,而静态转换是编译时检查
- 语法:
$cast(destination, source)
例子:
class parent_class;
...
endclass
class child_class extends parent_class;
...
endclass
module inheritence;
initial begin
parent_class p=new();
child_class c=new();
c.addr = 10;
c.data = 20;
p = c; //assigning child class handle to parent class handle
c.display();
end
endmodule
子类句柄赋值给父类是可以的。
但是如果换一下:
module inheritence;
initial begin
parent_class p=new();
child_class c=new();
c.addr = 10;
c.data = 20;
c = p; //assigning child class handle to parent class handle
c.display();
end
endmodule
父类句柄赋值给子类会报编译错误。
这也会导致下面这个例子不通过:
module inheritence;
initial begin
parent_class p;
child_class c=new();
child_class c1;
c.addr = 10;
c.data = 20;
p = c; //p is pointing to child class handle c.
c1 = p; //type check fails during compile time.
c1.display();
end
endmodule
p作为父类句柄,可以指向子类,而下面第二个子类句柄c1指向父类句柄p(实际指向的是c),按理说这样的赋值是没问题的。
但是在编译过程,这样的赋值不会通过,要让他通过,需要改成:
$cast(c1,p);
将c1强制转换成p。这一语句相当于跳过了编译过程中对该处的报错,而转到运行过程中检查错误。只要运行过程中,p确实可以被赋值给c1(p已经指向c),则不会报错。
11. 公有和私有
在SV中,所以成员都是公有的,除非标记为local或者protected。应该尽量使用默认值。
- local:
local bit [31:0] tmp_addr;
- 只供类内部访问
- 子类也无法访问
- protected:
protected bit [31:0] tmp_addr;
- 不能供外部访问
- 子类可以访问
12. 抽象类(abstract class)
- 抽象类为子类设置原型(prototype )
- 抽象类不能实例化,只能被继承
- 抽象类可以包含只有一个原型的函数,只做一个方法的定义(纯虚函数)
实例:
//abstract class
virtual class packet;
bit [31:0] addr;
endclass
class extended_packet extends packet;
function void display;
$display("Value of addr is %0d", addr);
endfunction
endclass
module virtual_class;
initial begin
extended_packet p;
p = new();
p.addr = 10;
p.display();
end
endmodule
12. 域分辨符::
域分辨符号可以访问类内的static变量,同时在子类中也可以通过这个操作符访问父类的公有和protected变量。
//class
class packet;
bit [31:0] addr;
static bit [31:0] id;
function display(bit [31:0] a,b);
$display("Values are %0d %0d",a,b);
endfunction
endclass
module sro_class;
int id=10;
initial begin
packet p;
p = new();
packet::id = 20;
p.display(packet::id,id);
end
endmodule
13. External
配合域分辨符可以支持在类之外定义function或task.
- 在定义时需要指定一切关键词(virtual,local, protected)以及所有的变量列表。
- 注意external定义函数的变量名需要和下面详细定义的函数的变量名一模一样
class packet;
//function declaration - extern indicates out-of-body declaration
extern virtual function void display(bit [31:0] addr, data );
endclass
//function implementation outside class body
function void packet::display(bit [31:0] addr_t, data_t);
$display("Addr = %0d Data = %0d",addr_t,data_t);
endfunction
module extern_method;
initial begin
packet p;
p = new();
p.display(20,30);
end
endmodule
上面代码会报错,因为变量名不一致。
14. typedef
为后面的类提供一个预先的定义
typedef class c2;
//class-1
class c1;
c2 c; //using class c2 handle before declaring it.
endclass
//class-2
class c2;
c1 c;
endclass
module typedef_class;
initial begin
c1 class1;
c2 class2;
$display("Inside typedef_class");
end
endmodule
上面的代码中,c1和c2互相依赖,如果没有第一句先对c2进行预定义,则会编译不通过。