FPGA中FIFO IP核配置与调用

目录

一、FIFO IP核简介

 二、SCFIFO IP核配置

三、SCFIFO IP核调用

3.1 顶层设计

 3.2 RTL代码

3.3 仿真

四、DCFIFO IP 核配置

五、DCFIFO IP 核调用

5.1 整体设计

5.2 RTL代码

5.3仿真验证

说明:

本人使用的是野火家Xilinx Spartan6系列开发板及配套教程,写博客记录自己的学习。

开发软件:ise14.7     仿真:modelsim 10.5 

一、FIFO IP核简介

      FIFO(First In First Out,即先入先出),是一种数据缓冲器,用来实现数据先入先出的读写。与 ROM 或 RAM 的按地址读写方式不同, FIFO 的读写遵循“先进先出”的原则,即数据按顺序写入 FIFO,先被写入的数据同样在读取的时候先被读出,所以 FIFO存储器没有地址线。 FIFO 有一个写端口和一个读端口外部无需使用者控制地址,使用方便。
      FIFO 存储器主要是作用为缓存,应用在同步时钟系统和异步时钟系统中,在很多的设计中都会使用如:多比特数据做跨时钟域的转换、前后带宽不同步等都用到了异步FIFO,示意图如下。 FIFO 根据读写时钟是否相同,分为 SCFIFO(同步 FIFO)和 DCFIFO(异步FIFO),SCFIFO 的读写为同一时钟,应用在同步时钟系统中; DCFIFO 的读写时钟不同,应用在异步时钟系统中。

 二、SCFIFO IP核配置

配置过程比较简单,故不展示这里注意选择时钟统一。

三、SCFIFO IP核调用

   实验目标:实现数据0-255的数据缓冲。

3.1 顶层设计

 3.2 RTL代码

`timescale  1ns/1ns

module fifo
(
    input   wire            sys_clk     ,  
    input   wire    [7:0]   pi_data     ,   //输入数据
    input   wire            pi_flag     ,   //输入数据有效标志信号
    input   wire            rd_en       ,   //FIFO读数据有效信号

    output  wire            empty       ,   //FIFO空标志信号,高有效
    output  wire            full        ,   //FIFO满标志信号,高有效
    output  wire    [7:0]   po_data     ,   //输出数据
	output  wire    [7:0]   data_count      //FIFO中存在的数据个数
);
//调用FIFOip核
scfifo_256X8 scfifo_256X8_inst
(
    .clk       (sys_clk   ),    
    .din       (pi_data   ),    
    .wr_en     (pi_flag   ),    
    .rd_en     (rd_en     ),    

    .dout      (po_data   ),    
    .full      (full      ),    
    .empty     (empty     ),    
    .data_count(data_count)     
);
endmodule

3.3 仿真

仿真代码:

`timescale  1ns/1ns

module tb_fifo();

reg         sys_clk     ;
reg [7:0]   pi_data     ;
reg         pi_flag     ;
reg         rd_en       ;
reg         sys_rst_n   ;
reg [1:0]   cnt_baud    ;

wire    [7:0]   po_data     ;
wire            empty       ;
wire            full        ;
wire    [7:0]   data_count  ;

initial begin
    sys_clk    = 1'b1;
    sys_rst_n <= 1'b0;
    #100;
    sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;

