1. FIFO簡介
FIFO是一種先進先出數據緩存器,它與普通存儲器的區別是沒有外部讀寫地址線,使用起來非常簡單,缺點是只能順序讀寫,而不能隨機讀寫。
2. 使用場景
數據緩沖:也就是數據寫入過快,并且間隔時間長,也就是突發寫入。那么通過設置一定深度的FIFO,可以起到數據暫存的功能,且使得后續處理流程平滑。
時鐘域的隔離:主要用異步FIFO。對于不同時鐘域的數據傳輸,可以通過FIFO進行隔離,避免跨時鐘域的數據傳輸帶來的設計和約束上的復雜度。比如FIFO的一端是AD,另一端是PCI;AD的采集速率是16位100KSPS,每秒的數據量是1.6Mbps。而PCI總線的速度是33MHz,總線寬度是32位
3. 分類
同步FIFO:指讀時鐘和寫時鐘是同一個時鐘
異步FIFO:指讀寫時鐘是不同的時鐘。
4. FIFO的常見參數
FIFO的寬度:即FIFO一次讀寫操作的數據位;
FIFO的深度:指的是FIFO可以存儲多少個N位的數據(如果寬度為N)。
滿標志:FIFO已滿或將要滿時由FIFO的狀態電路送出的一個信號,以阻止FIFO的寫操作繼續向FIFO中寫數據而造成溢出(overflow)。
空標志:FIFO已空或將要空時由FIFO的狀態電路送出的一個信號,以阻止FIFO的讀操作繼續從FIFO中讀出數據而造成無效數據的讀出(underflow)。
讀時鐘:讀操作所遵循的時鐘,在每個時鐘沿來臨時讀數據。
寫時鐘:寫操作所遵循的時鐘,在每個時鐘沿來臨時寫數據。
5. FIFO設計
5.1 空滿標志生成
FIFO設計的關鍵是產生可靠的FIFO讀寫指針和生成FIFO空/滿狀態標志。
當讀寫指針相等時,表明FIFO為空,這種情況發生在復位操作時;或者當讀指針讀出FIFO中最后一個字后,追趕上了寫指針時,這時FIFO處于滿的狀態。
為了區分到底是滿狀態還是空狀態,可以采用以下方法:
方法1:在指針中添加一個額外的位(extra bit),當寫指針增加并越過最后一個FIFO地址時,就將寫指針這個未用的MSB加1,其它位回零。對讀指針也進行同樣的操作。此時,對于深度為2^n的FIFO,需要的讀/寫指針位寬為(n+1)位,如對于深度為8的FIFO,需要采用4bit的計數器,0000~1000、1001~1111,MSB作為折回標志位,而低3位作為地址指針。
如果兩個指針的MSB不同,說明寫指針比讀指針多折回了一次;如r_addr=0000,而w_addr = 1000,為滿。
如果兩個指針的MSB相同,則說明兩個指針折回的次數相等。其余位相等,說明FIFO為空;
5.2 異步FIFO的設計還要注意跨時鐘域問題
將一個二進制的計數值從一個時鐘域同步到另一個時鐘域的時候很容易出現問題,因為采用二進制計數器時所有位都可能同時變化,在同一個時鐘沿同步多個信號的變化會產生亞穩態問題。而使用格雷碼只有一位變化,因此在兩個時鐘域間同步多個位不會產生問題。所以需要一個二進制到gray碼的轉換電路,將地址值轉換為相應的gray碼,然后將該gray碼同步到另一個時鐘域進行對比,作為空滿狀態的檢測
5.3 gray碼如何判斷空滿
對于“空”的判斷:依然依據二者完全相等(包括MSB);
對于“滿”的判斷:如下圖,由于gray碼除了MSB外,具有鏡像對稱的特點,當讀指針指向7,寫指針指向8時,除了MSB,其余位皆相同,不能說它為滿。因此不能單純的只檢測最高位了,在gray碼上判斷為滿必須同時滿足以下3條:
wptr和同步過來的rptr的MSB不相等,因為wptr必須比rptr多折回一次。
wptr與rptr的次高位不相等,如上圖位置7和位置15,轉化為二進制對應的是0111和1111,MSB不同說明多折回一次,111相同代表同一位置。
剩下的其余位完全相等
6. 同步FIFO實現
代碼的核心部分主要是data_count,并且full 信號是當data_count == DATA_DEPTH時拉高,除了data_count之外,還可以同過判斷r_ptr和w_ptr兩個指針是否相等來判斷空,滿信號
ssign empty = (w_ptr == r_ptr) ? 1 : 0;
assign full = (w_ptr[2:0] == r_ptr[2:0] && (w_ptr[3] == ~r_ptr[3])) ? 1 : 0;
這里還有一個注意的點,也就是w_ptr++會越界,同步FIFO就算用data_cnt來判斷空滿條件,在存數據和寫數據時還是應該用w_ptr來表示,如果直接用data_cnt來表示,那么設計的就是一個棧而不是FIFO了。
module syn_fifo(
clk,
rst_n,
data_in,
w_en,
full,
data_out,
r_en,
empty);
parameter DATA_WIDTH = 8;
parameter DATA_DEPTH = 16;
parameter ADDR_WIDTH = 4;
input wire clk, rst_n;
input wire [DATA_WIDTH-1:0] data_in;
input wire w_en, r_en;
output wire empty, full;
output reg [DATA_WIDTH-1:0] data_out;
reg [ADDR_WIDTH : 0] data_count;
reg [ADDR_WIDTH-1 : 0] w_ptr, r_ptr;
reg [DATA_WIDTH-1 : 0] mem[0 : DATA_DEPTH-1];
assign empty = (data_count == 'd0) ? 1 : 0;
assign full = (data_count == DATA_DEPTH) ? 1 : 0; //data_count == DATA_DEPTH
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
data_count <= 'd0;
else if (w_en && r_en)
data_count <= data_count;
else if (!full && w_en)
data_count <= data_count + 1'b1;
else if (!empty && r_en)
data_count <= data_count - 1'b1;
else
data_count <= data_count;
end
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
mem[w_ptr] <= 'd0;
else if (!full && w_en)
mem[w_ptr] <= data_in;
end
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
w_ptr <= 'd0;
else if (w_en && !full)
w_ptr <= w_ptr + 1'b1;
else
w_ptr <= w_ptr;
end
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
r_ptr <= 'd0;
else if (r_en && !empty)
r_ptr <= r_ptr + 1'b1;
else
r_ptr <= r_ptr;
end
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
data_out <= 'd0;
else if (!empty && r_en)
data_out <= mem[r_ptr];
end
endmodule
7. 異步FIFO實現
設計難點:
1.跨時鐘域數據比較,需要用到同步器,減少亞穩態的傳遞
2.用到gray碼,進一步減少亞穩態的產生
3.gray碼相等信號的比較 空:兩個gray碼相等 滿:高兩位相反,其余位相同。
4.指針計數需要比ADDR的位寬多一位,這一點和同步FIFO的設計是一樣的。
module asyn_fifo(
clk_w,
clk_r,
rst_n,
r_en,
w_en,
data_in,
data_out,
full,
empty
);
input wire clk_r, clk_w, rst_n;
input wire r_en, w_en;
input wire [7:0] data_in;
output wire full, empty;
output reg [7:0] data_out;
parameter DATA_DEPTH = 8;
parameter DATA_WIDTH = 8;
parameter ADDR_WIDTH = 3;
reg [3:0] w_ptr, r_ptr;
reg [7:0] mem[DATA_DEPTH-1 : 0];
always @ (posedge clk_w or negedge rst_n) begin
if (~rst_n)
w_ptr <= 'd0;
else if (w_en && !full)
w_ptr <= w_ptr + 1'b1;
else
w_ptr <= w_ptr;
end
wire [3:0] w_ptr_gray, r_ptr_gray;
assign w_ptr_gray = w_ptr ^ (w_ptr >> 1);
assign r_ptr_gray = r_ptr ^ (r_ptr >> 1);
//
reg [ADDR_WIDTH:0] rd1_wp, rd2_wp;
always @ (posedge clk_r or negedge rst_n) begin
if (!rst_n) begin
rd1_wp <= 'd0;
rd2_wp <= 'd0;
end else begin
rd1_wp <= w_ptr_gray;
rd2_wp <= rd1_wp;
end
end
assign empty = (rd2_wp == r_ptr_gray) ? 1 : 0;
always @ (posedge clk_r or negedge rst_n) begin
if (~rst_n)
r_ptr <= 'd0;
else if (r_en && !empty)
r_ptr <= r_ptr + 1'b1;
else
r_ptr <= r_ptr;
end
//wire [ADDR_WIDTH:0] r_ptr_gray;
assign r_ptr_gray = r_ptr ^ (r_ptr >> 1);
reg [ADDR_WIDTH:0] wd1_rp, wd2_rp;
always @ (posedge clk_w or negedge rst_n) begin
if (~rst_n) begin
wd1_rp <= 'd0;
wd2_rp <= 'd0;
end
else begin
wd1_rp <= r_ptr_gray;
wd2_rp <= wd1_rp;
end
end
assign full = ({(~wd2_rp[ADDR_WIDTH:ADDR_WIDTH-1]),wd2_rp[ADDR_WIDTH-2:0]} == w_ptr_gray) ? 1:0;
always @ (posedge clk_w or negedge rst_n) begin
if (~rst_n)
mem[w_ptr] <= 'd0;
else if (!empty && rd_en)
mem[w_ptr] <= data_in;
end
always @ (posedge clk_r or negedge rst_n) begin
if (~rst_n)
data_out <= 'd0;
else if (!full && w_en)
data_out <= mem[r_ptr];
end
endmodule
8. 對FIFO進行約束
set_false_path
在設計中,不需要滿足setup/hold時序的數據路徑需要設置成false path
set_disable_timing:可以使庫單元的時間弧(timing arc)無效
總的來說,set_false_path 只對data path起作用, EDA 工具還會分析計算這條時序路徑, 只是不報出來是否有時序違例。
set_disable_timing 對timing arc起作用,完全不去分析這條timing arc
9. 關于異步FIFO最小深度的計算
FIFO僅在數據突發時才有效,不使用與連續的數據輸出和輸入。如果存在連續的數據流,那么所需要的FIFO大小因該是無限的。因此需要知道突發速率,突發大小,頻率等,才能確定FIFO的深度。
最小深度的計算流程
確定讀時鐘fr和寫時鐘的頻率fw, 一般情況fw>fr的
根據fr和fw計算讀寫一次數據的周期Tr 和Tw,根據T= 1/f
根據突發寫長度的大小,計算這么多數據需要寫多少時間 tw = Tw*len
根據寫的時間tw計算讀了多少數據 n = tw/Tr
FIFO的最小深度等于 len-n
9.1 寫時鐘快于讀時鐘,寫和讀的過程中沒有空閑周期
分析過程:
寫時鐘周期Tw = 1000/80 ns = 12.5ns;同理讀時鐘周期為20ns;
突發寫長度為120個數據,寫120個數據耗時120 * 12.5 = 1500ns;
1500ns時間內讀出數據1500/20ns = 75個;
故最小FIFO深度為120 - 75 = 45;
9.2 寫時鐘頻率大于讀時鐘頻率,但在讀寫的過程中存在空閑周期
分析:
寫時鐘周期T_A = 12.5ns,讀時鐘周期為T_B = 20ns;
兩個寫時鐘寫一個數據,也就是寫一個數據需要時間2*T_A = 25ns,那么由于突發寫數據個數為120個,寫這么多數據需要時間120 * 25ns = 3000ns;
4個讀時鐘周期讀一個數據,因此讀一個數據需要時間80ns,3000ns讀了3000/80 = 37.5個數據(0.5不算一個數據,沒讀完整),約等于37個數據。
所以,FIFO的最小深度為120 - 37 = 83;
9.3 寫時鐘慢于讀時鐘,且讀寫過程中沒有空閑周期
分析:
這種情況下永遠也不會發生數據丟失的情況;
fifo的深度為1
9.4 寫時鐘頻率小于讀時鐘頻率,但讀寫過程中存在空閑周期
分析:
寫時鐘周期1000/30 ns = 100/3 ns;讀時鐘周期 20ns;
寫一個數據需要2個時鐘,也就是200/3 ns;讀一個數據需要4個時鐘,也就是80 ns;
寫120個數據需要時間8000ns,這段時間內讀出數據8000/80 = 100個;
因此,FIFO的最小深度為120 - 100 = 20;
9.5 讀寫時鐘速率相同,且無空閑時鐘
分析:
如果讀寫時鐘之間沒有相位差,則不需要FIFO就可以進行讀寫;
如果二者存在相位差,只需要FIFO的深度為1即可。
9.6 讀寫時鐘頻率一致,但在讀寫過程中存在空閑周期
分析:
兩個時鐘寫一個數據,需要時間40ns;
4個時鐘讀一個數據,需要80ns;
由于突發長度為120,需要120*40 = 4800ns寫完;這段時間讀出數據個數:4800/80 = 60;
所以,FIFO最小深度為120 - 60 = 60;
9.7 特定條件下,最壞情況分析FIFO最小深度
首先,從條件可知,寫頻率等于讀頻率;
其次,讀寫可以在如下限制下的任意時刻發生:
為了獲得更安全的FIFO深度,我們需要考慮最壞的情況,以防數據丟失;
對于最壞的情況,寫入和讀取之間的數據速率之間的差異應該是最大的。因此,對于寫操作,應考慮最大數據速率,對于讀操作,應考慮最小數據速率。從上表可以看出,最快的寫數據速率應該為第4種情況,寫操作在最小的時間內完成;
由于突發寫長度為160,所以160個時鐘寫160個數據;
由于讀速度為10個時鐘讀8個數據,因此一個數據需要10/8個時鐘;
所以160個時鐘讀了160*8/10 = 128個數據;
所以FIFO的最小深度為160-128=32.
9.8 條件拐彎抹角的給出,需要自己提取關鍵信息
假如clkA = 25MHz,則CLKB = 100MHz;
TA= 40ns, TB = 10ns;
en_B = 100*40 = 4000ns;占空比為1/4;
我們認為B為寫時鐘,寫使能時間為4000/4 = 1000ns,則突發寫長度為1000/10 = 100個數據;
在1000ns內讀出數據為1000/40 = 25個數據,所以FIFO最小深度為100 - 25 = 75
9.9 其它情況
輸入時鐘頻率煒250MHz,輸入數據率8Gbps,輸出的時鐘頻率200MHz,輸出的數據率為5Gbps,單位時間內輸入的數據總量為4Gb,在保證數據不丟失的情況下,最少需要多大的緩沖空間,并給出分析步驟。
解析:解答1,不考慮兩個包之間的背靠背的情況
4Gb/8Gbps * 5Gbps = 2.5Gb,因此緩沖空間=4Gb-2.5Gb = 1.5Gb;
解答2:考慮背靠背的情況
突發長度=8G, 寫應該是0440的情況,讀用最慢的情況,2.5, 2.5,0.5, 2.5,因此FIFO= 8-3 = 5G
9. Vivado FIFO IP核使用
在IP Catalog中搜索FIFO,會出現各種各樣的FIFO,一般選擇FIFO generator。
點擊IP之后,會出現FIFO配置的一些選項,包括Basic Native ports, flag等
在Basic中我們可以控制FIFO的接口形式和FIFO的類型
FIFO 的接口分為兩類,一類是 Native 接口,這類接口使用比較簡單,另一類是 AXI 協議接口,這類協議口線比較多,操作相對復雜。
FIFO 的類型主要區別:1.讀寫是否使用一個時鐘 2.使用何種硬件資源
其中區別1主要是通過common clk和 independent clk來確定,也就是同步FIFO和異步FIFO
區別2硬件資源:分為3種。BRAM:即塊RAM資源,這是FPGA內嵌的一種重要的專用RAM資源,可以在讀寫兩端使用不同的數據寬度,可以使用 ECC (一種數據校驗特性),支持 First-World Fall Through ,以及支持動態錯誤注入。;分布式RAM:Distributed RAM,即將FPGA中的LUT用作RAM,僅支持 First-World Fall Through 功能;專用FIFO,專用FIFO會提供很小的延遲。BRAM 是一種比較重要的資源,如果設計的 FIFO 對延時不敏感,可以使用分布式的 RAM 以節約 BRAM 資源。
Stand FIFO 和 First Word Fall Through的區別
standard FIFO 讀取數據時會延遲一個周期,也即會在使能信號拉高后延遲一個周期才有數據輸出,而First word fall through會和使能信號同時輸出。造成這種區別的原因在于FWFT模式下,第一個寫入的數據將從RAM中提前讀出到數據線。
FIFO 的復位使用的高電平復位,如果設計中系統復位信號是低電平有效的,那么不要忘記要將系統復位電平取反后再接入 FIFO 復位電平。一般我們在同步系統設計中使用異步復位。
status flag
包括 almost Full/Empty 信號,這兩個信號,顧名思義,就是在 FIFO 幾乎要滿或者幾乎要空的情況下置起,所謂的“幾乎“就是指還差一個數據滿或者空
這個頁面上還提供握手選項,但一般我們在初級設計中不會需要 FIFO 具有這種“交互”特性,實質上 AXI 協議接口也會提供握手特性。
第四個頁面 Data Count,顧名思義就是提供一個信號來表示當前 FIFO 中的數據總數
在頂層文件實例化IP
在IP Source中打開Instation Template目錄下的veo文件,里面就有實例化的例子
異步FIFO IP的使用
注意同步化synchronization stages
這個值用于表示FIFOempty拉低的時間長度,同時要注意FIFO的讀一定要有empty控制,并且發現empty并不是一寫入數據就拉低的。
10. FIFO IP使用注意事項
如果讀寫位寬不一樣的情況,比如寫位寬8, 讀位寬32,那么當寫入三次是, empty信號仍然為高電平,也就意味著是讀不出數據的。
FIFO的復位信號是高電平有效
standard FIFO 和FWFT的區別就是讀的時候需要延時一個周期和不需要延時
output register:嵌入式輸出寄存器可用于增加性能并向宏添加流水線寄存器,主要用于改善時序情況,但是只對Standard FIFO模式有用,添加output register, 讀延時會增加1個周期
Valid: This signal indicates that valid data is available on the output bus (dout).因此在FWFT模式下,只要FIFO有數據,valid信號就會拉高;而在Standard FIFO模式下,只有在讀信號使能之后,valid信號才會拉高
? ? ? ? ? ? ? ? ? ??
原文鏈接:https://blog.csdn.net/mu_guang_/article/details/108647477
審核編輯:劉清
-
數據傳輸
+關注
關注
9文章
1943瀏覽量
64751 -
EDA工具
+關注
關注
4文章
268瀏覽量
31877 -
二進制
+關注
關注
2文章
795瀏覽量
41718 -
計數器
+關注
關注
32文章
2259瀏覽量
94857 -
FIFO存儲
+關注
關注
0文章
103瀏覽量
6024
原文標題:FPGA學習-同步FIFO和異步FIFO總結
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設計論壇】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論