小生才疏學淺,孤陋寡聞,下文若有不當之處,還請賜教
1 代碼編寫
1. 編寫規範
- 排版
input,output,inout,reg,wire,parameter不縮進,且每次聲明各佔一行;
always,task,function不縮進;
輸入輸出信號的寬度定義與關鍵字之間,信號名與寬度間用空格分開;相同類型的定義對齊;
always中一般要用begin end區分,if else中僅有一個語句時,不使用begin end - 命名風格
端口,信號,變量名所用字母小寫,若含義較複雜,可分段描述(三段),如tx_data_val;
函數名,宏定義,參數定義用大寫;
低電平有效信號,後加"_n"
無條件寄存的寄存信號在原信號上加ff1,ff2...如data_in_ff1;
不能用reg作爲後綴名,因爲綜合工具會給寄存器自動加_reg,此時會擾亂網表可讀性 - 表達式書寫
在長式子中適當添加括號;
賦值時需給常數註明比特寬度 - 條件判斷語句
if else必須配合使用,case中也要添加default - FSM
狀態機的時序邏輯部分和組合邏輯部分要分開寫
推薦使用三段式寫法 - 註釋
最好每個always前都要加註釋
2.Verilog硬件描述編寫
- 數據類型
Verilog裏面出來parameter,整形等類型外,硬件電路類型只有reg和wire兩種。輸出儘量設置成reg類型,採用寄存器輸出方式。原因是組合邏輯輸出存在毛刺,寄存器輸出非常乾淨,毛刺較少 - 常量和數組
- 定義數字常量
由於verilog中沒有定義大小(size)整數缺省爲32位,因此定義數字最好用完整形式 如8’d50 - 定義數組
memory 型數據:在Verilog中通過對reg數據建立數組來對存儲器進行建模,verilog中沒有多維數組存在,memory型數據是通過擴展reg型數據的地址範圍實現的。存儲器的地址索引必須是常數表達式(n-1,m-1等必須是常量,符號常量也可以)。如:reg [n-1:0] array [0:m-1]; //包含了m個n位數據的數組array。
在這裏[n-1:0]定義了存儲器中每一個存儲單元的大小,即n位寄存器。array名後面的[0:m-1],表示定義的存儲器中有多少個這樣的寄存器。
- 條件說明語句(if-else/case)
條件說明語句必須放在always和Initial過程塊內部;同時語句必須完整if-else/case-default,否則可能會綜合出鎖存器。
if else和case的選擇:
if else的綜合是由與或非門構成的,也可能一組多路選擇器;case結構的綜合是由一條多路選擇器組成。
對於要寫出平行結構的條件,優先寫成case結構,例如地址譯碼等;而條件之間由重複和嵌套的情況則寫成if else結構
- 過程賦值語句:阻塞賦值(=)和非阻塞賦值(<=)
如果後面的賦值語句要等前面的賦值語句完成才執行,稱爲阻塞;如多條語句同時執行,則稱爲非阻塞。
(1) always塊中時序電路建模時,用非阻塞賦值。時序電路中,對於阻塞賦值,硬件沒有對應的電路,因而綜合結果未知
(2) always塊中建立時序和組合邏輯電路時,用非阻塞賦值。
(3) always塊建立組合邏輯模型時,用阻塞賦值。
(4) 在同一個always塊中不要既用非阻塞賦值又用阻塞賦值。
(5) 不要在一個以上的always塊中爲同一個reg變量賦值。
- 某些特殊符號
- {}大括號
(1){ }表示拼接,{第一位,第二位...};
(2)、{{ }}表示複製,{4{a}}等同於{a,a,a,a}; - $ 表示系統任務和函數
在系統任務名稱前加$使之與用戶定義的任務和函數相區分,比如常用的$display(顯示信號值),$monitor(監視信號值),$time(返回當前仿真時間),$stop(暫停仿真),$finish(結束仿真)等 - #
(1) #(a,b,c.......)表示改變參數型常量(parameter)的值。
(2) #10 表示延時10個時間單位。 - ^ 異或
- === / !==
判斷全等或不等,可包含x,z判斷
3.testbench編寫
在RTL代碼編寫結束後,需要對其編寫testbench完成對待測設計的例化,測試代碼的封裝,提供測試激勵,收集測試結果。如圖爲testbench結構。testbench是對DUT進行測試的方案描述文件,因此模塊沒有輸入輸出,用到的語句也是不可綜合的
(1)語句塊
- task
執行消耗時間的電路,測試文件中主要的測試任務內容。
task apb_write(input [31:0] addr, input [31:0] wdata );
... //無需begin-end
endtask
- function
函數功能,function和task都是描述功能塊,但function無時序控制,即不消耗時間,通常做一些聲明,創建的操作。
function 類型 name(); //int void等
...
return 類型值; //缺省的返回值是與函數名name同名的變量值;void無return
endfunction
- fork...join / join_any / join_none
fork引導的並行塊,其特點:
(1)並行塊中所有語句同時執行。join是當按時間時序排序在最後的語句執行完或者執行disable語句時,程序流程跳出程序塊;join_any當任意語句執行完後,程序跳出;join_none是fork塊和外部程序同時執行
(2)塊內每條語句的執行時間是相對於程序流程進入到塊內的仿真時間的
(3)延時時間是用來給賦值語句提供執行時序的
fork
塊內聲明語句 //塊內聲明語句可以是參數說明語句、 reg型變量聲明語句、 integer型變量聲明語句、
//real型變量聲明語句、time型變量聲明語句、事件(event)說明語句。
......
join
wait_fork; //等待所有fork進程執行完畢
disable_fork; //中斷所有fork進程
(2)語句
- `timescale
描述時間精度
`timescale 10ns/1ns //模塊test的時間單位爲10ns、 時間精度爲1ns
parameter d=1.55; //根據時間精度,參數d值被從1.55取整爲1.6。
#10 creg = areg; //在兩條賦值語句間延遲10個時間單位。
- $display()
打印信息 - $monitor($time, “a = %b, b = %h”, a, b)
當信號a或b的值發生變化時,系統任務$monitor顯示當前仿真時間,信號a值(二進制), 信號b值(16進制)。 - readmenmh()
文件操作
reg [31:0] memory [1023:0]; //1K word字長的存儲單元
$readmemh("file1", memory); //把file1中的內容讀1K word到memory中去
- event 觸發事件
event A, B; //聲明
#10 ->A; //觸發A
@(A); //等待A觸發,觸發信號爲衝擊信號,
wait( A.triggered ); //觸發時會形成一小段電平信號
wait_order(A, B); //觸發順序A>B
2 verilog代碼優化
- 計算方法
- 儘量使用移位操作或加法器替代乘法器
- 儘量用選擇器替代加法器
- 路徑
- 輸入離輸出儘可能近,避免關鍵信號路徑過長
3 代碼執行注意點
1. wire和reg型被綜合的情況
區分在組合邏輯電路和時序邏輯電路綜合情況,組合邏輯主要是由門級網表構成的,時序邏輯主要由門級網表和D觸發器構成。
2. 冒險和競爭
一個邏輯門的兩個輸入端的信號同時向相反方向變化,而變化的時間有差異的現象稱爲競爭。
由競爭而可能產生輸出干擾脈衝的現象稱爲冒險。
3.語句執行順序
verilog是描述硬件的語言,因此對於代碼的執行需要根據電路來理解。always之間均是並行執行;begin-end內是順序執行,但如果裏面是非阻塞賦值(<=),該賦值部分則是並行執行
4. 不可綜合的verilog
描述事件的語句,無法用門來實現,如:
=== !== / % delay Initial Repeat forever wait fork join event
5.全局時鐘資源語句
大型設計一般推薦使用同步時序電路。同步時序電路基於時鐘觸發沿設計,對時鐘的週期、佔空比、延時和抖動提出了更高的要求。爲了滿足同步時序設計的要求,一般在FPGA設計中採用全局時鐘資源驅動設計的主時鐘,以達到最低的時鐘抖動和延遲。與全局時鐘資源相關的原語常用的與全局時鐘資源相關的Xilinx器件原語包括:IBUFG、IBUFGDS、BUFG、BUFGP、BUFGCE、BUFGMUX、BUFGDLL和DCM
IBUFG:輸入全局緩衝,是與專用全局時鐘輸入管腳相連接的首級全局緩衝。所有從全局時鐘管腳輸入的信號必須經過IBUF元,否則在佈局佈線時會報錯。 IBUFG支持AGP、CTT、GTL、GTLP、HSTL、LVCMOS、LVDCI、LVDS、LVPECL、LVTTL、PCI、PCIX和 SSTL等多種格式的IO標準。
IBUFGDS:IBUFG的差分形式,當信號從一對差分全局時鐘管腳輸入時,必須使用IBUFGDS作爲全局時鐘輸入緩衝。
BUFG:全局緩衝,它的輸入是IBUFG的輸出,BUFG的輸出到達FPGA內部的IOB、CLB、選擇性塊RAM的時鐘延遲和抖動最小。
BUFGCE:帶有時鐘使能端的全局緩衝。它有一個輸入I、一個使能端CE和一個輸出端O。只有當BUFGCE的使能端CE有效(高電平)時,BUFGCE纔有輸出。
BUFGMUX:全局時鐘選擇緩衝,它有I0和I1兩個輸入,一個控制端S,一個輸出端O。當S爲低電平時輸出時鐘爲I0,反之爲I1。需要指出的是BUFGMUX的應用十分靈活,I0和I1兩個輸入時鐘甚至可以爲異步關係
BUFGP:相當於IBUG加上BUFG。
BUFGDLL:全局緩衝延遲鎖相環,相當於BUFG與DLL的結合。BUFGDLL在早期設計中經常使用,用以完成全局時鐘的同步和驅動等功能。隨着數字時鐘管理單元(DCM)的日益完善,目前BUFGDLL的應用已經逐漸被DCM所取代。
DCM:數字時鐘管理單元,主要完成時鐘的同步、移相、分頻、倍頻和去抖動等。DCM與全局時鐘有着密不可分的聯繫,爲了達到最小的延遲和抖動,幾乎所有的DCM應用都要使用全局緩衝資源。DCM可以用Xilinx ISE軟件中的Architecture Wizard直接生成。
4 Verilog編譯及仿真
- verilog仿真器
仿真器不同,結果可能不同
(1)Verilog-XL: cadence開發的解釋仿真器,“解釋”即有一個運行時間的解釋工具執行每一條Verilog指令並且與時間隊列進行交流。該仿真器在cadence系統裏是一個默認的verilog仿真器,但一直未被cadence更新,不具備verilog的新特點。
(2)NC-Verilog:cadence的編譯仿真器,把Verilog轉換成該程序的定製仿真器,即先轉換成C程序,再編譯成仿真器。與Verilog-2001大部分兼容。
(3)VCS:synopsys開發的編譯仿真器,與該公司的其他工具集成在一起。兼容Verilog-2001 - Verilog生成電路的方法
(1)behavioral(行爲級RTL):用高層次的Verilog描述所希望的系統行爲,隨後用綜合工具綜合成硬件電路。
(2)structural(結構級):Verilog完全由一個標準單元庫基本門的實例構成。該Verilog可以完全是文本的,或是採用標準庫中邏輯門符號的層次化原理圖。