同步FIFO

目录

FIFO的性质与结构特点

FIFO基本时序

FIFO的写数据时序结构

FIFO的读数据时序结构

[standard mode,标准模式的读取时序](#standard mode,标准模式的读取时序)

[First-Word Fall-Through,FWFT,ahead模式](#First-Word Fall-Through,FWFT,ahead模式)

同步FIFO的RTL设计方法


FIFO的性质与结构特点

  • 不管是同步FIFO还是异步FIFO,实际上都是一种先入先出的存储器队列,同步FIFO和异步FIFO归根结底实际上都是一个具有存储数据功能的数据存储器
  • FIFO虽然说还属于一种存储器,但是和一般的存储器又存在比较大的区别,一般的存储器,都是通过地址确定数据的位置,但是FIFO无需地址确定位置,因为FIFO是一种通过先后顺序确定数据位置的存储器。即输入到FIFO中的数据是什么顺序输入,那么输出数据的顺序也是一致(先入先出)
  • FIFO和其他存储器一样,是存在两个通道的,即写入通道和读取通道。外部数据通过写入通道将数据写入到FIFO中进行缓存,又可以通过读取通道将数据从FIFO中读取至外部通道
  • 其中同步FIFO的读取通道和写入通道是使用同一个时钟,即整个FIFO是在同一个时钟域下进行数据处理与信号运算的,但是异步FIFO的读取通道和写入通道是分别使用两个不同的时钟域进行划分的,这也导致异步FIFO的设计内部存在两个时钟域信号,当信号从一个时钟域跨时钟传输到另一个时钟域的时候就需要考虑异步信号的跨时钟域处理
  • 不管是同步FIFO还是异步FIFO,其容量大小最主要的参数就是FIFO的数据存储深度Data_DEPTH和FIFO存储的单位数据位宽Data_WIDTH

上图中的FIFO最多可以存储8个32bit数据位宽的数据

FIFO的写入通道由Wen和Wdata[31:0]信号组成,读取通道由Rcn和Rdata[31:0]信号组成

当FIFO中没有数据时,empty信号会拉高,提示当前信号为空,无法从FIFO中读取数据。当FIFO中已经存在8个32bit数据时,full信号会拉高,提示当前FIFO数据已经装满,无法再向FIFO中写入数据

FIFO基本时序

FIFO的写数据时序结构

当在时钟信号的上升沿检测到wr_en信号为高电平时,表示将din中的数据写入FIFO(写入时刻)

当把FIFO数据写满之后,对应的full信号会拉高,若在full信号为高电平的时候可以继续向FIFO写入数据,则会导致FIFO溢出,overflow信号会拉高进行提示

FIFO的读数据时序结构

standard mode,标准模式的读取时序

当在时钟上升沿检测到rd_en信号为高电平时,表示要从FIFO中读取数据,从dout信号输出

Standard模式下,当检测到rd_en信号为高电平时,下一个周期dout信号才会从FIFO中输出

在时序上看,dout数据会滞后rd_en信号一个时钟周奇奇

First-Word Fall-Through,FWFT,ahead模式

Xilinx/AMD的术语一般是用FWFT形容,Intel/Altera的术语一般是Look-Ahead Mode形容

这种模式下,默认dout可以不需要rd_en的允许直接输出FIFO的第一个数据,当检测到FIFO的读取使能信号rd_en之后,会去切换到第二个信号,输出顺序会比标准数据提前一个时钟周期

同步FIFO的RTL设计方法

FIFO也是存储器其的一种,即使FIFO在顶层是没有RAM的接口的,但是FIFO内部肯定是还会例化对应的RAM存储器来完成协作

定义一个ram存储器,使用二维数组直接定义

假设FIFO的数据位宽为:DATA_WIDTH

FIFO容量深度为:DATA_DEPTH

复制代码
reg [DATA_WIDTH-1:0] mem_ram[0:DATA_DEPTH-1];

定义位宽计算的函数:

复制代码
function integer clogb2(input integer data);
begin
    for(clogb2 = 0 ;data > 0;clogb2 = clogb2 + 1)
        data = data >>1;
endfunction

读写fifo指针

复制代码
reg [clgb(DATA_DEPTH-1)-1:0] r_wr_ptr;
reg [clgb(DATA_DEPTH-1)-1:0] r_rd_ptr;
reg [clgb(DATA_DEPTH-1):0] r_fifo_numbr;
  • 当写入一个数据时,wr_ptr指针+1
  • 当读取一个数据时,rd_ptr指针+1
  • 当wr_ptr[2:0]=3'b111时+1会变成wr_ptr[2:0]=3'b000,rd_ptr也是一样
  • 当wr_ptr[2:0]==rd_ptr[2:0]时是无法判断FIFO是满还是空
复制代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company       : NJUST-B335
// Engineer      : ZWZ 
// Create Date   : 2023/04/29 22:09:49
// Design Name   : sync_fifo
// Tool Versions : V1.2
// Description   : FIFO深度只能设置成2.4.8.16.32.64.128.256.512.1024等2的幂次方数据
//////////////////////////////////////////////////////////////////////////////////

module sync_fifo #(
    parameter   DATA_WIDTH = 8,
    parameter   DATA_DEPTH = 128 //2'b1000_0000,127-> 2'b0111_1111
)
(
    input  wire                         i_sys_clk                  ,
    input  wire                         i_sys_rst_n                ,
    input  wire                         i_wren                     ,
    input  wire                         i_rden                     ,
    input  wire        [DATA_WIDTH-1:0] i_wdata                    ,
    output wire        [DATA_WIDTH-1:0] o_rdata                    ,
    output wire                         o_full                     ,
    output wire                         o_empty                     
);


//位宽定义

    //定义ram的深度
    reg  [DATA_WIDTH-1:0]    mem_ram   [0:DATA_DEPTH-1];
    //读写fifo指针
    reg  [clogb2(DATA_DEPTH-1)-1 :0]    r_wr_ptr      ;
    reg  [clogb2(DATA_DEPTH-1)-1 :0]    r_rd_ptr      ;
    reg  [clogb2(DATA_DEPTH-1)   :0]    r_fifo_number ;

/*----------------------------------------------------------------------------------------\
----------------------------------write pointer address------------------------------------
\----------------------------------------------------------------------------------------*/


always@(posedge i_sys_clk or negedge i_sys_rst_n)
begin
    if(!i_sys_rst_n)
        r_wr_ptr <= 'd0;
    else if(i_wren && !o_full)
        r_wr_ptr <= r_wr_ptr + 1'b1;
    else 
        r_wr_ptr <= r_wr_ptr ;
end


always@(posedge i_sys_clk or negedge i_sys_rst_n)
begin
    if(!i_sys_rst_n)
        r_rd_ptr <= 3'b0;
    else if(i_rden && !o_empty)
        r_rd_ptr <= r_rd_ptr + 3'b1;
    else 
        r_rd_ptr <= r_rd_ptr ;
end


/*----------------------------------------------------------------------------------------\
----------------------------------- write read data --------------------------------------
\----------------------------------------------------------------------------------------*/
reg [DATA_WIDTH-1:0]       r_rdata  ;
always@(posedge i_sys_clk or negedge i_sys_rst_n)
begin
    if(!i_sys_rst_n)
        r_rdata <= 'd0;
    else if(i_rden && !o_empty)
        r_rdata <= mem_ram[r_rd_ptr];
end

integer  i  ;
always@(posedge i_sys_clk or negedge i_sys_rst_n)
begin
    if(!i_sys_rst_n)
    begin
        for(i = 0; i < DATA_DEPTH; i = i + 1)
        begin
            mem_ram[i] <= 'd0;
        end
    end
    else if(i_wren && !o_full)
        mem_ram[r_wr_ptr] <= i_wdata;
end


//fifo number cnt
always@(posedge i_sys_clk or negedge i_sys_rst_n)
begin
    if(!i_sys_rst_n)
        r_fifo_number <= 'd0 ;
    else if(i_wren  && i_rden  && !o_full && !o_empty)
        r_fifo_number <= r_fifo_number;
    else if(i_wren  && !i_rden && !o_full)
        r_fifo_number <= r_fifo_number + 1'b1;
    else if(!i_wren && i_rden  && !o_empty)
        r_fifo_number <= r_fifo_number - 1'b1;
    else 
        r_fifo_number <= r_fifo_number;
end

assign  o_rdata = r_rdata ;
assign  o_full  = (r_fifo_number == DATA_DEPTH) ;
assign  o_empty = (r_fifo_number == 4'd0      ) ;



//7  2'b111  3
function integer clogb2(input integer number);
begin
    for(clogb2 = 0 ; number > 0 ; clogb2 = clogb2 + 1 )
        number = number >> 1;
end
endfunction    

endmodule


`timescale 1ns/1ps  	 //时间精度
`define  CLK_PERILOD 20	 //时钟周期可变

module tb_sync_fifo;


parameter  DATA_WIDTH = 8   ;
parameter  DATA_DEPTH = 128 ;
//==================<端口>==================================================
reg                              clk          ;//时钟,50Mhz
reg                              rst_n        ;//复位,低电平有效
reg                              wren         ;
reg                              rden         ;
reg       [DATA_WIDTH-1:0]       wdata        ;
wire      [DATA_WIDTH-1:0]       rdata        ;
wire                             full         ;
wire                             empty        ;

//--------------------------------------------------------------------------
//--    模块例化
//--------------------------------------------------------------------------

sync_fifo u_sync_fifo(
    .i_sys_clk    (  clk     ),
    .i_sys_rst_n  (  rst_n   ),
    .i_wren       (  wren    ),
    .i_rden       (  rden    ),
    .i_wdata      (  wdata   ),
    .o_rdata      (  rdata   ),
    .o_full       (  full    ),
    .o_empty      (  empty   )
);


//----------------------------------------------------------------------
//--    时钟信号和复位信号
//----------------------------------------------------------------------
initial begin
    clk = 0;
    forever
        #(`CLK_PERILOD/2) clk = ~clk;
end

initial begin
    rst_n = 0; 
    repeat(20) @(posedge clk);
    rst_n = 1;
end

//----------------------------------------------------------------------
//--    设计输入信号
//----------------------------------------------------------------------
initial begin
    wren  = 0 ;
    wdata = 0 ;
    repeat(30) @(posedge clk);
    wren = 1 ;
    repeat(300) @(posedge clk) wdata = wdata + 1;
    $stop;
end

initial begin
    rden  = 0 ;
    repeat(130) @(posedge clk);
    rden = 1 ;
end

endmodule
相关推荐
水云桐程序员6 小时前
电子自动化技术(EDA技术)FPGA概述
运维·fpga开发·自动化
lf2824814316 小时前
05 AD9361 LVDS数字接口简介
fpga开发
Flamingˢ7 小时前
ZYNQ+OV5640+VDMA+HDMI视频链路搭建实录:从摄像头采集到实时显示
arm开发·嵌入式硬件·fpga开发·vim·音视频
Flamingˢ8 小时前
基于 FPGA 的帧间差分运动检测
人工智能·目标跟踪·fpga开发
fei_sun8 小时前
时序逻辑电路设计基础
fpga开发
知识充实人生1 天前
FPGA设计杂谈之十一:时序报告中时钟的上升沿与下降沿详解
fpga开发·时序分析·rise·fall·negedge·posedge
FPGA小迷弟2 天前
FPGA工程师面试题汇总(二十五)
网络协议·tcp/ip·fpga开发·verilog·fpga
Flamingˢ2 天前
ZYNQ + OV5640 + HDMI 视频系统调试记录:一次 RGB888 与 RGB565 引发的黑屏问题
arm开发·嵌入式硬件·fpga开发·vim·音视频
Flamingˢ2 天前
YNQ + OV5640 视频系统开发(二):OV5640_Data IP 核源码解析
arm开发·嵌入式硬件·网络协议·tcp/ip·fpga开发·vim·音视频