一、Vivado FIFO IP核的使用方法和注意事項
1、fifo類型主要分兩種,即同步fifo和異步fifo。
當使用異步fifo時,尤其要注意一點,復位信號rst要和wr_clk保持同步,否則將無法對fifo進行有效復位,會出現寫不進數等不正常的情況。
所以當復位信號為異步信號,且與wr_clk不同源時,要用wr_clk拍兩下rst信號。
always @ (posedge wr_clk) begin rst_fifo0 <= aclr; rst_fifo1 <= rst_fifo0; rst_fifo2 <= rst_fifo1; rst_fifo3 <= rst_fifo2; end
以下介紹的fifo使用方法主要適用於同步fifo。
2、fifo核的兩種讀出模式:standard fifo、first word fall through,它們的功能和操作上有一些區別。
(1)Standard FIFO(標准FIFO):
在標准FIFO中,數據輸入(寫入)和數據輸出(讀取)是獨立的操作。寫入和讀取操作是異步進行的,即它們可以在任何時刻進行。
在讀取操作之前,FIFO必須至少有一個完整的數據字來供讀取。否則,讀取操作將被阻塞直到有數據可用。
標准FIFO可以支持數據的雙向傳輸,即數據可以同時從FIFO讀取和寫入。
當選擇Standard模式的時候,在讀使能信號有效的下一個周期才能讀出第一個有效的數據。
(2)First Word Through (FWT) FIFO(首字通過FIFO):
在FWT FIFO中,寫入操作和讀取操作之間存在一種特殊的關系。當第一個數據字從寫入端輸入FIFO時,它會立即通過FIFO進行讀取。
在FWT FIFO中,讀取操作是優先於寫入操作的。當有數據可用時,讀取操作優先獲得數據,而不需要等待FIFO緩沖區填充完整。
FWT FIFO也支持數據的雙向傳輸,即可以在讀取操作和寫入操作同時進行。
當選擇FWT模式的時候,在讀使能信號有效的第一個周期就能能讀出第一個有效的數據; 這是因為在這種模式下,FIFO提前把數據已經准備到了數據輸出總線上,等待都使能有效就輸出到數據輸出端口(組合邏輯),但在這種模式下,valid信號將會在復位后就保持有效,這一點要特別注意;
總結起來,標准FIFO和首字通過FIFO之間的主要區別在於數據的讀取時機和操作的優先級。標准FIFO對讀取和寫入操作是異步的,而FWT FIFO在寫入第一個數據時即可立即讀取,讀取操作具有優先級。
2、在配置IP核時,一般不需要”Enable safety circuit“。
3、在配置IP核時,一般不需要設置all most full/all most empty,使用半空半滿的方式讀寫fifo。
二、FIFO的設計與開發技巧
1、FIFO的讀寫方式
正如前面提到的,一般不需要設置all most full/all most empty,使用半空半滿的方式讀寫fifo。除了這種方法之外,還有非空即讀的方式,乒乓FIFO(double buffer)的方式等。
(1)半空半滿讀寫
用fifo的計數器控制,寫半滿時開始讀,讀半空時開始寫,其它時間既讀又寫。
// 利用狀態機對fifo進行半空半滿的讀寫 module fifo_example; // 三段式狀態機 always @(posedge clk100m) begin if(Rst == 1'b1) curr_state <= #TCQ_STA_IDLE; else curr_state <= #TCQ_next_state; end // Calculate the next state always @ ( * ) begin case(curr_state) STA_IDLE:begin if (full == 1'b0) // fifo IP核的性質,初始化狀態full為1 next_state <= STA_WRITING; else next_state <= STA_IDLE; end STA_WRITING:begin if (wr_data_count > 8'd200) // 已寫入200個數 next_state <= STA_READING; else next_state <= STA_WRITING; end STA_READING:begin if (rd_data_count < 6'd20) // 可讀取的數小於20 next_state <= STA_WRITING; else next_state <= STA_READING; end default :begin next_state <= STA_IDLE; end endcase end // state output always @(posedge clk100m) begin if (Rst == 1'b1) begin wr_en <= #TCQ 1'b0; rd_en <= #TCQ 1'b0; din <= #TCQ 8'd0; din_r <= #TCQ 8'd0; end else case (curr_state) STA_IDLE: begin wr_en <= #TCQ 1'b0; rd_en <= #TCQ 1'b0; din <= #TCQ 8'd0; din_r <= #TCQ 8'd0; end STA_WRITING:begin wr_en <= #TCQ 1'b1; rd_en <= #TCQ 1'b0; if (din == 8'd255) din <= #TCQ 8'd0; else din <= #TCQ din + 8'd1; din_r <= #TCQ din; end STA_READING:begin wr_en <= #TCQ 1'b0; rd_en <= #TCQ 1'b1; end endcase end endmodule //fifo_example
(2)非空即讀,即當empty拉低后就讀出,如果只用一個異步時鍾FIFO也能實現這個效果,但有可能導致數據斷流,對於要保證fifo無縫銜接的設計,這種方法不可靠,所以推薦使用乒乓fifo的方式讀寫。需要乒乓fifo的場景如下:①異步fifo跨時鍾域;②效率第一,非空即讀;③要求不間斷寫(存),不間斷讀;④數據流的接口。
要注意異步fifo的讀取速度要比寫入速度快,否則就要添加更多的fifo來進行乒乓操作。
2、fifo的empty信號和full信號
對於fifo的empty信號,如果rden沒有賦值(紅色,懸空),那么empty信號在拉低后也會進入紅色,因為“不知道有沒有被讀出”。
fifo的empty沒有正常拉低時(一般發生於仿真過程中),可能是因為復位信號太短,或者復位后間隔時間不夠長就寫入數據;適當延長復位時間和工作到來時間。
初始化的fifo full信號是拉高的,寫入數據要等full拉低后。
3、fifo的特殊應用場景
(1)以UDP轉SDRAM的場景為例,PHY芯片過來的時鍾、數據、有效信號等引腳灌入fifo進行隔離,把兩個時鍾域隔開,避免用該時鍾直接作為UDP模塊的時鍾,否則會因為該時鍾扇出太大,導致程序不穩定,例如改了兩行代碼程序就不能正常運行了。用fifo進行兩個時鍾域的隔離還有一個好處,無論前端時鍾有多快都無所謂,只當隔離fifo滿了以后再讀出新的時鍾,即“網速自適應”。