FIFO 簡(jiǎn)介
FIFO 是英文 First In First Out 的縮寫(xiě),是一種先進(jìn)先出的數(shù)據(jù)緩存器,它與普通存儲(chǔ)器的區(qū)別是沒(méi)有外部讀寫(xiě)地址線,這樣使用起來(lái)非常簡(jiǎn)單,但缺點(diǎn)就是只能順序?qū)懭霐?shù)據(jù),順序的讀出數(shù)據(jù),其數(shù)據(jù)地址由內(nèi)部讀寫(xiě)指針自動(dòng)加 1 完成,不能像普通存儲(chǔ)器那樣可以由地址線決定讀取或?qū)懭肽硞€(gè)指定的地址。
用途 1:
異步 FIFO 讀寫(xiě)分別采用相互異步的不同時(shí)鐘。在現(xiàn)代集成電路芯片中,隨著設(shè)計(jì)規(guī)模的不斷擴(kuò)大,一個(gè)系統(tǒng)中往往含有數(shù)個(gè)時(shí)鐘,多時(shí)鐘域帶來(lái)的一個(gè)問(wèn)題就是,如何設(shè)計(jì)異步時(shí)鐘之間的接口電路。異步 FIFO 是這個(gè)問(wèn)題的一種簡(jiǎn)便、快捷的解決方案,使用異步 FIFO 可以在兩個(gè)不同時(shí)鐘系統(tǒng)之間快速而方便地傳輸實(shí)時(shí)數(shù)據(jù)。
用途 2:
對(duì)于不同寬度的數(shù)據(jù)接口也可以用 FIFO,例如單片機(jī)位 8 位數(shù)據(jù)輸出,而 DSP 可能是 16 位數(shù)據(jù)輸入,在單片機(jī)與 DSP 連接時(shí)就可以使用 FIFO 來(lái)達(dá)到數(shù)據(jù)匹配的目的。
分類
同步 FIFO 是指讀時(shí)鐘和寫(xiě)時(shí)鐘為同一個(gè)時(shí)鐘,在時(shí)鐘沿來(lái)臨時(shí)同時(shí)發(fā)生讀寫(xiě)操作;
異步 FIFO 是指讀寫(xiě)時(shí)鐘不一致,讀寫(xiě)時(shí)鐘是互相獨(dú)立的。
FIFO 的常見(jiàn)參數(shù)
FIFO 的寬度:即 FIFO 一次讀寫(xiě)操作的數(shù)據(jù)位;
FIFO 的深度:指的是 FIFO 可以存儲(chǔ)多少個(gè) N 位的數(shù)據(jù)(如果寬度為 N)。
滿標(biāo)志:FIFO 已滿或?qū)⒁獫M時(shí)由 FIFO 的狀態(tài)電路送出的一個(gè)信號(hào),以阻止 FIFO 的寫(xiě)操作繼續(xù)向 FIFO 中寫(xiě)數(shù)據(jù)而造成溢出(overflow)。
空標(biāo)志:FIFO 已空或?qū)⒁諘r(shí)由 FIFO 的狀態(tài)電路送出的一個(gè)信號(hào),以阻止 FIFO 的讀操作繼續(xù)從 FIFO 中讀出數(shù)據(jù)而造成無(wú)效數(shù)據(jù)的讀出(underflow)。
讀時(shí)鐘:讀操作所遵循的時(shí)鐘,在每個(gè)時(shí)鐘沿來(lái)臨時(shí)讀數(shù)據(jù)。
寫(xiě)時(shí)鐘:寫(xiě)操作所遵循的時(shí)鐘,在每個(gè)時(shí)鐘沿來(lái)臨時(shí)寫(xiě)數(shù)據(jù)。
1. 讀寫(xiě)指針的工作原理
讀指針:總是指向下一個(gè)將要被寫(xiě)入的單元,復(fù)位時(shí),指向第 1 個(gè)單元(編號(hào)為 0)。
寫(xiě)指針:總是指向當(dāng)前要被讀出的數(shù)據(jù),復(fù)位時(shí),指向第 1 個(gè)單元(編號(hào)為 0)
2.FIFO 的“空”/“滿”檢測(cè)
FIFO 設(shè)計(jì)的關(guān)鍵:產(chǎn)生可靠的 FIFO 讀寫(xiě)指針和生成 FIFO“空”/“滿”狀態(tài)標(biāo)志。
當(dāng)讀寫(xiě)指針相等時(shí),表明 FIFO 為空,這種情況發(fā)生在復(fù)位操作時(shí),或者當(dāng)讀指針讀出 FIFO 中最后一個(gè)字后,追趕上了寫(xiě)指針時(shí),如下圖所示:
當(dāng)讀寫(xiě)指針再次相等時(shí),表明 FIFO 為滿,這種情況發(fā)生在,當(dāng)寫(xiě)指針轉(zhuǎn)了一圈,折回來(lái)(wrapped around)又追上了讀指針,如下圖:
為了區(qū)分到底是滿狀態(tài)還是空狀態(tài),可以采用以下方法:
方法 1:在指針中添加一個(gè)額外的位(extra bit),當(dāng)寫(xiě)指針增加并越過(guò)最后一個(gè) FIFO 地址時(shí),就將寫(xiě)指針這個(gè)未用的 MSB 加 1,其它位回零。對(duì)讀指針也進(jìn)行同樣的操作。此時(shí),對(duì)于深度為 2n 的 FIFO,需要的讀 / 寫(xiě)指針位寬為(n+1)位,如對(duì)于深度為 8 的 FIFO,需要采用 4bit 的計(jì)數(shù)器,0000~1000、1001~1111,MSB 作為折回標(biāo)志位,而低 3 位作為地址指針。
如果兩個(gè)指針的 MSB 不同,說(shuō)明寫(xiě)指針比讀指針多折回了一次;如 r_addr=0000,而 w_addr = 1000,為滿。
如果兩個(gè)指針的 MSB 相同,則說(shuō)明兩個(gè)指針折回的次數(shù)相等。其余位相等,說(shuō)明 FIFO 為空;
3. 二進(jìn)制 FIFO 指針的考慮
將一個(gè)二進(jìn)制的計(jì)數(shù)值從一個(gè)時(shí)鐘域同步到另一個(gè)時(shí)鐘域的時(shí)候很容易出現(xiàn)問(wèn)題,因?yàn)椴捎枚M(jìn)制計(jì)數(shù)器時(shí)所有位都可能同時(shí)變化,在同一個(gè)時(shí)鐘沿同步多個(gè)信號(hào)的變化會(huì)產(chǎn)生亞穩(wěn)態(tài)問(wèn)題。而使用格雷碼只有一位變化,因此在兩個(gè)時(shí)鐘域間同步多個(gè)位不會(huì)產(chǎn)生問(wèn)題。所以需要一個(gè)二進(jìn)制到 gray 碼的轉(zhuǎn)換電路,將地址值轉(zhuǎn)換為相應(yīng)的 gray 碼,然后將該 gray 碼同步到另一個(gè)時(shí)鐘域進(jìn)行對(duì)比,作為空滿狀態(tài)的檢測(cè)。
4. 使用 gray 碼進(jìn)行對(duì)比,如何判斷“空”與“滿”
使用 gray 碼解決了一個(gè)問(wèn)題,但同時(shí)也帶來(lái)另一個(gè)問(wèn)題,即在格雷碼域如何判斷空與滿。
對(duì)于“空”的判斷依然依據(jù)二者完全相等(包括 MSB);
而對(duì)于“滿”的判斷,如下圖,由于 gray 碼除了 MSB 外,具有鏡像對(duì)稱的特點(diǎn),當(dāng)讀指針指向 7,寫(xiě)指針指向 8 時(shí),除了 MSB,其余位皆相同,不能說(shuō)它為滿。因此不能單純的只檢測(cè)最高位了,在 gray 碼上判斷為滿必須同時(shí)滿足以下 3 條:
wptr 和同步過(guò)來(lái)的 rptr 的 MSB 不相等,因?yàn)?wptr 必須比 rptr 多折回一次。
wptr 與 rptr 的次高位不相等,如上圖位置 7 和位置 15,轉(zhuǎn)化為二進(jìn)制對(duì)應(yīng)的是 0111 和 1111,MSB 不同說(shuō)明多折回一次,111 相同代表同一位置。
剩下的其余位完全相等。
5. 總體實(shí)現(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, // 寫(xiě)請(qǐng)求信號(hào),寫(xiě)時(shí)鐘,寫(xiě)復(fù)位
input rinc, rclk, rrst_n, // 讀請(qǐng)求信號(hào),讀時(shí)鐘,讀復(fù)位
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
*************************************************************//*在檢測(cè)“滿”或“空”狀態(tài)之前,需要將指針同步到其它時(shí)鐘域時(shí),使用格雷碼,可以降低同步過(guò)程中亞穩(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 碼計(jì)數(shù)邏輯 assign rbinnext = !rempty ? (rbin + rinc) : rbin;assign rgraynext = (rbinnext》》1) ^ rbinnext; // 二進(jìn)制到 gray 碼的轉(zhuǎn)換
assign raddr = rbin[ADDRSIZE-1:0];//---------------------------------------------------------------// FIFO empty when the next rptr == synchronized wptr or on reset//---------------------------------------------------------------/** 讀指針是一個(gè) n 位的 gray 碼計(jì)數(shù)器,比 FIFO 尋址所需的位寬大一位
* 當(dāng)讀指針和同步過(guò)來(lái)的寫(xiě)指針完全相等時(shí)(包括 MSB),說(shuō)明二者折回次數(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 碼計(jì)數(shù)邏輯 assign wbinnext = !wfull ? wbin + winc : wbin;assign wgraynext = (wbinnext》》1) ^ wbinnext; assign waddr = wbin[ADDRSIZE-1:0]; /*由于滿標(biāo)志在寫(xiě)時(shí)鐘域產(chǎn)生,因此比較安全的做法是將讀指針同步到寫(xiě)時(shí)鐘域*//**///------------------------------------------------------------------// 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 核,為安全起見(jiàn)推薦使用 IP 核定制 FIFO,本文的目的只是作為思路參考。
責(zé)任編輯:gt
評(píng)論