FIFO 本質是由 RAM 加上讀寫邏輯構成的先入先出的數據緩衝器。與 RAM 的區別是 FIFO 沒有外部讀寫地址線,順序寫入順序讀出數據,其數據地址是由內部讀寫指針自增完成,因此 FIFO 在讀寫時不需要考慮讀寫衝突的問題。
根據 FIFO 工作的時鐘域,可以分為同步 FIFO 和異步 FIFO,同步 FIFO 的讀時鐘和寫時鐘是同一個時鐘,常用於兩邊數據位寬不同的臨時緩衝,異步 FIFO 的讀時鐘和寫時鐘不一致,常用於數據信號跨時鐘域處理。
時序圖#
同步 fifo#
初始狀態 empty
信號是高電平,此時 fifo 為空,若此時對 fifo 發起讀操作,則讀取到的數據無效。當 wr_en
拉高後,開始向 fifo 內部寫入數據,fifo 中有數據後,empty
信號就會拉低。後面同時發起讀寫操作後,因為是同步 fifo,所以標誌位不會發生變化。
只寫不讀時,fifo 中存在兩個及以上的數據,此時 almost empty
也會拉低。當 fifo 處於寫滿狀態時,當 fifo 只能接受一次只寫不讀操作時,almost full
將會拉高,最後,在沒有進行讀操作的情況下,再進行了一次寫操作,full
信號就會被拉高,說明此時的 FIFO 已經寫滿了,在發出讀請求之前將無法再寫入任何數據,如果此時再寫入數據,數據就會丟失。
AXIS FIFO#
時序圖如下:
寫入數據時,s_axis_tvalid
為高電平,同時 full
為低電平,取反後為 s_axis_tready
為高電平,此時才能寫入數據。
讀數據時同理,只有 valid
和 ready
信號同時拉高時才能讀出數據。
m_axis_tvalid
對應 fifo
的 wr_en
信號,m_axis_tready
對應 fifo
的 full
信號取反,s_axis_tvalid
對應 fifo
的 empty
信號取反,s_axis_tready
對應 fifo
的 rd_en
。
AXIS fifo#
Axis fifo 的 IP 核介面如下:
本次是學習 FIFO 的異步讀寫,因此將 independent clock
選為‘是’,並且不使能 packet 模式。Bd 框圖如下:
其中 clk_wiz
的輸出有兩個,端口 1 的時鐘速率為 $100MHz$,端口 2 的時鐘速率為 $50MHz$,axis_data_source
和 axis_dest
的源代碼如下:
module axis_data_source (
input s_axis_clk,
input s_axis_rstn,
input m_axis_tready,
output [7:0] m_axis_tdata,
output m_axis_tvalid
);
reg [7:0] cnt;
always @(posedge s_axis_clk or negedge s_axis_rstn) begin
if(!s_axis_rstn) begin
cnt<=0;
end
else begin
if(m_axis_tready) begin
if(cnt==255) begin
cnt<=0;
end
else begin
cnt<=cnt+1'b1;
end
end
end
end
assign m_axis_tdata=cnt;
assign m_axis_tvalid=1'b1;
endmodule
/*read fifo data*/
module axis_dest (
input s_axis_clk,
input s_axis_tvalid,
input [7:0] s_axis_tdata,
output s_axis_tready
);
assign s_axis_tready=1'b1;
endmodule
其中 data_source
以 $100MHz$ 的速率生成並輸出 $0-0xFF$,axis_dest
一直以 $50MHz$ 的速率讀取數據。時序圖如下:
可以看到開始時 FIFO 未被寫滿,此時寫入和讀出互不干擾,隨著數據不斷寫入而不能被馬上讀出,在一段時間後 FIFO 的 full
信號拉高,對應 m_axis_tready
信號拉低,axis_data_source
不在生成新的數據,仿真圖如下:
可以看到此時寫入和讀出的速率經過 valid 和 ready 信號的使能而同步。