SystemVerilog 類和物件(三)

Zing冰發表於2020-12-05

一、類的基本概念

  1. 類class:是一種用來進行資料抽象的工具,將資料和對資料的操作封裝在一起,提供建立物件的模板,可以看做是一種資料結構。
  2. 物件object:是所屬類class的某一特定實體(也稱例項)。
  3. 控制程式碼handle:指向物件的指標,即記憶體的基地址
  4. 屬性property:類class的實體object中所包含的各種變數variable。
  5. 方法method:操作變數的任務task和函式function

img

二、物件的建立和銷燬

class word
    byte nb[];
    function new(int n);
        nb = new[n];        //動態陣列空間開闢
    endfunction
endclass
 
initial begin : initial_1
    word wd;//宣告控制程式碼
    for(int i=1;i<=4;i++) wd = new(i);   //建立了4個物件
end
initial begin : initial_2
    #1ps
    $display("How many Bytes are allocated for word instance?")
end

若wd = new(1)所需開闢的空間為1B,那麼執行完開闢空間:

wd執行完,控制程式碼指向第四個物件,4B。由於initial中是靜態變數,即使initial執行完了,那麼變數還在。

在這裡插入圖片描述

三、物件控制程式碼的深拷貝 與 淺拷貝

1)、 淺拷貝:只拷貝物件中的資料變數,淺拷貝前後的資料變數使用不同的記憶體空間;而對於物件中的**資料操作(任務和函式)**和其中定義的其它類的控制程式碼,採取類似“引用操作”的方式,淺拷貝前後共用同一記憶體空間。

BusTran    bt0, bt1;      //宣告兩個控制程式碼
b1 = new();                 //b1建立物件
b2 = new b1;              //b2建立物件,同時對b1進行淺拷貝

2)、 深拷貝:對於物件中的所有成員統一分配新的記憶體空間,區別於淺拷貝。

BusTran    bt0, bt1;      //宣告兩個控制程式碼
b1 = new();                 //b1建立物件
b2 = new();                 //b2建立物件
b2.copy(b1);              //深拷貝,自定義copy函式

四、類的特性

在這裡插入圖片描述

class clock 
    local bit is_summer = 0;
    local int nclock = 6;
    function int get_clock();
        if(!is_summer) return this.nclock;
        else return this.nclock+1;
    endfunction
    function bit set_summer(bit s);
        this.is_summer = s;
    endfunction
endclass
 
 
clock ck;
    initial begin
        ck = new();
        $display("now tome is %0d", ck.get_clock());
        ck.set_summer(1);
        $display("noe time is %0d", ck.nclock);
end

第一個列印為6;第二個編譯出錯。外部控制程式碼ck不能訪問nclock變數(加了限定local)。但可以訪問沒加限制的function。

4.1.封裝

在這裡插入圖片描述

4.2.繼承

子類繼承了父類所有的成員方法和屬性,並且可以擁有自己特性。通過關鍵字extends實現繼承解決了程式碼的重用問題。

子類既包含繼承父類的成員方法和屬性,也有自己獨特的個性方法(青出於藍而勝於藍),所以如果想要將一個指向父類的指標轉化為指向子類的指標,無法直接轉換,(父親大人,時代變了!)必須通過$cast(),在system veilog中被稱為向下型別轉換(downcasting)。

  • 向上型別轉換
transaction_class       tr = new();       //父類
subtransaction_class    sc = new();       //子類
tr = sc;               //子類(右)指向父類(左),這是正確的可以直接轉換
  • $cast向下型別轉換
transaction_class      tr;//父類
subtransaction_class   sc; //子類
sc = new();
tr = sc;        //控制程式碼型別轉化為同一型別,父控制程式碼型別轉化為子控制程式碼型別
$cast(sc,tr);   // 通過cast方式可以實現,可以看到tr的控制程式碼型別雖然是父類,但其指的物件型別是子類

子類繼承父類時新增加一個函式,該函式與父類中的某函式同名,呼叫時會呼叫新增的函式,而不是繼承下來的函式。

  1. 如果父類與子類的函式同名,但是引數不同,此時不論有無關鍵字virtual,父類的函式都將被隱藏。
  2. 如果父類與子類的函式同名,引數也相同,但是基類函式無關鍵字virtual,父類的函式都將被隱藏。(如果父類函式有virtual,則會形成多型)

