一、FIFO簡介
FIFO是英文First In First Out 的縮寫,是一種先進先出的數據緩存器,它與普通存儲器的區別是沒有外部讀寫地址線,這樣使用起來非常簡單,但缺點就是只能順序寫入數據,順序的讀出數據,其數據地址由內部讀寫指針自動加1完成,不能像普通存儲器那樣可以由地址線決定讀取或寫入某個指定的地址。
用途1:
異步FIFO讀寫分別采用相互異步的不同時鐘。在現代集成電路芯片中,隨著設計規模的不斷擴大,一個系統中往往含有數個時鐘,多時鐘域帶來的一個問題就是,如何設計異步時鐘之間的接口電路。異步FIFO是這個問題的一種簡便、快捷的解決方案,使用異步FIFO可以在兩個不同時鐘系統之間快速而方便地傳輸實時數據。
用途2:
對于不同寬度的數據接口也可以用FIFO,例如單片機位8位數據輸出,而DSP可能是16位數據輸入,在單片機與DSP連接時就可以使用FIFO來達到數據匹配的目的。
二、分類
同步FIFO是指讀時鐘和寫時鐘為同一個時鐘,在時鐘沿來臨時同時發生讀寫操作;
異步FIFO是指讀寫時鐘不一致,讀寫時鐘是互相獨立的。
三、FIFO的常見參數
FIFO的寬度:即FIFO一次讀寫操作的數據位;
FIFO的深度:指的是FIFO可以存儲多少個N位的數據(如果寬度為N)。
滿標志:FIFO已滿或將要滿時由FIFO的狀態電路送出的一個信號,以阻止FIFO的寫操作繼續向FIFO中寫數據而造成溢出(overflow)。
空標志:FIFO已空或將要空時由FIFO的狀態電路送出的一個信號,以阻止FIFO的讀操作繼續從FIFO中讀出數據而造成無效數據的讀出(underflow)。
讀時鐘:讀操作所遵循的時鐘,在每個時鐘沿來臨時讀數據。
寫時鐘:寫操作所遵循的時鐘,在每個時鐘沿來臨時寫數據。
===============================分 隔 符 ==============================
讀寫指針的工作原理
讀指針:總是指向下一個將要被寫入的單元,復位時,指向第1個單元(編號為0)。
寫指針:總是指向當前要被讀出的數據,復位時,指向第1個單元(編號為0)
FIFO的“空”/“滿”檢測
FIFO設計的關鍵:產生可靠的FIFO讀寫指針和生成FIFO“空”/“滿”狀態標志。
當讀寫指針相等時,表明FIFO為空,這種情況發生在復位操作時,或者當讀指針讀出FIFO中最后一個字后,追趕上了寫指針時,如下圖所示:
當讀寫指針再次相等時,表明FIFO為滿,這種情況發生在,當寫指針轉了一圈,折回來(wrapped around)又追上了讀指針,如下圖:
為了區分到底是滿狀態還是空狀態,可以采用以下方法:
方法1:在指針中添加一個額外的位(extra bit),當寫指針增加并越過最后一個FIFO地址時,就將寫指針這個未用的MSB加1,其它位回零。對讀指針也進行同樣的操作。此時,對于深度為2n的FIFO,需要的讀/寫指針位寬為(n+1)位,如對于深度為8的FIFO,需要采用4bit的計數器,0000~1000、1001~1111,MSB作為折回標志位,而低3位作為地址指針。
如果兩個指針的MSB不同,說明寫指針比讀指針多折回了一次;如r_addr=0000,而w_addr = 1000,為滿。
如果兩個指針的MSB相同,則說明兩個指針折回的次數相等。其余位相等,說明FIFO為空;
3.二進制FIFO指針的考慮
將一個二進制的計數值從一個時鐘域同步到另一個時鐘域的時候很容易出現問題,因為采用二進制計數器時所有位都可能同時變化,在同一個時鐘沿同步多個信號的變化會產生亞穩態問題。而使用格雷碼只有一位變化,因此在兩個時鐘域間同步多個位不會產生問題。所以需要一個二進制到gray碼的轉換電路,將地址值轉換為相應的gray碼,然后將該gray碼同步到另一個時鐘域進行對比,作為空滿狀態的檢測。
4.
使用gray碼進行對比,如何判斷“空”與“滿”
使用gray碼解決了一個問題,但同時也帶來另一個問題,即在格雷碼域如何判斷空與滿。
對于“空”的判斷依然依據二者完全相等(包括MSB);
而對于“滿”的判斷,如下圖,由于gray碼除了MSB外,具有鏡像對稱的特點,當讀指針指向7,寫指針指向8時,除了MSB,其余位皆相同,不能說它為滿。因此不能單純的只檢測最高位了,在gray碼上判斷為滿必須同時滿足以下3條:
wptr和同步過來的rptr的MSB不相等,因為wptr必須比rptr多折回一次。
wptr與rptr的次高位不相等,如上圖位置7和位置15,轉化為二進制對應的是0111和1111,MSB不同說明多折回一次,111相同代表同一位置。
剩下的其余位完全相等。
5.總體實現
系統的總體框圖如下:
1)頂層模塊
moduleAsyncFIFO
#(parameter ASIZE = 4, //地址位寬 parameter DSIZE = 8) //數據位寬 ( 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 *************************************************************/ /*在檢測“滿”或“空”狀態之前,需要將指針同步到其它時鐘域時,使用格雷碼,可以降低同步過程中亞穩態出現的概率*/ 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, // 數據位寬 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; end assign 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碼計數邏輯 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碼計數器,比FIFO尋址所需的位寬大一位 * 當讀指針和同步過來的寫指針完全相等時(包括MSB),說明二者折回次數一致,FIFO為空 * */ 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 pointer always @(posedge wclk or negedge wrst_n) if (!wrst_n) begin wbin <= 0; wptr <= 0; end else begin wbin <= wbinnext; wptr <= wgraynext; end //gray 碼計數邏輯 assign wbinnext = !wfull ? wbin + winc : wbin; assign wgraynext = (wbinnext>>1) ^ wbinnext; assign waddr = wbin[ADDRSIZE-1:0]; /*由于滿標志在寫時鐘域產生,因此比較安全的做法是將讀指針同步到寫時鐘域*/ /**/ //------------------------------------------------------------------ // 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,本文的目的只是作為思路參考。
審核編輯:郭婷
-
芯片
+關注
關注
456文章
51019瀏覽量
425343 -
存儲器
+關注
關注
38文章
7518瀏覽量
164074 -
fifo
+關注
關注
3文章
389瀏覽量
43769
原文標題:異步FIFO的FPGA實現
文章出處:【微信號:zhuyandz,微信公眾號:FPGA之家】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論