FIFO

weixin_49495368發表於2020-11-23

FIFO的基本知識與設計實現

最近在學習FPGA的FIFO,現在將最近的FIFO學習情況做一個小結:
首先從幾個問題來引入FIFO的概念吧
1) FIFO是什麼?
答:FIFO的英文是First in First out,也就是先進先出,即先進入FIFO的資料也最先被讀出,一般作為資料傳輸的緩衝器。他與普通的RAM和ROM的區別在於FIFO不用給定讀寫的地址,在進行讀寫操作的過程當中是順序進行的,不需要給定地址線。
2) FIFO的作用是?
a)FIFO的作用主要是在進行資料傳輸的過程當中,作為緩衝器對資料進行緩衝。
b)FIFO可以處理跨時鐘域的資料互動,主要是非同步FIFO,可以處理不同時鐘域的資料傳輸,因為來自不同時鐘域的資料傳輸速率不一樣,如果沒有FIFO的話,很可能產生接收資料不是傳輸資料的現象,同時也能避免亞穩態的現象。
3)同步FIFO和非同步FIFO的區別是?
FIFO分為同步FIFO和非同步FIFO,當配置為同步FIFO的時候,只使用wr_clk,即所有的輸入輸出的訊號都同步於寫時鐘;當被配置為非同步FIFO的時候,寫埠和讀埠分別由獨立的時鐘,即wr_clk和rd_clk。
到這裡想必大家對FIFO有了一個大致的瞭解,那麼FIFO大致是什麼樣子的呢?那就請看下圖:
在這裡插入圖片描述

其中左側Write Agent是寫資料側,右側為讀資料側,對FIFO操作時要注意各個訊號的方向,其中almost_full於almost_empty是FIFO的將滿和將空標誌,也就是full和empty訊號的前一拍,這兩個訊號的作用是在於避免使FIFO寫滿和讀空,所以這兩個訊號出現表示不能往FIFO裡面寫資料了,以及不能從FIFO裡面讀資料了。
同時要注意almost_full訊號是在Write_Agent模組產生的,如果在Read Agent模組使用的話,建議將這個先進行同步處理,也就是我們俗稱的打拍處理,同時如果在Write Agent模組中使用almost_empty訊號也是這樣。
那麼說到這兒,我們怎麼設計FIFO呢?我們可以呼叫Vivado當中的IP核,開啟IP Catalog搜尋FIFO,點選FIFO Generate就可以對FIFO進行配置。
另外附上fifo的設計思路如下:
在這裡插入圖片描述

大致FIFO的基礎知識瞭解的差不多了,那我們就上程式碼吧

1) 頂層模組,完成對讀FIFO模組、寫FIFO模組以及FIFO IP核進行例化
頂層模組程式碼如下:(該例程是非同步FIFO,但是寫時鐘和讀時鐘都是採用的同一個時鐘)
module FIFO_IP(
input sys_clk ,
input sys_rst_n ,
);
wire fifo_wr_en;
wire fifo_rd_en;
wire [7:0] fifo_din;
wire [7:0] fifo_dout;
wire full;
wire empty;
wire almost_full;
wire almost_empty;
wire [7:0] rd_data_count ;
wire [7:0] wr_data_count ;
//例化FIFO IP核
fifo_generator_0 your_instance_name (
.wr_clk(sys_clk), // input wire wr_clk
.rd_clk(sys_clk), // input wire rd_clk
.din(fifo_din), // input wire [31 : 0] din
.wr_en(fifo_wr_en), // input wire wr_en
.rd_en(fifo_rd_en), // input wire rd_en
.dout(fifo_out), // output wire [31 : 0] dout
.full(full), // output wire full
.almost_full(almost_full), // output wire almost_full
.empty(empty), // output wire empty
.almost_empty(almost_empty), // output wire almost_empty
.rd_data_count(rd_data_count), // output wire [7 : 0] rd_data_count
.wr_data_count(wr_data_count) // output wire [7 : 0] wr_data_count
);

