時鐘是整個電路最重要、最特殊的信號,系統內大部分器件的動作都是在時鐘的跳變沿上進行, 這就要求時鐘信號時延差要非常小, 否則就可能造成時序邏輯狀態出錯;因而明確FPGA設計中決定系統時鐘的因素,盡量較小時鐘的延時對保證設計的穩定性有非常重要的意義。
1.1 建立時間與保持時間
建立時間(Tsu:set up time)是指在時鐘沿到來之前數據從不穩定到穩定所需的時間,如果建立的時間不滿足要求那么數據將不能在這個時鐘上升沿被穩定的打入觸發器;保持時間(Th:hold time)是指數據穩定后保持的時間,如果保持時間不滿足要求那么數據同樣也不能被穩定的打入觸發器。建立與保持時間的簡單示意圖如下圖1所示。
圖1 保持時間與建立時間的示意圖
在FPGA設計的同一個模塊中常常是包含組合邏輯與時序邏輯,為了保證在這些邏輯的接口處數據能穩定的被處理,那么對建立時間與保持時間建立清晰的概念非常重要。下面在認識了建立時間與保持時間的概念上思考如下的問題。
圖2 同步設計中的一個基本模型
圖2為統一采用一個時鐘的同步設計中一個基本的模型。圖中Tco是觸發器的數據輸出的延時;Tdelay是組合邏輯的延時;Tsetup是觸發器的建立時間;Tpd為時鐘的延時。如果第一個觸發器D1建立時間最大為T1max,最小為T1min,組合邏輯的延時最大為T2max,最小為T2min。問第二個觸發器D2立時間T3與保持時間T4應該滿足什么條件,或者是知道了T3與T4那么能容許的最大時鐘周期是多少。這個問題是在設計中必須考慮的問題,只有弄清了這個問題才能保證所設計的組合邏輯的延時是否滿足了要求。
下面通過時序圖來分析:設第一個觸發器的輸入為D1,輸出為Q1,第二個觸發器的輸入為D2,輸出為Q2;
時鐘統一在上升沿進行采樣,為了便于分析我們討論兩種情況即第一:假設時鐘的延時Tpd為零,其實這種情況在FPGA設計中是常常滿足的,由于在FPGA 設計中一般是采用統一的系統時鐘,也就是利用從全局時鐘管腳輸入的時鐘,這樣在內部時鐘的延時完全可以忽略不計。這種情況下不必考慮保持時間,因為每個數據都是保持一個時鐘節拍同時又有線路的延時,也就是都是基于CLOCK的延遲遠小于數據的延遲基礎上,所以保持時間都能滿足要求,重點是要關心建立時間,此時如果D2的建立時間滿足要求那么時序圖應該如圖3所示。
從圖中可以看出如果:
T-Tco-Tdelay>T3
即:Tdelay< T-Tco-T3
那么就滿足了建立時間的要求,其中T為時鐘的周期,這種情況下第二個觸發器就能在第二個時鐘的升沿就能穩定的采到D2,時序圖如圖3所示。
圖3 符合要求的時序圖
如果組合邏輯的延時過大使得
T-Tco-Tdelay
那么將不滿足要求,第二個觸發器就在第二個時鐘的升沿將采到的是一個不定態,如圖4所示。那么電路將不能正常的工作。
圖4 組合邏輯的延時過大時序不滿足要求
從而可以推出
T-Tco-T2max>=T3
這也就是要求的D2的建立時間。
從上面的時序圖中也可以看出,D2的建立時間與保持時間與D1的建立與保持時間是沒有關系的,而只和D2前面的組合邏輯和D1的數據傳輸延時有關,這也是一個很重要的結論。說明了延時沒有疊加效應。
第二種情況如果時鐘存在延時,這種情況下就要考慮保持時間了,同時也需要考慮建立時間。時鐘出現較大的延時多是采用了異步時鐘的設計方法,這種方法較難保證數據的同步性,所以實際的設計中很少采用。此時如果建立時間與保持時間都滿足要求那么輸出的時序如圖5所示。
圖5 時鐘存在延時但滿足時序
從圖5中可以容易的看出對建立時間放寬了Tpd,所以D2的建立時間需滿足要求:
Tpd+T-Tco-T2max>=T3
由于建立時間與保持時間的和是穩定的一個時鐘周期,如果時鐘有延時,同時數據的延時也較小那么建立時間必然是增大的,保持時間就會隨之減小,如果減小到不滿足D2的保持時間要求時就不能采集到正確的數據,如圖6所示。
這時即T-(Tpd-Tco-T2min)
T-(Tpd+T-Tco-T2min)>=T4 即Tco+T2min-Tpd>=T4
從上式也可以看出如果Tpd=0也就是時鐘的延時為0那么同樣是要求Tco+T2min>T4,但是在實際的應用中由于T2的延時也就是線路的延時遠遠大于觸發器的保持時間即T4所以不必要關系保持時間。
圖6 時鐘存在延時且保持時間不滿足要求
綜上所述,如果不考慮時鐘的延時那么只需關 心建立時間,如果考慮時鐘的延時那么更需關心保持時間。下面將要分析在FPGA設計中如何提高同步系統中的工作時鐘。
1.2 如何提高同步系統中的工作時鐘
從上面的分析可以看出同步系統時對D2建立時間T3的要求為:
T-Tco-T2max>=T3
所以很容易推出T>=T3+Tco+T2max,其中T3為D2的建立時間Tset,T2為組合邏輯的延時。在一個設計中T3和Tco都是由器件決定的固定值,可控的也只有T2也就時輸入端組合邏輯的延時,所以通過盡量來減小T2就可以提高系統的工作時鐘。為了達到減小T2在設計中可以用下面不同的幾種方法綜合來實現。
1.2.1 通過改變走線的方式來減小延時
以altera的器件為例,我們在quartus里面的timing closure floorplan可以看到有很多條條塊塊,我們可以將條條塊塊按行和按列分,每一個條塊代表1個LAB,每個LAB里有8個或者是10個LE。它們的走線時延的關系如下:同一個LAB中(最快) < 同列或者同行 < 不同行且不同列。我們通過給綜合器加適當的約束(約束要適量,一般以加5%裕量較為合適,比如電路工作在100Mhz,則加約束加到105Mhz就可以了,過大的約束效果反而不好,且極大增加綜合時間)可以將相關的邏輯在布線時盡量布的靠近一點,從而減少走線的時延。
1.2.2 通過拆分組合邏輯的方法來減小延時
由于一般同步電路都不止一級鎖存(如圖8),而要使電路穩定工作,時鐘周期必須滿足最大延時要求,縮短最長延時路徑,才可提高電路的工作頻率。如圖7所示:我們可以將較大的組合邏輯分解為較小的幾塊,中間插入觸發器,這樣可以提高電路的工作頻率。這也是所謂“流水線”(pipelining)技術的基本原理。
對于圖8的上半部分,它時鐘頻率受制于第二個較大的組合邏輯的延時,通過適當的方法平均分配組合邏輯,可以避免在兩個觸發器之間出現過大的延時,消除速度瓶頸。
圖7 分割組合邏輯
圖8 轉移組合邏輯
那么在設計中如何拆分組合邏輯呢,更好的方法要在實踐中不斷的積累,但是一些良好的設計思想和方法也需要掌握。我們知道,目前大部分FPGA都基于4輸入 LUT的,如果一個輸出對應的判斷條件大于四輸入的話就要由多個LUT級聯才能完成,這樣就引入一級組合邏輯時延,我們要減少組合邏輯,無非就是要輸入條件盡可能的少,這樣就可以級聯的LUT更少,從而減少了組合邏輯引起的時延。
我們平時聽說的流水就是一種通過切割大的組合邏輯(在其中插入一級或多級D觸發器,從而使寄存器與寄存器之間的組合邏輯減少)來提高工作頻率的方法。比如一個32 位的計數器,該計數器的進位鏈很長,必然會降低工作頻率,我們可以將其分割成4位和 8位的計數,每當4位的計數器計到15后觸發一次8位的計數器,這樣就實現了計數器的切割,也提高了工作頻率。
在狀態機中,一般也要將大的計數器移到狀態機外,因為計數器這東西一般是經常是大于4輸入的,如果再和其它條件一起做為狀態的跳變判據的話,必然會增加LUT的級聯,從而增大組合邏輯。以一個6輸入的計數器為例,我們原希望當計數器計到111100后狀態跳變,現在我們將計數器放到狀態機外,當計數器計到111011后產生個enable信號去觸發狀態跳變,這樣就將組合邏輯減少了。狀態機一般包含三個模塊,一個輸出模塊,一個決定下個狀態是什么的模塊和一個保存當前狀態的模塊。組成三個模塊所采用的邏輯也各不相同。輸出模塊通常既包含組合邏輯又包含時序邏輯;決定下一個狀態是什么的模塊通常又組合邏輯構成;保存現在狀態的通常由時序邏輯構成。三個模塊的關系如下圖9所示。
圖9 狀態機的組成
所有通常寫狀態機時也按照這三個模塊將狀態機分成三部分來寫,如下面就是一種良好的狀態機設計方法:
/*---------------------------------------------------- This is FSM demo program Design Name : arbiter File Name : arbiter2.v -----------------------------------------------------*/ module arbiter2 ( clock , // clock reset , // Active high, syn reset req_0 , // Request 0 req_1 , // Request 1 gnt_0 , gnt_1); //-------------Input Ports----------------------------- input clock ; input reset ; input req_0 ; input req_1 ; //-------------Output Ports---------------------------- output gnt_0 ; output gnt_1 ; //-------------Input ports Data Type------------------- wire clock ; wire reset ; wire req_0 ; wire req_1 ; //-------------Output Ports Data Type------------------ reg gnt_0 ; reg gnt_1 ; //-------------Internal Constants-------------------------- parameter SIZE = 3 ; parameter IDLE = 3'b001 , GNT0 = 3'b010 , GNT1 = 3'b100 ; //-------------Internal Variables--------------------------- reg [SIZE-1:0] state ;// Seq part of the FSM wire [SIZE-1:0] next_state ;// combo part of FSM //----------Code startes Here------------------------ assign next_state = fsm_function(req_0, req_1); function [SIZE-1:0] fsm_function; input req_0; input req_1; case(state) IDLE : if (req_0 == 1'b1) fsm_function = GNT0; else if (req_1 == 1'b1) fsm_function= GNT1; else fsm_function = IDLE; GNT0 : if (req_0 == 1'b1) fsm_function = GNT0; else fsm_function = IDLE; GNT1 : if (req_1 == 1'b1) fsm_function = GNT1; else fsm_function =IDLE; default : fsm_function = IDLE; endcase endfunction always@(posedge clock) begin if (reset == 1'b1) state <=IDLE; else state <=next_state; end //----------Output Logic----------------------------- always @ (posedge clock) begin if (reset == 1'b1) begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; end else begin case(state) IDLE : begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; end GNT0 : begin gnt_0 <= #1 1'b1; gnt_1 <= #1 1'b0; end GNT1 : begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b1; end default : begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; end endcase end end // End Of Block OUTPUT_ endmodule
狀態機通常要寫成3段式,從而避免出現過大的組合邏輯。
上面說的都是可以通過流水的方式切割組合邏輯的情況,但是有些情況下我們是很 難去切割組合邏輯的,在這些情況下我們又該怎么做呢?
狀態機就是這么一個例子,我們不能通過往狀態譯碼組合邏輯中加入流水。如果我們的設計中有一個幾十個狀態的狀態機,它的狀態譯碼邏輯將非常之巨大,毫無疑問,這極有可能是設計中的關鍵路徑。那我們該怎么做呢?還是老思路,減少組合邏輯。我們可以對狀態的輸出進行分析,對它們進行重新分類,并根據這個重新定義成一組組小狀態機,通過對輸入進行選擇(case語句)并去觸發相應的小狀態機,從而實現了將大的狀態機切割成小的狀態機。在ATA6的規范中(硬盤的標準),輸入的命令大概有20十種,每一個命令又對應很多種狀態,如果用一個大的狀態機(狀態套狀態)去做那是不可想象的,我們可以通過case語句去對命令進行譯碼,并觸發相應的狀態機,這樣做下來 這一個模塊的頻率就 可以跑得比較高了。
總結:提高工作頻率的本質就是要減少寄存器到寄存器的時延,最有效的方法就是 避免出現大的組合邏輯,也就是要盡量去滿足四輸入的條件,減少LUT級聯的數量。我們 可以通過加約束、流水、切割狀態的方法提高工作頻率。
在FPGA中進行時鐘設計時也要注意一下幾點:
1. 一個模塊盡量只用一個時鐘,這里的一個模塊是指一個module或者是一個entity。在多時鐘域的設計中涉及到跨時鐘域的設計中最好有專門一個模塊做時鐘域的隔 離。這樣做可以讓綜合器綜合出更優的結果。
2. 除非是低功耗設計,不然不要用門控時鐘--這會增加設計的不穩定性,在要用到門控時鐘的地方,也要將門控信號用時鐘的下降沿 打一拍再輸出與時鐘相與。
3. 禁止用計數器分頻后的信號做其它模塊的時鐘,而要用改成時鐘使能的方式,否則這種時鐘滿天飛的方式對設計的可靠性極為不利,也大大增加了靜態時序分析的復雜性。
1.4 不同時鐘域之間的同步
當一個設計中的兩個模塊分別用的是兩個工作時鐘,那么在它們的接口處就工作在異步模式,這時為了保證數據能正確的處理那么就要對兩個模塊進行同步。
這里的不同的時鐘域通常是以下的兩種情況:
1、 兩個時鐘的頻率不同;
2、 雖然兩個時鐘的頻率相同,但是它們是兩個獨立的時鐘,其相位沒有任何關系。
分別如下兩個圖所示:
圖10 兩個時鐘的頻率完全不同
圖11兩個時鐘的頻率相同,但相位不相關
兩個時鐘域之間傳輸的數據根據不同的位寬通常采用不同的同步的方法。
1、單bit之間的同步且發送的每個pulse至少有1個周期寬度的情況
這類同步主要是用于一些控制信號自己的同步。通常的采用方法就是輸出數據在接收的模塊中利用兩個觸發器采用系統時鐘打兩拍,如下圖12所示。對于這種同步需要說明以下幾點。
圖12 一位同步器設計
(1)圖12中的同步電路其實叫"一位同步器",它只能用來對一位異步信號進行同步,而且這個信號的寬度必須大于本級時鐘的脈沖寬度,否則有可能根本采不到這個異步信號。
(2)為什么圖一中的同步電路只能用來對一位異步信號進行同步呢? (a)當有兩個或更多的異步信號(控制或地址)同時進入本時域來控制本時域的電路時,如果這些信號分別都用圖12中的同步電路來同步就會出現問題,由于連線延遲或其他延遲使兩個或更多的異步信號(控制或地址)之間產生了skew,那么這個skew經過圖12的同步器同步進入本時域后,會產生很大的skew 或產生競爭,導致本時域電路出錯。
出現的問題如下圖13所示:
圖13 同步多個控制信號時出錯
(b)如果是異步數據總線要進入本時域,同樣不能用圖12的電路,因為數據的變化是很隨機的,其0的寬度或1的寬度和本時域時鐘脈沖無關,所以圖12的電路可能會采不到正確數據。
(3)注意,第二個觸發器并不是避免“亞穩態的發生”,確切的說,該電路能夠防止亞穩態的傳播。也就是說,一旦第一個觸發器發生了亞穩態(可能性存在),由于有了第二個觸發器,亞穩態不會傳播到第二個觸發器以后的電路中去。
(4)第一級觸發器發生了亞穩態,需要一個恢復時間來穩定下來,或者叫退出亞穩態。當恢復時間加上第二級觸發器的建立時間(更精確的,還要減去clock skew)小于等于時鐘周期的時候(這個條件還是很容易滿足的,一般要求兩級觸發器盡量接近,中間沒有任何組合邏輯,時鐘的skew較小),第二級觸發器就可以穩定的采樣,得到穩定的確定的數據了,防止了亞穩態的傳播。
(5)FF2是采樣了FF1的輸出,當然是FF1輸出什么,FF2就輸出什么。僅僅延遲了1個周期。注意,亞穩態之所以叫做亞穩態,是指一旦FF1進入,其輸出電平不定,可能正確也可能錯誤。所以必須說明的是,雖然這種方法可以防止亞穩態的傳播,但是并不能保證兩級觸發器之后的數據是正確的,因此,這種電路都有一定數量的錯誤電平數據,所以,僅適用于少量對于錯誤不敏感的地方。對于敏感的電路,可以采用雙口RAM或FIFO。
2 輸入pulse有可能小于一個時鐘周期寬度情況下的同步電路
對2的情況通常采用如下圖14的反饋電路。該電路的分析如下:假設輸入的數據是高電平,那么由于第一個觸發器FF1是高電平清零,所有輸出也是高電平,采用正確。如果輸入是第電平那么被FF1被強制清零,這個時候輸出位零。這樣就保證了輸出的正確性。
圖14輸入pulse有可能小于一個時鐘周期寬度情況下的同步電路
編輯:黃飛
?
評論
查看更多