異步FIFO設(shè)計(jì),搞清楚這7點(diǎn)就夠了!
掃描二維碼
隨時(shí)隨地手機(jī)看文章
1、什么是格雷碼(Gray Code)?
格雷碼是美國(guó)學(xué)者Frank Gray于1947年提出的一種二進(jìn)制編碼方式,后面這種編碼方式就以他的名字命名。實(shí)際上,格雷碼是有多種編碼形式的。根據(jù)定義,在一組數(shù)的編碼中,若任意兩個(gè)相鄰的代碼只有一位二進(jìn)制數(shù)不同,則稱這種編碼為格雷碼。
下表是不同形式的格雷碼:

表中典型格雷碼具有代表性。若不作特別說明,格雷碼就是指典型格雷碼(后文將簡(jiǎn)稱格雷碼),它可從自然二進(jìn)制碼轉(zhuǎn)換而來。
好了,現(xiàn)在我問你,根據(jù)格雷碼的性質(zhì)你能默寫出16個(gè)4bit寬度的格雷碼來嗎?我想一般是比較難的,因?yàn)閱慰可鲜鲂再|(zhì)很難推導(dǎo)出來。比如說,0(0000)可以寫出來,1(0001)也可以寫出來,2(0011)也可以寫出來,但是3就不好寫了,因?yàn)楦鶕?jù)只能變化一位的性質(zhì),可以是0111,也可以是0010,那么到底是哪個(gè)呢?如何不是經(jīng)常使用的話,我想是很難判斷的。
所以接下來就要介紹格雷碼的第二個(gè)性質(zhì)了:當(dāng)?shù)贜位從0變到1的時(shí)候,之后的數(shù)的N-1位會(huì)關(guān)于前半段軸對(duì)稱,而比N位高的位是相同的。
我們看一下4bit格雷碼的前四位的例子。示意圖如下:
? 當(dāng)0001跳轉(zhuǎn)到下一位時(shí),毋庸置疑的是,第0位會(huì)維持1不變,而第1位會(huì)跳轉(zhuǎn)到1,所以可以據(jù)此畫出對(duì)稱軸
? 高2位(第3、2位)這保持不變
? 低位(該實(shí)例中只有第0位)關(guān)于對(duì)稱軸對(duì)稱
有了上述三點(diǎn)信息,那么就可以把剩下的2個(gè)格雷碼寫出來了。

4bit格雷碼的前8位示意圖如下(這我就不啰嗦了,你們肯定看得懂):

2、異步FIFO為什么要使用格雷碼?
異步FIFO設(shè)計(jì)最關(guān)鍵的點(diǎn)是什么?答案是“空”和“滿”的判斷?,F(xiàn)在回想一下,我們是如何進(jìn)行狀態(tài)判斷的。很簡(jiǎn)單,分別構(gòu)建讀指針和寫指針。寫指針總是指向下一個(gè)要寫的地址,而讀指針永遠(yuǎn)指向當(dāng)前要讀取的地址。再通過對(duì)讀、寫指針的比較來判斷空、滿。

