天天看點

數字IC秋招手撕代碼(六)異步FIFO

數字IC秋招手撕代碼(六)異步FIFO

    • 常用情況
    • FIFO的空滿标志
    • FIFO指針格雷碼
    • 格雷碼下的空滿信号
    • 重點
    • 最小深度FIFO的計算
    • 代碼

最近兩天晚上在看異步FIFO,之前也使用過異步FIFO,但是一直沒有仔細搞懂原理。這次準備寫篇部落格記錄一下。

常用情況

以下兩種情況,經常會使用到異步FIFO

1.跨時鐘域問題

2.位寬不一緻(例如 輸入8bit 輸出16bit)這時候fifo中的記憶體結構應該和常見的不同,具體還不清楚

FIFO的空滿标志

一個深度為(2^n)的FIFO,其位址位寬需要n+1位

  • MSB作為指針折回标志,當指針完成一輪讀或寫,MSB就翻轉一次。是以當wptr和rptr相遇時,如果MSB=1,為滿;MSB=0,為空。

FIFO指針格雷碼

由于二進制在跨時鐘域上可能有所有位同時翻轉的可能,而格雷碼每次隻有一位變化,可以大大降低亞穩态出現的可能。(同時如果多bit原碼直接同步器傳輸,傳輸線等差異,會在bit間很容易造成亞穩态)

格雷碼下的空滿信号

因為位址是格雷碼,是以不能單純的比較n位是否相同來判斷空滿,而要考慮格雷碼的對稱性。

  • 空:仍為wptr和rptr完全相等。
  • 滿:wptr和rptr的最高位和次高位都不同,而其餘位都相同。

重點

因為在位址的同步邏輯中常使用打兩拍來消除亞穩态,是以同步時常有延遲的現象,那麼這會導緻什麼問題嗎?

寫快讀慢的情況下

  • fifo_empty的産生:這時候從讀端看進去,要将wptr同步到rptr,是以wptr可能慢于實際wptr,不滿足讀空的條件時沒問題;當條件滿足時,因為延遲的關系,實際可能wptr>=rptr,fifo實際有寫入資料,但這寫不影響使用,這種情況叫虛空。
  • fifo_full的産生:這時候從寫端看進去,要将rptr同步到wptr,是以rptr可能慢于實際wptr,滿足寫滿的條件時,沒有問題;當條件不滿足時,由于延遲的關系,實際可能rptr>=wptr-fifo_depth,這時候信号可能顯示為full,實際已經多讀了,這種情況叫虛滿

寫慢讀快的情況分析類似

最小深度FIFO的計算

  • 如果是同步FIFO問題:

例如

數字IC秋招手撕代碼(六)異步FIFO

考慮背靠背情況,最壞在160cycle中寫入了160個資料,而這16個cycle隻能讀出16*8=128個資料,FIFO中還有32個資料,是以FIFO的最小深度為32.

  • 如果是異步FIFO,将上述情況改為
    數字IC秋招手撕代碼(六)異步FIFO

    那麼背靠背情況,80個cycle裡寫入了80個資料,而讀側在這些時間内隻能讀出

    8*(80cylce/10cycle)*(100MHz/200MHz)=32

    是以FIFO裡至少還有48個資料

    深度需要2^6 = 64

代碼

module afifo(
        input                   clka,
        input                   rstn,
        input                   wr_en,
        input   [len-1:0]       din,
        input                   clkb,
        input                   rd_en,
        output  reg [len-1:0]   dout,
        output                  full,       //for write domain
        output                  empty       //for read domain
    );
parameter       len = 4;    
parameter       depth = 16;

reg     [len-1:0]       memory  [depth-2:0];
reg     [3:0]           wr_ptr,rd_ptr,status_cnt;   
wire                    full,empty;

wire    [3:0]           gray_wr_ptr,gray_rd_ptr;
reg     [3:0]           gray_wr_clkb_dly0,gray_wr_clkb_dly1;
reg     [3:0]           gray_rd_clka_dly0,gray_rd_clka_dly1;

assign full  = (gray_wr_ptr[3:0] == {~gray_rd_clka_dly1[3:2],gray_rd_clka_dly1[1:0]});
              
assign empty = (gray_rd_ptr[3:0] == gray_wr_clkb_dly1[3:0]) ; 


always @(posedge clka or negedge rstn)begin
    if(!rstn)
        wr_ptr <= 0;
    else if(wr_ptr==depth-1)
        wr_ptr <= 0;
    else if(wr_en & ~full)
        wr_ptr <= wr_ptr + 1;
end

always @(posedge clkb or negedge rstn)begin
    if(!rstn)
        rd_ptr <= 0;
    else if(rd_ptr==depth-1)
        rd_ptr <= 0;
    else if(rd_en & ~empty)
        rd_ptr <= rd_ptr + 1;
end

integer i;

always @(posedge clka or negedge rstn)begin
    if(!rstn)
        for(i=0;i<depth;i=i+1)
            memory[i] <= 0;
    else if(wr_en & ~full)
        memory[wr_ptr] <= din;
end

always @(posedge clkb or negedge rstn)begin
    if(!rstn)
        dout <= 0;
    else if(rd_en & ~empty)
        dout <= memory[rd_ptr];
end

assign gray_wr_ptr = wr_ptr ^ (wr_ptr>>1);
assign gray_rd_ptr = rd_ptr ^ (rd_ptr>>1);

always @(posedge clkb or negedge rstn)begin
    if(!rstn)begin
        gray_wr_clkb_dly0 <= 0;
        gray_wr_clkb_dly1 <= 0;        
    end
    else begin
        gray_wr_clkb_dly0 <= gray_wr_ptr;
        gray_wr_clkb_dly1 <= gray_wr_clkb_dly0;
    end
end

always @(posedge clka or negedge rstn)begin
    if(!rstn)begin
        gray_rd_clka_dly0 <= 0;
        gray_rd_clka_dly1 <= 0;
    end
    else begin
        gray_rd_clka_dly0 <= gray_rd_ptr;
        gray_rd_clka_dly1 <= gray_rd_clka_dly0;
    end
end

endmodule
           

繼續閱讀