FPGA基礎學習(11) -- FIFO設計(style#2)

在上一篇FIFO設計(stlye#1)中總結了論文《Simulation and Synthesis Techniques for Asynchronous FIFO Design》提出的FIFO設計的第一種方法,本篇博客總結第二種方法,源自論文《Simulation and Synthesis Techniques for Asynchronous FIFO Design with Asynchronous Pointer Comparisons》。

style#1提出了一種增加補位的格雷碼作爲指針標識,來判斷FIFO是滿還是空的方法。 這種方法起源於想要解決“不知道讀指針敢上了寫指針(空)還是寫指針繞了一圈趕上了讀指針(滿)”的問題。實際上,爲了分清空還是滿,最重要的就是找準指針的來向。所以style#2就是用讀、寫指針的最高兩位劃分成4個象限,來識別讀寫指針的相對方向,從而正確的判斷空滿。

1. 空滿判斷

如下兩圖所示,分別示意了“即將滿”和“即將空”,注意“即將”兩字。以指針的最高兩位00、01、11、10,分別表示第一、第二、第三和第四象限,在判斷空滿之前遵循以下兩個原則:

  • 寫指針落後於讀指針一個象限,表示即將滿;
  • 讀指針落後於寫指針一個象限,表示即將空;

說“即將”是表示指針追趕的方向,先有方向,當兩指針相等之後,纔會產生真正的空或滿信號。

在確定了象限關係之後,會生成dirset_n和dirrst兩個信號,用於產生指示指針追趕方向的direction信號,從而確定空滿信號,如下圖所示。

如下圖,爲style#2 FIFO的整體結構框圖。

2. 分析

實際上style#2理解起來比style#1更簡單,但是由於指針是進行的異步比較,並且有一些關鍵的組合電路,所以論文討論時序、關鍵路徑以及組合電路的各信號跳變上用了不少篇幅。

如上圖所示,是產生full 和empty的電路圖,其中曲線是代碼async_cmp.v對應的原理圖,後面兩個兩拍寄存器在相應的代碼模塊中。

2.1 工作原理

  1. 論文中說“aempty_n在rclk的上升沿有效,在wclk的上升沿失效”,這句好什麼意思呢?因爲是組合電路,所以要結合前級的時序電路和代碼來分析。

先看代碼:

module async_cmp #(parameter   ADDRSIZE = 4)
(
    aempty_n, 
    afull_n, 
    wptr, 
    rptr, 
    wrst_n
);
    
    parameter   N = ADDRSIZE-1;
    
    output  aempty_n, afull_n;
    input   [N:0]   wptr, rptr;
    input   wrst_n;
    
    reg     direction;
    wire    high = 1'b1;
    wire    dirset_n = ~( (wptr[N]^rptr[N-1]) & ~(wptr[N-1]^rptr[N]));  // wptr在rptr後一個象限,快要滿了
    wire    dirclr_n = ~((~(wptr[N]^rptr[N-1]) & (wptr[N-1]^rptr[N])) | ~wrst_n);   // wptr在rptr前一個象限,快要空了
    
 /*    always @(posedge high or negedge dirset_n or negedge dirclr_n)
        if (!dirclr_n) 
            direction <= 1'b0;
        else if (!dirset_n) 
            direction <= 1'b1;
        else 
            direction <= high; */
            
    always @(negedge dirset_n or negedge dirclr_n)
    if (!dirclr_n) direction <= 1'b0;
    else direction <= 1'b1;
    
    assign  aempty_n = ~((wptr == rptr) && !direction);
    assign  afull_n = ~((wptr == rptr) && direction);
    
endmodule

aempty_n表示FIFO空的使能信號,FIFO既然要爲空,那麼肯定是讀指針“追上”寫指針,那什麼時候追上呢,肯定是在rclk驅動的讀指針在rclk的上升沿出變化的值等於wptr,當然前提是要滿足direction = 0的條件。

同理,什麼時候空失效呢?肯定是寫指針又繼續搶先一步,寫指針的變化是有wclk的上升沿觸發的。

那麼又有問題?aempty_n跟空信號有關,空信號又是控制讀操作的,它必須和讀時鐘保存緊密的聯繫,但是aempty_n的失效卻跟wclk有關,所以後面兩拍rclk驅動寄存器實現了空信號與rclk的同步。注意:aempty_n同時連接寄存器的D端和set端

同理可以分析afull_n。

只要結合指針的在各自時鐘驅動下的變化,再結合象限指示信號dirset_n和dirclr_n,以及direction信號就可以理解各信號的變化。論文中也現象的對各信號的變化展開了分析。

  1. 有關復位

從電路圖可以看出,復位只直接復位full信號,通過使讀寫指針相同(都置0),從而間接的使empty信號拉高,從而正確表徵FIFO復位後爲空。

2.2 時序及關鍵路徑

  1. 如上面象限比較的電路所示,因爲兩指針是異步是異步時鐘驅動,如果多位輸入或多或少不同步的變化,可能導致輸出有毛刺,但是採用格雷碼編碼則可以避免這個問題。

  2. 如下圖表示出的有關正確產生empty和full的關鍵信號。

3. 問題及解答

同樣這篇論文中,作者對幾個關心的問題做了分析和解答,這也是深入理解style#2 FIFO的途徑。

前面分析過aempty_n信號與rempty以及讀時鐘的基本關係,aempty_n作爲兩級同步寄存器的輸入端和置位端,在rclk的驅動下,產生rempty信號。那麼,之前我們又說過aempty_n在rclk的上升沿有效,在wclk的上升沿失效,因爲rclk和wclk是異步時鐘,在極端情況下,aempty_n剛剛有效(拉低),又迅速失效(拉高),形成一個毛刺(譯爲反向很短的脈衝,與競爭冒險產生的毛刺區分開),會不會出問題?作者給出了4個場景來說明這個。

  1. 毛刺沒有被第一級同步寄存器捕獲,故rempty沒有產生。這不會導致問題。PS:對這個問題我是這麼理解的,既然wclk上升沿迅速到來,並且讀寫指針不相等,那說明立馬又寫入了一個數據,這時候實際的FIFO不爲空,並且下一個讀操作還沒到來,所以不會出現讀空異常的情形;
  2. 毛刺置位了第一級寄存器,但是沒有被第二級寄存器捕獲,這種情形不可能出現。最終empty會在rclk的驅動下出現在第二級寄存器的輸出端,所以也不會出問題。(我不是很能理解);
  3. 毛刺只置位了第二級寄存器,丟了第一級寄存器,這幾乎不可能,只要關鍵路徑的時序滿足了設計要求,那麼可以在output輸出empty,至少會持續一個rclk週期,知道下一次第一級寄存器輸出爲0,從而失效empty,所以這也不是問題;
  4. 最有可能的情況是毛刺被兩級寄存器都捕獲,並因爲兩級同步的關係,被有效了2個rclk週期(但是要避免亞穩態,下面會討論),所以這也不是問題。

針對上面四種場景,作者進一步說明的其中的一些時序考慮,如下:

aempty_n的毛刺不會對同步寄存器有影響,因爲它本身就是靠rclk驅動的(與同步寄存器屬於同時鍾域),就算有毛刺,也只能是在rclk上升沿之後產生,並且不會持續到下一個rclk到來。

一個問題:假設寫時鐘的上升沿與讀時鐘的上升沿幾乎在一起,並失效了aempty_n,導致第一級寄存器出現亞穩態,這會導致什麼情況?

移除第二級寄存器的置位信號會違背第二級寄存器的恢復時間,會不會導致第二級寄存器出現亞穩態?作者認爲不會,如果置位信號使第二級寄存器輸出爲高了,那麼它的輸入端也必爲高(置位和輸入均與aempty_n有關),所以它不會因爲恢復時間而導致輸出狀態的不確定,即亞穩態(這一點實際上理解不是很深,只能略有感性體會)。

最後一個問題:一個毛刺,它失效於wclk的上升沿,與此同時,它失效時刻與第二級寄存器的rclk上升沿很接近,會不會導致第二級寄存器出現亞穩態?

答案是不會,前提是aempty_n的關鍵路徑滿足時序要求。因爲aempty_n拉低到穩定狀態,肯定只在rclk的第一個上升沿和第二個上升沿之間,所以這個毛刺必然在第二個上升沿之前到來。PS:言外之意就是第二個寄存器不會產生亞穩態。

同樣可以這樣分析full信號。

4. 仿真

本論文也給出了完整的源碼,借用上一篇的測試程序,可以通過仿真圖來理解上面分析的幾個信號的關係。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章