今天要介绍的异步FIFO,可以有不同的读写时钟,即不同的时钟域。由于异步FIFO没有外部地址端口,因此内部采用读写指针并顺序读写,即先写进FIFO的数据先读取(简称先进先出)。这里的读写指针是异步的,处理不同的时钟域,而异步FIFO的空满标志位是根据读写指针的情况得到的。为了得到正确的空满标志位,需要对读写指针进行同步。一般情况下,如果一个时钟域的信号直接给另一个时钟域采集,可能会产生亚稳态,亚稳态的产生对设计而言是致命的。为了减少不同时钟域间的亚稳态问题,我们先对它进行两拍寄存同步,如图1所示。当然,对异步信号的寄存越多,产生亚稳态的概率就越小,但延时越多。不过一般情况下,寄存两拍就够了。为了继续减少亚稳态产生的概率,在对异步信号同步之前,将其转换为格雷码,使其每个状态只有一个位在变化。例如,假设N位二进制变量产生的亚稳态概率为a,那么二进制转换成格雷码后其产生的亚稳态概率则为a/N。

图1  对异步信号用两级寄存器同步

根据上述原理,设计了异步FIFO的架构,如图2所示。

图2  异步FIFO设计架构

根据异步FIFO的设计架构,归纳以下设计步骤:

写时钟域:

(1)根据写使能wr_en和写满标志位wr_full产生二进制写指针

(2)根据二进制写指针产生双端口RAM的写地址

(3)由二进制写指针转换成格雷码写指针

(4)对格雷码读指针在写时钟域中进行两级同步得同步后格雷码读指针

(5)同步后格雷码读指针转化成同步后二进制读指针

(6)步骤(3)与步骤(4)比较得写满标志位wr_full

(7)步骤(1)与步骤(5)相减得指示写FIFO的数据量

读时钟域:

(8)根据读使能rd_en和读空标志位rd_empty产生二进制读指针

(9)根据二进制读指针产生双端口RAM的读地址

(10)由二进制读指针转换成格雷码读指针

(11)对格雷码写指针在读时钟域中进行两级同步得同步后格雷码写指针

(12)同步后格雷码写指针转化成同步后二进制写指针

(13)步骤(10)与步骤(11)比较得读空标志位rd_empty

(14)步骤(8)与步骤(12)相减得指示读FIFO的数据量

Verilog HDL设计电路,如下所示:

