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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM