一 數據類型
內建數據類型
verilog中,對於觸發器,鎖存器用reg類型,對於reg,會被綜合成register,latch
wire,做連接
sv中,logic可以被綜合為reg或wire,logic如果在驗證環境,只會作為單純的變量進行賦值操作。
verilog & sv區別:
verilog作為硬件描述語言,傾向於設計人員自身懂得所描述的電路中哪些變量應該實現為reg或是wire,但不利於后端綜合工具
sv側重於驗證語言,引入logic只會作為單純的變量進行賦值操作,這些變量只屬於軟件環境構建
bit是二值邏輯:0 1
logic是四值邏輯:0 1 X Z
Q:為什么有了四值邏輯還要引入二值邏輯?
A:SV在開始做設計的時候,期望將硬件和軟件分開,四值在硬件設計,二值在軟件中
四值邏輯:integer(32),logic,reg,net-type
二值邏輯:byte(8bit),shortint,int(32),longint,bit
有符號類型: byte,shortint,int,longint,integer
無符號類型:bit, logic, reg, net-type(wire/tri)
note:用最高位來表征有無符號
在變量運算中,應該盡量避免兩種不一致的變量操作
將有符號變量轉換為無符號:unsigned' 屬於靜態轉換
動態轉換 $cast(tgt, src)
對比:靜態轉換在編譯的時候做檢查,動態轉換在仿真的時候做檢查
靜態轉換和動態轉換均需要操作符號或者系統函數介入,統稱為顯式轉換
而不需要進行轉換的一些操作稱為隱式轉換
note:不同數據類型進行操作時應注意變量的:邏輯數據類型、符號類型、矢量位寬
軟件常用類型【定寬數組】
數組聲明
int lo_hi[0:15]; //16 ints [0] .. [15]
int c_style[16]; //16 ints [0] .. [15]
多維數組聲明和使用
int arr2 [0:7][0:3]; // 完整聲明
int arr3 [8][4]; //緊湊聲明
arr2[7][3] = 1; //設置最后一個元素
初始化和賦值
int ascend[4] = `{0, 1, 2, 3}; //對四個元素初始化
int descend[5];
descend[0:2] = `{5, 6, 7} //片段賦值操作
ascend = `{4{8}} //四個值全部為8
descend = `{9, 8, default:-1 } //{9,8,-1,-1,-1}
存儲空間考量
從實際硬件容量的角度出發,哪種方式存儲方式更小?
bit [3][7:0] b_pack; //合並型
bit [7:0] b_unpack[3]; //非合並
b_unpack 實際占用3個WORD存儲空間,但是b_pack則只會占據一個WORD存儲空間
note:當左邊也有維度,右邊也有維度聲明時,先考慮右邊維度
對於unpack的數組,初始化和賦值“ ` ”要存在
Q:當b_pack和b_unpack用logic存儲時,實際占用的大小為:
A:b_pack有24bit,用logic 4值表示時,每個需要2bit,i.e. 48bit =2WORD
b_unpack為3 *8,8位連續位,即3*16,其中16bit可以被一個WORD存儲,則需要3WORD
note:對於unpack的數組做初始化要有“ ` ”
for和foreach循環
initial begin
bit [31:0] src[5], dst[5];
for (int i=0; i< $size(src); i++) //$size(src,default =1)
src[i] = i;
foreach (dst[j]) begin
dst[j] = src[j]*2
end
復制和比較 “==” / “!=”
軟件常用類型【動態數組】
定寬數組類型的寬度在編譯時就確定了,但是如果在程序運行時再確定數據的寬度需要使用【動態數組】
動態數組用的是非合並的存儲方式,合並數組和非合並數組之間不能直接賦值
特點:可以在仿真運行時靈活調解數組的大小即存儲量
動態數組一開始聲明時,需要利用“[ ]”來聲明,而此時數組是空的,即0容量,其后使用new[ ]來分配空間,方括號中傳遞數組的寬度
此外,也可以在調用new[ ]時將數組名一並傳遞,將已有數組的值復制到新數組中
d2和dyn是兩個地址空間,integer默認值是X
刪除方法:也可以dyn = new[0] / dyn = `{ }
【隊列】
- 結合了鏈表和數組的優點,可以在它的任何地方添加或刪除元素,並且通過索引實現對任一元素的訪問
- 隊列的聲明是 [$] 隊列元素的標號從0到$
- 隊列不需要new[ ]去創建空間(new[]只對動態數組使用),只需要使用隊列的方法為其增減元素,一開始空間為0
- 隊列的一個簡單使用是通過自帶的push_back( )和pop_front( )結合來實現FIFO的用法
note:數組中用size( ),mailbox中用num( )
【關聯數組】
關聯數組中index不是連續的,為此盡可能不要用for循環
如果找不到第一個索引,說明第一個為空
【結構體】struct
note:verilog中沒有數據結構,sv中可以使用struct語句創建結構
只是一個數據的結合,通常是將若干相關變量組合到一個結構體中
用來創建一個新的類型 typedef,為此pixel_s是類型
第一行的pixel在這里是變量名
typedef在這里也可以替換為define,區別在於typedef創建的是類型,define是變量名
note:數據是非合並型,采用`{ }方式,如果結構體用合並型,只需struct packed {bit [7:0] r, g, b} pixel
【枚舉類型】
INIT,DECODE,IDLE默認是0,1,2
note:枚舉類型可以賦值給整型,INT = enum, 但是整型不能直接賦值給enum
【字符串】
字符串默認" " ,與null不同,null有且只用到句柄(對象)中,字符串中沒有null
note:verilog中沒有str,sv中str沒有"\0"
數組的大小用.size(), str用 .len() - 1
二 過程塊和方法
硬件過程塊 initial和always
module/endmodule,interface/endinterface 可以被視為硬件世界
program/endprogram,class/endclass可以被視為軟件世界
- always是為了描述硬件的行為
- always中的@(event)敏感列表是為了模擬硬件信號的觸發行為
- always過程塊是用來描述硬件時序電路和組合電路的正確打開方式,因此只可以在module或者interface中使用
note:不可以在always中初始化變量,可以在always中復位,而初始化變量是在軟件操作過程中,即可以在initial中
- initial只執行一次
- initial和always無法被延遲執行,在仿真一開始他們就會並行執行
- initial本身不可綜合,對於描述電路沒有任何幫助,不應該存在於硬件設計代碼中
- initial是為了測試而生的
- initial可以在module,interface和program中使用
note:sv中的class不允許出現initial和always
軟件方法 函數func
- 可以在參數列表中指定輸入參數(input),輸出參數(output),輸入輸出參數(inout),或引用參數(ref)
- 可以設置返回值或不設置返回值(void)
- 默認數據類型是logic,eg input[7: 0] addr
- 可以傳參數也可以傳數組(作為形參)
- 可以有返回值也可以沒有返回值(void function())
- 在使用ref時,為了保護數據對象只能被讀取,不能被寫入,可以通過const來限定ref的聲明
- 聲明參數時,默認方向是input
軟件方法 任務task
- task無法通過return添加返回值
- task可以置入耗時語句,包括@event, wait event, #delay
- function不可以調用task,task可以調用function
變量生命周期
分為automatic(動態,局部變量)和static(靜態,全局變量)
- func/task中的臨時變量,在方法調用結束后,自動釋放
- 如果數據變量被聲明為automatic,進入該進程/方法后,automatic變量會被創建,離開該線程/方法后會被銷毀
- 對於automatic方法,其內部所有變量內部默認也是automatic
- 對於static方法,其內部所有變量內部默認也是static
note:沒有顯式注明動態還是靜態變量的情況下,默認是static
- module,program,interface,task和func之外的變量擁有靜態的生命周期,即存在與整個仿真階段
- class中默認是automatic
- 在module, interface和program內部聲明,且在task,process或者func外部聲明的變量也是static
三 設計的例化和連接
例化:
硬件和軟件的連接有且只能通過interface,硬件和硬件的連接可以通過端口,也可以通過interface