FIFO 簡介
FIFO 是英文 First In First Out 的縮寫,是一種先進先出的數(shù)據(jù)緩存器,它與普通存儲器的區(qū)別是沒有外部讀寫地址線,這樣使用起來非常簡單,但缺點就是只能順序寫入數(shù)據(jù),順序的讀出數(shù)據(jù),其數(shù)據(jù)地址由內部讀寫指針自動加 1 完成,不能像普通存儲器那樣可以由地址線決定讀取或寫入某個指定的地址。
用途 1:
異步 FIFO 讀寫分別采用相互異步的不同時鐘。在現(xiàn)代集成電路芯片中,隨著設計規(guī)模的不斷擴大,一個系統(tǒng)中往往含有數(shù)個時鐘,多時鐘域帶來的一個問題就是,如何設計異步時鐘之間的接口電路。異步 FIFO 是這個問題的一種簡便、快捷的解決方案,使用異步 FIFO 可以在兩個不同時鐘系統(tǒng)之間快速而方便地傳輸實時數(shù)據(jù)。
用途 2:
對于不同寬度的數(shù)據(jù)接口也可以用 FIFO,例如單片機位 8 位數(shù)據(jù)輸出,而 DSP 可能是 16 位數(shù)據(jù)輸入,在單片機與 DSP 連接時就可以使用 FIFO 來達到數(shù)據(jù)匹配的目的。
分類
同步 FIFO 是指讀時鐘和寫時鐘為同一個時鐘,在時鐘沿來臨時同時發(fā)生讀寫操作;
異步 FIFO 是指讀寫時鐘不一致,讀寫時鐘是互相獨立的。
FIFO 的常見參數(shù)
FIFO 的寬度:即 FIFO 一次讀寫操作的數(shù)據(jù)位;
FIFO 的深度:指的是 FIFO 可以存儲多少個 N 位的數(shù)據(jù)(如果寬度為 N)。
滿標志:FIFO 已滿或將要滿時由 FIFO 的狀態(tài)電路送出的一個信號,以阻止 FIFO 的寫操作繼續(xù)向 FIFO 中寫數(shù)據(jù)而造成溢出(overflow)。
空標志:FIFO 已空或將要空時由 FIFO 的狀態(tài)電路送出的一個信號,以阻止 FIFO 的讀操作繼續(xù)從 FIFO 中讀出數(shù)據(jù)而造成無效數(shù)據(jù)的讀出(underflow)。
讀時鐘:讀操作所遵循的時鐘,在每個時鐘沿來臨時讀數(shù)據(jù)。
寫時鐘:寫操作所遵循的時鐘,在每個時鐘沿來臨時寫數(shù)據(jù)。
1. 讀寫指針的工作原理
讀指針:總是指向下一個將要被寫入的單元,復位時,指向第 1 個單元(編號為 0)。
寫指針:總是指向當前要被讀出的數(shù)據(jù),復位時,指向第 1 個單元(編號為 0)
2.FIFO 的“空”/“滿”檢測
FIFO 設計的關鍵:產(chǎn)生可靠的 FIFO 讀寫指針和生成 FIFO“空”/“滿”狀態(tài)標志。
當讀寫指針相等時,表明 FIFO 為空,這種情況發(fā)生在復位操作時,或者當讀指針讀出 FIFO 中最后一個字后,追趕上了寫指針時,如下圖所示:
當讀寫指針再次相等時,表明 FIFO 為滿,這種情況發(fā)生在,當寫指針轉了一圈,折回來(wrapped around)又追上了讀指針,如下圖:
為了區(qū)分到底是滿狀態(tài)還是空狀態(tài),可以采用以下方法:
方法 1:在指針中添加一個額外的位(extra bit),當寫指針增加并越過最后一個 FIFO 地址時,就將寫指針這個未用的 MSB 加 1,其它位回零。對讀指針也進行同樣的操作。此時,對于深度為 2n 的 FIFO,需要的讀 / 寫指針位寬為(n+1)位,如對于深度為 8 的 FIFO,需要采用 4bit 的計數(shù)器,0000~1000、1001~1111,MSB 作為折回標志位,而低 3 位作為地址指針。
如果兩個指針的 MSB 不同,說明寫指針比讀指針多折回了一次;如 r_addr=0000,而 w_addr = 1000,為滿。
如果兩個指針的 MSB 相同,則說明兩個指針折回的次數(shù)相等。其余位相等,說明 FIFO 為空;
3. 二進制 FIFO 指針的考慮
將一個二進制的計數(shù)值從一個時鐘域同步到另一個時鐘域的時候很容易出現(xiàn)問題,因為采用二進制計數(shù)器時所有位都可能同時變化,在同一個時鐘沿同步多個信號的變化會產(chǎn)生亞穩(wěn)態(tài)問題。而使用格雷碼只有一位變化,因此在兩個時鐘域間同步多個位不會產(chǎn)生問題。所以需要一個二進制到 gray 碼的轉換電路,將地址值轉換為相應的 gray 碼,然后將該 gray 碼同步到另一個時鐘域進行對比,作為空滿狀態(tài)的檢測。
4. 使用 gray 碼進行對比,如何判斷“空”與“滿”
使用 gray 碼解決了一個問題,但同時也帶來另一個問題,即在格雷碼域如何判斷空與滿。
對于“空”的判斷依然依據(jù)二者完全相等(包括 MSB);
而對于“滿”的判斷,如下圖,由于 gray 碼除了 MSB 外,具有鏡像對稱的特點,當讀指針指向 7,寫指針指向 8 時,除了 MSB,其余位皆相同,不能說它為滿。因此不能單純的只檢測最高位了,在 gray 碼上判斷為滿必須同時滿足以下 3 條:
wptr 和同步過來的 rptr 的 MSB 不相等,因為 wptr 必須比 rptr 多折回一次。
wptr 與 rptr 的次高位不相等,如上圖位置 7 和位置 15,轉化為二進制對應的是 0111 和 1111,MSB 不同說明多折回一次,111 相同代表同一位置。
剩下的其余位完全相等。
5. 總體實現(xiàn)
系統(tǒng)的總體框圖如下:
1)頂層模塊
module AsyncFIFO
#(parameter ASIZE = 4, // 地址位寬
parameter DSIZE = 8) // 數(shù)據(jù)位寬 ( input [DSIZE-1:0] wdata, input winc, wclk, wrst_n, // 寫請求信號,寫時鐘,寫復位
input rinc, rclk, rrst_n, // 讀請求信號,讀時鐘,讀復位
output [DSIZE-1:0] rdata, output wfull, output rempty
);wire [ASIZE-1:0] waddr, raddr;wire [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr; /************************************************************
* In order to perform FIFO full and FIFO empty tests using
* this FIFO style, the read and write pointers must be
* passed to the opposite clock domain for pointer comparison
*************************************************************//*在檢測“滿”或“空”狀態(tài)之前,需要將指針同步到其它時鐘域時,使用格雷碼,可以降低同步過程中亞穩(wěn)態(tài)出現(xiàn)的概率*/sync_r2w I1_sync_r2w(
.wq2_rptr(wq2_rptr),
.rptr(rptr),
.wclk(wclk),
.wrst_n(wrst_n));
sync_w2r I2_sync_w2r (
.rq2_wptr(rq2_wptr),
.wptr(wptr),
.rclk(rclk),
.rrst_n(rrst_n));/** DualRAM
*/DualRAM #(DSIZE, ASIZE) I3_DualRAM(
.rdata(rdata),
.wdata(wdata),
.waddr(waddr),
.raddr(raddr),
.wclken(winc),
.wclk(wclk)); /** 空、滿比較邏輯*/rptr_empty #(ASIZE) I4_rptr_empty(
.rempty(rempty),
.raddr(raddr),
.rptr(rptr),
.rq2_wptr(rq2_wptr),
.rinc(rinc),
.rclk(rclk),
.rrst_n(rrst_n));
wptr_full #(ASIZE) I5_wptr_full(
.wfull(wfull),
.waddr(waddr),
.wptr(wptr),
.wq2_rptr(wq2_rptr),
.winc(winc),
.wclk(wclk),
.wrst_n(wrst_n));endmodule
2)DualRAM 模塊
module DualRAM
#( parameter DATA_SIZE = 8, // 數(shù)據(jù)位寬
parameter ADDR_SIZE = 4 // 地址位寬)
( input wclken,wclk, input [ADDR_SIZE-1:0] raddr, //RAM read address
input [ADDR_SIZE-1:0] waddr, //RAM write address
input [DATA_SIZE-1:0] wdata, //data input
output [DATA_SIZE-1:0] rdata //data output); localparam RAM_DEPTH = 1 《《 ADDR_SIZE; //RAM 深度 = 2^ADDR_WIDTH
reg [DATA_SIZE-1:0] Mem[RAM_DEPTH-1:0]; always@(posedge wclk)begin
if(wclken)
Mem[waddr] 《= wdata;endassign rdata = Mem[raddr];endmodule
3)同步模塊
module sync_r2w
#(parameter ADDRSIZE = 4)
( output reg [ADDRSIZE:0] wq2_rptr, input [ADDRSIZE:0] rptr, input wclk, wrst_n
);reg [ADDRSIZE:0] wq1_rptr;always @(posedge wclk or negedge wrst_n) if (!wrst_n)
{wq2_rptr,wq1_rptr} 《= 0; else
{wq2_rptr,wq1_rptr} 《= {wq1_rptr,rptr};endmodule
4)同步模塊 2
module sync_w2r
#(parameter ADDRSIZE = 4)
( output reg [ADDRSIZE:0] rq2_wptr, input [ADDRSIZE:0] wptr, input rclk, rrst_n
); reg [ADDRSIZE:0] rq1_wptr;always @(posedge rclk or negedge rrst_n) if (!rrst_n)
{rq2_wptr,rq1_wptr} 《= 0; else
{rq2_wptr,rq1_wptr} 《= {rq1_wptr,wptr};endmodule
5)空判斷邏輯
module rptr_empty
#(parameter ADDRSIZE = 4)
( output reg rempty, output [ADDRSIZE-1:0] raddr, output reg [ADDRSIZE :0] rptr, input [ADDRSIZE :0] rq2_wptr, input rinc, rclk, rrst_n);
reg [ADDRSIZE:0] rbin;wire [ADDRSIZE:0] rgraynext, rbinnext;wire rempty_val;//-------------------// GRAYSTYLE2 pointer: gray 碼讀地址指針 //-------------------always @(posedge rclk or negedge rrst_n) if (!rrst_n)
begin
rbin 《= 0;
rptr 《= 0; end
else
begin
rbin 《= rbinnext ;
rptr 《= rgraynext; end// gray 碼計數(shù)邏輯 assign rbinnext = !rempty ? (rbin + rinc) : rbin;assign rgraynext = (rbinnext》》1) ^ rbinnext; // 二進制到 gray 碼的轉換
assign raddr = rbin[ADDRSIZE-1:0];//---------------------------------------------------------------// FIFO empty when the next rptr == synchronized wptr or on reset//---------------------------------------------------------------/** 讀指針是一個 n 位的 gray 碼計數(shù)器,比 FIFO 尋址所需的位寬大一位
* 當讀指針和同步過來的寫指針完全相等時(包括 MSB),說明二者折回次數(shù)一致,F(xiàn)IFO 為空
*
*/assign rempty_val = (rgraynext == rq2_wptr); always @(posedge rclk or negedge rrst_n)if (!rrst_n)
rempty 《= 1‘b1;else
rempty 《= rempty_val;endmodule
6)滿判斷邏輯
module wptr_full
#( parameter ADDRSIZE = 4)
( output reg wfull, output [ADDRSIZE-1:0] waddr, output reg [ADDRSIZE :0] wptr, input [ADDRSIZE :0] wq2_rptr, input winc, wclk, wrst_n);
reg [ADDRSIZE:0] wbin;wire [ADDRSIZE:0] wgraynext, wbinnext;wire wfull_val;// GRAYSTYLE2 pointeralways @(posedge wclk or negedge wrst_n) if (!wrst_n)
begin
wbin 《= 0;
wptr 《= 0; end
else
begin
wbin 《= wbinnext;
wptr 《= wgraynext; end//gray 碼計數(shù)邏輯 assign wbinnext = !wfull ? wbin + winc : wbin;assign wgraynext = (wbinnext》》1) ^ wbinnext; assign waddr = wbin[ADDRSIZE-1:0]; /*由于滿標志在寫時鐘域產(chǎn)生,因此比較安全的做法是將讀指針同步到寫時鐘域*//**///------------------------------------------------------------------// Simplified version of the three necessary full-tests:// assign wfull_val=((wgnext[ADDRSIZE] !=wq2_rptr[ADDRSIZE] ) &&// (wgnext[ADDRSIZE-1] !=wq2_rptr[ADDRSIZE-1]) &&// (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0]));//------------------------------------------------------------------assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],
wq2_rptr[ADDRSIZE-2:0]});always @(posedge wclk or negedge wrst_n)if (!wrst_n)
wfull 《= 1’b0;else
wfull 《= wfull_val;endmodule
P.S :在 quartus 中有異步 FIFO IP 核,為安全起見推薦使用 IP 核定制 FIFO,本文的目的只是作為思路參考。
責任編輯:gt
評論
查看更多