如上圖的FIFO深度為8,則其地址位寬應(yīng)該是3(2的三次方等于8)。當(dāng)寫指針與讀指針都指向同一個(gè)位置(即相同)時(shí),可能是空狀態(tài),但也可能是滿狀態(tài)(寫指針超過了讀指針一圈)。(這里說句題外話,不可能是讀指針超過寫指針一圈,因?yàn)榈x、寫指針第一次相等時(shí),就應(yīng)該輸出空狀態(tài),然后停止對(duì)FIFO進(jìn)行讀取操作。)所以,究竟如何判斷FIFO是空還是滿呢?答案是無法判斷,只能通過在高位增加一位的方式來判斷,當(dāng)除了最高位MSB外的其他位都相同,最高位不同時(shí)則表明此時(shí)寫指針超過了讀指針一圈,F(xiàn)IFO被寫滿了;當(dāng)除了最高位MSB外的其他位都相同,最高位相同時(shí)則表明此時(shí)寫指針等于讀指針,F(xiàn)IFO被讀空了。
這種方法用來對(duì)同步FIFO進(jìn)行判斷是沒有問題的,因?yàn)橥紽IFO的讀、寫指針是同一時(shí)鐘域下的信號(hào),可以直接對(duì)比。但是異步FIFO的讀、寫指針是不同時(shí)鐘域下的信號(hào),如果直接對(duì)比則會(huì)有亞穩(wěn)態(tài)的問題。為了對(duì)異步FIFO的讀、寫指針進(jìn)行判斷,我們首先需要將其同步到統(tǒng)一的時(shí)鐘域下,而這就引發(fā)出了新的問題----讀、寫指針在大多數(shù)情況下都不是個(gè)單bit信號(hào),而是個(gè)多bit信號(hào)。如果讀、寫指針直接使用2進(jìn)制的形式進(jìn)行同步,則難以避免同步過程中會(huì)出現(xiàn)的多個(gè)bit信號(hào)同時(shí)變化的問題。如7(0111)跳轉(zhuǎn)到8(1000),此時(shí)有4個(gè)bit信號(hào)都發(fā)生了變化,如果直接同步,則由于不同信號(hào)之間的延遲(skew),可能導(dǎo)致亞穩(wěn)態(tài)、錯(cuò)采、漏采等等問題。
此時(shí)不妨回想下格雷碼的性質(zhì):每相鄰位之間只有一個(gè)bit的變化。FIFO的指針是遞增的,這使得在傳輸遞增的多bit信號(hào)時(shí),格雷碼具有天然的優(yōu)勢(shì)。還是從7到8的例子,若使用格雷碼,則應(yīng)該是7(0100)--8(1100),這樣就只有1個(gè)bit的變化了(最高位),這樣就將多bit信號(hào)的跨時(shí)鐘域轉(zhuǎn)變成了單bit信號(hào)的跨時(shí)鐘域,而單個(gè)bit的跨時(shí)鐘域同步是很好實(shí)現(xiàn)的。
3、讀指針、寫指針應(yīng)該被同步到哪個(gè)時(shí)鐘域?
異步FIFO的讀、寫指針是不同時(shí)鐘域的信號(hào),那么就不能直接對(duì)比,而是需要將其同步到同一時(shí)鐘域才能進(jìn)行對(duì)比。現(xiàn)在問題來了?指針應(yīng)該被同步到哪個(gè)時(shí)鐘域?可選項(xiàng)有第三方時(shí)鐘域、讀時(shí)鐘域和寫時(shí)鐘域。接下來不妨逐個(gè)分析。
首先需要說明的是,這說的同步都是指使用2個(gè)(或者3個(gè),但此類情況不多)FF(觸發(fā)器)來進(jìn)行同步(俗稱“打兩拍”),這種同步方式是有延遲的(時(shí)序開銷,可以看做是兩個(gè)目同步時(shí)鐘周期)。
第三方時(shí)鐘域:不難知道一個(gè)信號(hào)從一個(gè)時(shí)鐘域同步到另一個(gè)時(shí)鐘域(被同步時(shí)鐘域)是需要時(shí)間的(這里僅考慮從滿到快,也就是暫時(shí)不考慮漏采的問題),需要的時(shí)間取決于被同步時(shí)鐘域的周期以及需要同步的個(gè)數(shù)。假設(shè)這個(gè)時(shí)間是T,那么經(jīng)過T時(shí)間后,由于讀寫時(shí)鐘不一致,原來的讀寫時(shí)針增加(也可能不變)的量是不一致。比如說實(shí)際上讀寫時(shí)針都指向4(且最高位相同),那么這種情況實(shí)際上是出現(xiàn)了讀空的情況。但是同步到第三方時(shí)鐘域后,可能寫指針成了6,而讀指針變成了8(讀時(shí)鐘比寫時(shí)鐘快),那么在這種情況下FIFO就不會(huì)報(bào)“讀空”,從而造成功能錯(cuò)亂。所以該種方法不可取。
同步到寫時(shí)鐘域:讀指針同步到寫時(shí)鐘域需要時(shí)間T,在經(jīng)過T時(shí)間后,可能原來的讀指針會(huì)增加或者不變,也就是說同步后的讀指針一定是小于等于原來的讀指針的。寫指針也可能發(fā)生變化,但是寫指針本來就在這個(gè)時(shí)鐘域,所以是不需要同步的,也就意味著進(jìn)行對(duì)比的寫指針就是真實(shí)的寫指針。
現(xiàn)在來進(jìn)行寫滿的判斷:也就是寫指針超過了同步后的讀指針一圈。但是原來的讀指針是大于等于同步后的讀指針的,所以實(shí)際上這個(gè)時(shí)候?qū)懼羔樒鋵?shí)是沒有超過讀指針一圈的,也就是說這種情況是“假寫滿”。那么“假寫滿”是一種錯(cuò)誤的設(shè)計(jì)嗎?答案是NO。前面我們說過異步FIFO設(shè)計(jì)的關(guān)鍵點(diǎn)是產(chǎn)生合適的“寫滿”和“讀空”信號(hào),那么何謂“合適”?該報(bào)的時(shí)候沒報(bào)算合適嗎?當(dāng)然不算合適。不該報(bào)的時(shí)候報(bào)了算不算合適?答案是算??梢韵胂笠幌?,假設(shè)一個(gè)深度為100的FIFO,在寫到第98個(gè)數(shù)據(jù)的時(shí)候就報(bào)了“寫滿”,會(huì)引起什么后果?答案是不會(huì)造成功能錯(cuò)誤,只會(huì)造成性能損失(2%),大不了FIFO的深度我少用一點(diǎn)點(diǎn)就是的。事實(shí)上這還可以算是某種程度上的保守設(shè)計(jì)(安全)。
接著進(jìn)行讀空的判斷:也就是同步后的讀指針追上了寫指針。但是原來的讀指針是大于等于同步后的讀指針的,所以實(shí)際上這個(gè)時(shí)候讀指針實(shí)際上是超過了寫指針。這種情況意味著已經(jīng)發(fā)生了“讀空”,卻仍然有錯(cuò)誤數(shù)據(jù)讀出。所以這種情況就造成了FIFO的功能錯(cuò)誤。
同步到讀時(shí)鐘域:寫指針同步到讀時(shí)鐘域需要時(shí)間T,在經(jīng)過T時(shí)間后,可能原來的讀指針會(huì)增加或者不變,也就是說同步后的寫指針一定是小于等于原來的寫指針的。讀指針也可能發(fā)生變化,但是讀指針本來就在這個(gè)時(shí)鐘域,所以是不需要同步的,也就意味著進(jìn)行對(duì)比的讀指針就是真實(shí)的讀指針。
現(xiàn)在來進(jìn)行寫滿的判斷:也就是同步后的寫指針超過了讀指針一圈。但是原來的寫指針是大于等于同步后的寫指針的,所以實(shí)際上這個(gè)時(shí)候?qū)懼羔樢呀?jīng)超過了讀指針不止一圈,這種情況意味著已經(jīng)發(fā)生了“寫滿”,卻仍然數(shù)據(jù)被覆蓋寫入。所以這種情況就造成了FIFO的功能錯(cuò)誤。
接著進(jìn)行讀空的判斷:也就是讀指針追上了同步后的指針。但是原來的寫指針是大于等于同步后的寫指針的,所以實(shí)際上這個(gè)時(shí)候讀指針其實(shí)還沒有追上寫指針,也就是說這種情況是“假讀空”。那么“假讀空”是一種錯(cuò)誤的設(shè)計(jì)嗎?答案是NO。前面我們說過異步FIFO設(shè)計(jì)的關(guān)鍵點(diǎn)是產(chǎn)生合適的“寫滿”和“讀空”信號(hào),那么何謂“合適”?該報(bào)的時(shí)候沒報(bào)算合適嗎?當(dāng)然不算合適。不該報(bào)的時(shí)候報(bào)了算不算合適?答案是算。可以想象一下,假設(shè)某個(gè)FIFO,在讀到還剩2個(gè)數(shù)據(jù)的時(shí)候就報(bào)了“讀空”,會(huì)引起什么后果?答案是不會(huì)造成功能錯(cuò)誤,只會(huì)造成性能損失(2%),大不了我先不讀了,等數(shù)據(jù)多了再讀就是的。事實(shí)上這還可以算是某種程度上的保守設(shè)計(jì)(安全)。
現(xiàn)在可以總結(jié)一下:
? “寫滿”的判斷:需要將讀指針同步到寫時(shí)鐘域,再與寫指針判斷
? “讀空”的判斷:需要將寫指針同步到讀時(shí)鐘域,再與讀指針判斷
假讀空示意如下:

