软件:vivado 2020.2
参考文献:XILINX官方文档(已被ex人上传为付费内容,笔者上传的免费文档未过申)
转载需标明出处,本文仅供免费交流学习,不得在付费内容中引用转载本文内容。否则,我对你送上最诚挚的问候。
一、代码思想
ip核FIFO Generator(13.2)为我们提供了一个类似于读写互不相关的储存器封装,所以要自己编写读模块/写模块,对其进行读/写。
如下图,写模块fifo_wr 在wr_en && ~full时,向fifo写入数据;读模块fifo_rd在rd_en && ~empty时,从fifo读取数据。
二、TOP文件
'timescale 1ns / 1ps
module fifo_native_top
#(parameter DATA_WIDTH = 32)
(
input sys_aclk,
input sys_rst
);
wire wr_en;
wire full;
wire [DATA_WIDTH - 1 : 0] din;
wire rd_en;
wire empty;
wire [DATA_WIDTH - 1 : 0] dout;
fifo_wr fifo_wr_inst0(
.m_aclk (sys_aclk),
.rst (sys_rst),
.full (full),
.wr_en (wr_en),
.din (din)
);
fifo_rd fifo_rd_inst0(
.s_aclk (sys_aclk),
.rst (sys_rst),
.empty (empty),
.rd_en (rd_en),
.dout (dout)
);
fifo_native fifo_native_inst0 (
.rst (sys_rst),
.wr_clk (sys_aclk),
.rd_clk (sys_aclk),
.full (full),
.empty (empty),
.wr_en (wr_en),
.rd_en (rd_en),
.din (din),
.dout (dout)
);
endmodule
三、写模块与读模块
`timescale 1ns/1ps
module fifo_wr
#(parameter DATA_WIDTH = 32)
(
input m_aclk,
input rst,
input full,
output reg wr_en,
output reg [DATA_WIDTH -1: 0] din
);
always @(posedge m_aclk) begin
if (rst) begin
wr_en <= 1'b0;
din <= 32'b1;
end
else begin
wr_en <= full ? 0:1;
din <= (wr_en && ~full) ? (din + 1) : din;
end
end
endmodule
`timescale 1ns/1ps
module fifo_rd
#(parameter DATA_WIDTH = 32)
(
input s_aclk,
input rst,
input empty,
output reg rd_en,
input [DATA_WIDTH -1: 0] dout
);
always @(posedge s_aclk) begin
if (rst)
rd_en <= 1'b0;
else
rd_en <= empty ? 0:1;
end
endmodule
四、仿真tb文件
`timescale 1ns/1ps
module ip_fifo_native_tb;
parameter CLK_PERIOD = 20;
reg sys_aclk;
reg sys_rst;
initial begin
sys_aclk <= 1'b0;
sys_rst <= 1'b1;
#201
sys_rst <= 1'b0;
end
always #(CLK_PERIOD) sys_aclk = ~sys_aclk;
fifo_native_top fifo_native_top_inst0(
.sys_aclk (sys_aclk),
.sys_rst (sys_rst)
);
endmoudle
五、ip核设置
生成ip核时选择Native接口类型,异步时钟
深度512,数据位宽32,其余默认。
值得注意的是,Native ports界面下的read mode有两种选择:标准fifo(standard FIFO)和First Word Fall Fall Through。二者的仿真代码没有区别,生成ip核时选不同选项即可完成仿真,并观察结果。
前者就是正常的时序逻辑,在传输数据开始时,的下一个时钟上升沿传输数据。而后者在rd_en开始的同一时钟读取数据。
用前者实现后者功能也很简单,在从机rd_en拉高前,先读取一个数据,存入寄存器,从机rd_en拉高后,先输出先前读取的数据,再正常读取后续数据。