Xilinx FIFO IP核使用及注意事项

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

相关推荐

  1. CUDA | 函数编写的注意事项

    2024-07-16 16:42:04       35 阅读
  2. 【docker】Docker Stack 详细使用注意事项

    2024-07-16 16:42:04       47 阅读
  3. c++ 智能指针使用注意事项解决方案

    2024-07-16 16:42:04       21 阅读
  4. Docker使用注意事项

    2024-07-16 16:42:04       24 阅读
  5. 【Qt】UDP使用注意事项

    2024-07-16 16:42:04       31 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-16 16:42:04       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-16 16:42:04       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-16 16:42:04       58 阅读
  4. Python语言-面向对象

    2024-07-16 16:42:04       69 阅读

热门阅读

  1. 域名转让要多久?

    2024-07-16 16:42:04       21 阅读
  2. MySQL 中的 CURDATE() 用法

    2024-07-16 16:42:04       21 阅读
  3. python join

    2024-07-16 16:42:04       20 阅读
  4. C语言——局部变量和全局变量

    2024-07-16 16:42:04       18 阅读
  5. 心跳检测的艺术:Eureka服务发现中的智能配置

    2024-07-16 16:42:04       22 阅读
  6. 【Vim】为什么程序员喜欢用 Vim

    2024-07-16 16:42:04       21 阅读
  7. MATLAB中Simulink.SimulationOutput用法

    2024-07-16 16:42:04       23 阅读
  8. 动手学深度学习—— 1.引言

    2024-07-16 16:42:04       23 阅读