verilog同步FIFO设计

同步FIFO设计

一、什么是FIFO

FIFO全称 First In First Out,即先进先出。

FIFO主要用于以下几个方面:

  • 跨时钟域数据传输
  • 将数据发送到芯片外之前进行缓冲,如发送到DRAM或SRAM
  • 存储数据以备后用

FIFO是异步数据传输时常用的存储器,多bit数据异步传输时,无论是从快时钟域到慢时钟域,还是从慢时钟域到快时钟域,都可以使用FIFO处理。

二、关于FIFO的重要参数

FIFO中重要的参数有`深度、宽度、空标志、满标志、读时钟、读时针、写时钟和写时针

**读时钟:**读操作所遵循的时钟,时钟沿到来时读取数据

**写时钟:**写操作所遵循的时钟,时钟沿到来时写入数据

**读指针:**指向下一个要读出的地址,读完自动加1

**写指针:**指向下一个要写入地址,写完自动加1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-66WEOqKQ-1664871465483)(C:\Users\12234\AppData\Roaming\Typora\typora-user-images\image-20221004160847770.png)]

FIFO可分为读数据一端和写数据一端

  • wr_enrd_en分别为写/读使能端,就像上面隧道的例子,两个使能端就好像是两边的门,只有门打开的时候才允许车辆进出
  • wr_datard_data分别是要写入FIFO的数据和要从FIFO中读取的数据
  • fifo_fullfifo_empty分别为FIFO的满/空标志位

三、FIFO设计中的重要原则

  1. 任何FIFO都不要向满FIFO中写入数据(写溢出)
  2. 任何FIFO都不要从空FIFO中读取数据(读溢出)

FIFO设计的核心便是空满的判断,如何判断FIFO是否写满(或读空),这里我们可以利用地址指针,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uihZWSm6-1664871465484)(C:\Users\12234\AppData\Roaming\Typora\typora-user-images\image-20221004161038670.png)]

每写入一次数据,写地址指针会加1,每读取一次数据,读地址指针会加1

就像上图所示,当读地址指针追上写地址指针,FIFO便是读空状态

同理,当写地址指针再次追上读地址指针,FIFO便是写满状态,就像下图

img

四、verilog代码实现

`timescale 1ns / 1ps
module syn_fifo(
input clk,  //时钟信号
input rstn, //下降沿复位
input wr_en, //写入使能
input rd_en, //读取使能
input wr_data, //写数据
output rd_data, //读数据
output fifo_full, //空满判断信号
output fifo_empty
);

//参数定义
parameter   width = 8;
parameter   depth = 8;
parameter   addr  = 3;

wire [width-1:0] wr_data;
reg [width-1:0] rd_data; 


    //定义一个计数器,用于判断空满
    reg [$clog2(depth): 0] cnt;

    //定义读写地址
    reg [addr - 1 : 0] wr_ptr;
    reg [addr - 1 : 0] rd_ptr;

    //定义一个宽度为为width,深度为depth的fifo
    reg [width - 1 : 0] fifo [depth - 1 : 0];

    //写地址操作
    always @ (posedge clk or negedge rstn) begin
        if(!rstn)
            wr_ptr <= 0;
        else if(wr_en && !fifo_full)    //写使能,且fifo未写满
            wr_ptr <= wr_ptr + 1;
        else
            wr_ptr <= wr_ptr;
    end

    //读地址操作
    always @ (posedge clk or negedge rstn) begin
        if(!rstn)
            rd_ptr <= 0;
        else if(rd_en && !fifo_empty)   //读使能,且fifo不为空
            rd_ptr <= rd_ptr + 1;
        else
            rd_ptr <= rd_ptr;
    end

    //写数据
    integer i;

    always @ (posedge clk or negedge rstn) begin
        if(!rstn) begin //复位清空fifo
            for(i = 0; i < depth; i = i + 1)
                fifo[i] <= 0;
        end
        else if(wr_en)  //写使能时将数据写入fifo
            fifo[wr_ptr] <= wr_data;
        else    //否则保持
            fifo[wr_ptr] <= fifo[wr_ptr];
    end

    //读数据
    always @ (posedge clk or negedge rstn) begin
        if(!rstn)
            rd_data <= 0;
        else if (rd_en)
            rd_data <= fifo[rd_ptr];    //从fifo中读取数据
        else
            rd_data <= rd_data;
    end

    //辅助计数,用于判断空满
    always @ (posedge clk or negedge rstn) begin
        if(!rstn)
            cnt <= 0;
        else if (wr_en && !rd_en && !fifo_full) //有效的只写入
            cnt <= cnt + 1;
        else if (!wr_en && rd_en && !fifo_empty) //有效的只读取
            cnt <= cnt - 1;
        else 
            cnt <= cnt;
    end

    //空满判断
    assign fifo_full = (cnt == depth)? 1 : 0;
    assign fifo_empty = (cnt == 0) ? 1 : 0;
endmodule 

    //空满判断
    assign fifo_full = (cnt == depth)? 1 : 0;
    assign fifo_empty = (cnt == 0) ? 1 : 0;
endmodule 

你可能感兴趣的:(fpga开发)