vivado常用IP调用配置——FIFO
FIFO-IP核介绍
- FIFO(First In First Out, 即先入先出),是一种数据缓冲器,用来实现数据先入先出的读写方式。FIFO存储器主要是作为缓存,应用在同步时钟系统核异步时钟系统中,在很多设计中都会用到,如:多比特数据做跨时钟域处理、前后带宽不同步等都用到了FIFO
- 根据读写时钟可以分为:同步FIFO(SCFIFO)、异步FIFO(DCFIFO)
SCFIFO-IP核的配置和验证
-
现在IP-Catalog中找到FIFO-IP核
-
Basic
总线类型就选默认的 -
Native Ports
-
Status Flags
补充一下快要空满标志信号,高有效。如果满深度为256,那么当写到255的时候,Almost Full Flag信号被拉高。Almost Empty Flag则是数据还有一个时信号被拉高。下面的Programmable Flags可以规定数据深度为多少的时候信号被拉高。
若FIFO满,再继续向FIFO里面写数据,则会导致现在的数据覆盖以前的数据,所以当full信号被拉高时,应停止写数据。
若FIFO空,再及速度数据,则会读出以前写入的数据。
-
Data Counts
添加一个数据个数计数器
-
Summary
这里可以看到一些总体的配置信息
ok 那就那就开始验证吧
想来一个顶层模块把IP核添加进去,IP核的实例化模板可以打开默认路径FIFO_IP.ip_user_files\ip\scfifo_8x256里面的.veo文件
module fifo(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] din ,
input wire wr_en ,
input wire rd_en ,
output wire [7:0] dout ,
output wire full ,
output wire empty ,
output wire [7:0] data_count
);
scfifo_8x256 scfifo_8_256_inst (
.clk (sys_clk), // input wire clk
.din (din), // input wire [7 : 0] din
.wr_en (wr_en), // input wire wr_en
.rd_en (rd_en), // input wire rd_en
.dout (dout), // output wire [7 : 0] dout
.full (full), // output wire full
.empty (empty), // output wire empty
.data_count (data_count) // output wire [7 : 0] data_count
);
endmodule
然后是测试模块
module sim_fifo( );
reg sys_clk ;
reg sys_rst_n ;
reg [7:0] din ;
reg wr_en ;
reg rd_en ;
wire [7:0] dout ;
wire full ;
wire empty ;
wire [7:0] data_count ;
reg [1:0] cnt ; // 计数器时间间隔
fifo fifo_inst (
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.din (din),
.wr_en (wr_en),
.rd_en (rd_en),
.dout (dout),
.full (full),
.empty (empty),
.data_count (data_count)
);
// 50MHz
parameter clk_period_50M = 20;
always # (clk_period_50M / 2) sys_clk = ~sys_clk;
initial begin
sys_clk = 0;
sys_rst_n = 0;
#20
sys_rst_n = 1;
end
// cnt:计数器变量的赋值
always @ (posedge sys_clk or negedge sys_rst_n)
if (!sys_rst_n)
cnt <= 2'd0;
else if (cnt == 2'd3)
cnt <= 2'd0;
else
cnt <= cnt + 1'b1;
// wr_en:写请求信号的赋值
always @ (posedge sys_clk or negedge sys_rst_n)
if (!sys_rst_n)
wr_en <= 1'b1;
else if ((cnt == 2'd0) && (rd_en == 1'b0))
wr_en <= 1'b1;
else
wr_en <= 1'b0;
// din:写数据
always @ (posedge sys_clk or negedge sys_rst_n)
if (!sys_rst_n)
din <= 8'd0;
else if ((din == 8'd255) && (wr_en == 1'b1))
din <= 8'd0;
else if (wr_en == 1'b1)
din <= din + 1'b1;
else
din <= din;
// rd_en:读请求信号的赋值
always @ (posedge sys_clk or negedge sys_rst_n)
if (!sys_rst_n)
rd_en <= 1'b0;
else if (full == 1'b1) // FIFO写满后开始读取
rd_en <= 1'b1;
else if (empty == 1'b1) // FIFO取空后停止读
rd_en <= 1'b0;
endmodule
波形如下
Native Ports配置界面的两个读模式,波形出来的差距从读-写、写-读的结交位置可以看出,可以试一下
写满之后开始读
读空之后又开始写
DCFIFO-IP核的配置和验证
-
选中IP核
-
Basic
-
Native Ports
-
Status Flags
-
Data Counts
-
Summary
开始仿真
顶层模块实例化FIFO-IP
module fifo_dcfifo(
input wire wr_clk ,
input wire wr_en ,
input wire [7:0] wr_data ,
input wire rd_clk ,
input wire rd_en ,
output wire [15:0] rd_data ,
output wire full ,
output wire empty ,
output wire [6:0] rd_data_count ,
output wire [7:0] wr_data_count
);
dcfifo_8x256_to_16x128 dcfifo_8x256_to_16x128_inst (
.wr_clk (wr_clk), // input wire wr_clk
.rd_clk (rd_clk), // input wire rd_clk
.din (wr_data), // input wire [7 : 0] din
.wr_en (wr_en), // input wire wr_en
.rd_en (rd_en), // input wire rd_en
.dout (rd_data), // output wire [15 : 0] dout
.full (full), // output wire full
.empty (empty), // output wire empty
.rd_data_count(rd_data_count), // output wire [6 : 0] rd_data_count
.wr_data_count(wr_data_count) // output wire [7 : 0] wr_data_count
);
endmodule
测试模块
module sim_fifo_dcfifo ();
reg wr_clk ;
reg wr_en ;
reg [7:0] wr_data ;
reg rd_clk ;
reg rd_en ;
wire [15:0] rd_data ;
wire full ;
wire empty ;
wire [6:0] rd_data_count ;
wire [7:0] wr_data_count ;
reg sys_rst_n ;
reg [1:0] cnt ; // 控制写时间间隔
reg wr_full_reg0; // 用于同步写满标志信号
reg wr_full_reg1;
// 写时钟:50MHz
parameter clk_period_50M = 20;
always # (clk_period_50M / 2) wr_clk = ~wr_clk;
// 读时钟:25MHz
parameter clk_period_25M = 40;
always # (clk_period_25M / 2) rd_clk = ~rd_clk;
initial begin
wr_clk = 0;
rd_clk = 0;
sys_rst_n = 0;
#20
sys_rst_n = 1;
end
// cnt:计数器
always @ (posedge wr_clk or negedge sys_rst_n)
if (!sys_rst_n)
cnt <= 2'd0;
else if (cnt == 2'd3)
cnt <= 2'd0;
else
cnt <= cnt + 1'b1;
// wr_en:写请求信号
always @ (posedge wr_clk or negedge sys_rst_n)
if (!sys_rst_n)
wr_en <= 1'b0;
else if ((cnt == 2'd0) && (wr_en == 1'b0))
wr_en <= 1'b1;
else
wr_en <= 1'b0;
// wr_data:写数据赋值
always @ (posedge wr_clk or negedge sys_rst_n)
if (!sys_rst_n)
wr_data <= 8'd0;
else if ((wr_data == 8'd255) && (wr_en == 1'b1))
wr_data <= 8'd0;
else if (wr_en == 1'b1)
wr_data <= wr_data + 1'b1;
else
wr_data <= wr_data;
// 时钟同步处理
always @ (posedge rd_clk or negedge sys_rst_n)
if (!sys_rst_n)
begin
wr_full_reg0 <= 1'b0;
wr_full_reg1 <= 1'b0;
end
else // 相当于多打一拍,就让写时钟同步到读时钟下
begin
wr_full_reg0 <= full;
wr_full_reg1 <= wr_full_reg0;
end
// rd_en:写请求信号
always @ (posedge rd_clk or negedge sys_rst_n)
if (!sys_rst_n)
rd_en <= 1'b0;
else if (wr_full_reg1 == 1'b1) // 此时数据已经写满
rd_en <= 1'b1;
else if (empty == 1'b1) // 此时数据已经读空
rd_en <= 1'b0;
fifo_dcfifo fifo_dcfifo_inst (
.wr_clk (wr_clk), // input wire wr_clk
.rd_clk (rd_clk), // input wire rd_clk
.wr_data (wr_data), // input wire [7 : 0] din
.wr_en (wr_en), // input wire wr_en
.rd_en (rd_en), // input wire rd_en
.rd_data (rd_data), // output wire [15 : 0] dout
.full (full), // output wire full
.empty (empty), // output wire empty
.rd_data_count(rd_data_count), // output wire [6 : 0] rd_data_count
.wr_data_count(wr_data_count) // output wire [7 : 0] wr_data_count
);
endmodule
波形: