FIFO IP的使用

1 FIFO的基本原理

1.0 IP核简介

  FIFO 的英文全称是 First In First Out,即先进先出。FPGA 使用的 FIFO 一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存,或者高速异步数据的交互也即所谓的跨时钟域信号传递。
  它与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址。

1.1 结构示意图

在这里插入图片描述
 上图的中间部分就是fifo IP核。
写数据时:写模块给fifo一个写使能,如果fifo的数据没有写满,写模块就往fifo里面写数据。所以wr_en和din对于fifo而言,是输入。
读数据时:读模块给fifo一个读使能,如果fifo的数据没有读空,读模块就继续从fifo读数据。所以rd_en对于fifo而言,是输入。dout是输出。

常见参数:
FIFO 的宽度:FIFO 一次读写操作的数据位 N。
FIFO 的深度:FIFO 可以存储多少个宽度为 N 位的数据。
将空标志:almost_empty。FIFO 即将被读空。
空标志:empty。FIFO 已空时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的读操作继续从 FIFO
    中读出数据而造成无效数据的读出。
将满标志:almost_full。FIFO 即将被写满。
满标志:full。FIFO 已满或将要写满时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的写操作继
    续向 FIFO 中写数据而造成溢出。
读时钟:读 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
写时钟:写 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。

1.2 跨时钟域和位宽不同步

跨时钟域:存储器A的时钟(100MHz)和存储器B的时钟(75MHz)不同,此时先将存储器A的数据写到FIFO中,然后存储器B从FIFO中读取数据。
带宽不同步:存储器A的数据宽度为[7:0],一共8位。存储器B的数据宽度为[11:0],一共12位。此时存储器A的数据不能直接写到存储器B。此时先将存储器A的数据写到FIFO中,然后存储器B从FIFO中读取数据。如图2.2所示。
  对于这两种情况,我们就用到FIFO了。

在这里插入图片描述

1.3 先入先出

  写数据时,第一个数据D0占据地址1,第二个数据D1占据地址2,… ,第N个数据D11占据地址N(N=12)。写数据需要的输入信号有:①写时钟:wr_clk;②写使能:wr_req;③写数据:wr_data,D0_D11。
  读数据时,首先读地址1的数据D0,此时D1进入地址1,其余的数据的地址也减1。第二次读取数据时,首先读地址1的数据D1,其余的数据的地址减1。读数据需要的信号有:①读时钟:rd_clk;②读使能:rd_req。输出为读出的数据: data_out。器示意图如下图所示。
在这里插入图片描述

1.4 同步FIFO和异步FIFO

如果读时钟和写时钟的频率相同,则为同步FIFO。如果读时钟和写时钟的频率不同,则为异步FIFO。

1.5 具体实现

子模块功能:
写模块 :产生原始数据
读模块:把数据从fifo读出来
FIFO :数据缓存区
在这里插入图片描述

在这里插入图片描述
写FIFO模块:它会对FIFO IP核进行写入数据,FIFO IP核反馈一个将空信号和将满信号给写FIFO模块。
读FIFO模块:发送读请求给FIFO IP核,FIFO IP核会将数据读出来。

2 新建工程

3 添加fifo IP核和ila IP核

3.1 添加fifo IP核,并设置参数。

参数设置如下:

(1)“Basic”选项
Interface Type”选项用于选择 FIFO 接口的类型,这里我们选择默认的“Native”,即传统意义上的 FIFO接口。
fifo Implementation”选项用于选择我们想要实现的是同步 FIFO 还是异步 FIFO 以及使用哪种资源实现 FIFO.

在这里插入图片描述

(2)Native Ports选项
Read Mode”选项用于设置读 FIFO时的读模式,这里我们选择默认的Standard FIFO
Data Port Parameters”一栏用于设置读写端口的数据总线的宽度以及 FIFO 的深度,写宽度“Write Width”我们设置为16位,写深度“Write Depth”我们设置为 1024.

在这里插入图片描述