/*******************************版权申明********************************
**                     电子技术应用网站, CrazyBird
**     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**------------------------------文件信息--------------------------------
** 文件名:          asyn_fifo.v
** 创建者:          CrazyBird
** 创建日期:        2016-1-16
** 版本号:           v1.0
** 功能描述:        异步FIFO,用于处理不同的时钟域
**                   
***********************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module asyn_fifo(
    wr_rst_n,
    wr_clk,
    wr_en,
    wr_data,
    wr_full,
    wr_cnt,
    rd_rst_n,
    rd_clk,
    rd_en,
    rd_data,
    rd_empty,
    rd_cnt
    );
    //******************************************************************
    //  参数定义
    //******************************************************************
    parameter   C_DATA_WIDTH = 8;
    parameter   C_FIFO_DEPTH_WIDTH = 4;
    
    //******************************************************************
    //  端口定义
    //******************************************************************
    input                                   wr_rst_n;
    input                                   wr_clk;
    input                                   wr_en;
    input       [C_DATA_WIDTH-1:0]          wr_data;
    output reg                              wr_full;
    output reg  [C_FIFO_DEPTH_WIDTH:0]      wr_cnt;
    input                                   rd_rst_n;
    input                                   rd_clk;
    input                                   rd_en;
    output      [C_DATA_WIDTH-1:0]          rd_data;
    output reg                              rd_empty;
    output reg  [C_FIFO_DEPTH_WIDTH:0]      rd_cnt;
    
    //******************************************************************
    //  内部变量定义
    //******************************************************************
    reg     [C_DATA_WIDTH-1:0]      mem     [0:(1 << C_FIFO_DEPTH_WIDTH)-1];
    wire    [C_FIFO_DEPTH_WIDTH-1:0]        wr_addr;
    wire    [C_FIFO_DEPTH_WIDTH-1:0]        rd_addr;
    wire    [C_FIFO_DEPTH_WIDTH:0]          next_wr_bin_ptr;
    wire    [C_FIFO_DEPTH_WIDTH:0]          next_rd_bin_ptr;
    reg     [C_FIFO_DEPTH_WIDTH:0]          wr_bin_ptr;
    reg     [C_FIFO_DEPTH_WIDTH:0]          rd_bin_ptr;
    wire    [C_FIFO_DEPTH_WIDTH:0]          next_wr_gray_ptr;
    wire    [C_FIFO_DEPTH_WIDTH:0]          next_rd_gray_ptr;
    wire    [C_FIFO_DEPTH_WIDTH:0]          syn_wr_bin_ptr_rd_clk;
    wire    [C_FIFO_DEPTH_WIDTH:0]          syn_rd_bin_ptr_wr_clk;
    wire    [C_FIFO_DEPTH_WIDTH:0]          syn_wr_gray_ptr_rd_clk;
    wire    [C_FIFO_DEPTH_WIDTH:0]          syn_rd_gray_ptr_wr_clk;
    wire    [C_FIFO_DEPTH_WIDTH:0]          wr_cnt_w;
    wire    [C_FIFO_DEPTH_WIDTH:0]          rd_cnt_w;
    wire                                    wr_full_w;
    wire                                    rd_empty_w;
    
    //******************************************************************
    //  双端口RAM的读写
    //******************************************************************
    //  写RAM
    always @(posedge wr_clk)
    begin
        if((wr_en & ~wr_full) == 1'b1)
            mem[wr_addr] <= wr_data;
    end
    //  读RAM
    assign rd_data = mem[rd_addr];
    
    //******************************************************************
    //  二进制写指针的产生
    //******************************************************************
    assign next_wr_bin_ptr = wr_bin_ptr + (wr_en & ~wr_full);
    
    always @(posedge wr_clk or negedge wr_rst_n)
    begin
        if(wr_rst_n == 1'b0)
            wr_bin_ptr <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};
        else
            wr_bin_ptr <= next_wr_bin_ptr;
    end
    
    //******************************************************************
    //  RAM写地址的产生
    //******************************************************************
    assign wr_addr = wr_bin_ptr[C_FIFO_DEPTH_WIDTH-1:0];
    
    //******************************************************************
    //  二进制写指针转换成格雷码写指针
    //******************************************************************
    bin2gray #(
        .C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
    )
    u_bin2gray_wr (
        .bin    (   next_wr_bin_ptr     ),
        .gray   (   next_wr_gray_ptr    )
    );
    
    //******************************************************************
    //  对格雷码读指针在写时钟域中进行两级同步
    //******************************************************************
    double_syn_ff #(
        .C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
    )
    u_double_syn_ff_wr (
        .rst_n  (   wr_rst_n                ),
        .clk    (   wr_clk                  ),
        .din    (   next_rd_gray_ptr         ),
        .dout   (   syn_rd_gray_ptr_wr_clk   )
    );
    
    //******************************************************************
    //  同步后的格雷码读指针转换成同步后的二进制读指针
    //******************************************************************
    gray2bin #(
        .C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
    )
    u_gray2bin_wr (
        .gray   (   syn_rd_gray_ptr_wr_clk  ),
        .bin    (   syn_rd_bin_ptr_wr_clk   )
    );
    
    //******************************************************************
    //  FIFO写满标志位的产生和写FIFO数据量的计数
    //******************************************************************
    assign wr_full_w = (next_wr_gray_ptr == ({~syn_rd_gray_ptr_wr_clk[C_FIFO_DEPTH_WIDTH:C_FIFO_DEPTH_WIDTH-1],
                        syn_rd_gray_ptr_wr_clk[C_FIFO_DEPTH_WIDTH-2:0]}));
    assign wr_cnt_w  = next_wr_bin_ptr - syn_rd_bin_ptr_wr_clk;
    
    always @(posedge wr_clk or negedge wr_rst_n)
    begin
        if(wr_rst_n == 1'b0)
        begin
            wr_full <= 1'b0;
            wr_cnt  <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};
        end
        else
        begin
            wr_full <= wr_full_w;
            wr_cnt  <= wr_cnt_w;
        end
    end
    
    //******************************************************************
    //  二进制读指针的产生
    //******************************************************************
    assign next_rd_bin_ptr = rd_bin_ptr + (rd_en & ~rd_empty);
    
    always @(posedge rd_clk or negedge rd_rst_n)
    begin
        if(rd_rst_n == 1'b0)
            rd_bin_ptr <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};
        else
            rd_bin_ptr <= next_rd_bin_ptr;
    end
    
    //******************************************************************
    //  RAM读地址的产生
    //******************************************************************
    assign rd_addr = rd_bin_ptr[C_FIFO_DEPTH_WIDTH-1:0];
    
    //******************************************************************
    //  二进制读指针转换成格雷码读指针
    //******************************************************************
    bin2gray #(
        .C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
    )
    u_bin2gray_rd (
        .bin    (   next_rd_bin_ptr     ),
        .gray   (   next_rd_gray_ptr    )
    );
    
    //******************************************************************
    //  对格雷码写指针在读时钟域中进行两级同步
    //******************************************************************
    double_syn_ff #(
        .C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
    )
    u_double_syn_ff_rd (
        .rst_n  (   rd_rst_n                ),
        .clk    (   rd_clk                  ),
        .din    (   next_wr_gray_ptr        ),
        .dout   (   syn_wr_gray_ptr_rd_clk  )
    );
    
    //******************************************************************
    //  同步后的格雷码写指针转换成同步后的二进制写指针
    //******************************************************************
    gray2bin #(
        .C_DATA_WIDTH(C_FIFO_DEPTH_WIDTH+1)
    )
    u_gray2bin_rd (
        .gray   (   syn_wr_gray_ptr_rd_clk  ),
        .bin    (   syn_wr_bin_ptr_rd_clk   )
    );
    
    //******************************************************************
    //  FIFO读空标志位的产生和读FIFO数据量的计数
    //******************************************************************
    assign rd_empty_w = (next_rd_gray_ptr == syn_wr_gray_ptr_rd_clk);
    assign rd_cnt_w   = syn_wr_bin_ptr_rd_clk - next_rd_bin_ptr;
    
    always @(posedge rd_clk or negedge rd_rst_n)
    begin
        if(rd_rst_n == 1'b0)
        begin
            rd_empty <= 1'b0;
            rd_cnt   <= {(C_FIFO_DEPTH_WIDTH+1){1'b0}};
        end
        else
        begin
            rd_empty <= rd_empty_w;
            rd_cnt   <= rd_cnt_w;
        end
    end
    
endmodule

其中,模块gray2bin是格雷码转二进制码,模块bin2gray是二进制码转格雷码,详情见上一篇博客,地址:http://blog.chinaaet.com/crazybird/p/5100000866 。模块double_syn_ff是两级寄存器,用于同步信号,对应的Verilog HDL实现如下所示:

/*******************************版权申明********************************
**                     电子技术应用网站, CrazyBird
**     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**------------------------------文件信息--------------------------------
** 文件名:          double_syn_ff.v
** 创建者:          CrazyBird
** 创建日期:        2016-1-16
** 版本号:           v1.0
** 功能描述:        对输入信号进行两级同步后输出
**                   
***********************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module double_syn_ff(
    rst_n,
    clk,
    din,
    dout
    );
    //******************************************************************
    //  参数定义
    //******************************************************************
    parameter   C_DATA_WIDTH = 8;
    
    //******************************************************************
    //  端口定义
    //******************************************************************
    input                               rst_n;
    input                               clk;
    input       [C_DATA_WIDTH-1:0]      din;
    output reg  [C_DATA_WIDTH-1:0]      dout;
    
    //******************************************************************
    //  内部变量定义
    //******************************************************************
    reg         [C_DATA_WIDTH-1:0]      data_r;
    
    //******************************************************************
    //  对输入信号进行两级同步后输出
    //******************************************************************
    always @(posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
            {dout,data_r} <= {(2*C_DATA_WIDTH){1'b0}};
        else
            {dout,data_r} <= {data_r,din};
    end
    
endmodule

由于字数的限制,异步FIFO的功能验证放在下一篇博文中吧!!!

转载:http://blog.chinaaet.com/crazybird/p/5100000872

基于FPGA的异步FIFO设计的更多相关文章

  1. 基于FPGA的异步FIFO验证

    现在开始对上一篇博文介绍的异步FIFO进行功能验证,上一篇博文地址:http://blog.chinaaet.com/crazybird/p/5100000872 .对异步FIFO验证的平台如图1所示 ...

  2. 基于FPGA的HDMI显示设计(三)

    上一篇:基于FPGA的VGA显示设计(二) 10月10日 ~ 20日期间实习,令我万万没想到的是实习题目是 “便携式高清电视显示屏测试系统原型设计” 也就是 “基于FPGA的视频显示”. 实习要求用 ...

  3. 基于FPGA的VGA显示设计(二)

    上一篇:基于FPGA的VGA显示设计(一)     参照 CrazyBingo 的 基于FPGA的VGA可移植模块终极设计代码  的工程代码风格,模块化处理了上一篇的代码,并增加了一点其它图形. 顶层 ...

  4. 基于SEDA的异步框架设计与实现

    基于SEDA的异步框架设计与实现 二.为什么使用SEDA 目前,面对并发环境,主流互联网服务器编程模型有两种:多线程模型以及事件驱动模型.但是这两个模型都不足以解决这个问题.我们来首先看一下这两种编程 ...

  5. 基于FPGA的VGA显示设计(一)

    前言 FPGA主要运用于芯片验证.通信.图像处理.显示VGA接口的显示器是最基本的要求了. 原理 首先需要了解 : (1)VGA接口协议:VGA端子_维基百科 .VGA视频传输标准_百度 引脚1 RE ...

  6. 异步FIFO设计

    参考http://www.cnblogs.com/BitArt/archive/2013/04/10/3010073.html http://blog.sina.com.cn/s/blog_6d30f ...

  7. 异步FIFO总结

    异步FIFO总结 异步FIFO的基本概念 异步FIFO读写分别采用相互异步的不同时钟,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据 FIFO的常见参数 FIFO的宽度:即FIFO ...

  8. 异步fifo的设计(FPGA)

    本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一.FIFO简单讲解 FIFO的本质是RAM, 先进先出 重要参数:fifo深度(简单来说就是需要存多少个数据)           ...

  9. 优化基于FPGA的深度卷积神经网络的加速器设计

    英文论文链接:http://cadlab.cs.ucla.edu/~cong/slides/fpga2015_chen.pdf 翻译:卜居 转载请注明出处:http://blog.csdn.net/k ...

随机推荐

  1. 漫话Asp.net

    经过一段时间的接触,对asp.net这一块进行了很多其它的了解,漫话一下. Asp.net与Web : asp.net属于动态网页技术,属于web应用程序开发. Web应用程序通常是B/S模式. 和B ...

  2. Android Studio中安装Genymotion模拟器

    Genymotion的安装: Genymotion无疑是目前最快最好用的模拟器.官网下载地址:https://www.genymotion.com/ 进到官网却找不到免费下载地址了,都需要money, ...

  3. ZH奶酪:PHP上传图片三个步骤

    1. 上传图片三步骤 第一步:首先判断文件类型是否为图片格式,若是则上传文件; 第二步:然后重命名文件(一般都是避免上传文件重名,现在基本上都是以为时间来命名); 第三步:最后把文件上传到指定目录,成 ...

  4. MySQL auto_increment_increment 和 auto_increment_offset

    参考这一篇文章:(不过我对这一篇文章有异议) http://blog.csdn.net/leshami/article/details/39779509 1:搭建测试环境 create table t ...

  5. 【转】Html.RenderPartial与 Html.RenderAction的区别

    Html.RenderPartial与Html.RenderAction 这个两个方法都是用于把MVC用户控件嵌入到View中. Html.RenderPartial是直接将MVC用户控件嵌入到界面上 ...

  6. Python之L.pop()和del L[i]

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #Python之L.pop()和del L[i] #http://python.jobbole.com/826 ...

  7. 非IT人士的云栖酱油之行 (程序猿迷妹的云栖之行)

    摘要: 熟悉我的人都知道,我是一个贪玩儿且不学无术的姑娘,对于互联网我也是知之甚少:这次去到杭州参加阿里巴巴集团主办的为期4天的科技大会也是很例外:但是不得不说这次的会议真是让我很震惊.今天我就和大家 ...

  8. Go 1.5keyword搜索文件夹、文件、文件内容_修复一个小BUG

    package main import ( "bufio" "bytes" "flag" "fmt" "io& ...

  9. 【TP3.2】模块化设计,新建Admin模块

    1.模块化: Application 默认应用目录(可以设置) ├─Common 公共模块(不能直接访问) ├─Home 前台模块 ├─Admin 后台模块 ├─... 其他更多模块 ├─Runtim ...

  10. 微信小程序:input输入框和form表单几种传值和取值方式

    1.传值:index下标传值.页面navigator传值 1.index下标 实现方式是:data-index="{{index}}"挖坑及e.currentTarget.data ...