带你快速入门AXI4总线--AXI4-Stream篇(3):详解XILINX IP AXI4 STREAM DATA FIFO

本文转载自:孤独的单刀的CSDN博客

1、AXI4 STREAM DATA FIFO是什么?

 IP核----AXI4 STREAM DATA FIFO也是一种先入先出形式的数据缓存队列(FIFO),不过输入输出接口均为AXIS接口。可用在数据缓存,跨时钟域传输等各类场景。搭载的AXIS接口方便了模块移植,比较适合SOC系统。

在IP catalog搜索,AXI4 STREAM DATA FIFO,再双击出现其配置界面:

1.png

点击documentation--product guide有XILINX提供的IP手册,需要注意的是这个IP的手册是和其他多个IP构成的一个手册(PG085),所以内容不是特别详尽。 

该IP的参数如下:

        Component Name:自己例化的IP名称,根据自己需求来命名即可

        FIFO Depth:FIFO深度,可选择范围16 and 32768,我们这里设置32

        Memory type:实现FIFO的RAM类型,一般选择自动auto即可

        Independent:是否选择独立时钟,即同步FIFO或者异步FIFO,我们这里不搞复杂了,选择同步FIFO ,选择NO

        CDC sync stages:跨时钟域处理的同步阶数,我们选择的同步时钟,用不到

        Enable Packet Mode: 使能包模式:设置为Yes将使能包模式。此项设定需要TLAST信号被使能。FIFO的操作在包模式下被修改为存储传送的数据,直到TLAST信号被置位。当TLAST信号被置位或者FIFO满了,存储的传送数据将被送至AXI4-Stream master interface。我们这里先不使用。

        ACLKEN Conversion Mode(选择NONE) :ACLKEN转换模式: 这个下拉选项为ACLKEN信号的转换模式。当ACLKEN转换执行时会消耗额外的延迟和逻辑。这个选项有: 

  • None - 这里没有ACLKEN信号关联于IP。 

  • S AXIS Only - 一个S_AXIS_ACLKEN信号关联到S_AXIS_ACLK时钟信号和没M_AXIS_ACLKEN信号。 

  • M AXIS Only - 一个M_AXIS_ACLKEN信号关联到M_AXIS_ACLK时钟信号和没有S_AXIS_ACLKEN信号。 

  • S AXIS & M AXIS - 两边时钟都有 ACLKEN信号关联到他们。

Enable ECC:Error Correction Checking ,错误纠正检查。不使用

        TDATA Width(bytes): TDATA位宽(字节为最小单位), 该参数指定了所有AXI4-Stream interfaces中的TDATA信号的位宽,以字节为最小单位。该参数为整数,在0到512(IP核设置界面是256)之间。设为0将省略TDATA信号。如果TDATA信号被省略,TKEEP和TSTRB信号也会被省略。接口数据的位宽按bits计算,需要乘以8。我们这里设置1

        Enable TSTRB :使能TSTRB信号 ,如果设定为Yes,这个参数指定是否在所有AXI4-Stream interfaces使用可选的TSTRB信号。这个选项只能在TDATA Width(bytes)参数大于0时才可以使能。这个信号和TKEEP一起构成了对TDATA的描述,表明当前传输的数据是有效、或是占位符或无效。

        Enable TKEEP :使能TSTRB信号 ,如果设定为Yes,这个参数指定是否在所有AXI4-Stream interfaces使用可选的TKEEP信号。这个选项只能在TDATA Width(bytes)参数大于0时才可以使能。这个信号和TKEEP一起构成了对TDATA的描述,表明当前传输的数据是有效、或是占位符或无效。

        Enable TLAST: 使能TLAST。如果设定为Yes,这个参数指定是否在所有AXI4-Stream interfaces使用可选的TLAST信号。对于TLAST信号要重点说明,因为在使用STREAM FIFO时TLAST的作用特别的重要。对于STREAM FIFO来说,TLAST信号的作用是指示一次传输数据流的最后一个数据,也指示着该数据流的结束。其会记录下TLAST信号的位置,及当其SLAVE接口(SFIFO的数据写入接口)的某一个数据写入的同时TLASET信号也为高的话,当MASTER接口(SFIFO的数据读出接口)读出该数据的同时也会将TLAST信号拉高。总结起来就是,进的数据有TLAST,该数据出的时候就会有TLAST。

        TID Width(bits): TID位宽(比特为单位): 如果该参数大于0,这个参数指定是否在所有的AXI4-Stream interfaces中使用TID信号。值大于0省略这个信号。适用于多个AXIS接口构成的系统通信,用于区别源信号。这里不使用

        TDEST Width(bits): TDEST位宽(bits): 如果该参数大于0,这个参数指定是否在所有的AXI4-Stream interfaces中使用TDEST信号。值大于0省略这个信号。适用于多个AXIS接口构成的系统通信,用于区别目标信号。这里不使用

        TUSER Width(bits): TUSER 位宽(bits): 如果该参数大于0,这个参数指定是否在所有的AXI4-Stream interfaces中使用TDEST信号。值大于0省略这个信号。传输用于的定制信息,这里不使用

