數字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問題:
例如
考慮背靠背情況,最壞在160cycle中寫入了160個資料,而這16個cycle隻能讀出16*8=128個資料,FIFO中還有32個資料,是以FIFO的最小深度為32.
- 如果是異步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