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