FIFO的Verilog设计(一)——同步FIFO

本文介绍了同步FIFO的工作原理,包括其分类、用途,以及同步FIFO的同步时钟实现机制。给出了Verilog代码示例,展示了如何使用RAM、读写控制和空满判别部分构建一个深度为16的同步FIFO,并提供了相应的仿真测试。
摘要由CSDN通过智能技术生成


前言

  FIFO在系统设计中被广泛应用,但是大多时候使用X家和A家封装的IP核,为了更好的理解FIFO工作原理并且为了方便在不同厂商工作环境之间移植,自己编写一个FIFO IP也是不错的选择。下面将介绍同步FIFO的工作原理和Verilog实现。

什么是FIFO

1. 简介

  FIFO(First in first out)是一种先进先出的数据缓冲器,实质上是RAM加上一些外围电路实现而来。它和RAM的区别在于:RAM需要外部的读写地址线,而FIFO只能顺序读出和顺序写入数据,其数据地址会根据读写使能信号自动加减,达到先存入数据先读出的效果,类似于栈的效果。

2. 分类

  FIFO根据读写时钟是否同步分为同步时钟和异步时钟,同步FIFO读写时钟相同,异步FIFO读写时钟不同。两者应用场景不同,需要工程师根据实际情况使用。

3. 用途

  • 数据缓存:当读写速度不匹配的时候可以使用FIFO,进行数据的一个缓存,比如说外部数据输入不需要马上使用的时候,即读写使能不同时。
  • 数据匹配:当读写数据位宽不同时候可以使用FIFO,进行数据的一个拼接或者分割,比如说写入数据位宽为8bit,输出为16bit,通过FIFO可以将8bit的数据拼接缓存再输出16bit,达到数据匹配的功能。
  • 多bit数据跨时钟域处理:当读写时钟不同时候可以使用异步FIFO,可以实现在两个不同时钟系统之间进行安全的数据交互。
      注意:当需要实现少bit写入多bit读出功能时,需要注意不同厂商提供的FIFO IP核是小端写入还是大端写入。比如说8bit写入16bit写出,X家的是先存高8位,A家的是先存低8位

同步FIFO的实现原理

  同步FIFO的读写时钟相同,FIFO内部均为同步逻辑,可以用于数据的缓存。同步FIFO模块一般会有时钟clk、复位rst_n、读端口和写端口。读端口一般包括读使能(rd_en)、读数据(data_out)、读空(empty)。写端口一般包括写使能(wr_en)、写数据(wr_data)、写满(full)。
  实现FIFO难点在于如何得到读空和写满信号。以下以深度为8的FIFO举例说明。在设计FIFO时,可以将读写地址扩展一位,用于判断是否读写完一轮,即深度为8,地址为4bit。
  初始状态下,写地址为4’b0000,读地址为4’b0000,此时empty信号拉高。
在这里插入图片描述

  写入8个数据,写地址为4’b1000,读地址为4’b0000,此时full信号拉高。
在这里插入图片描述

  读出8个数据,写地址为4’b1000,读地址为4’b1000,此时empty信号拉高。
在这里插入图片描述

  写入8个数据,写地址为4’b0000,读地址为4’b1000,此时full信号拉高。
在这里插入图片描述

  综上所述,读空empty信号拉高的判决条件是写地址等于读地址。写满信号拉高的判决条件是写地址和读地址最高位相反其余位相同

同步FIFO的硬件实现

  由前文分析可以知道,同步FIFO的实现包括RAM、读控制、写控制、空满判别四部分。以下为systermverilog代码,如果想用verilog代码可以将localparam ADDR_BIT = $clog2(DEPTH)更改。

module sync_fifo#(  
    parameter    DEPTH = 16,  
    parameter    WIDTH = 16,
    parameter    RAM_STYLE_VAL = "distributed"     
)(  
    input        clk, 
    input        rst_n, 
    input        wr_en, 
    input   [WIDTH-1:0]  data_in, 
    input        rd_en, 
    output  [WIDTH-1:0]  data_out, 
    output        full,  
    output        empty
);
    localparam ADDR_BIT = $clog2(DEPTH);
    
    (*ram_style=RAM_STYLE_VAL*) reg  [WIDTH-1:0]  mem  [DEPTH-1:0];         //generate mem
    
    reg  [ADDR_BIT:0]    waddr;               
    reg  [ADDR_BIT:0]    raddr;               
    reg  [WIDTH-1 :0]    data_out_r;

    //write address
    always@(posedge clk or negedge rst_n)begin  
        if(!rst_n)    waddr <= 'd0; 
        else if(wr_en == 1'b1)    waddr <= waddr + 1; 
        else    waddr <= waddr;
    end
    //read address
    always@(posedge clk or negedge rst_n)begin 
        if(!rst_n)    raddr <= 'd0; 
        else if(rd_en == 1'b1)    raddr <= raddr + 1; 
        else    raddr <= raddr;
    end
    //write data
    always@(posedge clk)begin 
        if(wr_en) mem[waddr] <= data_in;
        else      mem[waddr] <= mem[waddr];
    end
    //read data delay 1clk
    always@(posedge clk or negedge rst_n)begin  
        if(!rst_n)    data_out_r <= {WIDTH{1'b0}};
          else    data_out_r <= mem[raddr];
    end
    assign  data_out = data_out_r;
    //Empty signal judgment
    assign  empty = (waddr == raddr)?1'b1:1'b0;
    //Full signal judgment
    assign  full = ({~waddr[ADDR_BIT],waddr[ADDR_BIT-1:0]} == raddr)?1'b1:1'b0;
endmodule

同步FIFO仿真测试

  以下为测试文件。

module tb;
    reg      clk;
    reg      rst_n;
    reg      wr_en;
    reg      rd_en;
    reg  [7:0] data_in;
    wire [7:0] data_out;
    wire    full;
    wire    empty;
    
    sync_fifo#(
        .DEPTH(16),
        .WIDTH(8))
    sync_fifo(  
        .clk        (clk        ),  
        .rst_n      (rst_n      ),  
        .wr_en      (wr_en      ),  
        .data_in    (data_in    ),  
        .rd_en      (rd_en      ), 
        .data_out   (data_out   ), 
        .full       (full       ),  
        .empty      (empty)     );
    initial begin  
        clk = 0;rst_n = 0;  
        wr_en = 0;rd_en = 0;data_in  = 'd0;    
        #15  rst_n = 1; 
        write_data();  
        #100
        read_data();
        #100
        $finish;
    end
    always # 5 clk = ~clk;
    
    task  write_data;  
    begin
        repeat(16)begin
            #10 
            data_in <= {$random}%256;
            wr_en   <= 1;
        end
        #10 wr_en <= 0;data_in <= 'd0;
    end
    endtask  
    
    task  read_data;  
        integer  i;  
        begin    
            repeat(16)begin
                #10
                rd_en   <= 1;
            end
            #10 rd_en <= 0;
        end  
    endtask    
    
endmodule

  仿真结果如下,可以发现该同步fifo逻辑正确。
在这里插入图片描述

  • 27
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值