(3)“Status Flags”选项卡,用于设置用户自定义接口或者用于设定专用的输入口。这里我们使用“即将写满”和“即将读空”这两个信号,所以我们把它们勾选上,其他保持默认即可,如下图所示。
在这里插入图片描述

在这里插入图片描述

4 程序

在这里插入图片描述

4.1 top 文件

`timescale 1ns / 1ps
// 函数功能:当 FIFO 空时,向 FIFO 中写入数据,写入的数据量和 FIFO 深度一致,即 FIFO 被写满;
//           当 FIFO 满时,从 FIFO 中读出数据,直到 FIFO 被读空为止。 
// FIFO数据位宽16bit,深度:1024


module my_FIFO(
    // ------写数据的相关接口
    input               adc_clk        , //fifo写时钟  65M 
    input               adc_rst_n      , //fifo写复位
    input  [15:0]       adc_data       , //fifo写数据      
      

     //-------读数据的相关接口
    input               M_AXIS_CLK     ,//fifo读时钟  100M 
    input               M_AXIS_RSTN    ,//fifo读复位   
    //AXI接口
    input               M_AXIS_tready  ,//外部准备好接收FIFO读出的数据              
    output reg [15:0]   M_AXIS_tdata   ,//fifo读出的数据
    output     [1:0]    M_AXIS_tkeep   ,
    output reg          M_AXIS_tlast   ,//fifo读数据的最后一个数据
    output reg          M_AXIS_tvalid   //fifo输出给外部的读数据有效信号
);
     

    
    
    
//---------------1、例化写模块  
//模块功能:控制写使能信号fifo_wr_en和FIFO的写数据fifo_wr_data
wire full                    ;  // FIFO满信号
wire empty                   ;   // FIFO空信号
//写模块
wire          fifo_wr_en     ;// 写使能
wire [15:0]   fifo_wr_data   ;// 写数据
fifo_wr  fifo_wr_inst(
       // input 
       .adc_clk      (adc_clk     ),//写时钟
       .adc_rst_n    (adc_rst_n   ),//写使能
       .empty        (empty       ),//fifo将空信号
       .full         (full        ),//fifo将满信号
       .adc_data     (adc_data    ),//外部待写入FIFO的数据
   
       //output
       .fifo_wr_en   (fifo_wr_en  ),// 写使能
       .fifo_wr_data (fifo_wr_data)// FIFO的写数据
 );
     
         
         
// ----------------2、例化读模块
// 模块功能:控制读使能信号fifo_rd_en  
wire fifo_rd_en  ;// 读使能
fifo_rd  fifo_rd_inst(
     //input
    .M_AXIS_CLK      (M_AXIS_CLK    ),  // 读时钟
    .M_AXIS_RSTN     (M_AXIS_RSTN   ),  // 读复位
    .empty           (empty         ),
    .full            (full          ), 
    .M_AXIS_tready   (M_AXIS_tready ),  //外部准备好接收FIFO的读数据
    
    //output 
    .fifo_rd_en      (fifo_rd_en    )  // 读使能
 );
     
     
     
 //--------------3、例化FIFO IP核   
wire [15 : 0] dout;
fifo_generator_0 Inst_fifo_generator_0(
   .rst     (!adc_rst_n     ),        // input wire rst
   .wr_clk  (adc_clk        ),  // input wire wr_clk
   .rd_clk  (M_AXIS_CLK     ),  // input wire rd_clk
   .din     (fifo_wr_data   ),        // input wire [15 : 0] din
   .wr_en   (fifo_wr_en     ),    // input wire wr_en
   .rd_en   (fifo_rd_en     ),    // input wire rd_en
   .dout    (dout           ),      // output wire [15 : 0] dout
   .full    (full           ),      // output wire full
   .empty   (empty           )    // output wire empty
);
 


 
//------------4、FIFO的读出数据有效信号ReadData_vaild控制
//为什么要这样写,是根据仿真波形得到的
reg        ReadData_vaild ;
always@(posedge M_AXIS_CLK)
begin
     ReadData_vaild<=fifo_rd_en;//相当于ReadData_vaild等于fifo_rd_en延后一个clk的信号
end
 
 
//------------5、输出数据的有效信号和输出数据控制
always@(posedge M_AXIS_CLK)
begin
     if(!M_AXIS_RSTN)//复位
         begin
             M_AXIS_tvalid<=1'b0;
         end
     else if(ReadData_vaild && M_AXIS_tready)//FIFO读出的数据有效,且外部准备好接收数据
         begin
             M_AXIS_tvalid<=1'b1;//FIFO给外部的数据是有效的
             M_AXIS_tdata<=dout;// FIFO给外部的有效数据
         end
     else
         begin
              M_AXIS_tvalid<= 1'b0;
              M_AXIS_tdata<=16'dx;
         end   
end
 
assign M_AXIS_tkeep  = 2'b11 ;
 
 
//-------- 6、读有效数据计数器,记到1024,就将M_AXIS_tlast拉高
reg [15:0]   dma_cnt ;   //dma counter
always@(posedge M_AXIS_CLK)
begin
     if(M_AXIS_RSTN == 1'b0)
         begin
             dma_cnt <= 16'd0;
             M_AXIS_tlast<= 1'b0;
         end
     else if (M_AXIS_tvalid & ~M_AXIS_tlast)//主端数据有效,且last信号为低
        begin
            dma_cnt <= dma_cnt + 1'b1 ;
        end    
     else 
         begin
            dma_cnt <= dma_cnt ;
         end    
 end
 
// ----7、M_AXIS_tlast信号控制
// 读有效数据计数器记到1024,就将M_AXIS_tlast拉高,
always@(posedge M_AXIS_CLK)
begin
      if(M_AXIS_tvalid && (dma_cnt == 1024-1'd1))//sample_len=1024
         begin
             M_AXIS_tlast  <= 1'b1 ;
         end
      else
          begin
              M_AXIS_tlast  <= 1'b0 ;
          end        
end
 
 
 
 
//-----------8、找1024个点的最大值
wire [15:0]   data_max        ;
wire          DataMax_Vaild    ;
Max_Get Inst_Max_Get(
     //input
     .clk           (M_AXIS_CLK        ),
     .reset         (!adc_rst_n        ),
     .En            (ReadData_vaild    ),
     .data_in       (M_AXIS_tdata      ),
      //output
     .data_max      (data_max     )  ,
     .DataMax_Vaild (DataMax_Vaild)
);
 
 
 
     
     
endmodule




4.2 FIFO写模块(fifo_wr)

`timescale 1ns / 1ps



