zoukankan      html  css  js  c++  java
  • 基于FPGA的异步FIFO设计

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

    对异步信号进行同步.jpg

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

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

    异步FIFO设计框架.jpg

    图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

  • 相关阅读:
    混沌的艺术--- YChaos通过数学公式生成混沌图像
    相声段子:How Are You
    太阳崇拜---64幅由算法生成的八芒星图像
    自然的密码---36幅由算法生成的六芒星图像
    雪花六出---几幅算法生成的雪花图像,并祝大家平安夜和圣诞节快乐
    18个分形图形的GIF动画演示
    python的with用法(参考)
    彻底解决django 2.2以上版本与mysql兼容性问题(不用改源码)
    python操作MySQL数据库的三个模块
    MySql 外键约束 之CASCADE、SET NULL、RESTRICT、NO ACTION分析和作用
  • 原文地址:https://www.cnblogs.com/chengqi521/p/8392158.html
Copyright © 2011-2022 走看看