//例化fifo寫模組
fifo_wr u_fifo_wr(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.almost_empty (almost_empty),
.almost_full (almost_full ),
.fifo_wr_data (fifo_wr_data),
.fifo_wr_en (fifo_wr_en )
);

//例化fifo讀模組
fifo_rd u_fifo_rd(
.clk (s’y’s_clk ),
.rst_n (sys_rst_n ),
.fifo_rd_en (fifo_rd_en ),
.almost_empty (almost_empty ),
.almost_full (almost_full ),
.fifo_rd_data (fifo_dout )
);

endmodule

讀寫控制模組如下:
module fifo_wr(
input clk ,
input rst_n ,
input almost_empty ,
input almost_full ,
output reg [7:0] fifo_wr_data ,
output reg fifo_wr_en
);
reg [1:0] state ;
reg almost_empty_d0;//因為empty是讀時鐘域過來的,所以要做同步處理
reg almost_empty_d1;
reg [3:0] dly_cnt ;//延時計數器
always@(posedge clk )begin//上升沿有效
if(!rst_n) begin //打拍處理
almost_empty_d0 <= 1’b0;
almost_empty_d1 <= 1’b1;
end
else begin
almost_empty_d0 <= almost_empty;
almost_empty_d1 <= almost_empty_d0;
end
end

//像fifo寫資料(上升沿寫資料的哈,所以沒有復位的下降沿)
always@(posedge clk)begin
if(!rst_n)begin
state <= 2’d0;
fifo_wr_en<=1’b0;
fifo_wr_data<=32’d0;
dly_cnt<=4’d0 ;
end
else begin
case(state)
2’d0:begin
if(almost_empty_d1)begin
state<=2’d1;
end
else
state <= state;
end
2’d1:begin
if(dly_cnt == 4’d10)begin
dly_cnt <= 4’d0;
state <= 2’d2;
fifo_wr_en<=1’b1;
end
else
dly_cnt <=dly_cnt ;
end
2’d2:begin
if(almost_empty)begin
state<=2’d0;//回到1狀態
fifo_wr_en<=1’b0;
fifo_wr_data<=32’d0;
end
else begin
fifo_wr_en <=1’b1;
fifo_wr_data<=fifo_wr_data+1’b1;
end
end
default:state<=2’d0;
endcase
end
end
endmodule

3) 讀資料模組如下:(與寫模組很相似)
module fifo_rd(
input clk ,
input rst_n ,
output reg fifo_rd_en ,
input almost_empty ,
input almost_full ,
input [7:0] fifo_rd_data
);

reg almost_full_d0;
reg almost_full_d1;
reg [3:0] dly_cnt;
reg [1:0] state;
always@(posedge clk)begin
if(!rst_n)begin
almost_full_d0<=1’b0;
almost_full_d1<=1’b0;
end
else begin
almost_full_d0 <= almost_full ;
almost_full_d1 <= almost_full_d0 ;
end
end

always@(posedge clk)begin
if(!rst_n)begin
fifo_rd_en<=1’d0;
state <=2’d0;
dly_cnt<=4’d0;
end
else begin
case(state)
2’d0:begin
if(almost_full_d1)
state<=2’d1;
else
state <= state;
end
2’d1:begin
if(dly_cnt==4’d10)begin
dly_cnt<=4’d0;
state<=2’d2;
end
else
dly_cnt<=dly_cnt;
end
2’d2:begin
if(almost_empty)begin
fifo_rd_en <= 1’b0;
state <= 2’d0;
end
else
fifo_rd_en<=1’b1;
end
default:state<=2’d0;
endcase
end
end
endmodule

至此整個FIFO的大致講解到此結束,如果大家有什麼問題可以一起探討哦,並且一起學習FPGA,後面也會持續更新一些FPGA相關知識