第二章:數據類型
初學SystemVerilog我們在Verilog的基礎之上提供了很多改進的數據結構。本章將分為幾篇文章來講述一下對驗證很有用的數據結構。
2.1 Verilog中的數據類型
通常,在Verilog中我們有兩種常見的數據類型:變量和線網。他們各自有0、1、Z、X這四種狀態。其中最為常見的應用也就是reg型和wire型。
變量
- 無符號的數:reg
- 32比特的有符號數:integer
- 64比特的無符號數或浮點數:time
若幹變量可以被一起存放在定寬數組裏。所有的存儲都是靜態的,意味著所有的變量在整個仿真過程中都是存活的。
線網 wire
線網通常用來連接設計中的不同部分。通常是連接設計模塊的端口。2.1.1邏輯(logic)類型
相對於通常的Verilog的reg類型,logic類型在其基礎之上對其做了改進,使得它不僅可以作為一個變量,還可以被連續賦值、門單元和模塊所驅動。任何使用線網類型的數據均可使用logic。logic不能有多個結構性的驅動,在定義雙向總線的時候,就只能使用wire而不能使用logic。
2.1.2 雙狀態數據類型
相比於四態(0、1、X、Z)的數據類型,SystemVerilog引入了雙態(0、1)的數據類型,這有利於提高仿真器的性能並減少內存的使用量。
無符號單比特的數據類型bit,帶符號的數據類型是byte,shortint,int,longint。- bit b
- bit [31:0] b32,雙狀態,32比特無符號整數
- int unsigned ui,雙狀態,32比特無符號整數
- int i,雙狀態,32位有符號整數
- byte b8,雙狀態,8比特有符號整數,取值範圍為-128~127
- shortint s,雙狀態,16比特有符號整數
- longint l,雙狀態,64比特有符號整數
- integer i4,四狀態,32比特有符號整數
- time t,四狀態,64比特無符號整數
real r,雙狀態,雙精度浮點數
2.2確定寬度的數組
SystemVerilog提供了多個數據類型,功能上也大大增強。
2.2.1定寬數組的聲明和初始化
所有的數組都是以0作為索引的起始點,所以SystemVerilog允許只給出數組寬度的便捷聲明方式。
int i[0:15]; //16個整數[0] [1] ......[15]
int i_1[16]; //16個整數[0][1].......[15]
多維數組的定義:大小為8行4列int array2[0:7][0:3]; //完整定義
int array3[8][4]; //緊湊的聲明
array2[7][3]=1;//對最後一個元素設置為12.2.2常量數組
一個單引號加大括號來初始化數組,註意這裏的單引號不同於編譯器指引或宏定義中的單引號。
int ascend [4]=‘{0,1,2,3}; //對4個元素進行初始化
int descend [5];
descend=‘{4,3,2,1,0}; //對5個元素進行初始化
descend[0:2]=‘{5,6,7};//對前三個元素賦值
ascend=‘’{4{8}};//四個值全部為8
descend =‘{9,8,default:1};//{9,8,1,1,1}2.2.3for和foreach
對數組進行操作的最常見的方式就是使用for或foreach循環。$size函數會自動返回數組的寬度。foreach循環只需要指定數組名稱並在其後面的方括號中給出索引量,SystemVerilog便會自動遍歷數組中的元素。
對一維數組的操作代碼如下:module test_enum(); initial begin bit[31:0] src[5],dst[5]; int i,j; for(int i=0;i<$size(src);i++) begin src[i]=i; $display("src[%0d]=%0d",i,src[i]); end foreach(dst[j]) begin dst[j]=src[j]*2; $display("dst[%0d]=%0d",j,dst[j]); end endmodule
其結果如下:
# src[0]=0 # src[1]=1 # src[2]=2 # src[3]=3 # src[4]=4 # dst[0]=0 # dst[1]=2 # dst[2]=4 # dst[3]=6 # dst[4]=8
對多維數組foreach循環方括號的下標並不是我們想象的[i][j],而是[i,j]。下面以兩個例子來進行說明。
module test_enum(); int md[2][3]=‘{‘{0,1,2},‘{3,4,5}}; //對多維數組的賦初始值是在initial之外 initial begin // int i,j; //並不需要對i,j進行類型定義 foreach(md[i,j]) $display("md[%0d][%0d]=%0d",i,j,md[i][j]); end endmodule
結果為:
# md[0][0]=0
# md[0][1]=1
# md[0][2]=2
# md[1][0]=3
# md[1][1]=4
# md[1][2]=5
另一個例子:
module test_enum();
initial
begin
byte two[4][6];
foreach(two[i,j])
two[i][j]=i*10+j;
foreach(two[i])
begin
$write("%0d:",i);
foreach(two[,j])
$write("%3d",two[i][j]); //利用位寬來表示空格
$display;
end
end
endmodule
結果是:
# 0: 0 1 2 3 4 5
# 1: 10 11 12 13 14 15
# 2: 20 21 22 23 24 25
# 3: 30 31 32 33 34 35
能夠對上述兩個例子有一個深入的了解就能夠掌握for和foreach的用法。
2.2.4基本的數組操作-------復制和比較
數組的復制和比較其實很簡單,下面我就用一段代碼來給大家講述一下。
module test_enum();
bit[31:0] src[5]=‘{0,1,2,3,4},
dst[5]=‘{5,4,3,2,1}; //賦初始值放在外面
initial
begin
if(src==dst)
$display("src==dst");
else
$display("src!=dst");
dst=src;//將src賦給dst
src[0]=5;//將src的第一個元素賦值為5
$display("src %s dst",(src==dst)? "==":"!="); //以這種方式來比較
$display("src[1:4] %s dst[1:4]",(src[1:4]==dst[1:4])? "==":"!=");
end
endmodule
2.2.5數組的幾種表示方式
同時使用位下標和數組下標
src[0]=5;src[0][0]=1;src[0][2:1]=‘b10bit[31:0] src[5]=‘{5{5}};
合並數組
聲明合並數組時,合並的位和數組大小作為數據類型的一部分必須在變量名前面指出。數組大小定義的格式必須是[msb:lsb],而不是[size]。bit [3:0][7:0] bytes; //四個字節合並的數組,使用單獨的32比特的字來存放。 bytes//32比特 bytes[3]//最高位字節 bytes[3][7]//最高字節的最高位 bit [3:0][7:0] barray[3];//3*32比特 barray[2]//32比特的數據 barray[2][3]//8比特的數據 barray[2][3][7]//單比特的數據
2.3動態數組
我們知道Verilog數組類型中,都是定寬度的數組,其寬度在編譯時就確定了。但是如果我們事先並不知道數組的寬度,那麽我們又該如何分配數組的寬度呢?下面我們就來介紹一下動態數組。
動態數組在聲明時使用空下標[],數組在最開始時是空的,必須使用new[]操作符來分配空間,同時在方括號中傳遞數組寬度。
下面我們通過一個例子來深入了解一下動態數組。module test_enum(); int dyn[],d2[]; //聲明動態數組 initial begin dyn=new[5];//dyn的寬度為5 foreach(dyn[j]) dyn[j]=j; d2=dyn; //復制動態數組 d2[0]=5; $display("%d %d",dyn[0],d2[0]); // 0,5 dyn=new[20](dyn); //給dyn分配20個整數值並將前五個值進行復制 $display("%d %d",dyn[3],dyn[19]);//3,0 dyn=new[100];//分配100個整數值給dyn dyn.delete(); //刪除所有元素 end endmodule
2.4隊列
SystemVerilog引進了一種新的數據類型隊列。在一個隊列中的任何一個地方增加或刪除元素,這類操作在性能上的損失要比動態數組小的多,因為動態數組需要分配新的數組並復制所有元素。
隊列的聲明是使用的帶有美元符號的下標[$],隊列元素的編號從0到$。module test_enum(); int j, q2[$]={3,4}, q[$]={0,2,5}; initial begin j=q2[$]; //j=4 j=q2[0]; //j=3 q.insert(1,1); //在第1位插入1{0,1,2,5} q.insert(2,3);//在第2位插入3{0,1,3,2,5} q.delete(1);//刪除第一位{0,3,2,5} q.push_front(6); //最前面插入6{6,0,3,2,5} j=q.pop_back; //j=5 {6,0,3,2} q.push_back(8);//在最後插入8{6,0,3,2,8} j=q.pop_front;//j=6{0,3,2,8} foreach(q[i]) $display("%0d",q[i]); q.delete();//等價於命令q={}; end endmodule
註意:把$放在一個範圍表達式的左邊,那麽$將代表最小值[$:2]等價於[0:2],將$放在一個範圍表達式的右邊,那麽$將代表最小值[1:$]等價於[1:2]
2.5關聯數組
如果你只是需要對一個有著幾個G字節尋址範圍的處理器進行建模。在典型的測試中,這個處理器可能只訪問了用來存放可執行代碼和數據的幾百或幾千個字節,這種情況下對幾個G字節的存儲空間進行分配和初始化顯然是浪費的。
仿真器一般采用32位地址線或者64位數據作為索引的數據包,顯然這是有一定的額外開銷的。
關聯數組采用在方括號中放置數據類型的形式來進行聲明module test_enum(); bit[63:0] assoc[bit[63:0]], //64個bit[63:0] idx=1; initial begin repeat(64) //對1,2,4,8,16等等的稀疏元素進行初始化。 begin assoc[idx]=idx; idx=idx<<1; end foreach(assoc[i]) //foreach遍歷數組 $display("assoc[%0d]=%0d",i,assoc[i]); if(assoc.first(idx)) //遍歷數組的另一種形式 begin do $display("assoc[%h]=%h",idx,assoc[idx]); while(assoc.next(idx)); end assoc.first(idx); //找到並刪除第一個元素 assoc.delete(idx); $display("the array now has %0d elements",assoc.num); end endmodule
第二章:數據類型