System verilog基礎(chǔ)-數(shù)據(jù)類型總結(jié)
? ? ? ? verilog中將寄存器(register)類型reg和線網(wǎng)(net)類型wire區(qū)分的較為清楚,SV則在此基礎(chǔ)上引入了一個(gè)新的數(shù)據(jù)類型logic。
????????SV作為側(cè)重于驗(yàn)證的語(yǔ)言,并不十分關(guān)切l(wèi)ogic對(duì)應(yīng)的邏輯應(yīng)該被綜合為寄存器還是線網(wǎng),因?yàn)閘ogic被使用的場(chǎng)景如果是驗(yàn)證環(huán)境,那么它只會(huì)作為單純的變量進(jìn)行賦值操作,而這些變量也只屬于軟件環(huán)境構(gòu)建。
數(shù)據(jù)類型?
????????與logic相對(duì)應(yīng)的類型是bit類型,它們均可以構(gòu)建矢量類型(vector)而它們的區(qū)別在于logic為四值邏輯,即可以表示0、1、x、z;bit為二值邏輯,只可以表示0和1。
四值邏輯類型:integer、logic、reg、net-type(例如wire、tri)
二值邏輯類型:byte、shortint、int、longint、bit
如果按照有符號(hào)和無(wú)符號(hào)的類型進(jìn)行劃分,那么可以將常見(jiàn)的變量類型劃分為:
有符號(hào)類型:byte、shortint、int、longint、integer
無(wú)符號(hào)類型:bit、logic、reg、net-type(例如wire、tri)
????????在遇到這些變量類型時(shí),應(yīng)該注意他們的邏輯類型和符號(hào)類型,因?yàn)樵谧兞窟\(yùn)算中,應(yīng)該盡量避免兩種不一致的變量進(jìn)行操作,而導(dǎo)致以外的錯(cuò)誤。比如有符號(hào)數(shù)和無(wú)符號(hào)數(shù)的混用導(dǎo)致結(jié)果錯(cuò)誤:

如上題,byte類型是有符號(hào)的,把8位的有符號(hào)數(shù)賦值給9位的無(wú)符號(hào)數(shù),第一步它會(huì)先把自己擴(kuò)展成9位,因?yàn)閟igned_vec的符號(hào)位為1,所以擴(kuò)展位第9位也會(huì)為1,即signed_vec會(huì)將自己擴(kuò)展成:9‘h180,然后再賦值給result_vec,最后這句賦值的輸出結(jié)果為:result_vec = 'h180。
第二次賦值時(shí),先把有符號(hào)數(shù),通過(guò)unsigned 轉(zhuǎn)換成無(wú)符號(hào)數(shù)再賦值,轉(zhuǎn)換成無(wú)符號(hào)數(shù)后變?yōu)?’h80,然后再擴(kuò)展為9位,高位補(bǔ)0,最后result_vec = unsigned(signed_vec)的結(jié)果為:result_vec = 'h080。

????????上面展示的轉(zhuǎn)換數(shù)據(jù)類型的方式稱為---靜態(tài)轉(zhuǎn)換,即需要在轉(zhuǎn)換的表達(dá)式前面加上單引號(hào)。該方式不會(huì)對(duì)轉(zhuǎn)換值做檢查,如果轉(zhuǎn)換失敗我們也不會(huì)知道。所以與之對(duì)應(yīng)的動(dòng)態(tài)轉(zhuǎn)換$cast(tgt,src) 也被經(jīng)常運(yùn)用到轉(zhuǎn)換操作中,會(huì)在仿真的時(shí)候告訴你轉(zhuǎn)換成功還是失敗,靜態(tài)轉(zhuǎn)換是在編譯的過(guò)程中進(jìn)行的。
????????靜態(tài)轉(zhuǎn)換和動(dòng)態(tài)轉(zhuǎn)換均需要操作符號(hào)或者系統(tǒng)函數(shù)介入,我們統(tǒng)稱為顯示轉(zhuǎn)換。而不需要進(jìn)行轉(zhuǎn)換的一些操作,我們稱之為隱式轉(zhuǎn)換。