module fifo_wr(
    input               adc_clk     , // 时钟信号
    input               adc_rst_n   , // 复位信号
    input               empty       , //读空信号
    input               full        , //写满信号
    input [15:0]        adc_data    ,// 外部采集的数据  
   
     
    output reg         fifo_wr_en   ,// 写使能
    output reg [15:0]  fifo_wr_data  // 写数据  
);
    
 
 //**--------------1、写使能控制----------------**//   
reg [3:0] ST_WirteData;    // 状态机
reg [7:0] delay_cnt;
always@(posedge adc_clk or negedge adc_rst_n)   
begin  
     if(adc_rst_n==1'b0) // 复位  
        begin
            ST_WirteData <= 4'd0;//
            fifo_wr_en   <= 1'b0;
            delay_cnt    <= 8'd0;
        end
     else    
        begin
           case(ST_WirteData)
                0://适当延时---第一种延时方法
                    begin
                        if(delay_cnt==8'd5)
                            begin
                                ST_WirteData <= 4'd1;
                            end
                        else     
                            begin
                                delay_cnt <= delay_cnt+1'b1;//等待fifo空
                                ST_WirteData <= 4'd0;
                            end                            
                    end
                1:   
                    begin
                        if(empty) // fifo为空,开始写数据 
                            begin
                                ST_WirteData <= 4'd2;
                            end
                        else     
                            begin
                                ST_WirteData <= 4'd1;//等待fifo空
                            end
                    end
                2:  //适当延时---第2种延时方法  可以不要
                    ST_WirteData<= 4'd3;//跳到状态3  
                3:
                    begin
                        if(full)  //FIFO写满
                            begin
                                fifo_wr_en   <= 1'b0;//写使能无效
                                fifo_wr_data <= 16'dx;
                                ST_WirteData <= 4'd0;//跳到状态0 
                            end
                         else//FIFO没有写满
                            begin
                                fifo_wr_en   <= 1'b1;   //写使能有效                       
                                fifo_wr_data <= adc_data;  //将外部数据送给FIFO
                                ST_WirteData<= 4'd3;//跳到状态2  
                            end
                    end
                default:
                    ST_WirteData<= 4'd0;//跳到状态0 
           endcase
        end
end   
 
 
       
endmodule

4.3 FIFO读模块(fifo_rd)

`timescale 1ns / 1ps
    
//-----------模块功能:FIFO读使能控制
module fifo_rd(
    input       M_AXIS_CLK      , // 时钟信号
    input       M_AXIS_RSTN     , // 复位信号
    input       empty           , //快要读空信号
    input       full            , //快要写满信号
    input       M_AXIS_tready   , //外部准备好接收FIFO的读数据

         
    output reg  fifo_rd_en        // 读使能  fifo读模块给fifo一个读数据的请求,对于读模块而言,是输出
);
 
 
 
    
reg [3:0] ST_ReadData;  //状态机
always@(posedge M_AXIS_CLK)   
begin  
    if(!M_AXIS_RSTN) // 复位  
       begin
           fifo_rd_en <= 1'b0;
           ST_ReadData<= 4'd1;//进入状态1
       end
    else    
       begin
          case(ST_ReadData)
               0:        
                   begin
                       if(M_AXIS_tready)//外部准备好接收FIFO的读数据
                           begin
                               ST_ReadData <= 4'd1;//跳到下一个状态
                           end
                       else
                           begin    
                               ST_ReadData <= 4'd0;//等待外部准备好接收FIFO的读数据
                           end  
                   end
             1:      
                   begin
                       if(full)//FIFO数据已经写满
                           begin
                               ST_ReadData <= 4'd2;                                
                           end
                       else 
                           begin    
                               ST_ReadData <= 4'd1;//等待FIFO写满
                           end    
                    end
               2:
                    begin
                         if(empty)//FIFO数据读空
                             begin
                                 fifo_rd_en <= 1'b0;//读使能无效
                                 ST_ReadData<= 4'd0;
                             end
                         else
                             begin
                                 fifo_rd_en <= 1'b1; //使能有效,继续读数据
                                 ST_ReadData<= 4'd2;
                             end
                    end
                 default:
                   ST_ReadData<= 4'd0; 
             endcase
       end
end 

 
   
endmodule

4.3 最大值寻找模块(Max_Get)

`timescale 1ns / 1ps

// 找1024个点的最大值

module Max_Get(
    input               clk             ,
    input               reset           ,
    input               En              ,//模块开始工作的使能信号
    input [15:0]        data_in         ,// 输入数据
    
    output reg [15:0]   data_max        ,//搜索得到的最大值
    output reg          DataMax_Vaild   
 );
 
 
  
reg [15:0]   data_max_temp;  
reg [3:0]    ST_max_search;
reg [15:0]   count;
   
always@(posedge clk)
begin
   if(reset)
        begin
             data_max_temp<=16'd0;    
             ST_max_search<=4'd0;
             count<=16'd0;
             DataMax_Vaild<=1'b0;
        end
   else
        begin
              case(ST_max_search)
              0:
                    begin
                        if(En==1'b1)
                            begin
                                ST_max_search<=4'd1;
                            end
                       else
                            begin
                                ST_max_search<=4'd0;
                            end                             
                    end
                1:
                    begin
                        if(count<16'd1024)
                            begin
                                count<=count+1'd1;
                                ST_max_search<=4'd1;
                                if(data_in>data_max_temp)
                                    data_max_temp<=data_in;
                                else
                                    data_max_temp<=data_max_temp;
                            end
                        else//if(count==16'd1024)  
                             begin
                                count<=16'd0;
                                ST_max_search<=4'd0;                           
                                data_max<= data_max_temp;
                                data_max_temp<=16'd0;//数据归零,避免影响下一次寻求最大值
                                DataMax_Vaild<=1'b1;
                            end                                    
                    end           
                default:         
                    ST_max_search<= 4'd0;//跳到状态0 end   
           endcase    
      end  
end      
      
     
     
     
        
endmodule

4.4 testbench文件

`timescale 1ns / 1ps

module sim_FIFO;



// ------写数据的相关接口
wire           adc_clk        ;
reg            adc_rst_n      ;
reg  [15:0]    adc_data       ;    

                 
// AXIS 接口
wire           M_AXIS_tready  ;
wire           M_AXIS_CLK     ;//时钟
wire           M_AXIS_RSTN    ;//复位
assign M_AXIS_tready=1'b1;
assign M_AXIS_RSTN=adc_rst_n;


//output  
wire [15:0]   M_AXIS_tdata   ;
wire [1:0]    M_AXIS_tkeep   ;
wire          M_AXIS_tlast   ;
wire          M_AXIS_tvalid  ;



my_FIFO Inst_my_FIFO(//新增代码
     // ------写数据的相关接口
     //input
     .adc_clk       (adc_clk      ) ,//fifo写时钟   
     .adc_rst_n     (adc_rst_n    ) ,//fifo写复位   
     .adc_data      (adc_data     ) ,//fifo写数据   
      
      
     //-------读数据的相关接口      
     //input                             
     .M_AXIS_CLK    (M_AXIS_CLK   ) , //fifo读时钟  
     .M_AXIS_RSTN   (M_AXIS_RSTN  ) , //fifo读复位  
     .M_AXIS_tready (M_AXIS_tready) ,
     
     //output    
     .M_AXIS_tdata  (M_AXIS_tdata ) , //fifo读数据            
     .M_AXIS_tkeep  (M_AXIS_tkeep ) ,
     .M_AXIS_tlast  (M_AXIS_tlast ) , //fifo读数据的最后一个数据     
     .M_AXIS_tvalid (M_AXIS_tvalid)   //fifo输出给外部的读数据有效信号  
);

reg clk;
initial 
begin
	   clk <= 1'b0;
	   adc_rst_n <=1'b0;
	#20;
       adc_rst_n <=1'b1;

end

always #10 clk = ~ clk;   //20ns一个周期,产生50MHz时钟源



//产生输入数据
always @(posedge adc_clk)
begin  
    if(!adc_rst_n)  
        begin
            adc_data<=16'd0;
        end
    else 
        begin
            adc_data<=adc_data+1'd1;
        end          
end   


//产生读、写模块的时钟
  clk_wiz_0 Inst_clk_wiz_0
 (
  // Clock out ports
  .clk_out1(adc_clk),     // output clk_out1  65M 
  .clk_out2(M_AXIS_CLK),     // output clk_out2  100M 
  // Status and control signals
  .reset(!adc_rst_n), // input reset
  .locked(),       // output locked
 // Clock in ports
  .clk_in1(clk)// 50M 
 );      // input clk_in1



endmodule

5 仿真结果

在这里插入图片描述
FIFO写初值

在这里插入图片描述
FIFO写终值和FIFO读初值
在这里插入图片描述
FIFO读终值
在这里插入图片描述
这几个信号控制正确

     output reg [DATA_WIDTH:0]  M_AXIS_tdata   ,
     output     [1:0]           M_AXIS_tkeep   ,
     output reg                 M_AXIS_tlast   ,
     output reg                 M_AXIS_tvalid  

6 完整工程链接

工程下载

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值