FIFO
FIFO(First In First Out,即先入先出),是一种数据缓冲器,用来实现数据先入先出的读写方式。FIFO 根据读写时钟是否相同,分为 SCFIFO(single-clock FIFO)和 DCFIFO(duabl-clock FIFO),SCFIFO 的读写为同一时钟,应用在同步时钟系统中;DCFIFO 的读写时钟不同,应用在异步时钟系统中。
SCFIFO
单时钟FIFO常用于片内数据交互,例如,在FPGA的控制下从外部传感器读取到的一连串传感器数据,首先被写入FIFO中,然后再以UART串口的数据发送速率将数据依次发送出去。由于传感器的单次读取数据可能很快,但并不是时刻都需要采集数据。
**full:**写满标志位,有效表示 FIFO 已经存储满了,此时应该通过该信号控制写请求信号(也称为写使能 信号),禁止再往 FIFO 中写入数据,防止数据溢出丢失。当写入数据量达到 FIFO 设置的最大空间时, 时钟上升沿写入最后一个数据同时 full 拉高;读取数据时随时钟上升沿触发同时拉低。
**empty:**读空标志位,有效表示 FIFO 中已经没有数据了,此时应该通过该信号控制读请求信号(也称为读使能信号),禁止 FIFO 继续再读出数据,否则读出的将是无效数据。与 full 相反,写入数据同时拉低;读到最后一个数据同时拉高。
**usedw:**显示当前 FIFO 中已存数据个数,与写入数据的个数是同步的,即写第一个数据时就置 1,空或满时值为 0(满是因为寄存器溢出)。
**almost full:**几乎满标志信号,我们可以控制 FIFO 快要被写满的时候和 full 信号的作用一样。
almost empty:几乎空标志信号,我们可以控制 FIFO 快要被读空的时候和 empty 信号的作用一样。
Asynchronous clear:异步复位信号,用于清空 FIFO。
**Synchronous clear:**同步复位信号,用于清空 FIFO。
FIFO读数据两种模式,上图序号1普通同步FIFO模式,当有效的时钟和读请求信号到来时的下一个周期才会从FIFO输出数据。换个说法就是数据滞后读请求一个时钟周期。第2个则是信号先出,也就是数据和读请求信号同时输出。
FIFO的优化及电路保护。yes是获取最大的速度,但是会占用更多的资源。no是使用更少的资源,但是速度会有所下降。蓝色框选项表示是否禁止上溢检测和下溢检测的保护电路(上溢检测保护电路主要是用于在 FIFO 存储满时禁止继续写数据;下溢检测保护电路主要是用于 FIFO 被读空时,禁止继续读数据)。默认不勾选是使用禁止保护
DCFIFO
双时钟FIFO的一个典型应用就是异步数据的收发。实现跨时钟域数据处理
所谓异步数据是指数据的发送端和接收端分别同步与不同的时钟域,使用双时钟FIFO的独立的读写时钟结构,能够将不同时钟域中的数据同步到所需的时钟域系统中。例如,在一个高速数据采集系统中,实现将高速 ADC采集的数据通过千兆以太网发送到PC机。ADC 的采样时钟(CLK1)由外部专用锁相环芯片产生,则高速ADC采样得到的数据就是同步于该时钟信号的,在 FPGA内部,如果 FPGA工作时钟(CLK2)是由独立的时钟芯片加片上锁相环产生的,则CLK1和 CLK2就是两个不同时钟域的时钟,他们的频率和相位没有必然的联系,假如CLK1为65M,CLK2为125M,那么就不能使用125M的数据来直接采集65M速率的数据,因为两者数据速率不匹配,在采集过程中会出现包括亚稳态问题在内的一系列问题,所以这里就可以使用一个具备双时钟结构的FlIFO来进行异步数据的收发。
异步FIFO可配置输入输出不同的位宽。满足输入数据位宽乘以FIFO深度等于输出位宽乘以输出数据深度:8256=16128。
异步FIFO优化选择。
第一个表示生成的IP有最低的延迟,但是需要一个同步时钟,该选项使用一个同步阶段,没有亚稳态保护,适用于同步时钟。面积最小,提供良好的性能。
第二个有两个同步阶段,具有良好的亚稳态保护。面积中等,提供良好的性能。
第三个选项使用三个或更多的同步阶段,具有最好的亚稳态保护。它的面积是最大的,但给出了最好的性能。且为异步时钟。
DCFIFO读写与时序仿真
直接调用厂家的fifo IP
module fifo
(
input wire wrclk , //同步于FIFO写数据的时钟50MHz
input wire [7:0] pi_data , //输入顶层模块的数据,要写入到FIFO中,的数据同步于wrclk时钟
input wire wrreq , //输入数据有效标志信号,也作为FIFO的,写请求信号,同步于wrclk时钟
input wire rdclk , //同步于FIFO读数据的时钟25MHz
input wire rdreq , //FIFO读请求信号,同步于rdclk时钟
output wire wrempty , //FIFO写端口空标志信号,高有效,同步于wrclk时钟
output wire wrfull , //FIFO写端口满标志信号,高有效,同步于wrclk时钟
output wire [8:0] wrusedw , //FIFO写端口中存在的数据个数,同步于wrclk时钟
output wire [7:0] po_data , //FIFO读出的数据,同步于rdclk时钟
output wire rdempty , //FIFO读端口空标志信号,高有效,同步于rdclk时钟
output wire rdfull , //FIFO读端口满标志信号,高有效,同步于rdclk时钟
output wire [8:0] rdusedw //FIFO读端口中存在的数据个数,同步于rdclk时钟
);
dcfifo_256x8to128x16 dcfifo_256x8to128x16_inst
(
.data (pi_data),
.rdclk (rdclk ),
.rdreq (rdreq ),
.wrclk (wrclk ),
.wrreq (wrreq),
.q (po_data),
.rdempty(rdempty),
.rdfull (rdfull ),
.rdusedw(rdusedw),
.wrempty(wrempty),
.wrfull (wrfull ),
.wrusedw(wrusedw)
);
endmodule
testbench
`timescale 1ns/1ns
module tb_fifo();
reg wrclk ;
reg [7:0] pi_data ;
reg wrreq ;
reg rdclk ;
reg rdreq ;
reg sys_rst_n ;
wire wrempty ;
wire wrfull ;
wire [8:0] wrusedw ;
wire [7:0] po_data ;
wire rdempty ;
wire rdfull ;
wire [8:0] rdusedw ;
//初始化时钟、复位
initial begin
wrclk = 1'b1;
rdclk = 1'b1;
sys_rst_n <= 1'b0;
#100;
sys_rst_n <= 1'b1;
end
always #7 wrclk = ~wrclk;
always #20 rdclk = ~rdclk;
always@(posedge wrclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wrreq <= 1'b0;
else if(wrempty)
wrreq <= 1'b1;
else if(wrfull)
wrreq <= 1'b0;
//pi_data:输入顶层模块的数据,要写入到FIFO中的数据
always@(posedge wrclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_data <= 8'b0;
////pi_data的值为0~255依次循环
else if(pi_data == 8'd255)
pi_data <= 8'b0;
else if(wrreq == 1'b1)
pi_data <= pi_data + 1'b1;
//rdreq:FIFO读请求信号同步于rdclk时钟
always@(posedge rdclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rdreq <= 1'b0;
else if(rdfull == 1'b1)
rdreq <= 1'b1;
else if(rdempty == 1'b1)
rdreq <= 1'b0;
fifo fifo_inst(
//FIFO写端
.wrclk (wrclk ),
.pi_data(pi_data),
.wrreq(wrreq) ,
//FIFO读端
.rdclk (rdclk ),
.rdreq (rdreq ),
//FIFO写端
.wrempty(wrempty),
.wrfull (wrfull ),
.wrusedw(wrusedw),
//FIFO读端
.po_data(po_data),
.rdempty(rdempty),
.rdfull (rdfull ),
.rdusedw(rdusedw)
);
endmodule
当wrempty有效时拉高wrreq,此时写计数是在写请求有效的下一个周期才开始计数。可以勾选Add an extra MSB to usedw port(s),这样计数就不会计数到255时归零。
当写满的时,写满信号拉高,注意到写满信号实在wrusedw计数到255就拉高了,而且255还滞后了一个时钟周期,是因为当写入第一数据的,下一个周期wrusedw才开始计数,滞后了一个周期。而且计数从1开始,所以写满整个fifo要计数到256。当写满信号拉高时,在读时钟上升沿检测到写满信号为高时的下一个上升沿到来读满信号也会被拉高。写满信号被拉高,fifo写满,这是从fifo读取数据,如果以写满信号位标志信号,则要把写满信号同步到读时钟下,上诉用读满为标志信号,不用同步。当读空信号被写时钟检测到为高电平的下一个周期写空也会被拉高。当读空时,读请求拉低。
有上两张图可以知道,写请求信号总共占用了257个周期,因为写请求时在写满的时候才被拉低,所以在写入第256个数据写满拉高,但是被检查到占了一个时钟后期,所以写请求的对应的最后一个数据不会被写入fifo。这样子就会引出一个问题,如果想要固定bit的数据完整的写入fifo中,那这样子就得改进fifo。引入fifo最小深度计算。