本文转载自:孤独的单刀的CSDN博客
1、AXI4 STREAM DATA FIFO是什么?
IP核----AXI4 STREAM DATA FIFO也是一种先入先出形式的数据缓存队列(FIFO),不过输入输出接口均为AXIS接口。可用在数据缓存,跨时钟域传输等各类场景。搭载的AXIS接口方便了模块移植,比较适合SOC系统。
在IP catalog搜索,AXI4 STREAM DATA FIFO,再双击出现其配置界面:
点击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省略这个信号。传输用于的定制信息,这里不使用
FLAG类的信号设置如下:
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框图:
其中除了标志信号、时钟、复位信号外,就是两个接口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
仿真结果如下:
局部放大,写数据部分仿真图:
读数据部分仿真图:
这里对时序图不做讲解(图上的注释已经非常详尽了),只说一下FIFO的深度问题。从图中我们发现两点:
FIFO设置的深度为32,仿真实际深度为34
读取FIFO时,数据并没有落后1个时钟周期
关于XILINX的FIFO IP核,有两种读取模式:standard FIFO,first word fall through。这两种模式的时序如下:
不难发现,standard FIFO模式读取数据会有一个周期的延迟;而first word fall through模式下,读取数据没有延迟。所以在本文中的FIFO都是first word fall through模式。
而这两种模式的actual depth而会有一点点区别。依然打开FIFO generator,分别选择两种模式看看实际深度的区别,如下:
可以看到standard FIFO模式的实际深度是32,而first word fall through模式的实际深度是34。这就解释了为什么我们的仿真FIFO深度是34了。
3、XILINX官方例程仿真
同其他许多IP一样,这个IP Xilinx也会我们提供example design。右击IP,选择open IP example design
点击OK
此时会新生成一个工程,该工程目录如下:
点击run simulation进行行为仿真,添加波形如下:
仿真结果如下所示:
过程较长,我们截取一些局部图。
下图中,FIFO复位未完成时,存在不稳定的现象 ;FIFO的写入和读取均需要等待一定的时间。
下图中:
每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)
下图中:
由于写时钟是读时钟的2倍,所以FIFO很快会被写满,被写满后只有等待读出数据后才能继续写入,由于写使能一直拉高,就会形成一种读写平衡状态
下图中:
一次写入一包数据,个数16个,s_axis_tlast表示写入一包数据的最后一个
下图中:
一次读出一包数据,个数16个,m_axis_tlast表示读取一包数据的最后一个
下图中:
用户拉低s_axis_tvalid信号,停止写入FIFO;继续读取FIFO;待FIFO中的数据全部被读出来后,FIFO拉低m_axis_tvalid信号,表示FIFO被读空
4、其他
————————————————
版权声明:本文为CSDN博主「孤独的单刀」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wuzhikaidetb/article/details/121484105