
回到這張看起來很複雜的AFIFO架構圖 (*藍色訊號為write clk *紅色訊號為read clk)
我們開始來專心探討一下圖中B2G這區塊的功用相信各位看懂架構後coding就不是甚麼大問題
回顧一下,
我們先思考ptr在傳輸時沒有處理CDC issue時會發生甚麼事?
ptr屬於一個multibits的訊號,
在2個clk domain間傳輸時會碰到一個問題,
每個bits之間從clkA到clkB的時間點不同,
clkB所驅動的reg並無法知道當前取樣的ptr訊號每個bits是否真實出現過這個.
如果不知道上述再說甚麼的話,可以回顧一下這篇文章
跨clk domain後所掛入的2DFF並沒辦法知道value是否正確,
他只能確保說後面抓到的訊號是strong 0或strong 1,
也就是說只要有個機制能讓clkB的reg取樣時確保value肯定沒錯,
那麼就解決了當前ptr傳遞所碰到的最大問題.
也就是當前gray code的應用
甚麼是gray code?為甚麼他可以讓value正確卻又不像2DFF隨處可見
如果真這麼強gray code綁2DFF應該早就要變成一個module變成跨CDC時使用的卍解,
但目前看下來卻好像不是如此,更詭異的是B2G後面還是掛了2DFF.
因此這邊再來介紹一下gray code
wiki上的簡介如下,gray code是對原先的數值做加密或轉換的一種方式,
將原先的binary表示的數值,改用另一種格式來表示,
並且在新的格式下每個相連數值間的差異都只有1個bit的改動.


看個實際的例子
二進位下 0->1 和gray 一樣都只有1個bit跳動
1->2: gray (001 -> 011) / bin (001 -> 010) ,
gray下只有bit[1] 由0變1,
在B2G output給2DFF時於前後幾個cycle內,有機會被取樣到的data為 {001 -> 001 -> 011 -> 011}
bin下則bit[1] 由 0變1 且 bit[0] 由1變0,總共有2個bits的跳動,於前後幾個cycle內
,有機會被取樣到的data則為 {001 -> 011 -> 010 -> 010} or {001 -> 000 -> 010 -> 010}.
因為每個bit抵達下一集reg的時間長短不同,
因此在bin下有機會被看到011 (3) 或000 (0) 這兩種可能在過程中根本還沒使用到或已經使用過的ptr被重新取樣,
這個行為將會破壞fifo的function,因為fifo的工作原理就是靠wptr先寫入data再將wptr update給read端, 假如wptr只寫到了2,理論上rptr最多就只能讀到2的位置,
但因為了multibits傳輸過程出現的暫態導致011 (3)被取樣到了,
read端就會以為write已經寫到3了,因此2,3都可以被讀取,
儘管下個cycle被修正回010好了,fifo操作也很有可能直接亂掉導致function break
然而在gray下,我們只有機會看到兩種001或011,
看到001 read端就只是以為還沒有任何update而已,看到011則發現write端update了可以多讀一筆出來,不會造成function上的誤判
一路往下看大致上就是這種概念
gray code下 0到1 1到2 2到3 ... 以及最後overflow導致的7到0,每個相鄰的數字都只有1個bit會動
但對於bin來看在overflow時7到0,會由111變成000 總共有3個bits同時需要做跳動,
在看完概念來總結一下,
其實gray code沒什麼就是一種新的數值表示方式,每個十進位的數值都可以對應到一個特定的數值,但他有一個特點,在十進位上相間的兩個數值在gray code下只會有一個bit會不一樣,我們將利用這個特性去解接下來所碰到的問題
由於我們在傳遞的data內容為pointer,
pointer有個特性是每次的操作都只會讓數值+1,
也就是說數值會這樣跳 0,1,2,3,4..7, 0,1,2,..7 依序的遞增到111後overflow成0再重複開始新的計數
也在有這個先前條件下,我們的gray code才有機會派上用場,
因為Data的源頭變化是有既定的rule的,
所以我們在這邊就可以來回答一下上面提出的疑問,
他好像可以讓value在傳遞時保持正確性,但是卻不是所有multibits傳輸時的通解,
因為必須在source data在這種有固定rule每次都只會執行+1的行為下才有機會work.
至於我們在gray code上解決的是multibits傳輸時多個bits跳躍的問題,
利用Gray code限制了我們在同一個時間下只會有1個bit的跳動,
但對於跨clk domain所導致的metastable的問題卻沒辦法被這個方式解決,
也就是說把問題限縮回了1bit CDC的題目,
因此在B2G後面的2DFF就是用來解決data跨wptr clk和rptr clk的問題
總結一下中間的ptr區塊在做甚麼?
1.將當前的wptr給予memory告知新的data要寫的位置,
並同時經gray code編譯後,傳遞給read ctrl端,
但這是一個由wclk跨rclk的操作,
因此需要再rclk端敲2T DFF做sync來消除metastable的問題,
再傳遞到read ctrl內給controller做判斷是否可繼續read data
2.將當前的rptr給予memory告知需拿取Data的位置,
並同時經gray code編譯後,傳遞給write ctrl端,
但這時是變成rclk跨wclk的操作,
因此需要利用wclk敲2T dff做sync後消除metastable的問題,
再傳遞給write ctrl內讓controller判斷是否可以繼續write新data
當我們好像理解ptr在幹嘛後,
面試官最喜歡考的題目就是,
雖然在使用gray code編譯後可以讓data每次的跳躍只有1bit,
讓我們在multibits傳輸時不會抓到那種沒出現過的暫態訊號,
但仔細想想,當write/read clk速度差很多的時候呢?
假如write寫了5次才有機會被read取樣到1次
這樣在read端看到的不是也是000 -> 111
一次同時有3個bits在跳動,這樣不就和binany一樣了嗎?
當你碰到這個問題也有同樣的困惑時就是因為你還沒感受到gray code的魅力
雖然read端看到了3個bits一起跳了,
但是我們限制了source端每次的update只能有1 bit
所以跟binary時source端每次update是沒限制是不同的
在我們都能保證source端每次update只有1bit時,
儘管sync的過程中看到了多bits翻轉造成的暫態,
這些暫態都一定是曾經出現過的合法value,
一切的Data在你decode回binary後都會發現ptr都<=當前合法的數字,
並不會像binary一樣才update到2卻讀到了3的位置,
因此當clk速度差很多時在gray code的保護下並不會造成function上的錯誤,
唯一會導致的就只是performance有機會會下降而已,
這個部分適合大家自己手寫模擬中間可能會出現的data,
就會發現gray code的奧妙.