FPGA(四)FIFO
4.1 IP核使用
4.1.1 Basic配置

主要是FIFO Implementation选项,总体上分为同步FIFO和异步FIFO,下面的表格列出了使用的资源以及支持的特性。
Non-symmetric aspect ratios:支持读写宽度不同。

如果选择异步FIFO还会有Synchronization Stages配置项,表示写时钟域的数据经过多少(读时钟)周期同步到读时钟域。
4.1.2 Port配置

Read Mode
- Standard FIFO:默认需要读使能才有输出数据。
- First Word Fall Through:只要FIFO不为空,第一个写入的数据会自动(无需读使能)出现在输出端口,后续的数据才需要读使能,如下图所示。

Width和Depth:按需填写。注意异步FIFO的实际深度会比配置的少。
Output Registers:同RAM IP核。
Embedded Registers表示使用BRAM资源自带的寄存器;Fabric Registers表示使用通用逻辑资源。Initailization
- Synchronous Reset:同步复位。
- Asynchronous
Reset:异步复位。会多出
rd_rst_busy和wr_rst_busy信号,表示读/写复位忙(FIFO内部还在处理复位过程),为高电平时应当禁止读/写操作。异步FIFO只能异步复位。 - Full Flags Reset
Value:控制复位后
full信号的初始状态。同步复位只能为0;异步复位可以为0或1,为1表示复位后FIFO满,需要先读才能写。 - Dout Reset Value:复位时默认的读端口输出。
4.1.3 Status Flags配置

Almost Full Flag:再写一个数据就满了。
Almost Empty Flag:再读一个数据就空了。
Write Acknowledge:写应答。写入操作成功完成的下一个周期(即
wr_en的下一个周期)拉高。Overflow:写溢出。向已满FIFO写入数据时拉高。
Valid Flag:读有效。读端口
dout有效时拉高。Underflow Flag:读下溢出。从空的FIFO中读数据时拉高。
Programmable Flags:可以自定义设置full或empty的阈值。可以是单一阈值(Single)或一个阈值区间(Multiple)。也可以选择该阈值由外部传入。
Full Threshold Assert Value表示数据个数达到该值时自定义full信号拉高;Full Threshold Negate Value表示个数小于该值时才拉低,使用该信号需要选择Multiple,起到防止边界抖动的作用。
4.1.4 Data Counts配置

- Data Count:指示FIFO中有多少数据。
上面的图片是同步FIFO,只能设置
Data Count Width。下面的读写计数异步FIFO才支持。
4.2 同步FIFO测试
设置FIFO的深度为16,先写入18个数据,会有2个数据overflow,然后一直读取FIFO。

`timescale 1ns / 1ps
module syn_tb();
logic clk;
logic rst_n;
logic [7:0] din;
logic wr_en;
logic rd_en;
wire [7:0] dout;
wire full;
wire almost_full;
wire wr_ack;
wire overflow;
wire empty;
wire almost_empty;
wire valid;
wire underflow;
wire [3:0] data_count;
always #5 clk = ~clk;
initial begin
clk = 1;
rst_n = 0;
#30
rst_n = 1;
end
logic [4:0] cnt;
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
cnt <= 0;
din <= 0;
wr_en <= 0;
end
else if (cnt < 18) begin
cnt <= cnt + 1;
wr_en <= 1;
din <= din + 1;
end
else begin
wr_en <= 0;
end
end
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
rd_en <= 0;
end
else if (cnt == 18) begin
rd_en <= 1;
end
end
fifo_generator_0 fifo_u (
.clk(clk), // input wire clk
.srst(!rst_n), // 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 [3 : 0] data_count
);
endmodule4.3 异步FIFO测试
写位宽16,深度15;读位宽8,深度30。写频率50MHz,读频率100MHz。
Synchronization Stages = 2时波形如下:
- 两个标记(读写count)之间隔了
Synchronization Stages + 2 = 4个读时钟周期。(为什么+2,网上说是因为Empty Threshold Assert Value设置的2) - 写深度为15,所以有1周期的overflow。


Synchronization Stages = 3时波形如下,两个标记之间间隔3 + 2 = 5个读时钟周期。

`timescale 1ns / 1ps
module asyn_tb();
logic rst_n;
logic wr_clk;
logic rd_clk;
logic [15:0]din;
logic wr_en;
logic rd_en;
wire [7:0] dout;
wire full;
wire almost_full;
wire wr_ack;
wire overflow;
wire empty;
wire almost_empty;
wire valid;
wire underflow;
wire [4:0] rd_data_count;
wire [3:0] wr_data_count;
wire wr_rst_busy;
wire rd_rst_busy;
always #10 wr_clk = ~wr_clk;
always #5 rd_clk = ~rd_clk;
initial begin
wr_clk = 1;
rd_clk = 1;
rst_n = 0;
#30 rst_n = 1;
end
logic flag; // control when to write
logic [4:0] cnt;
initial begin
flag = 0;
#350 flag = 1;
#20 flag = 0;
end
always_ff @(posedge wr_clk, negedge rst_n) begin
if (!rst_n) begin
cnt <= 0;
din <= 0;
wr_en <= 0;
end
else if(flag == 1 || (1 <= cnt && cnt < 16)) begin
cnt <= cnt + 1;
wr_en <= 1;
din <= din + 1;
end
else begin
wr_en <= 0;
end
end
always_ff @(posedge rd_clk, negedge rst_n) begin
if(!rst_n) begin
rd_en <= 0;
end
else if(cnt == 16) begin
rd_en <= 1;
end
end
fifo_generator_1 fifo_u (
.rst(!rst_n), // input wire rst
.wr_clk(wr_clk), // input wire wr_clk
.rd_clk(rd_clk), // input wire rd_clk
.din(din), // input wire [15 : 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
.rd_data_count(rd_data_count), // output wire [4 : 0] rd_data_count
.wr_data_count(wr_data_count), // output wire [3 : 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
);
endmodule