對(duì)于上面這種,4位的四值邏輯,賦值給3位的二值邏輯,結(jié)果是什么樣呢?高位賦值給低位,那么溢出的位就會(huì)被截掉,所以只剩下11x,這里要注意,四值邏輯中的x賦值給二值邏輯的時(shí)候,就是0。所以上面的賦值結(jié)果為b_vec = 110

綜上,在對(duì)不同數(shù)據(jù)類型進(jìn)行操作的時(shí)候,應(yīng)當(dāng)注意:
1.數(shù)據(jù)類型是四值邏輯還是兩值邏輯?
2.數(shù)據(jù)是有符號(hào)還是無(wú)符號(hào)的?
3.數(shù)據(jù)的位寬是多少,是否會(huì)出現(xiàn)上溢情況?
數(shù)組
數(shù)組的聲明

這兩種聲明都是可以聲明出一個(gè)一維數(shù)組,數(shù)組的大小都是16。
多維度數(shù)組聲明:

????????實(shí)際在敲代碼的時(shí)候,只在module里敲上面三行會(huì)報(bào)錯(cuò),賦值只能在聲明的時(shí)候直接賦值,或者在過(guò)程塊里面賦值,不能直接賦值。所以如果在聲明后對(duì)變量賦值,需要在initial過(guò)程語(yǔ)句塊中賦值。
初始化和賦值

這里要聲明一點(diǎn),變量的賦值必須在聲明之后,哪怕是聲明a→賦值a→聲明b→賦值b,這樣寫也會(huì)報(bào)錯(cuò)?。。∫?yàn)橘x值a操作在聲明b操作之前也是違法的。


執(zhí)行報(bào)錯(cuò)。試了一下,無(wú)論是questasim還是vcs,編譯都會(huì)報(bào)錯(cuò)。所以任何賦值都不能在變量聲明之前!

合并數(shù)組與非合并數(shù)組:
合并數(shù)組聲明:bit [3] [7:0] b_pack;
非合并數(shù)組聲明:bit [7:0] b_unpack [3]; //表示的都是24bit數(shù)據(jù)容量,但是實(shí)際的硬件容量來(lái)說(shuō),哪個(gè)存儲(chǔ)容量更小呢?
合并數(shù)組它所占的空間是連續(xù)存儲(chǔ)的,在存儲(chǔ)空間里連續(xù)存儲(chǔ)三個(gè)8bit。即連續(xù)占用24bit存儲(chǔ)空間,對(duì)硬件來(lái)說(shuō)就是一個(gè)WORD(32位操作系統(tǒng)一個(gè)WORD是4byte,64位就是8byte),而非合并數(shù)組,則會(huì)開(kāi)三個(gè)WORD,分別存放b_unpack的三個(gè)8bit,占用三個(gè)WORD。
結(jié)果:b_pack占用實(shí)際存儲(chǔ)1WORD,b_unpack占用實(shí)際存儲(chǔ)3WORD。
問(wèn):如果把上面這題的bit類型,換成logic類型呢?結(jié)果有什么變化?
logic類型是四值邏輯,每1位需要2bit的實(shí)際存儲(chǔ)空間,所以對(duì)合并數(shù)組來(lái)說(shuō),連續(xù)存儲(chǔ)24位的 logic 變量需要48bit實(shí)際存儲(chǔ)空間,如果是32位的操作系統(tǒng),就需要2WORD來(lái)存儲(chǔ),而對(duì)于非合并數(shù)組來(lái)說(shuō),存儲(chǔ)8位的logic變量,需要16bit實(shí)際存儲(chǔ)空間,分配的1WORD也夠用,所以非合并數(shù)組仍然占用3WORD實(shí)際存儲(chǔ)空間。
基本數(shù)組操作: for 和 foreach
$size()函數(shù)可以取到變量的維度,src是位寬為32位,深度為5的二維數(shù)組。$size(src) = 5,取的是src的深度,而不是位寬。所以循環(huán)就是從0~4給src賦值,而foreach會(huì)把dst中所有元素遍歷一次,我們會(huì)發(fā)現(xiàn)變量j沒(méi)有聲明但是可以使用,這就是foreach的特點(diǎn),在foreach后面括號(hào)中寫了什么變量,在foreach的循環(huán)里就得用該變量。

