以往使用 Xilinx 的 FIFO IP 核,往往使用 Native 的接口形式(wr_en, din[x:0], full; rd_en, dout[x:0], empty),这在大多数的情况下是方便的,然而在与某些具有 AXI 接口的其他 IP 配合使用时,Native 接口使用起来就不是很方便了。这种情况下,可以使用 FIFO IP 的 AXI-Stream (AXIS)接口形式,可以大大简化设计,本文对这一接口形式下的 FIFO Generator IP 进行介绍。
IP 配置
要使用 AXIS 接口形式,需要在 IP 配置界面勾选 AXI Stream 接口类型,如上图所示。
第二页配置中,可配置 AXIS 的接口位宽。在 AXIS 接口形式下,数据位宽不能任意选择,其位宽以 8bits 为单位,TDATA NUM BYTES 指示了数据位宽为多少个字节。TUSER WIDTH 提供了可选的 tuser 位宽,通常用于指示发生一个错误或报文属于某一类型(例如三速 MAC IP 的帧过滤器输出)。勾选 TLAST 以启用 tlast 信号。
第三页可选 FIFO 的工作类型:Data FIFO、Packet FIFO,第一种是以单个数据为单位存储进 FIFO 的,第二种以数据包(Packet)为单位存储进 FIFO。其中 Packet FIFO 只有在 TLAST 启用的状态下才可以生成。除此之外,FIFO 深度、可编程空满标志以及 FIFO 元素计数也在这一页进行配置。
第四页配置是否启用 underflow、overflow 标志,只有在 Data FIFO 下才可以勾选。
AXI-Stream 接口形式的数据接口分别为 S_AXIS 和 M_AXIS,S_AXIS 即数据写入端口,M_AXIS 即数据读取端口,如下图所示。
仿真
Data FIFO
DATA FIFO 以单个数据为单位进行写入,数据写入不受 tuser 和 tlast 影响,m_tuser 和 m_tlast 分别标记 s_tuser 和 s_tlast 标记的数据。生成 256 深度的 AXI-Stream 接口形式的异步 Data FIFO,并编写 testbench 如下
`timescale 1ns/1ns
`default_nettype none
module FIFO_AXI_Stream_tb();
reg clk_100M = 1'b1;
reg rst_n = 1'b1;
always #5 begin
clk_100M <= ~clk_100M;
end
wire FIFO_wr_rst_busy; //这两个信号一直高阻是什么意思?也无法不生成
wire FIFO_rd_rst_busy;
reg s_axis_tvalid = 1'b0;
wire s_axis_tready;
reg [7:0] s_axis_tdata = 8'd0;
reg s_axis_tuser = 1'b0;
reg s_axis_tlast = 1'b0;
wire m_axis_tvalid;
reg m_axis_tready = 1'b0;
wire [7:0] m_axis_tdata;
wire m_axis_tuser;
wire m_axis_tlast;
//use tlast,Packet FIFO
FIFO_AXI_Stream FIFO_AXI_Stream(
.s_aresetn (rst_n),
.wr_rst_busy (FIFO_wr_rst_busy),
.rd_rst_busy (FIFO_rd_rst_busy),
.s_aclk (clk_100M),
.s_axis_tvalid (s_axis_tvalid),
.s_axis_tready (s_axis_tready), //指示初始化完成,但不指示~full
.s_axis_tdata (s_axis_tdata),
.s_axis_tuser (s_axis_tuser),
.s_axis_tlast (s_axis_tlast),
.m_aclk (clk_100M),
.m_axis_tvalid (m_axis_tvalid), //相当于~empty
.m_axis_tready (m_axis_tready),
.m_axis_tdata (m_axis_tdata),
.m_axis_tuser (m_axis_tuser),
.m_axis_tlast (m_axis_tlast)
);
task wr_fifo;
input [15:0] wr_num;
input en_tlast;
input en_tuser;
integer i;
begin
wait(clk_100M);
for(i = 0; i < wr_num; i = i + 1'b1) begin
wait(~clk_100M);
s_axis_tvalid <= 1'b1;
s_axis_tdata <= s_axis_tdata + 1'b1;
if(i == wr_num-1'b1 && en_tlast) begin
s_axis_tlast <= 1'b1;
end
else begin
s_axis_tlast <= 1'b0;
end
if(i==wr_num-1'b1 && en_tuser) begin
s_axis_tuser <= 1'b1;
end
else begin
s_axis_tuser <= 1'b0;
end
wait(clk_100M);
end
wait(~clk_100M);
s_axis_tvalid <= 1'b0;
s_axis_tlast <= 1'b0;
s_axis_tuser <= 1'b0;
wait(clk_100M);
end
endtask
task rd_fifo;
input [15:0] rd_num;
integer j;
begin
wait(clk_100M);
for(j = 0; j < rd_num; j = j + 1'b1) begin
wait(~clk_100M);
m_axis_tready <= 1'b1;
wait(clk_100M);
end
wait(~clk_100M);
m_axis_tready <= 1'b0;
wait(clk_100M);
end
endtask
initial begin
#10;
rst_n <= 1'b0;
#100;
rst_n <= 1'b1;
#100;
wait(s_axis_tready); //等待初始化完成
#100;
wr_fifo(16,0,1);
#100;
wr_fifo(16,1,0);
#100;
rd_fifo(40); //测试读空
#100;
wr_fifo(32,1,0);
#100;
fork
begin
wr_fifo(64,1,1);
end
begin
rd_fifo(64);
end
join
#100;
rd_fifo(32);
#100;
wr_fifo(150,1,0);
#100;
wr_fifo(150,1,0); //测试写满
#100;
rd_fifo(300);
#100;
#100;
$stop;
end
endmodule
仿真结果如下:
由于 Data FIFO 以单个数据为单位进行写入,因此当发生写满异常时,仅有超出 FIFO 深度部分的数据发生丢失。
Packet FIFO
Packet FIFO 以数据包为单位进行写入,数据在 s_tlast 断言时才被统一写入 FIFO,写入不受 tuser 影响,m_tuser 和 m_tlast 分别标记 s_tuser 和 s_tlast 标记的数据。生成 256 深度的 AXI-Stream 接口形式的异步 Packet FIFO,testbench 与前面的相同。
仿真结果如下:
由于Packet FIFO 以数据包为单位进行写入,因此当出现写满情况时(即 s_tlast 在 s_tready=Low 时断言)则这一组数据被整体舍弃,如本仿真的最后一组的写入所展示的那样。
参考文献
- pg057-fifo-generator.pdf