假寫滿示意如下:

4、如何判斷異步FIFO的空和滿?
在同步FIFO的設(shè)計(jì)中,我們把讀、寫指針的位寬拓寬了1bit,目的是區(qū)分原來的讀、寫指針相等時(shí)判斷空、滿的問題。同步FIFO的指針使用的是2進(jìn)制碼的形式,而異步FIFO為了減少多bit信號(hào)跨時(shí)鐘域傳輸?shù)膩喎€(wěn)態(tài)問題,采用的是格雷碼形式的指針,那么格雷碼形式的指針應(yīng)該如何對(duì)比來判斷空和滿?
首先要說明的是,將格雷碼轉(zhuǎn)換成2進(jìn)制再進(jìn)行對(duì)比是一種很好的辦法,但是會(huì)消耗多的組合邏輯資源,所以我們暫時(shí)不討論。

先觀察上圖,假定我們要設(shè)計(jì)的FIFO深度為8,那么指針寬度是4,因?yàn)閷挾葹?時(shí)無法區(qū)分空、滿。
空的判斷很好判斷,只要讀寫指針?biāo)形蝗恳恢?,則說明此時(shí)是空狀態(tài)。
滿的判斷復(fù)雜一點(diǎn),假設(shè)采用同步FIFO的判斷方法----最高位不同,其他位一致。我們用實(shí)際的數(shù)值來看看是什么情況:當(dāng)讀指針的值為0100,則說明此時(shí)讀指針指向最高的空間7,那么若是FIFO滿了,則寫指針應(yīng)該是1100,那么1100對(duì)應(yīng)的二進(jìn)制是多少呢?是8。那么讀寫指針、一個(gè)指向7,另一個(gè)卻指向8,這是滿?顯然不是。
如果真的是滿的話,寫指針應(yīng)該是多少?應(yīng)該是15,也就是1000,此時(shí)寫指針是超過讀指針8,也就是一圈的。那么0100和1000有什么聯(lián)系?很顯然,高兩位相反,低位相同。這種規(guī)律的形成原因是我們之前提到的,格雷碼的變化都關(guān)于某個(gè)對(duì)稱軸對(duì)稱。
總結(jié):
? 當(dāng)最高位和次高位相同,其余位相同認(rèn)為是讀空
? 當(dāng)最高位和次高位不同,其余位相同認(rèn)為是寫滿
5、空和滿的判斷是準(zhǔn)確的嗎?
在第4點(diǎn)我們知道了----將讀指針同步到寫時(shí)鐘域來判斷滿;將寫指針同步到讀時(shí)鐘域來判斷空。既然是異步FIFO,那么讀寫時(shí)鐘域的信號(hào)是不一致的,其中一個(gè)的頻率快,另一個(gè)的頻率這慢。那么在兩次同步過程中,一定是一次慢時(shí)鐘采快時(shí)鐘和一次快時(shí)鐘采慢時(shí)鐘??鞎r(shí)鐘采慢時(shí)鐘是不會(huì)有問題的,因?yàn)檫@符合采樣定理。但是慢時(shí)鐘采快時(shí)鐘則會(huì)有問題,因?yàn)椴蓸舆^程不符合采樣定理。
那么會(huì)造成什么問題?答案是漏采。某些數(shù)值可能會(huì)被漏掉。例如原本是連續(xù)的0--1--2---3的信號(hào),從快時(shí)鐘同步到慢時(shí)鐘后,就變成了離散的0--3,其中的1、2被漏掉了。那么這樣一種現(xiàn)象會(huì)導(dǎo)致空、滿的判斷是準(zhǔn)確的嗎?答案是不準(zhǔn)確,但沒關(guān)系。
設(shè)想讀慢寫快與讀快寫慢兩種情況:
讀慢寫快:
進(jìn)行寫滿判斷的時(shí)候需要將讀指針同步到寫時(shí)鐘域,因?yàn)樽x慢寫快,所以不會(huì)有讀指針遺漏,同步消耗時(shí)鐘周期,所以同步后的讀指針滯后(小于等于)當(dāng)前讀地址,所以可能寫滿會(huì)提前產(chǎn)生,并非真寫滿。
進(jìn)行讀空判斷的時(shí)候需要將寫指針同步到讀指針 ,因?yàn)樽x慢寫快,所以當(dāng)讀時(shí)鐘同步寫指針的時(shí)候,必然會(huì)漏掉一部分寫指針,我們不用關(guān)心那到底會(huì)漏掉哪些寫指針,我們?cè)诤醯氖锹┑舻闹羔槙?huì)對(duì)FIFO的讀空產(chǎn)生影響嗎?比如寫指針從0寫到10,期間讀時(shí)鐘域只同步捕捉到了3、5、8這三個(gè)寫指針而漏掉了其他指針。當(dāng)同步到8這個(gè)寫指針時(shí),真實(shí)的寫指針可能已經(jīng)寫到10 ,相當(dāng)于在讀時(shí)鐘域還沒來得及覺察的情況下,寫時(shí)鐘域可能寫了數(shù)據(jù)到FIFO去,這樣在判斷它是不是空的時(shí)候會(huì)出現(xiàn)不是真正空的情況,漏掉的指針也沒有對(duì)FIFO的邏輯操作產(chǎn)生影響。
讀快寫慢:
進(jìn)行讀空判斷的時(shí)候需要將寫指針同步到讀指針 ,因?yàn)樽x快寫慢,所以不會(huì)有寫指針遺漏,同步消耗時(shí)鐘周期,所以同步后的寫指針滯后(小于等于)當(dāng)前寫地址,所以可能讀空會(huì)提前產(chǎn)生,并非真讀空。
進(jìn)行寫滿判斷的時(shí)候需要將讀指針同步到寫時(shí)鐘域,因?yàn)樽x快寫慢,所以當(dāng)寫時(shí)鐘同步讀指針的時(shí)候,必然會(huì)漏掉一部分讀指針,我們不用關(guān)心那到底會(huì)漏掉哪些讀指針,我們?cè)诤醯氖锹┑舻闹羔槙?huì)對(duì)FIFO的寫滿產(chǎn)生影響嗎?比如讀指針從0讀到10,期間寫時(shí)鐘域只同步捕捉到了3、5、8這三個(gè)讀指針而漏掉了其他指針。當(dāng)同步到8這個(gè)讀指針時(shí),真實(shí)的讀指針可能已經(jīng)讀到10 ,相當(dāng)于在寫時(shí)鐘域還沒來得及覺察的情況下,讀時(shí)鐘域可能從FIFO讀了數(shù)據(jù)出來,這樣在判斷它是不是滿的時(shí)候會(huì)出現(xiàn)不是真正滿的情況,漏掉的指針也沒有對(duì)FIFO的邏輯操作產(chǎn)生影響。
現(xiàn)在我們會(huì)發(fā)現(xiàn),所謂的空滿信號(hào)實(shí)際上是不準(zhǔn)確的,在還沒有空、滿的時(shí)鐘就已經(jīng)輸出了空滿信號(hào),這樣的空滿信號(hào)一般稱為假空、假滿。假空、假滿信號(hào)本質(zhì)上是一種保守設(shè)計(jì),想象一下,一個(gè)深度為16的異步FIFO,在其寫入14個(gè)數(shù)據(jù)時(shí),即輸出了寫滿(假滿)標(biāo)志,這會(huì)對(duì)我們的設(shè)計(jì)造成影響嗎?會(huì),會(huì)削弱我們的效率,我們實(shí)際使用的FIFO深度成了14,但是會(huì)使得我們的設(shè)計(jì)產(chǎn)生錯(cuò)誤嗎?顯然不會(huì)。同樣的,在FIFO深度仍有2時(shí)即輸出了讀空(假空)標(biāo)志,也不會(huì)使得我們的設(shè)計(jì)出錯(cuò),但是會(huì)降低效率,因?yàn)槲覀兪褂玫腇IFO深度又少了2。
6、既然有假滿、假空,那么有沒有真滿、真空?
還真有,但是沒意義。既然我們可以將讀指針同步到寫時(shí)鐘域來判斷假滿;將寫指針同步到讀時(shí)鐘域來判斷假空。那么對(duì)應(yīng)地,可以讀指針同步到寫時(shí)鐘域來判斷空;將寫指針同步到讀時(shí)鐘域來判斷滿。
在寫時(shí)鐘域判斷空:
讀指針被同步過來的信號(hào)(同步后讀指針)是滯后于真實(shí)讀指針的信號(hào),當(dāng)同步后讀指針等于寫指針時(shí),真實(shí)讀指針實(shí)際上早就等于寫指針了,也就是說此時(shí)的空一定是空,甚至已經(jīng)空了一段時(shí)間了。這樣的空標(biāo)志顯然是沒有使用意義的,因?yàn)闀?huì)造成對(duì)FIFO的過讀操作,你來來回回讀個(gè)空FIFO有什么意義呢?也就是說真空能實(shí)現(xiàn),但是沒實(shí)際使用意義。
在讀時(shí)鐘域判斷滿:
寫指針被同步過來的信號(hào)(同步后寫指針)是滯后于真實(shí)寫指針的信號(hào),但同步后寫指針超過讀指針一圈時(shí),真實(shí)寫指針實(shí)際上早就超過讀指針一圈了,也就是說此時(shí)的滿一定是滿,甚至已經(jīng)滿了一段時(shí)間了。這樣的滿標(biāo)志顯然是沒有使用意義的,因?yàn)闀?huì)造成對(duì)FIFO的過寫操作,你來來回回寫個(gè)滿FIFO有什么意義呢?也就是說真滿也能實(shí)現(xiàn),但是同樣沒實(shí)際使用意義。
7、非2次冪深度的FIFO如何設(shè)計(jì)?
在第1點(diǎn)關(guān)于格雷碼的性質(zhì)中,我們闡述了:
? 在一組數(shù)的編碼中,若任意兩個(gè)相鄰的代碼只有一位二進(jìn)制數(shù)不同,則稱這種編碼為格雷碼
? 當(dāng)?shù)贜位從0變到1的時(shí)候,之后的數(shù)的N-1位會(huì)關(guān)于前半段軸對(duì)稱,而比N位高的位是相同的。
由這兩點(diǎn),我們發(fā)現(xiàn)格雷碼都可以關(guān)于某條對(duì)稱軸對(duì)稱。所以只有當(dāng)FIFO深度為2的冪次方時(shí),才能做到格雷碼繞一圈后,回到初始位置仍然只有1bit變化,如15(1000)----0(0000)。當(dāng)FIFO深度不為2的冪次方時(shí),顯然從最尾端跳轉(zhuǎn)到開頭端,變化的就不止一個(gè)bit了。比如FIFO深度為7,顯然,從13(1011)----0(0000),變化了可不止1bit。這樣的話在這一次跳變就失去了格雷碼存在的意義了,所以得想點(diǎn)其他辦法解決。
前面說過,格雷碼相鄰每位只變化1bit,而且關(guān)于中軸對(duì)稱的。那么我們可以這樣編碼:針對(duì)深度為7的FIFO,從1(0001)開始,一直到(14個(gè)數(shù)表示7深度,高位區(qū)分狀態(tài))14(1001),1(0001)與14(1001)是關(guān)于中軸對(duì)稱的(高位為變化位),所以也只有1bit的跳變。同樣的如果深度為6的FIFO,就從2(0011)開始,到13(1011),同樣只跳變1bit。

空標(biāo)志只用判斷讀、寫指針是否全部相等即可。但是滿標(biāo)志就不能用“高兩位相反,其他位相同來判斷了”,需要找其他規(guī)律了。從這里可以看出,格雷碼作為一種無權(quán)碼,在比較和運(yùn)算等方面不如有權(quán)碼二進(jìn)制靈活。所以,咱要不還是轉(zhuǎn)回二進(jìn)制再比較吧。
不管你設(shè)計(jì)FIFO用RAM還是直接調(diào)用IP也好,最終實(shí)現(xiàn)都是用的Block RAM資源,其生成的位寬肯定是2的冪次。所以啊,不用也是浪費(fèi)啊。