對於子類類新增同名函式成員的訪問:“物件名.成員名”;
對於父類中同名函式成員的訪問:“(子類)物件名.基類名::成員名”

4.3.多型

呼叫同一個函式,實現不同的行為就是多型。但要滿足兩點要求:

  1. 必須通過父類的指標或者引用呼叫虛擬函式
  2. 被呼叫的函式必須是虛擬函式,且子類必須對基類的虛擬函式進行重寫

虛擬函式virtual function

被virtual修飾的類成員函式稱為虛擬函式。虛擬函式是動態繫結的,如果子類需要修改父類的行為(即重寫與基類函式同名的函式),就應該在父類中將相應的函式宣告為虛擬函式。使用虛擬函式應當注意:

  • 父類中某一成員函式宣告為virtual虛擬函式後,子類中的同名函式(同名、同參、同型別)自動生成虛擬函式。
  • 子類的同名虛擬函式會重寫或覆蓋原來父類中的同名虛擬函式.。

虛類virtual class

虛類通過關鍵字virtual宣告,不能被例化(不能建立物件),但可以通過派生,生成有用的子類,也可宣告一個虛類的指標,通過抽象類的指標指向不同的子類物件。進而訪問子類物件的虛擬函式,實現多型性。

正常情況下,父類是不可以訪問子類中方法的,但是通過虛父類可以實現。

module OOP_EXTENDS();
  class BusTran;
     bit [31:0] addr,crc,data[7];
      
     virtual function bit[31:0] calc_crc;      //虛方法目的——實現多型
       crc=addr^data.xor;            //異或:奇數個1異或,結果為1;偶數個1異或,結果為0
       calc_crc=crc;         //返回值 
     endfunction
     
     function void dsp_addr(input string handle);
        $display("*****%s.ADDR = %0h",handle,addr);
     endfunction

     function void dsp_crc(input string handle);
        $display("*****BusTran %s.crc = %h",handle,crc);
     endfunction

   endclass
    
   class BadBusTran extends BusTran;       //繼承
     bit  bad_crc;
      
     virtual function  bit[31:0]  calc_crc();   //虛方法目的——實現多型; 派生類中的同名函式的virtual可以省略
        super.calc_crc();                //通過關鍵字super,實現子類對父類成員的呼叫
        if(bad_crc)  crc = ~crc;
           calc_crc = crc;
     endfunction

   endclass

   BusTrans   bt0;       //宣告父類控制程式碼
   BadBusTran bbt0;      //宣告父類控制程式碼
 
   initial begin
      bt0 = new;
      $display("*****handle bt0 is : %0h",bt0);
      bbt0 = new;
      $display("*****handle bbt0 is : %0h",bbt0);
       
      bt0.addr = 32'hFFFF_FFFF;
      bt0.dsp_addr("bt0");
      bbt0.addr = 32'h1111_1111;
      bt0.dsp_addr("bbt0");      
       
      foreach(bt0.data[i]) begin
        bt0.data[i] = 32'h0000_FFFF;
      end
      bt0.calc_crc;
      bt0.dsp_crc("bt0");

      foreach(bbt0.data[i]) begin
        bbt0.data[i] = 32'hFFFF_FFFF;
      end
      bbt0.calc_crc;           //此時未設定bad_crc,二值變數預設初始值為0
      bbt0.dsp_crc("bbt0");
      bbt0.bad_crc = 1;
      $display("*****After bad crc set to 1");
      bbt0.calc_crc;
      bbt0.dsp_crc("bbt0");
   end  

//Polymorphism——多型
   function bit[31:0]  crc(BusTran  bt);          //多型——控制程式碼傳遞過程中,會根據控制程式碼自動識別所屬類
      crc = bt.calc_crc();
   endfunction
    
   bit [31:0] crc_value;
   initial begin 
      #10;
      crc_value = crc(bt0);
      $display("*****crc from bt0 = %0h",crc_value);
      crc_value = crc(bbt0);
      $display("*****crc from bt0 = %0h",crc_value);      
   end

endmodule

在這裡插入圖片描述

包的使用

在這裡插入圖片描述

參考:

Mr.翟的部落格

Systemverilog(綠皮書)

相關文章