【不止IP】First In First Out FIFO核的使用

发布时间 2023-07-16 14:57:40作者: AnchorX

一、Vivado FIFO IP核的使用方法和注意事项

1、fifo核的两种工作模式:standard fifo、first word fall through,它们的功能和操作上有一些区别。

(1)Standard FIFO(标准FIFO):

  在标准FIFO中,数据输入(写入)和数据输出(读取)是独立的操作。写入和读取操作是异步进行的,即它们可以在任何时刻进行。
  在读取操作之前,FIFO必须至少有一个完整的数据字来供读取。否则,读取操作将被阻塞直到有数据可用。
  标准FIFO可以支持数据的双向传输,即数据可以同时从FIFO读取和写入。
  当选择Standard模式的时候,在读使能信号有效的下一个周期才能读出第一个有效的数据。

(2)First Word Through (FWT) FIFO(首字通过FIFO):

  在FWT FIFO中,写入操作和读取操作之间存在一种特殊的关系。当第一个数据字从写入端输入FIFO时,它会立即通过FIFO进行读取。
  在FWT FIFO中,读取操作是优先于写入操作的。当有数据可用时,读取操作优先获得数据,而不需要等待FIFO缓冲区填充完整。
  FWT FIFO也支持数据的双向传输,即可以在读取操作和写入操作同时进行。
  当选择FWT模式的时候,在读使能信号有效的第一个周期就能能读出第一个有效的数据; 这是因为在这种模式下,FIFO提前把数据已经准备到了数据输出总线上,等待都使能有效就输出到数据输出端口(组合逻辑),但在这种模式下,valid信号将会在复位后就保持有效,这一点要特别注意;

总结起来,标准FIFO和首字通过FIFO之间的主要区别在于数据的读取时机和操作的优先级。标准FIFO对读取和写入操作是异步的,而FWT FIFO在写入第一个数据时即可立即读取,读取操作具有优先级。

2、在配置IP核时,一般不需要”Enable safety circuit“。

3、在配置IP核时,一般不需要设置all most full/all most empty,使用半空半满的方式读写fifo。

 

 

二、FIFO的设计与开发技巧

1、FIFO的读写方式

正如前面提到的,一般不需要设置all most full/all most empty,使用半空半满的方式读写fifo。除了这种方法之外,还有非空即读的方式,乒乓FIFO(double buffer)的方式等。

(1)半空半满读写

用fifo的计数器控制,写半满时开始读,读半空时开始写,其它时间既读又写。

// 利用状态机对fifo进行半空半满的读写
module fifo_example;

// 三段式状态机
always @(posedge clk100m) begin
    if(Rst == 1'b1)
        curr_state <= #TCQ_STA_IDLE;
    else
        curr_state <= #TCQ_next_state;
end

// Calculate the next state
always @ ( * ) begin
    case(curr_state)
        STA_IDLE:begin
            if (full == 1'b0) // fifo IP核的性质,初始化状态full为1
                next_state <= STA_WRITING;
            else
                next_state <= STA_IDLE;
        end

        STA_WRITING:begin
            if (wr_data_count > 8'd200) // 已写入200个数
                next_state <= STA_READING;
            else
                next_state <= STA_WRITING;
        end
        
        STA_READING:begin
            if (rd_data_count < 6'd20)  // 可读取的数小于20
                next_state <= STA_WRITING;
            else
                next_state <= STA_READING;
        end

        default :begin
            next_state <= STA_IDLE;
        end
    endcase
end

// state output
always @(posedge clk100m) begin
    if (Rst == 1'b1) begin
        wr_en        <= #TCQ 1'b0;
        rd_en        <= #TCQ 1'b0;
        din          <= #TCQ 8'd0;
        din_r        <= #TCQ 8'd0;
    end
    else case (curr_state)
        STA_IDLE: begin
            wr_en        <= #TCQ 1'b0;
            rd_en        <= #TCQ 1'b0;
            din          <= #TCQ 8'd0;
            din_r        <= #TCQ 8'd0;
        end

        STA_WRITING:begin
            wr_en        <= #TCQ 1'b1;
            rd_en        <= #TCQ 1'b0;
            if (din == 8'd255)
                din      <= #TCQ 8'd0;
            else 
                din      <= #TCQ din + 8'd1;
            din_r        <= #TCQ din;
        end

        STA_READING:begin
            wr_en        <= #TCQ 1'b0;
            rd_en        <= #TCQ 1'b1;
        end
    endcase
end

endmodule //fifo_example

 (2)非空即读,即当empty拉低后就读出,如果只用一个异步时钟FIFO也能实现这个效果,但有可能导致数据断流,对于要保证fifo无缝衔接的设计,这种方法不可靠,所以推荐使用乒乓fifo的方式读写。需要乒乓fifo的场景如下:①异步fifo跨时钟域;②效率第一,非空即读;③要求不间断写(存),不间断读;④数据流的接口。

  要注意异步fifo的读取速度要比写入速度快,否则就要添加更多的fifo来进行乒乓操作。

 

2、fifo的empty信号和full信号

  对于fifo的empty信号,如果rden没有赋值(红色,悬空),那么empty信号在拉低后也会进入红色,因为“不知道有没有被读出”。

  fifo的empty没有正常拉低时(一般发生于仿真过程中),可能是因为复位信号太短,或者复位后间隔时间不够长就写入数据;适当延长复位时间和工作到来时间。

  初始化的fifo full信号是拉高的,写入数据要等full拉低后。

 

3、fifo的特殊应用场景

  (1)以UDP转SDRAM的场景为例,PHY芯片过来的时钟、数据、有效信号等引脚灌入fifo进行隔离,把两个时钟域隔开,避免用该时钟直接作为UDP模块的时钟,否则会因为该时钟扇出太大,导致程序不稳定,例如改了两行代码程序就不能正常运行了。用fifo进行两个时钟域的隔离还有一个好处,无论前端时钟有多快都无所谓,只当隔离fifo满了以后再读出新的时钟,即“网速自适应”。