SV——面向對象編程基礎


1、OOP術語

a.類(class):包含變量和子程序(函數或者任務)的基本構建塊。Verilog中與之對應的是模塊(module)。

b.對象(object):類的一個實例。在Verilog中,你需要實例化一個模塊才能使用它。

c.句柄(handle):指向對象的指針。在Verilog中,你通過實例名在模塊外部引用信號和方法。一個OOP句柄就像一個對象的地址,但是它保存在一個只能指向單一數據類型的指針中。

d.屬性(property):存儲數據變量。在Verilog中,就是寄存器(reg)或線網(wire)類型的信號。

e.方法(method):任務或者函數中操作變量的程序性代碼。Verilog模塊除了initial和always塊以外,還含有任務和函數。

f.原型(prototype):程序的頭,包括程序名、返回類型和參數列表。程序體則包含了執行代碼。

類是對象的一個模板,其內部定義了數據和方法。對象是類的一個例化和實現。

注:《SystemVerilog驗證 測試平台編寫指南》中使用 變量(variable) 程序(routine),而沒有使用OOP中的屬性(property)和方法(method)。

2、用戶使用對象的三個步驟

a.定義類:

class packet;
...
endclass:packet

b.在module、class、function、task等地方聲明對象:

packet my_packet;    //聲明一個句柄
packet packet_array[32];

對象標識符(my_packet/packet_array)是例化該對象的句柄(指向對象的指針)。當該對象被創建的時候,該句柄有效,默認情況下句柄將為空(null)。

c.通過構造函數new創建對象的例化

通過new這個構造函數給對象分配內存空間,並且把入口地址賦給對象的句柄:

my_packet=new(168);        //為一個packet對象分配空間

例1 簡單的Transaction類

class Transaction;
    bit [31:0] addr,crc,data[8];

     function void display;
         $display("Transaction:%h",addr);
     endfunction:display

    function void calc_crc;
         crc=addr^data.xor;
    endfunction:calc_crc

endclass:Transaction

3、在哪里定義類?

program、module、package中,或者在這些塊之外的任何地方。

例2 Class in a package

//File abc.svh
package abc;
calss Transaction;
//Class body
endclass
endpackage

例3 Importing a package in program

program automatic test;
import abc::*;
Transaction tr;
//Test code
endprogram

sv中,激勵對象不斷的被創建並且用來驅動DUT,檢查結果。最后這些對象所占用的內存可以被釋放,以供新的對象使用。

例4 聲明和使用一個句柄

Transaction tr;    //聲明一個句柄
tr=new();     //為 Transaction對象分配空間 

注:
1、調用new函數例化一個對象,即在為該對象申請一個新的內存塊來保存對象的變量。
2、new除了分配內存外,還初始化變量。default 二值變量為0,四值變量為X。
3、new函數不能有返回值,因為構造函數是返回一個指向類的對象的句柄,其類型就是類本身。

例5.4 簡單的用戶定義的new()函數

class Transaction;
    logic [31:0] addr,crc,data[8];
    function new;
        addr = 3;        //固定值
        data= '{default:5};        //crc未定義,初始化位默認值為X
    endfunction
endclass

例5 一個帶有參數的new函數

class Transaction;
    logic [31:0] addr,crc,data[8]; 
    function new(logic [31:0] a=3,d=5);
        addr = a;
        data = '{default:5};//crc未定義,默認值為X
    endfunction
endclass
 
initial begin
    Transaction tr;
     tr = new(.a(10));//a=10,d uses default of 5 tr=new(10);
end

sv調用哪個new()函數,取決於賦值操作符左邊的句柄類型。tr是 Transaction的句柄,索引會調用 Transaction的new函數。

例6 調用正確的new函數

class Transaction;
    logic [31:0] addr,csm,data[8];
endclass:Transaction

class Driver;
    Transaction tr;
    function new();//Driver's new function
        tr = new();//Call the Transaction new function
    endfunction
endclass:Driver

4、new()和new[]的區別

調用new()函數僅創建了一個對象,而new[]操作則建立一個含有多個元素的數組。

new()可以使用參數設置對象的值,而new[]只需使用一個數值來設置數組的大小。

通過聲明一個句柄來創建一個對象

例7 為多個對象分配地址

Transaction t1,t2;//聲明兩個句柄
t1=new();    //為第1個Transaction對象分配地址
t2=t1;      //t1和t2都指向該對象
t1=new();    //為第2個Transaction對象分配地址

