IP核之FIFO
FIFO简介
FIFO(First In First Out):即先进先出,FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存(如数据读写带宽不同步),或者高速异步数据的交互也即所谓的跨时钟域信号传递。FIFO只能顺序访问,而RAM则可以随机访问。
FIFO分类
同步FIFO:指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作
异步FIFO:指读写时钟不一致,读写时钟是互相独立的
IP核端口

full:已满;almost_full:差一个数据满;Prog_full:自定义设置满阈值
FIFO读写时序图



实验任务
使用 Vivado 生成一个异步 FIFO,并实现以下功能:当FIFO为空时,向FIFO中写入数据,直至将FIFO写满后停止写操作;当FIFO为满时,从FIFO中读出数据,直到FIFO被读空后停止读操作。如:将FIFO设置的深度和宽度分别为256和8进行读写测试
系统框图


波形图
写模块时序

读模块时序

IP核配置


1.支持读写位宽一致
2.支持预读模式
3.使用Built-in FIFO原语
4.是否支持ECC校验
5.动态错误写入



fifo_wr写模块代码
module fifo_wr(
//mudule clock
input wr_clk , // 时钟信号
input rst_n , // 复位信号
//FIFO interface
input wr_rst_busy , // 写复位忙信号
input empty , // FIFO空信号
input almost_full , // FIFO将满信号
output reg fifo_wr_en , // FIFO写使能
output reg [7:0] fifo_wr_data // 写入FIFO的数据
);
//reg define
reg empty_d0;
reg empty_d1;
//*****************************************************
//** main code
//*****************************************************
//因为empty信号是属于FIFO读时钟域的
//所以对empty打两拍同步到写时钟域下
always @(posedge wr_clk or negedge rst_n) begin
if(!rst_n) begin
empty_d0 <= 1'b0;
empty_d1 <= 1'b0;
end
else begin
empty_d0 <= empty;
empty_d1 <= empty_d0;
end
end
//对fifo_wr_en赋值,当FIFO为空时开始写入,写满后停止写
always @(posedge wr_clk or negedge rst_n) begin
if(!rst_n)
fifo_wr_en <= 1'b0;
else if(!wr_rst_busy) begin
if(empty_d1)
fifo_wr_en <= 1'b1;
else if(almost_full)
fifo_wr_en <= 1'b0;
end
else
fifo_wr_en <= 1'b0;
end
//对fifo_wr_data赋值,0~254
always @(posedge wr_clk or negedge rst_n) begin
if(!rst_n)
fifo_wr_data <= 8'b0;
else if(fifo_wr_en && fifo_wr_data < 8'd254)
fifo_wr_data <= fifo_wr_data + 8'b1;
else
fifo_wr_data <= 8'b0;
end
endmodule
fifo_rd写模块代码
module fifo_rd(
//system clock
input rd_clk , //时钟信号
input rst_n , //复位信号
//FIFO interface
input rd_rst_busy , //读复位忙信号
input [7:0] fifo_rd_data, //从FIFO读出的数据
input full , //FIFO满信号
input almost_empty, //FIFO将空信号
output reg fifo_rd_en //FIFO读使能
);
//reg define
reg full_d0;
reg full_d1;
//*****************************************************
//** main code
//*****************************************************
//因为full信号是属于FIFO写时钟域的
//所以对full打两拍同步到读时钟域下
always @(posedge rd_clk or negedge rst_n) begin
if(!rst_n) begin
full_d0 <= 1'b0;
full_d1 <= 1'b0;
end
else begin
full_d0 <= full;
full_d1 <= full_d0;
end
end
//对fifo_rd_en进行赋值,FIFO写满之后开始读,读空之后停止读
always @(posedge rd_clk or negedge rst_n) begin
if(!rst_n)
fifo_rd_en <= 1'b0;
else if(!rd_rst_busy) begin
if(full_d1)
fifo_rd_en <= 1'b1;
else if(almost_empty)
fifo_rd_en <= 1'b0;
end
else
fifo_rd_en <= 1'b0;
end
endmodule
顶层模块代码,例化fifo_rw,fifo_rd模块和IP核
module ip_fifo(
input sys_clk , // 系统时钟信号
input sys_rst_n // 系统复位信号
);
//wire define
wire clk_50m ; // 50M时钟
wire clk_100m ; // 100M时钟
wire locked ; // 时钟锁定信号
wire rst_n ; // 复位,低有效
wire wr_rst_busy ; // 写复位忙信号
wire rd_rst_busy ; // 读复位忙信号
wire fifo_wr_en ; // FIFO写使能信号
wire fifo_rd_en ; // FIFO读使能信号
wire [7:0] fifo_wr_data ; // 写入到FIFO的数据
wire [7:0] fifo_rd_data ; // 从FIFO读出的数据
wire almost_full ; // FIFO将满信号
wire almost_empty ; // FIFO将空信号
wire full ; // FIFO满信号
wire empty ; // FIFO空信号
wire [7:0] wr_data_count ; // FIFO写时钟域的数据计数
wire [7:0] rd_data_count ; // FIFO读时钟域的数据计数
//*****************************************************
//** main code
//*****************************************************
//通过系统复位信号和时钟锁定信号来产生一个新的复位信号
assign rst_n = sys_rst_n & locked;
//例化PLL IP核
clk_wiz_0 clk_wiz_0 (
// Clock out ports
.clk_out1(clk_50m ), // output clk_out1
.clk_out2(clk_100m), // output clk_out2
// Status and control signals
.locked (locked ), // output locked
// Clock in ports
.clk_in1 (sys_clk ) // input clk_in1
);
//例化FIFO IP核
fifo_generator_0 fifo_generator_0 (
.rst (~rst_n ), // input wire rst
.wr_clk (clk_50m ), // input wire wr_clk
.rd_clk (clk_100m ), // input wire rd_clk
.wr_en (fifo_wr_en ), // input wire wr_en
.rd_en (fifo_rd_en ), // input wire rd_en
.din (fifo_wr_data ), // input wire [7 : 0] din
.dout (fifo_rd_data ), // output wire [7 : 0] dout
.almost_full (almost_full ), // output wire almost_full
.almost_empty (almost_empty ), // output wire almost_empty
.full (full ), // output wire full
.empty (empty ), // output wire empty
.wr_data_count (wr_data_count), // output wire [7 : 0] wr_data_count
.rd_data_count (rd_data_count), // output wire [7 : 0] rd_data_count
.wr_rst_busy (wr_rst_busy ), // output wire wr_rst_busy
.rd_rst_busy (rd_rst_busy ) // output wire rd_rst_busy
);
//例化写FIFO模块
fifo_wr u_fifo_wr (
.wr_clk (clk_50m ), // 写时钟
.rst_n (rst_n ), // 复位信号
.wr_rst_busy (wr_rst_busy ), // 写复位忙信号
.fifo_wr_en (fifo_wr_en ), // fifo写请求
.fifo_wr_data (fifo_wr_data), // 写入FIFO的数据
.empty (empty ), // fifo空信号
.almost_full (almost_full ) // fifo将满信号
);
//例化读FIFO模块
fifo_rd u_fifo_rd (
.rd_clk (clk_100m ), // 读时钟
.rst_n (rst_n ), // 复位信号
.rd_rst_busy (rd_rst_busy ), // 读复位忙信号
.fifo_rd_en (fifo_rd_en ), // fifo读请求
.fifo_rd_data (fifo_rd_data), // 从FIFO输出的数据
.almost_empty (almost_empty), // fifo将空信号
.full (full ) // fifo满信号
);
//写时钟域下ila
ila_0 u_ila_wr (
.clk (clk_50m ), // input wire clk
.probe0 (fifo_wr_en ), // input wire [0:0] probe0
.probe1 (fifo_wr_data ), // input wire [7:0] probe1
.probe2 (almost_full ), // input wire [0:0] probe2
.probe3 (full ), // input wire [0:0] probe3
.probe4 (wr_data_count) // input wire [7:0] probe4
);
//读时钟域下ila
ila_1 u_ila_rd (
.clk (clk_100m ), // input wire clk
.probe0 (fifo_rd_en ), // input wire [0:0] probe0
.probe1 (fifo_rd_data ), // input wire [7:0] probe1
.probe2 (almost_empty ), // input wire [0:0] probe2
.probe3 (empty ), // input wire [0:0] probe3
.probe4 (rd_data_count) // input wire [7:0] probe4
);
endmodule
FPGA激励文件(Testbench)编写,其用于验证FPGA设计功能正确性的测试脚本,通过模拟输入信号并检查输出响应,确保设计符合预期。
`timescale 1ns / 1ps //仿真单位/仿真精度
module tb_ip_fifo();
//parameter define
parameter CLK_PERIOD = 20; //时钟周期 20ns
//reg define
reg sys_clk;
reg sys_rst_n;
//信号初始化
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#200
sys_rst_n = 1'b1;
//模拟按下复位
#10000 ;
sys_rst_n = 0;
#160 ;
sys_rst_n = 1;
end
//产生时钟
always #(CLK_PERIOD/2) sys_clk = ~sys_clk;
ip_fifo u_ip_fifo (
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n)
);
endmodule
2302

被折叠的 条评论
为什么被折叠?



