文章目录
FIFO的实现方式
接口类型:Native、AXI Memory Mapped、AXI Stream。
Interface Type | description |
---|---|
Native | Implements a Native FIFO. |
AXI Memory Mapped | Implements an AXI4, AXI3 and AXI4-Lite FIFOs in First-Word-Fall-Through mode. |
AXI Stream | Implements an AXI4-Stream FIFO in First-Word-Fall-Through mode. |
其中在Native接口模式下可使用块RAM、分布式RAM、移位寄存器、内嵌FIFO四种选项实现FIFO。
对于内嵌的FIFO和块RAM FIFO,支持错误注入和纠正检测(ECC),支持嵌入式寄存器选项。
典型应用
FIFO类型 | 典型应用 |
---|---|
跨时钟域 | |
Native FIFO | 低延时存储器缓存 |
总线位宽调整 | |
AXI4-Stream FIFOs | AXI4-Stream FIFO 最适合非基于地址的点对点应用。使用它们可连接使用该接口的其他 IP 内核(例如,FFT、DDS 和 FIR 编译器等 DSP 功能的 AXI4 版本)。 |
AXI4/AXI3 Memory Mapped FIFOs | AXI4/AXI3 接口的完整版本称为 AXI4/AXI3。也可称为 AXI 内存映射。在内存映射系统总线设计中使用 AXI4/AXI3 FIFO,如需要内存映射接口连接其他 AXI4/AXI3 块的桥接应用。 |
AXI4-Lite FIFOs | AXI4-Lite 接口是一种更简单的 AXI 接口,支持只需执行简单的控制/状态寄存器访问或外设访问的应用。 |
位宽转换的数据对应关系
Native接口下只有用BRAM才能实现位宽转换。
以写位宽为8bit,读位宽为16bit;和写位宽为8bit,读位宽为4bit为例,如下图。
可以看出,FIFO的位宽转换和RAM中相反Xilinx RAM IP核的使用及注意事项,更符合我们先进先出的思想。
低位宽的起始地址从高位宽起始地址中数据的高位开始取数据。简单讲就是低字节的数据会被放在高地址。
写满/读空后的状态
写满后继续写:写的数据是无效的,不会影响到已经写入FIFO中的数据。
可以看到在256个地址中写入0-255后继续写数据,此时写入有效标志信号wr_ack置0,表示写入无效;在读取时将FIFO读空后最后的数据仍然是255(ff),继续读取数据,此时读数据有效标志信号valid置0表示读取无效,但输出数据端口仍然后继续输出最后一位数据f。
读空后继续读:可以看到如果在FIFO读空后(empty置1),继续保持读使能,此时的输出数据会保持最后一个数据不变。
验证代码见附录一。
读数据延迟
IP核的输出给定读使能后,读数据会滞后至少一个时钟周期输出,选择Output Registers会再延迟一个时钟周期。具体输出延迟的周期可查看总结页面。
未选择Output Registers,输出延迟使能信号一个时钟周期:
选择Output Registers,输出延迟使能信号两个时钟周期:
Read Mode选择
读模式设置
Standard FIFO:标谁读模式,接收到读使能后下一个周期输出数据。
First Word Fall Through(FWFT):先入先出模式,只要FIFO中有数据,则自动输出第一个数据,并保持这个数据输出,到读使能拉高后输出下一个数据。
Standard FIFO:(rd_en拉高后的下一周期输出数据)
First Word Fall Through(FWFT):(rd_en拉高的同时输出第一个数据,下一周期输出下一个数据)
配置深度不等于实际深度(Actual Depth)
平时使用FIFO的时候,很少有使用到FULL的状态;而且由于开发板资源足够,所有的FIFO也都是留有一部分的冗余;
FIFO的实际深度取决于它的实现方式并且影响其实现的功能,所以配置的FIFO深度以后面的Actual Depth为准,否者在写满的情况下可能造成数据的丢失。
具体看官方手册PG057_Page137的介绍:
Xilinx FIFO Generator 需要注意Actual Depth
读写计数器
wr_data_count、rd_data_count计数器可以在FIFO配置时进行勾选。
可以看出写数据计数器与写时钟同步,读数据计数器与读时钟同步。
wr_data_count(写数据计数器)表示此时写入到FIFO中的数据;写入一个数据该值加一;读出数据后,该值根据相应的位宽减。
rd_data_count(读数据计数器)表示此时FIFO中可读的数据量;读出一个数据该值减一,写入数据后,该值根据相应的位宽加。
FIFO安全电路
在异步FIFO的BRAM实现模式下,存在Enable Safety Circuit选项。在进行复位之后wr_rst_busy拉高时,此时是不能写入数据的;rd_rst_busy拉高时,此时是不能读取数据的,wr_en和rd_en需拉低。
Enable Safety Circuit选项:
所以在复位后的几个时钟内直接给写使能,如果发现写不进去数据,可以考虑是由于此问题造成。
参考:FPGA设计心得(11)关于FIFO IP核使用的一点注意事项
参考
Xilinx FIFO Generator 需要注意Actual Depth
FPGA设计心得(11)关于FIFO IP核使用的一点注意事项
附录
附录1
配置如下:
RTL部分:
module fifo
(
input wire sys_clk , //系统时钟 50Mhz
input wire sys_rst_n , //复位信号
input wire [7:0] pi_data , //输入顶层模块的数据
//要写入到 FIFO 中的数据
input wire pi_flag , //输入数据有效标志信号
//也作为 FIFO 的写请求信号
input wire rd_en , //FIFO 读请求信号
output wire [3:0] po_data , //FIFO 读出的数据
output wire empty , //FIFO 空标志信号,高有效
output wire full, //FIFO 满标志信号,高有效
output wire wr_ack,
output wire valid
);
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
scfifo_256x8 scfifo_256x8_inst
(
.clk (sys_clk ), // input clk
.srst (~sys_rst_n ), // input srst
.din (pi_data ), // input [7 : 0] din
.wr_en (pi_flag ), // input wr_en
.rd_en (rd_en ), // input rd_en
.dout (po_data ), // output [7 : 0] dout
.full (full ), // output full
.empty (empty ), // output empty
.wr_ack(wr_ack ), // output wire wr_ack
.valid(valid ) // output wire valid
);
endmodule
仿真部分(70us,写入和读取均溢出):
//70us
module tb_fifo();
//reg define
reg sys_clk ;
reg [7:0] pi_data ;
reg pi_flag ;
reg rd_en ;
reg sys_rst_n ;
reg [1:0] cnt_baud ;
//wire define
wire [3:0] po_data ;
wire empty ;
wire full ;
reg [5:0] cnt_full;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、复位
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#200;
sys_rst_n <= 1'b1;
end
//sys_clk:模拟系统时钟,每 10ns 电平翻转一次,周期为 20ns,频率为 50Mhz
always #10 sys_clk = ~sys_clk;
//cnt_baud:计数从 0 到 3 的计数器,用于产生输入数据间的间隔
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_baud <= 2'b0;
else if(&cnt_baud == 1'b1)
cnt_baud <= 2'b0;
else
cnt_baud <= cnt_baud + 1'b1;
//pi_flag:输入数据有效标志信号,也作为 FIFO 的写请求信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_flag <= 1'b0;
//每 4 个时钟周期且没有读请求时产生一个数据有效标志信号
else if((cnt_baud == 2'd0) && (rd_en == 1'b0))//写满后还多写了个
pi_flag <= 1'b1;
else
pi_flag <= 1'b0;
//pi_data:输入顶层模块的数据,要写入到 FIFO 中的数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_data <= 8'h00;
//pi_data 的值为 0~255 依次循环
else if((pi_data == 8'd255) && (pi_flag == 1'b1))
pi_data <= 8'b0;
else if(pi_flag == 1'b1) //每当 pi_flag 有效时产生一个数据
pi_data <= pi_data + 8'h01;
//rd_en:FIFO 读请求信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if(full == 1'b1 && (cnt_full == 6'h3f)) //当 FIFO 中的数据存满时,开始读取 FIFO 中的数据
rd_en <= 1'b1;
else if(empty == 1'b1 && cnt_baud == 2'd1) //当 FIFO 中的数据被读空时多读了几个周期
rd_en <= 1'b0;
//==========================================================================
//== 验证满后继续写的情况
//==========================================================================
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_full <= 4'b0;
else if(&cnt_full)
cnt_full <= cnt_full;
else if(full == 1'b1)
cnt_full <= cnt_full + 1'b1;
else ;
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------fifo_inst------------------------
fifo fifo_inst
(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.pi_data (pi_data ), //input [7:0] pi_data
.pi_flag (pi_flag ), //input pi_flag
.rd_en (rd_en ), //input rd_en
.po_data (po_data ), //output [7:0] po_data
.full (full ), //output full
.empty (empty )//output empty
);
endmodule