對(duì)多維數(shù)組使用foreach語(yǔ)法和我們想的不太一樣,使用時(shí)并不想[ i ][ j ]這樣把每個(gè)下標(biāo)分別列在不同的方括號(hào)里,而是用逗號(hào)隔開(kāi)后放在同一個(gè)方括號(hào)里:[ i ,?j ]。
????????要補(bǔ)充的是,foreach循環(huán)會(huì)遍歷原始聲明中的數(shù)組范圍。數(shù)組f[5]等同于f[0:4],而foreach(f[i])等同于for (int i=0;i<=4;i++)。對(duì)于數(shù)組rev[6:2]來(lái)說(shuō),foreach(rev[i])語(yǔ)句等同于for(int i=6;i>=2;i--)。
數(shù)組的復(fù)制和比較


數(shù)組的復(fù)制,可以直接用一個(gè)等號(hào)復(fù)制。
動(dòng)態(tài)數(shù)組
????????剛才提到的數(shù)組是定長(zhǎng)數(shù)組,數(shù)組的長(zhǎng)度在編譯的時(shí)候就已經(jīng)確定了。如果在程序運(yùn)行時(shí)再確定數(shù)組的寬度就需要使用“動(dòng)態(tài)數(shù)組”了。動(dòng)態(tài)數(shù)組的最大特點(diǎn)就是可以在仿真時(shí)靈活調(diào)節(jié)數(shù)組的大小,類似C中的malloc函數(shù)。
????????動(dòng)態(tài)數(shù)組在一開(kāi)始聲明時(shí),需要利用‘[ ]’來(lái)聲明,而數(shù)組此時(shí)是空的,即0容量。其后,需要使用‘new [ ] ’來(lái)分配空間,在方括號(hào)中傳遞數(shù)組的寬度。此外,也可以在調(diào)用new[ ] 時(shí)將數(shù)組名一并傳遞,將已有的數(shù)組的值復(fù)制到新數(shù)組中。

動(dòng)態(tài)數(shù)組分配空間,及復(fù)制。d2 = dyn,這條語(yǔ)句,d2在賦值之前還是0個(gè)元素,賦值之后就是5個(gè)元素了,這就是動(dòng)態(tài)數(shù)組。
dyn = new[20] (dyn)這條語(yǔ)句把dyn分配20個(gè)新值,并且低5五位是dyn的原始值,高15位是新值,都是0。然后緊接著下一條語(yǔ)句,dyn = new [100]分配100個(gè)新值,dyn全都變成0,舊的值全沒(méi)了。
dyn.delete();可以刪除動(dòng)態(tài)數(shù)組中的所有元素,同樣的還可以使用dyn = new [0];?以及dyn = ‘{ };都可以刪除動(dòng)態(tài)數(shù)組中的所有元素。
????????當(dāng)你把一個(gè)定寬數(shù)組復(fù)制給一個(gè)動(dòng)態(tài)數(shù)組時(shí),System verilog 會(huì)調(diào)用構(gòu)造函數(shù)new[ ] 來(lái)分配空間并復(fù)制數(shù)值。
隊(duì)列
????????隊(duì)列結(jié)合了鏈表和數(shù)組的優(yōu)點(diǎn),可以在它的任何地方添加或刪除元素,并且通過(guò)索引實(shí)現(xiàn)對(duì)任一元素的訪問(wèn)。隊(duì)列的聲明是使用帶美元符號(hào)的下標(biāo):[ $ ],隊(duì)列元素的標(biāo)號(hào)從0到$。隊(duì)列不需要new[ ] 去創(chuàng)建空間,你只需要使用隊(duì)列的方法為其增減元素,一開(kāi)始其空間為0。
????????隊(duì)列的一個(gè)簡(jiǎn)單使用就是通過(guò)其自帶方法push_back()和pop_front()的結(jié)合來(lái)實(shí)現(xiàn)FIFO的用法。

