FIFO用法详解(附有代码)
本次讲解以V7芯片为例,主要讲解同步fifo如何使用,以及其中的一些flag的含义和使用方法。
生成IP核
1.首先打开IP Catalog,在查询栏中输入fifo,双击打开FIFO Generator,打开如下所示界面,1中命名规范:尽量写出写、读宽度及其深度,让人一目了然;2中选择native,一般设计都选择这个,这里不详细解释;3中选择同步时钟的RAM,这里的同步时钟和异步时钟表示读写时钟是否同步,一般情况下,读时钟和写时钟一致,或数据没有跨时钟那就选择同步时钟即可,否则选择异步时钟。
2.在native ports选项中主要区分下图中红框1的选项,前者的读数据相比于读使能延时一个周期,后者(FWFT)的读使能和读数据在时序上同步(这一点在仿真模块进一步说明)。这个在数据通信的发送端关系很大,必须选择后者,才能正确发送数据,在通信过程中的接收端则没关系,选哪个都一样。图中的2红框表示选择FIFO的复位信号,可选读写端同步或异步,如果是同步FIFO可选同步复位,否则应该选择异步复位,且读写端的复位应各自在所在的时钟域中,才能保证不出现亚稳态。高有效或低有效可自选。界面的中间是读位宽写位宽和数据深度设置,数据深度表示这个FIFO能容纳多少个这样位宽的数据。
3.在status flags界面中主要是设置一些标志位,红框1可设置将满和将空,一般可用可不用,因为用空满信号就可以;红框2表示读出数据有效信号,这个标志位与读出数据同步,有数据读出就有效,否则无效,可用来判断从FIFO中是否有数据读出;红框3表示可编程满和可编程空,即空满的值可由用户设定,比如这里设置了可编程满的值为56,且是常数(还有一种是在代码模块中通过引脚输入),意思是FIFO中有55个数时,就表示满了而不是到64个才满,这能更好的避免数据溢出,可编程空原理一样,这里没有设置。
4.data counts一般不用设置,用来累计读写数据个数的,与空满信号功能重复,不用也可以。然后点击OK和generate生成IP。。
5.打开工程Sources中的IP Sources如下图所示,双击.veo文件,找到ip例化程序端口,便于调用。
例化程序模块如下:
fifo_64w64d_d64 your_instance_name (
.clk(clk), // input wire clk
.srst(srst), // input wire srst
.din(din), // input wire [63 : 0] din
.wr_en(wr_en), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(dout), // output wire [63 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.valid(valid), // output wire valid
.prog_full(prog_full) // output wire prog_full
);
例程讲解
本次例程主要目的是教大家如何使用fifo,包括写数据、写使能、读数据、读使能、空信号、满信号等的控制。其中的fifo_din可来自外部,例如从adc端口、ddr3端口、aurora端口等获取的数据;fifo_dout可用于某些通信的发送端口,或直接获取信号进行检验、观察等。在这只是抛砖引玉。
1.在顶层模块中调用IP核例化模块:
`timescale 1ns / 1ps
module fifo_test_top(
input clk,
input rstn
);
wire locked ;
reg [7:0] rst_50m_cnt ;
reg rst_50m ;
wire clk_50m ;
wire [63:0] fifo_din ;
wire fifo_din_ena ;
wire fifo_dout_ena ;
wire [63:0] fifo_dout ;
wire full ;
wire empty ;
wire fifo_dout_vad ;
wire prog_full ;
//板上时钟转到全局时钟,并分频(mmcm IP核)
clk_50m u_clk_50m(
.clk_out1 (clk_50m),
.reset (1'b0 ),
.locked (locked ),
.clk_in1 (clk )
);
//板上复位同步到clk_50m时钟域
always@( posedge clk_50m or negedge rstn ) begin
if(~rstn) begin
rst_50m_cnt <= 8'b0;
rst_50m <= 1'b1;
end
else if(rst_50m_cnt == 8'hff) begin
rst_50m_cnt <= rst_50m_cnt;
rst_50m <= 1'b0;
end
else begin
rst_50m_cnt <= rst_50m_cnt + 8'b1;
rst_50m <= 1'b1;
end
end
//FIFO IP核
fifo_64w64d_d64 u_fifo_64w64d_d64 (
.clk (clk_50m ),
.srst (rst_50m ),
.din (fifo_din ),
.wr_en (fifo_din_ena ),
.rd_en (fifo_dout_ena ),
.dout (fifo_dout ),
.full (full ),
.empty (empty ),
.valid (fifo_dout_vad ),
.prog_full (prog_full )
);
//FIFO信号发生模块
fifo_dat_gen u_fifo_dat_gen(
.clk (clk_50m ),
.rst (rst_50m ),
.prog_full (prog_full ),
.fifo_din (fifo_din ),
.fifo_din_ena (fifo_din_ena )
);
//FIFO接收信号检验模块
fifo_dat_che u_fifo_dat_che(
.clk (clk_50m ),
.rst (rst_50m ),
.empty (empty ),
.fifo_dout_vad (fifo_dout_vad ),
.fifo_dout (fifo_dout ),
.fifo_dout_ena (fifo_dout_ena )
);
endmodule
clk_50m的IP核设置如下:
FIFO写入数据生成模块如下:
`timescale 1ns / 1ps
module fifo_dat_gen(
input clk,
input rst,
input prog_full,
output [63:0] fifo_din,
output fifo_din_ena
);
reg [63:0] fifo_din;
reg fifo_din_ena;
reg [63:0] fifo_din_cnt;
always@( posedge clk or negedge rst ) begin
if(rst) begin
fifo_din <= 64'b0;
fifo_din_cnt <= 64'b0;
fifo_din_ena <= 1'b0;
end
else if(~prog_full) begin
fifo_din <= fifo_din_cnt;
fifo_din_cnt <= fifo_din_cnt + 64'b1;
fifo_din_ena <= 1'b1;
end
else begin
fifo_din <= 64'b0;
fifo_din_cnt <= fifo_din_cnt;
fifo_din_ena <= 1'b0;
end
end
endmodule
读数据检验模块如下:
`timescale 1ns / 1ps
module fifo_dat_che(
input clk,
input rst,
input empty,
input fifo_dout_vad,
input [63:0] fifo_dout,
output fifo_dout_ena
);
(* mark_debug = "true" *)
reg [63:0] fifo_dout_cmp;
(* mark_debug = "true" *)
reg err;//数据检验标志位,为1表示FIFO读出数据与valid不同步
assign fifo_dout_ena = ~empty;
always@( posedge clk or negedge rst ) begin
if(rst) begin
fifo_dout_cmp <= 64'b0;
end
else if(fifo_dout_vad) begin
fifo_dout_cmp <= fifo_dout_cmp + 64'b1;
end
else begin
fifo_dout_cmp <= fifo_dout_cmp;
end
end
always@( posedge clk or negedge rst ) begin
if(rst) begin
err <= 1'b0;
end
else if(fifo_dout_vad) begin
if(fifo_dout_cmp != fifo_dout) begin
err <= 1'b1;
end
else begin
err <= 1'b0;
end
end
else begin
err <= err;
end
end
endmodule
仿真模块如下:
`timescale 1ns / 1ps
module fifo_test_top_tb;
reg clk;
reg rstn;
initial begin
clk = 0;
rstn = 1;
#100 rstn = 0;
#500 rstn = 1;
end
always #8 clk = ~clk;
fifo_test_top u_fifo_test_top(
.clk (clk ),
.rstn (rstn )
);
endmodule
仿真结果如下:
err的结果显示无误,仿真正确,详细的可以对照着每一个标志位和对应的数据,查看变化过程。
为了更清晰的查看FIFO IP核中的standard fifo 和first word fall through的区别,特截取了信号产生的初始端如下图所示,standard fifo模式的读使能fifo_dout_ena高有效之后的下一个时钟,读数据接口fifo_dout才产生数据;而另一种的读使能和读数据同时产生,这就是区别之处。这个区别在数据通信的时候影响很大,尤其是在不间断的数据发送端必须设置为first word fall through,保证信号立即反应,避免丢失,在接收端没关系(读者可以想想为什么没关系)。
standard fifo仿真信号:
first word fall through仿真信号: