建立時間(setup time)是指在觸發器的時鍾信號上升沿到來以前,數據穩定不變的時間,如果建立時間不夠,數據將不能在這個時鍾上升沿被打入觸發器;保持時間(hold time)是指在觸發器的時鍾信號上升沿到來以后,數據穩定不變的時間,如果保持時間不夠,數據同樣不能被打入觸發器。數據穩定傳輸必須滿足建立時間和保持時間的要求,當然在一些情況下,建立時間和保持時間的值可以為零。
1.PLD內部產生毛刺的原因
使用分立元件設計數字系統時,由於PCB走線時存在分布電感和電容,所以幾納秒的毛刺將自然濾除,而在PLD內部並無分布電感和電容,所以在PLD/FPGA設計中,競爭和冒險問題將變得較為突出。
2.PLD內部毛刺的消除
一種常見的方法是利用D觸發器的D輸入端對毛刺信號不敏感的特點,在輸出信號的保持時間內,用觸發器讀取組合邏輯的輸出信號,這種方法類似於將異步電路轉化為同步電路。
在仿真時,也可能會發現在FPGA器件對外輸出引腳上有輸出毛刺,但由於毛刺很短,加上PCB本身的寄生參數,大多數情況下,毛刺通過PCB走線基本可以被自然濾除,不用再外加阻容濾波。
優秀的設計方案,如采用格雷碼計數器、同步電路,可以大大減少毛刺,但並不能完全消除毛刺。毛刺並不是對所有輸入都有危害,例如D觸發器的D輸入端,只要毛刺不能出現在時鍾的上升沿並且滿足數據的建立和保持時間,就不會對系統造成危害。因此可以說D觸發器的D輸入端對毛刺不敏感。但D觸發器的時鍾端、置位端、清零端,則都是對毛刺敏感的輸入端,任何一點毛刺就會使系統出錯,但只要認真處理,可以把危害降到最低直至消除。
阻塞賦值與非阻塞賦值的區別
在always塊中,阻塞賦值可以理解為賦值語句是順序執行的,而非阻塞賦值可以理解為賦值語句是並發執行的。實際的時序邏輯設計中,一般情況下非阻塞賦值語句被更多地使用,有時為了在同一周期實現相互關聯的操作,也使用阻塞賦值語句。注意:在實現組合邏輯的assign結構中,無一例外地都必須采用阻塞賦值語句。因此,要避免verilog HDL仿真時出現競爭與冒險現象,應遵循以下兩個要點:
(1)在描述組合邏輯的always塊中用阻塞賦值,則綜合成組合邏輯的電路結構;
(2)在描述時序邏輯的always塊中用非阻塞賦值,則綜合成時序邏輯的電路結構。
所謂阻塞是指在同一個always塊中,后面的賦值語句從概念上是在前一賦值語句結束后再開始賦值的。
如果在一個過程塊中阻塞賦值的右邊變量正好是另一個過程塊中的左邊變量,這兩個過程塊又用同一個時鍾沿觸發,這時阻塞賦值操作會出現問題,即如果阻塞賦值的順序安排不好,就會出現競爭。若值兩個阻塞賦值操作用同一個時鍾沿觸發,則執行的順序是無法確定的。例如下面的程序
1 module fboscl(clk, rst, y1, y2) ; 2 input clk, rst ; 3 output y1, y2 ; 4 reg y1, y2 ; 5 always @(posedge clk or posedge rst) 6 begin 7 if(rst) 8 y1 = 0 ; 9 else 10 y1 = y2 ; 11 end 12 13 always @(posedge clk or posedge rst) 14 begin 15 if(rst) 16 y2 = 1 ; 17 else 18 y2 = y1 ; 19 end 20 endmodule
程序中的兩個always塊是並行執行的,與先后順序無關。如果前一個always塊的復位信號先到0時刻,則y1和y2都會取1;而如果后一個always塊的復位信號先到0時刻,則y1和y2都會取0。由此可知,該模塊是不穩定的,必定會產生競爭和冒險的情況。
非阻塞賦值是指操作時刻開始時計算非阻塞賦值符號右邊的表達式,賦值操作時刻結束時更新左邊的值。在計算非阻塞賦值符號右邊的表達式和更新左邊的值期間,其他的verilog HDL語句,包括其他的Verilog非阻塞賦值語句都能同時計算非阻塞賦值符右邊的表達式和更新左邊的值;非阻塞賦值允許其他的Verilog語句同時進行操作。
非阻塞賦值操作只能用於對寄存器變量進行賦值,不允許用於連續賦值。如下程序:
module fboscl(clk, rst, y1, y2) ; input clk, rst ; output y1, y2 ; reg y1, y2 ; always @(posedge clk or posedge rst) begin if(rst) y1 <= 0 ; else y1 <= y2 ; end always @(posedge clk or posedge rst) begin if(rst) y2 <= 1 ; else y2 <= y1 ; end endmodule
在這個程序中的兩個always塊是並行執行的,與先后順序無關。無論哪一個always塊復位信號先到,兩個always塊中的非阻塞賦值都在賦值開始時刻計算非阻塞賦值符號右邊的表達式,而在賦值操作時刻結束時更新左邊的值。所以,這兩個always塊在復位信號到來后,在always塊結束時,y1為0,y2為1是確定的。
可綜合風格的Verilog模塊的編程要點有8個,在編寫程序時要牢記這8個要點。在絕大多數情況下,可以避免在綜合后仿真出現的冒險問題,初學者按照這幾點來編寫Verilog模塊程序,可以省去很多麻煩。這8個編程要點是:
(1)時序電路建模時,用非阻塞賦值;
(2)鎖存器電路建模時,用非阻塞賦值;
(3)用always和組合邏輯模型時,用阻塞賦值;
(4)在同一個always塊中建立時序和組合邏輯模型時,用非阻塞賦值;
(5)在同一個always塊中不要既用非阻塞賦值又用阻塞賦值;
(6)不要在一個以上的always塊中為同一變量賦值;
(7)用$strobe系統任務來顯示使用非阻塞賦值的變量值;
(8)賦值時不要使用#0延時。
代碼對綜合的影響
使用Verilog對邏輯硬件進行建模和模擬的同時,必須理解代碼與硬件實現的聯系。
避免在綜合時引入鎖存器的方法有:
(1)組合函數的輸出必須在每個可能的控制路徑中被賦值。
(2)每次執行always塊時,在生成組合邏輯的always塊中賦值的所有信號都必須有明確的值。
(3)組合電路的每一個if語句都對應一個else語句。
(4)每一個case語句都對應一個default語句(在沒有優先級的情況下優先使用。設計路徑延時要小於if...else語句)。在使用條件語句時,要注意列出所有條件分支,否則,編譯器認為條件不滿足時,會引進一個觸發器保持原值。在組合電路設計中,應避免這種隱含觸發器的存在。但一般的設計不可能列出所有分支,為包含所有分支,可在if語句最后加上else語句,在case語句的最后加上default語句
用always塊實現較復雜的組合邏輯電路
使用assign結構實現組合邏輯電路,在設計中會發現很多地方顯得冗長且效率低下。如果適當地采用always塊來設計組合邏輯,往往會更具實效。
設計一個簡單的指令譯碼電路。
要求:電路通過對指令進行判斷,對輸入數據執行相應的操作,包括加、減、與、或和求反,並且無論是指令作用的數據還是指令本身發生變化,結果都要做出及時的反映。
解:顯然,這是一個較為復雜的組合邏輯電路,如果采用assign語句,表達起來非常復雜。如果使用電平敏感的always塊,並且運用case結構來進行分支判斷,不但設計思想得到直觀的體現,而且代碼看起來非常整齊,便於理解。程序如下:
`define plus 3'd0 `define minus 3'd1 `define band 3'd2 `define bor 3'd3 `define unegate 3'd4 module alu_circuit(opcode, a, b, out) ; input [2:0]opcode ; input [7:0]a, b ; output [7:0]out ; reg [7:0]out ; always @(opcode or a or b) begin case(opcode) `plus: out = a + b ; `minus: out = a - b ; `band: out = a & b ; `bor: out = a | b ; `unegate: out = ~a ; default: out = 8'hx ; endcase end endmodule
同一組合邏輯電路用always塊和連續賦值語句assign描述時,它們的代碼形式完全不同。在always塊中,雖然被賦值的變量一定要定義為reg型,但是適當運用default(在case結構中)和else(在if...else結構中)語句,通常可以綜合為純組合邏輯。值得注意的是如果不使用default或case語句對缺省項進行說明,則易生成意想不到的鎖存器。