2.pngFLAG类的信号设置如下:

Enable write data count:使能写入数据计数,同步于写时钟

Enable almost full:使能写入几乎满,同步于写时钟

Enable programmable full:使能可编程满,同步于写时钟。可以自己设置阈值来提醒当前FIFO的写入状态

Programmable full threshold:可编程满阈值

Enable read data count:使能读取数据计数,同步于读时钟

Enable almost empty:使能读取几乎空,同步于读时钟

Enable programmable empty:使能可编程空,同步于读时钟。可以自己设置阈值来提醒当前FIFO的读取状态

Programmable empty threshold:可编程空阈值

再来看一下此时的FIFO框图:

3.png

其中除了标志信号、时钟、复位信号外,就是两个接口M_AXIS、S_AXIS。我们知道AXIS是一种半双工的总线,数据传输永远是从MASTER发送给SLAVE,所以可以判断出M_AXIS是发送接口来发送FIFO中的数据,即FIFO读取端;S_AXIS是接收接口来将数据写入FIFO中,即FIFO写入端。

2、自己编写的仿真验证

接下来我们例化一个FIFO,并依照AXIS的握手协议来对其进行仿真验证,预期实现以下功能:

  • FIFO深度32,AXIS的数据位宽32位,读写时钟50M

  • 先将FIFO写满,等待几个周期,将其读空

编写的Testbench如下:

