《SystemVerilog驗證-測試平臺編寫指南》學習 - 第2章 數據類型
SystemVerilog引進了一些新的數據類型,它們具有如下優點:
(1)雙狀態數據類型:更好的性能,更低的內存消耗;
(2)隊列、動態和關聯數組:減少內存消耗,自帶搜索和分類功能;
(3)類和結構:支持抽象數據結構;
(4)聯合和合並結構:允許對同一數據有多種視圖(view);
(5)字符串:支持內建的字符序列;
(6)枚舉類型:方便代碼編寫,增加可讀性;
2.1 內建數據類型
logic類型不能有多個結構性的驅動,在雙向總線建模的時候要使用線網類型。
logic類型只能有一個驅動,否則編譯報錯,所以logic可以用來查找頂層多驅動的錯誤。
雙狀態數據類型
最簡單的雙狀態數據類型是bit,它是無符號的。另外4種帶有符號的雙狀態數據類型是byte、shortint、int和longint,如下所示:
bit b ; // 雙狀態,單比特無符號
bit [31:0] b32 ; // 雙狀態,32比特無符號整數
int unsigned ui ; // 雙狀態,32比特無符號整數
int i ; // 雙狀態,32比特有符號整數
byte b8 ; // 雙狀態,8 比特有符號整數
shortint s ; // 雙狀態,16比特有符號整數
longint l ; // 雙狀態,64比特有符號整數
integer i4 ; // 四狀態,32比特有符號整數
time t ; // 四狀態,64比特無符號整數
real r ; // 雙狀態,雙精度浮點數
記:integer和time是四狀態數據類型,integer是32位有符號,time是64位無符號。
注:
你可能會樂意用byte數據類型代替logic[7:0]的聲明,以使得程序更加簡潔。但是需要注意的是這些新的數據類型是帶符號的,所以byte變量的最大值只有127(取值範圍-128 ~ 127)。可以使用byte unsigned,但這其實比使用bit[7:0]還要麻煩。在進行隨機化時,帶符號變量可能會造成意想不到的結果。
若把雙狀態變量連接到待測設計,務必小心,如果待測設計產生了X或者Z,這些值會被轉換爲雙狀態值,而測試代碼可能永遠也察覺不了。這些值被轉換爲了0還是1並不重要,重要的是要隨時檢查未知值的傳播。使用 $isunknown 操作符,可以在表達式的任意位出現X或者Z時返回1,如下例所示:
if ($isunknown(iport) == 1)
$display("@%0t: 4-state value detected on iport %b", $time, iport);
2.2 定寬數組
2.2.1 聲明
SystemVerilog允許只給出數組寬度的便捷聲明方式,和C語言類似。
可以通過在變量名後面指定維度的方式來創建多維數組。緊湊型聲明方式是SystemVerilog特有的。
int lo_hi [0:15] ; // 16個整數[0]...[15]
int c_style [16] ; // 便捷聲明,16個整數[0]...[15]
int array2 [0:7][0:3] ; // 完整的聲明
int array3 [8][4] ; // 緊湊的聲明
array2[7][3] = 1 ; // 設置最後一個元素爲1
若代碼中試圖從一個越界的地址中讀取數據,那麼SystemVerilog將返回數組元素的缺省值。比如,四狀態logic返回X,雙狀態int或bit則返回0。這適用於所有數組類型,包括定寬數組、動態數組、關聯數組和隊列,也同時適用於地址中包含有X和Z的情況。線網在沒有驅動時輸出是Z。
對於非壓縮數組(非合併數組),很多SystemVerilog仿真器存放數組元素時使用32bit的字符邊界,所以byte、shortint和int都是存放在相同長度的一個字中,而longint則存放到兩個字中。例如:
bit [7:0] b_unpack [3] ; // 定義3個8位的非壓縮數組
注:非壓縮數組(非合併數組) 佔用更多的內存空間。
2.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} ; // 爲前3個元素賦值
ascend = '{4{8}} ; // 4個值全部爲8
descend = '{9, 8, default:1} ; // {9, 8, 1, 1, 1}
2.2.3 基本的數組操作 -- for和foreach
操作數組最常見的方式是使用 for 或者 foreach 循環。
$size函數返回數組的寬度。
在 foreach 循環中,只需要指定數組名並在後面的方括號中給出索引變量,SystemVerilog便會自動遍歷數組中的元素,索引變量將自動聲明,並只在循環內有效。
// 在數組操作中使用 for 和 foreach 循環
initial begin
bit [31:0] src [5] ; // 聲明5個32位整數
bit [31:0] dst [5] ; // 聲明5個32位整數
//bit [31:0] src[5], dst[5] ;
for (int i = 0; i < $size(src); i++)
src[i] = i ;
foreach (dst[j])
dst[j] = src[j] * 2 ; // dst的值是srcd的2倍
end
// 初始化並遍歷多維數組
int md[2][3] = '{'{0, 1, 2}, '{3, 4, 5}} ; // 定義常量數組
initial begin
$display ("Initial Value:") ;
foreach (md[i, j]) // 正確語法格式
$display ("md[%0d][%0d]=%0d", i, j, md[i][j]) ;
$display ("New Value:") ;
// 對最後3個元素重複賦值5
md = '{'{9, 8, 7}, '{3{32'd5}}} ;
foreach (md[i, j]) // 正確語法格式
$display ("md[%0d][%0d]=%0d", i, j, md[i][j]) ;
end
打印結果:
Initial Value:
md[0][0]=0
md[0][1]=1
md[0][2]=2
md[1][0]=3
md[1][1]=4
md[1][2]=5
New Value:
md[0][0]=9
md[0][1]=8
md[0][2]=7
md[1][0]=5
md[1][1]=5
md[1][2]=5
注意:foreach 的使用。
如果不需要遍歷數組中的所有維度,可以在 foreach 循環裏忽略掉它們。看下面的例子,把一個二維數組打印成一個方形的陣列。它在外層循環遍歷一個維度,然後再內層循環遍歷第二個維度。
// 打印一個多維數組
initial begin
byte twoD [4][6] ; //
foreach (twoD[i, j])
twoD [i][j] = i * 10 + j ; //賦初值
foreach (twoD[i]) begin
$write ("%2d: ", i) ;
foreach (twoD[, j])
$write ("%3d", twoD[i][j]) ;
$display ;
end
end
打印結果如下:
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
補充:foreach 循環會遍歷原始聲明中的數組範圍。如,
數組 f[5]等同於f[0:4],而 foreach (f[i]) 等同於 for (int i = 0; i <= 4; i++)。
數組 rev[6:2]來說,foreach (rev[i]) 等同於 for (int i=6; i >= 2; i--)。