什么是FIFO
FIFO (First In First Out) ,也就是先进先出。FPGA或者ASIC中使用到的FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存或者高速异步数据的交互。它与普通存储器的区别是没有外部读写地址线,这样使用起来相对简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。
FIFO作用:对于存储的数据,先存入FIFO的先被读出,可以确保数据的连续性
1,特征:数据产生速率>数据消耗速率
FIFO写入侧位宽 > FIFO读出侧位宽
2,特征:数据产生速率<数据消耗速率
FIFO写入侧位宽 < FIFO读出侧位宽
标准读模式同步FIFO
利用以下例子来熟悉FIFO使用:
打开vivado 创建工程
点击ok FIFO配置完成
然后编写FIFO测试文件
代码如下
`timescale 1ns / 1ps
module bram_sync_fifo_tb;
reg clk; // input wire clk
reg srst; // input wire srst
reg [7:0]din; // input wire [7 : 0] din
reg wr_en; // input wire wr_en
reg rd_en; // input wire rd_en
wire [7:0]dout; // output wire [7 : 0] dout
wire full; // output wire full
wire almost_full; // output wire almost_full
wire wr_ack; // output wire wr_ack
wire overflow; // output wire overflow
wire empty; // output wire empty
wire almost_empty; // output wire almost_empty
wire valid; // output wire valid
wire underflow; // output wire underflow
wire [7:0]data_count; // output wire [7 : 0] data_count
bram_sync_fifo your_instance_name (
.clk(clk), // input wire clk
.srst(srst), // input wire srst
.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
.almost_full(almost_full), // output wire almost_full
.wr_ack(wr_ack), // output wire wr_ack
.overflow(overflow), // output wire overflow
.empty(empty), // output wire empty
.almost_empty(almost_empty), // output wire almost_empty
.valid(valid), // output wire valid
.underflow(underflow), // output wire underflow
.data_count(data_count) // output wire [7 : 0] data_count
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
srst = 1'b1;
wr_en = 1'b0;
rd_en = 1'b0;
din = 8'hff;
#21;
srst = 1'b0;
//写操作 从0-255 共256数据
while(full == 1'b0)
begin
@(posedge clk);
#1;
wr_en = 1'b1;
din = din + 1'b1;
end
//再多写1个数据 观察overflow的变化
din = 8'hf0;
@(posedge clk);
#1;
wr_en = 1'b0;
#2000;
//读操作 读256次
while(empty == 1'b0)
begin
@(posedge clk)
#1;
rd_en = 1'b1;
end
//再多给一个读使能 看underflow的变化
@(posedge clk)
#1;
rd_en = 1'b0;
//复位
#200;
srst = 1'b1;
#21;
srst = 1'b0;
#2000;
$stop;
end
endmodule
仿真波形
写入时的波形
写满时的波形
读出时的波形
读完时的波形
FWFT读模式异步FIFO
点击ok FIFO配置完成
然后编写FWFT读模式异步FIFO测试文件
代码如下
`timescale 1ns / 1ps
module bram_asynv_fifo_tb;
reg rst; // input wire rst
reg wr_clk; // input wire wr_clk
reg rd_clk; // input wire rd_clk
reg [7:0]din; // input wire [7 : 0] din
reg wr_en; // input wire wr_en
reg rd_en; // input wire rd_en
wire [15:0]dout; // output wire [15 : 0] dout
wire full; // output wire full
wire almost_full; // output wire almost_full
wire wr_ack; // output wire wr_ack
wire overflow; // output wire overflow
wire empty; // output wire empty
wire almost_empty; // output wire almost_empty
wire valid; // output wire valid
wire underflow; // output wire underflow
wire [7:0]rd_data_count; // output wire [7 : 0] rd_data_count
wire [8:0]wr_data_count; // output wire [8 : 0] wr_data_count
wire wr_rst_busy; // output wire wr_rst_busy
wire rd_rst_busy; // output wire rd_rst_busy
bram_async_fifo your_instance_name (
.rst(rst), // input wire rst
.wr_clk(wr_clk), // input wire wr_clk
.rd_clk(rd_clk), // input wire rd_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 [15 : 0] dout
.full(full), // output wire full
.almost_full(almost_full), // output wire almost_full
.wr_ack(wr_ack), // output wire wr_ack
.overflow(overflow), // output wire overflow
.empty(empty), // output wire empty
.almost_empty(almost_empty), // output wire almost_empty
.valid(valid), // output wire valid
.underflow(underflow), // output wire underflow
.rd_data_count(rd_data_count), // output wire [7 : 0] rd_data_count
.wr_data_count(wr_data_count), // output wire [8 : 0] wr_data_count
.wr_rst_busy(wr_rst_busy), // output wire wr_rst_busy
.rd_rst_busy(rd_rst_busy) // output wire rd_rst_busy
);
initial wr_clk = 1;
always #10 wr_clk = ~wr_clk;
initial rd_clk = 1;
always #5 rd_clk = ~rd_clk;
reg [5:0]cnt;
always@(posedge rd_clk or posedge rst)
if(rst)
#1 cnt <= 1'b0;
else if(cnt >= 6'd31)
#1 cnt <= 1'b0;
else if(rd_en)
#1 cnt <= cnt + 1'b1;
always@(posedge rd_clk or posedge rst)
if(rst)
#1 rd_en <= 1'b0;
else if(rd_data_count > 9'd31)
#1 rd_en <= 1'b1;
else if(cnt >= 6'd31)
#1 rd_en <= 1'b0;
initial begin
rst = 1'b1;
wr_en = 1'b0;
din = 8'hff;
#(20*3+1);
rst = 1'b0;
//写数据
@(negedge wr_rst_busy);
wait(rd_rst_busy == 1'b0);
repeat(257)begin
@(posedge wr_clk)
#1;
wr_en = 1'b1;
din = din +1'b1;
end
wr_en = 1'b0;
#1000;
$stop;
end
endmodule
仿真波形
写入时的波形
wr_en拉高 开始写数据
问题1解答:
这一块是由于仿真器设置导致的,在设置中语言选择的是Verilog和VHDL混合模式,所以会导致这个现象,但不建议直接改为Verilog语言模式,因为在一些IP核的底层是由Verilog和VHDL写好的,如果在这里改过来,虽然这一块波形是好的,但有可能在其他地方出现问题。
问题2解答:
看手册可以得知 空FIFO下 wr_data_count的值和 写深度与读深度的比例有关
在前面FIFO设置中,写深度与读深度比例是(256:128)即2:1
看手册可知 wr_data_count的值为4。
问题3解答:
查看手册:
可知:在设置more accurate data counts后,当FIFO为空或几乎空时,写计数会多计数两个读数据量。当empty取消断言后,在almost_empty取消断言后的几个写时钟周期内,写计数会存在一个过渡期,来让自己的值正确。
为此会出现下列情况:
1·虽然没有读操作,但是写计数会出现递减
2.由于写操作的持续,写计数的值并不会按照预期增加
读出时波形
rd_en拉高 开始读数据