簡明 非同步FIFO設計
阿新 • • 發佈:2019-01-25
原文摘自:http://www.cnblogs.com/aslmer/p/6114216.html
本文首先對非同步 FIFO 設計的重點難點進行分析
最後給出詳細程式碼
一、FIFO簡單講解
FIFO的本質是RAM, 先進先出
重要引數:fifo深度(簡單來說就是需要存多少個數據)
fifo位寬(每個資料的位寬)
FIFO有同步和非同步兩種,同步即讀寫時鐘相同,非同步即讀寫時鐘不相同
同步FIFO用的少,可以作為資料快取
非同步FIFO可以解決跨時鐘域的問題,在應用時需根據實際情況考慮好fifo深度即可
本次要設計一個非同步FIFO,深度為8,位寬也是8.
程式碼是學習Simulation and Synthesis Techniques for Asynchronous FIFO Design Clifford E. Cummings, Sunburst Design, Inc.這篇文章的,
百度搜搜很容易找到,雖然是英文的但是寫的確實值得研究。
下面我會對設計的要點進行分析,也是對自己學習過程的一個總結,希望能和大家交流共同進步。
二、設計要點解析
1、讀空訊號如何產生?寫滿訊號如何產生?
讀空訊號:復位的時候,讀指標和寫指標相等,讀空訊號有效(這裡所說的指標其實就是讀地址、寫地址)
當讀指標趕上寫指標的時候,寫指標等於讀指標意味著最後一個數據被讀完,此時讀空訊號有效
寫滿訊號:當寫指標比讀指標多一圈時,寫指標等於讀指標意味著寫滿了,此時寫滿訊號有效
我們會發現 讀空的條件是寫指標等於讀指標,寫滿的條件也是寫指標等於讀指標,到底如何區分呢?
解決方法:將指標的位寬多定義一位
舉個例子說明:假設要設計深度為 8 的非同步FIFO,此時定義讀寫指標只需要 3 位(2^3=8)就夠用了,
但是我們在設計時將指標的位寬設計成 4 位,最高位的作用就是區分是讀空還是寫滿,具體理論 1 如下
當最高位相同,其餘位相同認為是讀空
當最高位不同,其餘位相同認為是寫滿
注意:理論1試用的是二進位制數之間的空滿比較判斷。
但是這篇文章中確不是這樣比較的,而是用的理論2,這裡我解釋一下
由於文章在設計中判斷是讀指標是否等於寫指標的時候,用的是讀寫指標的格雷碼形式(為什麼用格雷碼後面解釋),此時若用上面的理論1就會出問題,
因為格雷碼是映象對稱的,若只根據最高位是否相同來區分是讀空還是寫滿是有問題的,詳情我會慢慢說,請看圖 1
綠色框起來的是0--15的格雷碼,用紅線將格雷碼分為上下兩部分
通過觀察格雷碼相鄰位每次只有1位發生變化,且上下兩部分,除了最高位相反,其餘位全都關於紅線映象對稱,
7 --> 8 ,格雷碼從 0100 --> 1100 ,只有最高位發生變化其餘位相同
6 --> 9 , 格雷碼從 0101 --> 1101 , 只有最高位發生變化其餘位相同
以此類推,為什麼要說映象對稱呢?
試想如果讀指標指向 8,寫指標指向 7 ,我們可以知道此時此刻並不是讀空狀態也不是寫滿狀態
但是如果在此刻套用理論 1 來判斷,看會出現什麼情況,我們來套一下
7的格雷碼與8的格雷碼的最高位不同,其餘位相同,所以判斷出為寫滿。這就出現誤判了,同樣套用在 6 和 9,5 和 10等也會出現誤判。
因此用格雷碼判斷是否為讀空或寫滿時應使用理論 2,看最高位和次高位是否相等,具體如下:
當最高位和次高位相同,其餘位相同認為是讀空
當最高位和次高位不同,其餘位相同認為是寫滿
補:理論2這個判斷方法適用於用格雷碼判斷比較空滿
在實際設計中如果不想用格雷碼比較,就可以利用格雷碼將讀寫地址同步到一個時鐘域後再將格雷碼再次轉化成二進位制數再用理論1進行比較就好了。。
圖 1
2、由於是非同步FIFO的設計,讀寫時鐘不一樣,在產生讀空訊號和寫滿訊號時,會涉及到跨時鐘域的問題,如何解決?
跨時鐘域的問題:上面我們已經提到要通過比較讀寫指標來判斷產生讀空和寫滿訊號
但是讀指標是屬於讀時鐘域的,寫指標是屬於寫時鐘域的,而非同步FIFO的讀寫時鐘域不同,是非同步的,
要是將讀時鐘域的讀指標與寫時鐘域的寫指標不做任何處理直接比較肯定是錯誤的,因此我們需要進行同步處理以後仔進行比較
解決方法:兩級暫存器同步 + 格雷碼
同步的過程有兩個:
(1)將寫時鐘域的寫指標同步到讀時鐘域,將同步後的寫指標與讀時鐘域的讀指標進行比較產生讀空訊號
(2)將讀時鐘域的讀指標同步到寫時鐘域,將同步後的讀指標與寫時鐘域的寫指標進行比較產生寫滿訊號
同步的思想就是用兩級暫存器同步,簡單說就是打兩拍,相信有點基礎的早都爛熟於心,就不再多做解釋,不懂的可以看看程式碼結合理解。
只是這樣簡單的同步就可以了嗎?no no no ,可怕的亞穩態還在等著你。
我們如果直接用二進位制編碼的讀寫指標去完成上述的兩種同步是不行的,使用格雷碼更合適,為什麼呢?
因為二進位制編碼的指標在跳變的時候有可能是多位資料一起變化,如二進位制的7-->8 即 0111 --> 1000 ,在跳變的過程中 4 位全部發生了改變,這樣很容易產生毛刺,例如
非同步FIFO的寫指標和讀指標分屬不同時鐘域,這樣指標在進行同步過程中很容易出錯,比如寫指標在從0111到1000跳變時4位同時改變,這樣讀時鐘在進行寫指標同步後得到的寫指標可能是0000-1111的某個值,一共有2^4個可能的情況,而這些都是不可控制的,你並不能確定會出現哪個值,那出錯的概率非常大,怎麼辦呢?到了格雷碼發揮作用的時候了,而格雷碼的編碼特點是相鄰位每次只有 1 位發生變化, 這樣在進行指標同步的時候,只有兩種可能出現的情況:1.指標同步正確,正是我們所要的;2.指標同步出錯,舉例假設格雷碼寫指標從000->001,將寫指標同步到讀時鐘域同步出錯,出錯的結果只可能是000->000,因為相鄰位的格雷碼每次只有一位變化,這個出錯結果實際上也就是寫指標沒有跳變保持不變,我們所關心的就是這個錯誤會不會導致讀空判斷出錯?答案是不會,最多是讓空標誌在FIFO不是真正空的時候產生,而不會出現空讀的情形。所以gray碼保證的是同步後的讀寫指標即使在出錯的情形下依然能夠保證FIFO功能的正確性。在同步過程中的亞穩態不可能消除,但是我們只要保證它不會影響我們的正常工作即可。
3、由於設計的時候讀寫指標用了至少兩級暫存器同步,同步會消耗至少兩個時鐘週期,勢必會使得判斷空或滿有所延遲,這會不會導致設計出錯呢?
非同步FIFO通過比較讀寫指標進行滿空判斷,但是讀寫指標屬於不同的時鐘域,所以在比較之前需要先將讀寫指標進行同步處理,
將寫指標同步到讀時鐘域再和讀指標比較進行FIFO空狀態判斷,因為在同步寫指標時需要時間,而在這個同步的時間內有可能還會寫入新的資料,因此同步後的寫指標一定是小於或者等於當前實際的寫指標,所以此時判斷FIFO為空不一定是真空,這樣更加保守,一共不會出現空讀的情況,雖然會影響FIFO的效能,但是並不會出錯,同理將讀指標同步到寫時鐘域再和寫指標比較進行FIFO滿狀態判斷,同步後的讀指標一定是小於或者等於當前的讀指標,所以此時判斷FIFO為滿不一定是真滿,這樣更保守,這樣可以保證FIFO的特性:FIFO空之後不能繼續讀取,FIFO滿之後不能繼續寫入。總結來說非同步邏輯轉到同步邏輯不可避免需要額外的時鐘開銷,這會導致滿空趨於保守,但是保守並不等於錯誤,這麼寫會稍微有效能損失,但是不會出錯。
舉個例子:大多數情形下,非同步FIFO兩端的時鐘不是同頻的,或者讀快寫慢,或者讀慢寫快,慢的時鐘域同步到快的時鐘域不會出現漏掉指標的情況,但是將指標從快的時鐘域同步到慢的時鐘域時可能會有指標遺漏,舉個例子以讀慢寫快為例,進行滿標誌判斷的時候需要將讀指標同步到寫時鐘域,因為讀慢寫快,所以不會有讀指標遺漏,同步消耗時鐘週期,所以同步後的讀指標滯後(小於等於)當前讀地址,所以可能滿標誌會提前產生,滿並非真滿。進行空標誌判斷的時候需要將寫指標同步到讀指標
,因為讀慢寫快,所以當讀時鐘同步寫指標 的時候,必然會漏掉一部分寫指標,我們不用關心那到底會漏掉哪些寫指標,我們在乎的是漏掉的指標會對FIFO的空標誌產生影響嗎?比如寫指標從0寫到10,期間讀時鐘域只同步捕捉到了3、5、8這三個寫指標而漏掉了其他指標。當同步到8這個寫指標時,真實的寫指標可能已經寫到10 ,相當於在讀時鐘域還沒來得及覺察的情況下,寫時鐘域可能偷偷寫了資料到FIFO去,這樣在判斷它是不是空的時候會出現不是真正空的情況,漏掉的指標也沒有對FIFO的邏輯操作產生影響。
4、多位二進位制碼如何轉化為格雷碼
二進位制碼轉換成二進位制格雷碼,其法則是保留二進位制碼的最高位作為格雷碼的最高位,而次高位格雷碼為二進位制碼的高位與次高位相異或,而格雷碼其餘各位與次高位的求法相類似。
我再換種更簡單的描述 二進位制數 1 0 1 1 0 二進位制數右移1位,空位補0 0 1 0 1 1 異或運算 1 1 1 0 1 這樣就可以實現二進位制到格雷碼的轉換了,總結就是移位並且異或,verilog程式碼實現就一句:assign wgraynext = (wbinnext>>1) ^ wbinnext; 是不是非常簡單。 三、程式碼解析 非同步FIFO的訊號介面: wclk wrst_n winc wdata //寫時鐘、寫復位、寫請求、寫資料 這幾個與寫有關的全部與wclk同步 rclk rrst_n rinc rdata //讀時鐘、讀 復位、讀 請求、讀 資料 這幾個與讀有關的全部與rclk同步 wfull //寫滿 與wclk同步 rempty // 讀空 與rclk同步 本次程式碼共分為6個module 1、fifo.v 是頂層模組,作用是將各個小模組例化聯絡起來 View Code
我再換種更簡單的描述 二進位制數 1 0 1 1 0 二進位制數右移1位,空位補0 0 1 0 1 1 異或運算 1 1 1 0 1 這樣就可以實現二進位制到格雷碼的轉換了,總結就是移位並且異或,verilog程式碼實現就一句:assign wgraynext = (wbinnext>>1) ^ wbinnext; 是不是非常簡單。 三、程式碼解析 非同步FIFO的訊號介面: wclk wrst_n winc wdata //寫時鐘、寫復位、寫請求、寫資料 這幾個與寫有關的全部與wclk同步 rclk rrst_n rinc rdata //讀時鐘、讀 復位、讀 請求、讀 資料 這幾個與讀有關的全部與rclk同步 wfull //寫滿 與wclk同步 rempty // 讀空 與rclk同步 本次程式碼共分為6個module 1、fifo.v 是頂層模組,作用是將各個小模組例化聯絡起來 View Code
2、fifomem.v 生成儲存實體,FIFO 的本質是RAM,因此在設計儲存實體的時候有兩種方法:用陣列儲存資料或者呼叫RAM的IP核
View Code3、sync_r2w.v 將 rclk 時鐘域的格雷碼形式的讀指標同步到 wclk 時鐘域,簡單來講就是用兩級暫存器同步,即打兩拍
View Code4、sync_w2r.v 將 wclk 時鐘域的格雷碼形式的寫指標同步到 rclk 時鐘域
View Code5、rptr_empty.v 將 sync_w2r.v 同步後的寫指標與 rclk 時鐘域的讀指標進行比較生成都空訊號
View Code6、wptr_full.v 將 sync_r2w.v 同步後的讀指標與wclk 時鐘域的寫指標進行比較生成寫滿訊號
View Code7、測試檔案
View Code8、模擬結果
由於截圖篇幅的限制請自己驗證模擬。