????????如圖使用了多個(gè)自帶的隊(duì)列函數(shù)。但是并不是所有的Systemverilog 仿真器都支持使用insert()一個(gè)隊(duì)列,比如q.insert(3,q2);即在q的第三個(gè)位置之后插入隊(duì)列q2,這個(gè)我使用questasim編譯報(bào)錯(cuò)。
????????這里要說(shuō)一點(diǎn),在對(duì)數(shù)組賦值的時(shí)候無(wú)論是定長(zhǎng)數(shù)組還是動(dòng)態(tài)數(shù)組,我們都要加一個(gè)單引號(hào)來(lái)表示賦值,而在隊(duì)列的賦值中,我們沒(méi)有使用單引號(hào),這點(diǎn)注意。
????????如果把$放在一個(gè)范圍表達(dá)式的左邊,那么$將代表最小值,例如[$:2]就代表[0:2]。如果$放在表達(dá)式的右邊,則代表最大值。
關(guān)聯(lián)數(shù)組
????????假設(shè)我們需要對(duì)幾個(gè)G字節(jié)尋址范圍的處理器進(jìn)行建模,在測(cè)試中發(fā)現(xiàn),這個(gè)處理器可能只訪問(wèn)了用來(lái)存放可執(zhí)行代碼和數(shù)據(jù)的幾百或幾千個(gè)字節(jié),其余的幾G字節(jié)的存儲(chǔ)空間都浪費(fèi)了。針對(duì)這種情況,System Verilog提供了關(guān)聯(lián)數(shù)組類型,用來(lái)保存稀疏矩陣的元素。

關(guān)聯(lián)數(shù)組采用在方括號(hào)中放置數(shù)據(jù)類型的形式來(lái)進(jìn)行聲明,例如[int] 或 [Packet]

看過(guò)一個(gè)面經(jīng),面試官問(wèn)怎么用非遍歷的方式對(duì)稀疏矩陣結(jié)果順序輸出?
foreach會(huì)找到稀疏分布的元素1,2,4,8,16等,但輸出的時(shí)候未必按照順序輸出,如果想要按照順序輸出,則可以先把標(biāo)號(hào)放到一個(gè)數(shù)組里,然后利用數(shù)組的排序函數(shù)sort()來(lái)對(duì)下標(biāo)進(jìn)行排序,接著再按照下標(biāo)對(duì)結(jié)果進(jìn)行輸出。
數(shù)組縮減方法
最常用的縮減方法是sum,它對(duì)數(shù)組中的所有元素求和。但是必須對(duì)System verilog的操作位寬規(guī)則小心,缺省情況下,如果把一個(gè)單比特?cái)?shù)組的所有元素相加,其和也是單比特的。

綠皮書中說(shuō)第二條和第三條display出來(lái)的結(jié)果都會(huì)是5,但經(jīng)過(guò)實(shí)際測(cè)試,結(jié)果均為1,在這里給綠皮書糾錯(cuò)。

用戶自定義結(jié)構(gòu)
typedef語(yǔ)句可以用來(lái)創(chuàng)建新的類型,可以把parameter和typedef語(yǔ)句放到一個(gè)程序包(package)里,以使它們能被整個(gè)設(shè)計(jì)和測(cè)試平臺(tái)所共用,用戶自定義的最有用的類型是雙狀態(tài)的32比特的無(wú)符號(hào)整數(shù),在測(cè)試平臺(tái)中,很多數(shù)值都是正整數(shù),例如字段長(zhǎng)度或事務(wù)次數(shù),所以把對(duì)uint的定義放到通用定義程序包中,這樣就可以在仿真程序的任何地方使用它。
用戶自定義變量:

用戶自定義數(shù)組:


用戶自定義結(jié)構(gòu)體:
由于結(jié)構(gòu)體struct 只是一個(gè)數(shù)據(jù)的集合,所以它是可綜合的。如果想在設(shè)計(jì)代碼中對(duì)一個(gè)復(fù)雜的數(shù)據(jù)類型進(jìn)行建模,例如像素,可以把它放到struct里。

仿真結(jié)果:

字符串變量
System Verilog 中的string類型可以用來(lái)保存長(zhǎng)度可變的字符串。單個(gè)字符是byte類型。長(zhǎng)度為N的字符串中,元素編號(hào)從0到N-1。注意跟C語(yǔ)言不一樣的是,字符串的結(jié)尾并不帶標(biāo)識(shí)符null,所有嘗試使用字符“\0”的操作都會(huì)被忽略。
substr(start,end)函數(shù)能提取出從位置start到end之間的所有字符。

仿真結(jié)果:

SV重要的數(shù)據(jù)類型基本就這么多。
參考資料:綠皮書20~50頁(yè)