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進行預定義,則會編譯不通過。