`timescale 1ns/1ns//时间单位/精度
//------------<模块及端口声明>----------------------------------------
module tb_fifo();
 
regs_axis_aresetn;
regs_axis_aclk;
//写FIFO端口
regs_axis_tvalid;
reg[31 : 0] s_axis_tdata;
regs_axis_tlast;
wire  [31 : 0] axis_wr_data_count;
wires_axis_tready;
wirealmost_full;
//读FIFO端口
regm_axis_tready;
wirem_axis_tvalid;
wire  [31 : 0]m_axis_tdata;
wire m_axis_tlast;
wire  [31 : 0]axis_rd_data_count;
wirealmost_empty;
//------------<设置初始测试条件>----------------------------------------
initial begin
s_axis_aclk = 1'b0;//初始时钟为0
s_axis_aresetn <= 1'b0;//初始复位
s_axis_tvalid <= 1'b0;
s_axis_tdata <= 32'd0;
s_axis_tlast <= 1'b0;
m_axis_tready <= 1'b0;
#60//60个时钟周期后
s_axis_aresetn <= 1'b1;//拉高复位,系统进入工作状装
#70
s_axis_tvalid <= 1'b1;//拉高tvalid,准备发送信好
wait(s_axis_tready);//等待从机响应
repeat(35)begin//重复35个时钟周期
@(posedge s_axis_aclk)
s_axis_tdata <= s_axis_tdata + 1;//发数据从1开始累加1
end
s_axis_tvalid <= 1'b0;//结束发送
m_axis_tready <= 1'b1;//拉高tready,准备接收
@(posedge almost_empty)
#20 m_axis_tready <= 1'b0;//接收完所有数据后拉低m_axis_tready
#40$finish;
end
//------------<设置时钟>----------------------------------------------
always #10 s_axis_aclk = ~s_axis_aclk;//系统时钟周期20ns
 
//tlast
always@(posedge s_axis_aclk or negedge s_axis_aresetn ) begin
if(!s_axis_aresetn)
s_axis_tlast <= 1'b0;
//发送33个数据(最后一个数据)拉高
else if(axis_data_fifo_0_inst.s_axis_tdata == 'd33)
s_axis_tlast <= 1'b1;
else
s_axis_tlast <= 1'b0;
end
 
//------------<例化被测试模块>----------------------------------------
axis_data_fifo_0 axis_data_fifo_0_inst (
//系统端口
.s_axis_aresetn(s_axis_aresetn),          // input wire s_axis_aresetn
.s_axis_aclk(s_axis_aclk),              // input wire s_axis_aclk
//写FIFO端口 
.s_axis_tvalid(s_axis_tvalid),            // input wire s_axis_tvalid
.s_axis_tready(s_axis_tready),            // output wire s_axis_tready
.s_axis_tdata(s_axis_tdata),             // input wire [7 : 0] s_axis_tdata
.s_axis_tlast(s_axis_tlast),             // input wire s_axis_tlast
.axis_wr_data_count(axis_wr_data_count),  // output wire [31 : 0] axis_wr_data_count
.almost_empty(almost_empty),              // output wire almost_empty 
//读FIFO端口  
.m_axis_tvalid(m_axis_tvalid),            // output wire m_axis_tvalid
.m_axis_tready(m_axis_tready),            // input wire m_axis_tready
.m_axis_tdata(m_axis_tdata),            // output wire [7 : 0] m_axis_tdata
.m_axis_tlast(m_axis_tlast),            // output wire m_axis_tlast
.axis_rd_data_count(axis_rd_data_count),  // output wire [31 : 0] axis_rd_data_count
.almost_full(almost_full)               // output wire almost_full
);
 
endmodule

仿真结果如下:

4.png

局部放大,写数据部分仿真图:

5.png读数据部分仿真图:

6.png这里对时序图不做讲解(图上的注释已经非常详尽了),只说一下FIFO的深度问题。从图中我们发现两点:

  • FIFO设置的深度为32,仿真实际深度为34

  • 读取FIFO时,数据并没有落后1个时钟周期 

关于XILINX的FIFO IP核,有两种读取模式:standard FIFO,first word fall through。这两种模式的时序如下:

7.png

8.png

不难发现,standard FIFO模式读取数据会有一个周期的延迟;而first word fall through模式下,读取数据没有延迟。所以在本文中的FIFO都是first word fall through模式。

而这两种模式的actual depth而会有一点点区别。依然打开FIFO generator,分别选择两种模式看看实际深度的区别,如下:

9.jpg10.jpg可以看到standard FIFO模式的实际深度是32,而first word fall through模式的实际深度是34。这就解释了为什么我们的仿真FIFO深度是34了。

3、XILINX官方例程仿真

同其他许多IP一样,这个IP Xilinx也会我们提供example design。右击IP,选择open IP example design

11.jpg

点击OK

12.jpg

此时会新生成一个工程,该工程目录如下:

13.jpg 


点击run simulation进行行为仿真,添加波形如下:

14.png

仿真结果如下所示:

15.png过程较长,我们截取一些局部图。

下图中,FIFO复位未完成时,存在不稳定的现象 ;FIFO的写入和读取均需要等待一定的时间。

16.png

下图中:

每1个时钟周期写入1个数据 ;每2个时钟周期读取1个数据

tlast一直被拉高,代表每包数据只有一个

写入数据累加1(32‘d16843 == 32'b0000_0001_0000_0001_0000_0001_0000_0001;32‘d33686 == 32'b0000_0002_0000_0002_0000_0002_0000_0002)         

17.png

下图中:

由于写时钟是读时钟的2倍,所以FIFO很快会被写满,被写满后只有等待读出数据后才能继续写入,由于写使能一直拉高,就会形成一种读写平衡状态

18.png

下图中:

一次写入一包数据,个数16个,s_axis_tlast表示写入一包数据的最后一个

19.png

下图中:

一次读出一包数据,个数16个,m_axis_tlast表示读取一包数据的最后一个

20.png

下图中:

用户拉低s_axis_tvalid信号,停止写入FIFO;继续读取FIFO;待FIFO中的数据全部被读出来后,FIFO拉低m_axis_tvalid信号,表示FIFO被读空

21.png

4、其他

————————————————

版权声明:本文为CSDN博主「孤独的单刀」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/wuzhikaidetb/article/details/121484105


最新文章

最新文章