前言

FIFO(First Input First Output),即先进先出队列。在FPGA中的作用是为了实现两个模块之间通信的速率不统一的问题,起到缓存数据的作用。本教程为入门教程,将从最简单的FIFO开始学习。

一、ISE中FIFO的IP核配置

SEM IP核端口信号说明_数据

SEM IP核端口信号说明_tcp/ip_02

接下来保持默认设置,然后生成IP文件。打开文件模板得到例化端口

ip_fifo your_instance_name (
  .clk(clk), // input clk
  .rst(rst), // input rst
  .din(din), // input [7 : 0] din
  .wr_en(wr_en), // input wr_en
  .rd_en(rd_en), // input rd_en
  .dout(dout), // output [7 : 0] dout
  .full(full), // output full
  .empty(empty) // output empty
);
特别注意,FIFO的IP核文件的复位使能和PLL(分频器)IP核 的RST是一样的,都是低电平使能模块。

二、FIFO功能验证

实验方法:往FIFO中装载数据并读取数据,查看各个端口的信号变化。

实验1

SEM IP核端口信号说明_网络协议_03


思想方法:

设置一个位宽为10的计数变量cnt,复位之前cnt为0,当复位结束以后开始从【0:1023】循环计数,在【0:1023】之间选取两个时钟段,在时钟段1,往FIFO中装载数据,当在时间段2,从FIFO中读取数据。

代码

`timescale 1ns / 1ps 

module fifo_tst(
           input sys_clk,
           input sys_rst_n,
           output [ 7: 0 ] fifo_rd_data,
           output fifio_empty,
           output fifo_full,
           output reg fifo_rdrdy
       );
reg [ 7: 0 ] fifio_data_in;
reg wr_en; //fifo的写使能
reg rd_en; //fifo的读使能
reg [ 9: 0 ] cnt;


/* 时间戳产生 */
always@( posedge sys_clk or negedge sys_rst_n )
    begin
        if ( !sys_rst_n )
            cnt <= 0;
        else
            cnt <= cnt + 1;
    end

/* FIFO的写入与读出操作 */
always@( posedge sys_clk or negedge sys_rst_n )
    begin
        if ( !sys_rst_n )
            begin
                wr_en <= 0;
                rd_en <= 0;
                fifio_data_in <= 0;

            end

        /* 数据写入 */
        else if ( ( cnt > 50 ) && ( cnt < 116 ) )
            begin
                wr_en <= 1;
                rd_en <= 0;
                fifio_data_in <= cnt + 10;

            end

        /* 数据读出 */
        else if ( ( cnt > 150 ) && ( cnt < 216 ) )
            begin
                wr_en <= 0;
                rd_en <= 1;
                fifio_data_in <= 0;
            end

        else
            begin
                wr_en <= 0;
                rd_en <= 0;
                fifio_data_in <= 0;
            end

    end


/* fifo_rdrdy延后rd_en一个时钟周期,代表读出的数据有效 */
always@( posedge sys_clk or negedge sys_rst_n )
    begin
        if ( !sys_rst_n )
            fifo_rdrdy <= 0;
        else
            fifo_rdrdy <= rd_en;


    end

ip_fifo my_fifo (
            .clk( sys_clk ),      // input clk
            .rst( ~sys_rst_n ),      // input rst
            .din( fifio_data_in ),      // input [7 : 0] din
            .wr_en( wr_en ),      // input wr_en
            .rd_en( rd_en ),      // input rd_en
            .dout( fifo_rd_data ),      // output [7 : 0] dout
            .full( fifo_full ),      // output full
            .empty( fifio_empty )      // output empty
        );

endmodule

实验1仿真

把FIFO的输入与输出的完整的一个周期分成了4个时间段。

SEM IP核端口信号说明_数据_04


时间段1:

SEM IP核端口信号说明_数据_05

时间段2:

SEM IP核端口信号说明_网络协议_06

时间段3:

SEM IP核端口信号说明_网络协议_07

时间段4:

SEM IP核端口信号说明_fpga开发_08

从上边的实验数据中可以得到以下的结论:

  1. 从阶段1和阶段2的实验数据对比可以发现,FIFO中存放的第一个数据是WR_EN由低变高以后第一个时钟周期的数据。 fifo的empty信号在FIFO为空的时候会被置为高电平,当写入第一个数据后的时钟上升沿,empty信号会被拉低同时发生。
  2. SEM IP核端口信号说明_tcp/ip_09

  3. 最后一个数据输入以后,full信号拉高,FIFO记满数据。
  4. SEM IP核端口信号说明_SEM IP核端口信号说明_10

  5. 3.读第一个数据,rd_en拉高后的第二的时钟,数据输出和full拉低同时发生。
  6. SEM IP核端口信号说明_tcp/ip_11

  7. 最后一个数据读出和empty信号由低变高同时发生,但是最后一个数据的re_en是低电平,所以充分验证了读出的数据要滞后re_en一个时钟周期。
  8. SEM IP核端口信号说明_数据_12


  9. SEM IP核端口信号说明_SEM IP核端口信号说明_13

附:

单时钟的FIFIO实现同时读取数据与接收数据。

SEM IP核端口信号说明_网络协议_14