FPGA自学——FIFO缓存器

FPGA自学------FIFO缓存器

文章目录

一、介绍

定义: FIFO(First In First Out),也就是先进先出。FPGA或者ASIC中使用到的FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存或者高速异步数据的交互。

与普通存储器的区别: 没有外部读写地址线

优点: 使用起来相对简单

缺点: 只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

作用: 对于存储的数据,先进先出,可以确保数据的连续性

应用场景1:


应用场景2:

**应用场景3:**独立时钟

二、调用FIFIO的IP核

一)基本配置信息

选择FIFO的IP核进行配置

1.basic

2.native ports

标准模式时序图

FT模式时序图

3.status flags

4.data counts

由于对于FT模式下的FIFO是不准确的,看一下下列示例如何调整





二)创建同步标准读的块RAM的FIFO

1.拟设计目标
2.fifo设置




3.仿真文件
复制代码
`timescale 1ns / 1ps
module bram_sync_fifo_test();

    reg         clk           ;
    reg         reset         ;
    reg  [7:0]  din           ;
    reg         wr_en         ;
    reg         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  [7:0]  data_count    ;


bram_sync_fifo bram_sync_fifo (
  .clk              (clk),                    // input wire clk
  .srst             (reset),                  // 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
        reset = 1'b1;
        wr_en = 1'b0;
        rd_en = 1'b0;
        din = 8'hff;
        #21;
        reset = 1'b0;
        
        //写操作 从0到255,共256个数据
        while(full == 1'b0)
        begin
            @(posedge clk);
                #1;
                //由于这个延时1ns,与时钟错开,所以在下一个时钟才会写入fifo,din的原始值为ff,+1之后变为00
                wr_en = 1'b1;
                din   = din + 1'b1;
        end
        
        //再多写一个数据,看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;
        reset = 1'b1;
        #21;
        reset = 1'b0;
        #2000;
        $stop;
        
    end


endmodule
4.仿真波形分析




三)创建异步FT读的块RAM的FIFO

1.拟设计目标

摄像头图像采集显示,示波器,电压表

2.fifo配置



3.仿真文件
复制代码
`timescale 1ns / 1ps

module bram_async_fifo_test();

reg             reset          ;
reg             wr_clk         ;
reg             rd_clk         ;
reg   [7:0]     din            ;
reg             wr_en          ;
reg             rd_en          ;
wire  [15:0]    dout           ;
wire            full           ;
wire            almost_full    ;
wire            wr_ack         ;
wire            overflow       ;
wire            empty          ;
wire            almost_empty   ;
wire            valid          ;
wire            underflow      ;
wire  [7:0]     rd_data_count  ;
wire  [8:0]     wr_data_count  ;
wire            wr_rst_busy    ;
wire            rd_rst_busy    ;






 bram_async_fifo bram_async_fifo (
  .rst                      (reset),                      // 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
);
    //读侧是100M的时钟
    initial wr_clk = 1;
    always #10 wr_clk = !wr_clk;
    //写侧50M的时钟
    initial rd_clk = 1;
    always #5 rd_clk = !rd_clk;
    
    reg [5:0] cnt;
    
    
    //cnt计数
    always@(posedge rd_clk or posedge reset)
        if(reset)
            cnt <= 6'b0;
        else if(cnt >= 6'd31)
            cnt <= 6'd0;
        else if(rd_en)
           #1  cnt <= cnt + 1;
    
    //读使能信号
    always@(posedge rd_clk or posedge reset)
        if(reset)begin
            #1 rd_en <= 1'b0;
        end
        else if(rd_data_count > 9'd31)//当可以读的数据大于32了,将读使能打开
            #1 rd_en <= 1'b1;
        else if(cnt >= 6'd31)//经过了32个读时钟周期后拉低
            #1 rd_en <= 1'b0;
    
    
    initial begin
        reset = 1'b1;
        din   = 8'hff;
        wr_en = 1'b0;
        rd_en = 1'b0;
        #(20*3+1)
        //异步复位,至少持续3个慢的时钟周期
        reset = 1'b0;
    
    //写数据
        @(negedge wr_rst_busy)
        wait(rd_rst_busy == 1'b0)
            repeat(257)begin//257
            @(posedge wr_clk)
            #1;
            wr_en = 1'b1;
            din <= din + 1'b1;
            end
         
         wr_en = 1'b0;
         #1000;
         $stop;
    
    end
    
endmodule
4.仿真波形分析