//cnt_baud:计数从0到3的计数器,用于产生输入数据间的间隔
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_baud <= 2'b0;
    else    if(&cnt_baud == 1'b1)
        cnt_baud <= 2'b0;
    else
        cnt_baud <= cnt_baud + 1'b1;

//pi_flag:输入数据写请求信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        pi_flag <= 1'b0;
    //每4个时钟周期且没有读请求时产生一个数据有效标志信号
    else    if((cnt_baud == 2'd0) && (rd_en == 1'b0))
        pi_flag <= 1'b1;
    else
        pi_flag <= 1'b0;

//pi_data:输入数据
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        pi_data <= 8'b0;
    else    if((pi_data == 8'd225) && (pi_flag == 1'b1))
        pi_data <= 8'b0;
    else    if(pi_flag  == 1'b1)    //每当pi_flag有效时产生一个数据
        pi_data <= pi_data + 1'b1;

//rd_en:FIFO读请求信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_en <= 1'b0;
    else    if(full == 1'b1)  //当FIFO中的数据存满时,开始读取FIFO中的数据
        rd_en <= 1'b1;
    else    if(empty == 1'b1) //当FIFO中的数据被读空时停止读取FIFO中的数据
        rd_en <= 1'b0;
fifo fifo_inst
(
    .sys_clk    (sys_clk    ),  
    .pi_data    (pi_data    ),  
    .pi_flag    (pi_flag    ),  
    .rd_en      (rd_en      ),  

    .po_data    (po_data    ),  
    .empty      (empty      ),  
    .full       (full       ),  
    .data_count (data_count )   
);

endmodule

仿真结果:

 从上图中可以看出full为高电平时,rd_en也在下一个节拍拉高,fifo开始读数据。

  从上图中可以看出empty为高电平时,rd_en也在下一个节拍拉低并且pi_flag为高电平,fifo开始写数据。

四、DCFIFO IP 核配置

配置过程比较简单,故不展示这里注意选择时钟统一。

五、DCFIFO IP 核调用

实验目标:实现输入 256 个深度 8 位宽、 输出 128个深度 16 位宽的数据缓存器。

5.1 整体设计

5.2 RTL代码

`timescale  1ns/1ns

module fifo
(    
    input   wire  [7:0]   pi_data   ,   //FIFO写入的数据,同步于wrclk时钟	
    input   wire          wr_clk    ,   //同步于FIFO 写数据 的时钟 50MHz                                  
    input   wire          pi_flag   ,   //输入数据有效标志信号,同步于wr_clk时钟
    output  wire          full      ,   //满标志信号,高有效
	output  wire  [7:0]   wr_data_count, //FIFO写端口中存在的数据个数,
                                       //同步于wrclk时钟
	
    output  wire  [15:0]  po_data   ,   //FIFO读出的数据,同步于rdclk时钟	
    input   wire          rd_clk    ,   //同步于FIFO 读数据 的时钟 25MHz
    input   wire          rd_en     ,   //FIFO读请求信号,同步于rdclk时钟 
    output  wire          empty     ,   //空标志信号,高有效,
    output  wire  [6:0]   rd_data_count //FIFO读端口中存在的数据个数,
                                        //同步于rdclk时钟
    
);
//调用FIFO ip核
dcfifo_256x8to128x16    dcfifo_256x8to128x16_inst
(
    .din            (pi_data),      
    .rd_clk         (rd_clk ),      
    .rd_en          (rd_en  ),      
    .wr_clk         (wr_clk ),      
    .wr_en          (pi_flag),      
                                   
    .dout           (po_data),      
    .empty          (empty  ),      
    .full           (full   ),      
    .rd_data_count  (rd_data_count),
    .wr_data_count  (wr_data_count) 
);
endmodule

5.3仿真验证

仿真代码:

`timescale  1ns/1ns

module tb_fifo();

reg          wr_clk         ;
reg  [7:0]   pi_data        ;
reg          pi_flag        ;
reg          rd_clk         ;
reg          rd_en          ;
reg          sys_rst_n      ;
reg  [1:0]   cnt_baud       ;
reg          full_reg0      ;
reg          full_reg1      ;

wire            empty           ;
wire            full            ;
wire    [7:0]   wr_data_count   ;
wire    [15:0]  po_data         ;
wire    [6:0]   rd_data_count   ;

initial begin
    wr_clk     = 1'b1;
    rd_clk     = 1'b1;
    sys_rst_n <= 1'b0;
    #100;
    sys_rst_n <= 1'b1;
    
end

//wr_clk:模拟FIFO的写时钟,每10ns电平翻转一次,周期为 20ns,频率为50MHz
always #10 wr_clk = ~wr_clk;
//rd_clk:模拟FIFO的读时钟,每20ns电平翻转一次,周期为40ns,频率为25MHz
always #20 rd_clk = ~rd_clk;

//cnt_baud:计数从0到3的计数器,用于产生输入数据间的间隔
always@(posedge wr_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_baud <= 2'b0;
    else    if(cnt_baud == 2'd3)
        cnt_baud <= 2'b0;
    else
        cnt_baud <= cnt_baud + 1'b1;
always@(posedge wr_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        pi_flag <= 1'b0;
    else    if((cnt_baud == 2'd0) && (rd_en == 1'b0))
        pi_flag <= 1'b1;
    else
        pi_flag <= 1'b0;

//pi_data:输入顶层模块的数据,要写入到FIFO中的数据
always@(posedge wr_clk 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_flag == 1'b1))
        pi_data <= 8'b0;
    else    if(pi_flag  == 1'b1)    //每当pi_flag有效时产生一个数据
        pi_data <= pi_data + 1'b1;

//将同步于rd_clk时钟的写满标志信号full在rd_clk时钟下打两拍
always@(posedge rd_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)   
        begin
            full_reg0 <= 1'b0;
            full_reg1 <= 1'b0;
        end
    else
        begin
            full_reg0 <= full;
            full_reg1 <= full_reg0;//打两拍
        end

//rd_en:FIFO读请求信号同步于rd_clk时钟
always@(posedge rd_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_en <= 1'b0;
    else    if(full_reg1 == 1'b1)
        rd_en <= 1'b1;
    else    if(empty == 1'b1)//当FIFO中的数据被读空时停止读取FIFO中的数据
        rd_en <= 1'b0;

fifo    fifo_inst
(
    .wr_clk  (wr_clk    ),  
    .pi_data (pi_data   ),  
    .pi_flag (pi_flag   ),  
    .rd_clk  (rd_clk    ),  
    .rd_en   (rd_en     ),  

    .po_data (po_data   ),  
    .empty   (empty     ),  
    .full    (full      ),  
    .rd_data_count(rd_data_count),
    .wr_data_count(wr_data_count) 
);

endmodule

仿真结果:

整体波形变化:

 数据输入部分:

 数据输出部分:

由上可知读写位宽不同的FIFO,数据输入输出顺序是当输入位宽小于输出位宽时,先入存高位后入存低位。反之则,先入存低位后入存高位。 

学完fifo,简单总结三点:

1. 合理控制 IP核大小(数据深度与位宽),避免资源浪费。

2. 注意利用好 FIFO 的关键信号,如读写时钟、读写使能、空满标志信号。

3. 写数据的总带宽一定要等于读数据的总带宽,否则一定会存在写满或读空的现象。

猜你喜欢

转载自blog.csdn.net/m0_72885897/article/details/128649678