注:最后t2指向第一次分配的對象,t1指向第二次分配的對象。

5、對象的解除分配(deallocation)

垃圾回收就是一種自動釋放不再被引用的對象的過程。sv分辨對象不再被引用的辦法就是記住指向它的句柄的數量,當最后一個句柄不再引用某個對象了,sv就釋放該對象的空間。

例8 創建多個對象

Transaction t;//創建一個句柄
t=new();    //分配一個新的 Transaction
t=new();    //分配第二個,並且釋放第一個t
t=null;     //解除分配第二個

對對象使用“.”符號來引用變量和子程。

直接訪問變量會限制以后對代碼的修改,因此OOP規定只能通過對象的公有方法訪問對象的變量。

例9 使用對象的變量和子程序

Transaction t;    //聲明一個 Transaction句柄
t=new();    //創建一個 Transaction對象
t.addr=32'h42;    //設置變量的值
t.display();    //調用一個子程序

i++和++i區別:
i++:先引用后增加,先在i所在的表達式中使用i的當前值,后讓i加1。
++i:先增加后引用,先讓i加1,然后在i所在的表達式中使用i的新值。

6、靜態屬性與方法

6.1 簡單的靜態變量

1、每一個類的例化(也就是一個對象)都擁有它自身每個變量的復制,因此OOP規定只能通過對象的公有方法訪問對象的變量。

2、對象1:變量1,變量2…
     對象2:變量1,變量2…
對象1 和 對象2 自身成員變量不和其他對象共享。

3、類的靜態屬性是存在類中的,而不是在每個類的對象中,在類中,使用靜態static方式聲明的屬性可以被多個對象共享。

例10 含有一個靜態變量的類

class Transaction;
    static int count=0;    //已創建的對象的數目
    int id;    //實例的唯一標志
    function new();
        id=count++;    //設置標志,count遞增
    endfunction
endclass
 
Transaction t1,t2;
initial begin
    t1=new();    //第一個實例,id=0,count=1
    t2=new();    //第二個實例,id=1,count=2
    $display("Second id=%d,count=%d",t2.id,t2.count); //使用句柄來引用靜態變量
end

6.2 例10使用了句柄來引用靜態變量,也可以直接使用類名加上::,即類作用操作符。

例11 類的作用域操作符 ::

class Transaction;
    static int count=0;    //創建的對象數
    ...
endclass

initial begin
    run_test();
    $display("%d transaction were creat",Transaction::count);//引用靜態句柄
end

6.3 靜態變量的初始化

靜態變量通常在聲明時初始化,不能簡單的在類的構造函數中初始化靜態變量,因為每一個新的對象都會調用構造函數。

7、類的方法

類中的程序也稱為方法,也就是在類的作用域內定義的內部task或function。

例12 為類Transaction和PCI_Tran定義了display()方法。System Verilog會根據句柄的類型調用正確的display()方法。

例12 類中的方法

class Transaction;
    bit [31:0] addr,crc,data[8];
    function void display();
    $display("@%0t:TR addr=%h,crc=%h",$time,addr,crc);
    $write("\tdata[0-7]=");
    foreach(data[i]) $write(data[i]);
    $display();
    endfunction
endclass

class PCI_Tran;
    bit [31:0] addr,data;
    function void display();
    $display("@%0t:PCI:addr=%h,data=%h",$time,addr,data);
    endfunction
endclass

Transaction t;
PCI_Tran pc;

initial begin
    t=new();//創建一個 Transaction對象
    t.display();//調用 Transaction的方法
    pc=new();//創建一個PCI事務
    pc.display();//調用PCI事務的方法
end

類中的方法默認使用自動存儲,所以你不必擔心忘記使用automatic修飾符。

8、在類之外定義方法

塊外方法聲明:

在開始處添加關鍵字extern,將整個方法移至類定義后面,並在方法名前面加上類名和::作用域操作符。

class Transaction;
    bit [31:0] addr,crc,data[8];
    extern function void display();
endclass
 
function void Transaction::display();
    $display("@%0t:TR addr=%h,crc=%h",$time,addr,crc);
    $write("\tdata[0-7]=");
    foreach(data[i]) $write(data[i]);
    $display();
endfunction

class PCI_Tran;
    bit[31:0] addr,data;//使用實名
    extern function void display();
endclass

function void PCI_Tran::display();
    $display(@%0t:PCI:addr=%h,data=%h,$time,addr,data);
endfunction

 


免責聲明!

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



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