精华内容
下载资源
问答
  • 异步FIFO设计

    2021-02-03 18:00:37
    摘要:本文介绍如何应用美国QUICKLOGIC公司的QUICKRAM器件设计高速、高可靠异步FIFO(AsynchronousFIFO)。 关键词:异步FIFO 异步FIFO广泛地用于计算机网络工业中进行非同步数据传送,这里的非同步指按一种速率...
  • 深度不为2的幂次方的异步FIFO设计!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  • 本文提出一种新颖的异步FIFO设计方案,它通过先比较读写地址并结合象限检测法产生异步的空/满标志,再把异步的空/满标志同步到相应的时钟域。通过仿真验证,该方法是稳定有效的。
  • 摘要:FIFO经常应用于从一个时钟域传输数据到另一个异步时钟域...为解决异步FIFO设计过程中空满标志判断难以及FPGA亚稳态的问题,提出一种新颖的设计方案,即利用格雷码计数器(每次时钟到来仅有1位发生改变)表示读/写
  • 分析了异步FIFO的结构和关键技术,在与利用格雷码作为异步FIFO指针编码对比的基础上,提出了一种采用移位码编码方式的FIFO,不仅减小了亚稳态出现的概率,也简化了电路结构,降低了电路面积和功耗,在此基础上也缩短...
  • 本文介绍一种充分利用FPGA内部的RAM资源,在FPGA内部实现异步FIFO模块的设计方法。这种异步FIFO比外部FIFO 芯片更能提高系统的稳定性。  1 FIFO的基本结构和工作原理  FIFO(First In First Out)是一种采用环形...
  • 异步FIFO设计思路指导

    2019-02-20 13:08:56
    在大规模ASIC或FPGA设计中,多时钟系统往往是不可避免的,这样就产生了不同时钟域数据传输的问题,其中一个比较好的解决方案就是使用异步FIFO来作不同时钟域数据传输的缓冲区,这样既可以使相异时钟域数据传输的时序...
  • 异步fifo设计

    2014-05-07 18:28:28
    异步fifo程序设计
  • 数字电路设计-异步FIFO设计

    千次阅读 2019-05-08 09:39:52
    本文大部分内容来自Clifford E. Cummings的《Simulation and Synthesis ...FIFO Design》,经过自己的一些改变,理论部分为转载,代码自己完成。 一、FIFO简介  FIFO是英文First In First Out 的缩写,是一种先进...

      本文大部分内容来自Clifford E. Cummings的《Simulation and Synthesis Techniques for Asynchronous FIFO Design》,经过自己的一些改变,理论部分为转载,代码自己完成。

    一、FIFO简介

      FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,它与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

    用途1:

      异步FIFO读写分别采用相互异步的不同时钟。在现代集成电路芯片中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步FIFO是这个问题的一种简便、快捷的解决方案,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据。

    用途2:

      对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。

    二、分类

      同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作;

      异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

    三、FIFO的常见参数

    • FIFO的宽度:即FIFO一次读写操作的数据位;
    • FIFO的深度:指的是FIFO可以存储多少个N位的数据(如果宽度为N)。
    • 满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。
    • 空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。
    • 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
    • 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

    ===============================分   隔    符 ==============================

    1. 读写指针的工作原理

      写指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为0)

      读指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0)

     

    1. FIFO的“空”/“满”检测

      FIFO设计的关键:产生可靠的FIFO读写指针和生成FIFO“空”/“满”状态标志。

      当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时,或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,如下图所示:

              

     

      当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:

            

     

        为了区分到底是满状态还是空状态,可以采用以下方法:

        方法1:在指针中添加一个额外的位(extra bit),当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2nFIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8FIFO,需要采用4bit的计数器,0000100010011111MSB作为折回标志位,而低3位作为地址指针。

      • 如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,w_addr = 1000,为满。
      • 如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空;

    3.二进制FIFO指针的考虑

      将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。

        

     

    4.

    • 使用gray码进行对比,如何判断“空”与“满”

       使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。

      对于“空”的判断依然依据二者完全相等(包括MSB)

      而对于“满”的判断,如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:

    • wptr和同步过来的rptrMSB不相等,因为wptr必须比rptr多折回一次。
    • wptrrptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是01111111MSB不同说明多折回一次,111相同代表同一位置。
    • 剩下的其余位完全相等。

          

    5.总体实现

        系统的总体框图如下:

            

     四、同步化分析

    由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决?
      跨时钟域的问题:由于读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域的写指针不做任何处理直接比较肯定是错误的,因此我们需要进行同步处理以后仔进行比较
      解决方法加两级寄存器同步 + 格雷码(目的都是消除亚稳态)
     
    1.使用异步信号进行使用的时候,好的设计都会对异步信号进行同步处理,同步一般采用多级D触发器级联处理,如下图。这种模型大部分资料都说的是第一级寄存器产生亚稳态后,第二级寄存器稳定输出概率为90%,第三极寄存器稳定输出的概率为99%,如果亚稳态跟随电路一直传递下去,那就会另自我修护能力较弱的系统直接崩溃。

     

    2.将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。

    那么,多位二进制码如何转化为格雷码?

     

    换一种描述方法:

    verilog代码实现就一句:assign  gray_code = (bin_code>>1)  ^  bin_code;

     使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。 

    这里直接给出结论:

      判断读空时:需要读时钟域的格雷码rgray_next和被同步到读时钟域的写指针rd2_wp每一位完全相同;

      判断写满时:需要写时钟域的格雷码wgray_next和被同步到写时钟域的读指针wr2_rp高两位不相同,其余各位完全相同;

    assign full = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ;//高两位不同 assign empty = ( rd_addr_gray == wr_addr_gray_d2 );

    五、Verilog实现

    module fifo_async#(
                     parameter   data_width = 16,
                     parameter   data_depth = 256,
                     parameter   addr_width = 8
    )
    (
                      input                           rst,
                      input                           wr_clk,
                      input                           wr_en,
                      input      [data_width-1:0]     din,         
                      input                           rd_clk,
                      input                           rd_en,
                      output reg                     valid,
                      output reg [data_width-1:0]     dout,
                      output                          empty,
                      output                          full
        );
    
    
    reg    [addr_width:0]    wr_addr_ptr;//地址指针,比地址多一位,MSB用于检测在同一圈
    reg    [addr_width:0]    rd_addr_ptr;
    wire   [addr_width-1:0]  wr_addr;//RAM 地址
    wire   [addr_width-1:0]  rd_addr;
    
    wire   [addr_width:0]    wr_addr_gray;//地址指针对应的格雷码
    reg    [addr_width:0]    wr_addr_gray_d1;
    reg    [addr_width:0]    wr_addr_gray_d2;
    wire   [addr_width:0]    rd_addr_gray;
    reg    [addr_width:0]    rd_addr_gray_d1;
    reg    [addr_width:0]    rd_addr_gray_d2;
    
    
    reg [data_width-1:0] fifo_ram [data_depth-1:0];
    
    //=========================================================write fifo 
    genvar i;
    generate 
    for(i = 0; i < data_depth; i = i + 1 )
    begin:fifo_init
    always@(posedge wr_clk or posedge rst)
        begin
           if(rst)
              fifo_ram[i] <= 'h0;//fifo复位后输出总线上是0,并非ram中真的复位。可无
           else if(wr_en && (~full))
              fifo_ram[wr_addr] <= din;
           else
              fifo_ram[wr_addr] <= fifo_ram[wr_addr];
        end   
    end    
    endgenerate    
    //========================================================read_fifo
    always@(posedge rd_clk or posedge rst)
       begin
          if(rst)
             begin
                dout <= 'h0;
                valid <= 1'b0;
             end
          else if(rd_en && (~empty))
             begin
                dout <= fifo_ram[rd_addr];
                valid <= 1'b1;
             end
          else
             begin
                dout <=   'h0;//fifo复位后输出总线上是0,并非ram中真的复位,只是让总线为0;
                valid <= 1'b0;
             end
       end
    assign wr_addr = wr_addr_ptr[addr_width-1-:addr_width];
    assign rd_addr = rd_addr_ptr[addr_width-1-:addr_width];
    //=============================================================格雷码同步化
    always@(posedge wr_clk )
       begin
          rd_addr_gray_d1 <= rd_addr_gray;
          rd_addr_gray_d2 <= rd_addr_gray_d1;
       end
    always@(posedge wr_clk or posedge rst)
       begin
          if(rst)
             wr_addr_ptr <= 'h0;
          else if(wr_en && (~full))
             wr_addr_ptr <= wr_addr_ptr + 1;
          else 
             wr_addr_ptr <= wr_addr_ptr;
       end
    //=========================================================rd_clk
    always@(posedge rd_clk )
          begin
             wr_addr_gray_d1 <= wr_addr_gray;
             wr_addr_gray_d2 <= wr_addr_gray_d1;
          end
    always@(posedge rd_clk or posedge rst)
       begin
          if(rst)
             rd_addr_ptr <= 'h0;
          else if(rd_en && (~empty))
             rd_addr_ptr <= rd_addr_ptr + 1;
          else 
             rd_addr_ptr <= rd_addr_ptr;
       end
    
    //========================================================== translation gary code
    assign wr_addr_gray = (wr_addr_ptr >> 1) ^ wr_addr_ptr;
    assign rd_addr_gray = (rd_addr_ptr >> 1) ^ rd_addr_ptr;
    
    assign full = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ;//高两位不同
    assign empty = ( rd_addr_gray == wr_addr_gray_d2 );
    
    endmodule
    
    

    仿真

    六、重要补充

    关于异步FIFO的关键技术,有两个,一个是格雷码减小亚稳态,另一个是指针信号跨异步时钟域的传递。我在自己写异步FIFO的时候也很疑惑,地址指针在同步化的时候,肯定会产生至少两个周期的延迟,如果是从快时钟域到慢时钟域,快时域的地址指针并不能都被慢时域的时钟捕获,同步后的指针比起实际的指针延迟会更大。如果以此来产生fifo_empty和fifo_full 信号会非常不准器。
    查找资料和仿真后发现,数字电路的世界真的很神奇,还有很多的东西需要去学习。非常巧妙,FIFO中的一个潜在的条件是write_ptr总是大于或者等于read_ptr;分为两种情况,写快读慢和写慢读快。
    1.在写时钟大于读时钟时,产生fifo_empty信号,需要将write_ptr同步到读时钟域,写指针会有延时,可能比实际的写地址要小,如果不满足fifo_empty的产生条件,没问题。如果满足fifo_empty的触发条件,说明此时同步后的write_ptr == read_ptr,即实际的write_ptr >= read_ptr,最坏的情况就是write_ptr > read_ptr,像这种FIFO非空而产生空标志信号的情况称为“虚空”,但是也并不影响FIFO的功能。
    2.在写时钟大于读时钟时,产生fifo_full信号,需要将read_ptr同步到写时钟域,读指针会有延时,可能比实际的读地址要小,如果不满足fifo_full的产生条件,没问题。如果满足fifo_full的触发条件,说明此时同步后的read_ptr == write_ptr - fifo_depth,即实际的read_ptr >= read_ptr - fifo_depth,最坏的情况就是read_ptr > read_ptr - fifo_depth,像这种FIFO非满而产生满标志信号的情况称为“虚满”,但是也并不影响FIFO的功能。
    写慢读快的情况也同上,并没有大的差异,不再分析。

    关于格雷码减小亚稳态,如果读写时钟差距过大,从快时钟域同步到慢时钟域的信号,时钟捕获的相邻两个数据变化并不是只有一个bit位的改变,可能导致格雷码失去原来的意义,嗯,目前的理解是这样。

    展开全文
  • 在大规模ASIC或FPGA设计中,多时钟系统往往是不可避免的,这样就产生了不同时钟域数据传输的问题,其中一个比较好的解决方案就是使用异步FIFO来作不同时钟域数据传输的缓冲区,这样既可以使相异时钟域数据传输的时序...
  • FPGA内部具有丰富的存储单元,易于实现各种存储器(如FIFO、双口RAM等);另外,基于查找表的逻辑单元可用于实现各种数字信号处理(如滤波等),以辅助DSP处理器做各种预处理。  TI公司推出的高性能数字信号处理芯片...
  • 异步FIFO设计注意事项

    2019-08-25 22:21:05
    在我看来,异步FIFO设计注意事项大体分为两点: 亚稳态的处理 空满状态的判断 第一,来说一下亚稳态。亚稳态是指触发器无法在某个规定时间内达到一个可确认的状态。在FIFO的设计中,产生的原因主要由两点:...

    近日学习用到异步FIFO,故写下这篇博客记录学习心得。
    在我看来,异步FIFO的设计注意事项大体分为两点:

    1. 亚稳态的处理
    2. 空满状态的判断

    第一,来说一下亚稳态。亚稳态是指触发器无法在某个规定时间内达到一个可确认的状态。在FIFO的设计中,产生的原因主要由两点:一是逻辑电平的误判,也就是如果通过二进制作为指针来判断空满状态,因二进制数值变化引起的位数变化大,对电路的危害也随之增加,故在本设计中使用个格雷码。二是信号和时钟之间不满足要求,即建立时间和保持时间未满足,在这里通过打两级寄存器来消除亚稳态使信号稳定。

    第二,关于空满状态的判断,常使用的方法为附加位比较法。对于二进制指针而言,FIFO的满状态时写指针比读指针多循环了一次,而且两个数只是最高位不同,其余各位都相同。那么我就可以这样判断,如果读指针和写指针的最高位不同,而其他位都相同时,此时的状态为满状态。FIFO的空状态时写指针和读指针相同,那么此时读指针和写指针相等。这样读指针和写指针就变成了一个n位指针,其中低n-1位时用来存放FIFO存储器的地址,可以用来对2^(n-1)个存储单元寻址,而最高位则用来辨别空满状态。

    但是格雷码在判断空满状态时和二进制有出入。当格雷码由7到8时(0100到1100),可以看到指针的附加位改变,但是地址位并未变化,这是因为格雷码是一种镜像码造成的。所以我们需要附加位和地址位分开循坏,此时就需要既能产生n位格雷码又能产生n-1位格雷码的计数器,这种计数器被称为“两重格雷码计数器”。格雷码指针的空状态也是判断读指针和写指针是否相等。但是判断满状态和二进制指针有很大不同。首先应该明白数据存入的顺序并不是按照000递增开始的,而是随机的。例如一次写入数据是从十进制的地址6开始,连续写入8个数据,地址指向14,这是存储器存满8个数据,应该产生满状态输出。地址6的格雷码0101,地址14的格雷码1001。假如从十进制的地址2开始,结束地址指向10,此时地址2的格雷码为0011,地址10的格雷码为1111,观察两次例子,起始地址和结束地址的格雷码后两位都相同,前两位的异或值相等,并且起始地址和结束地址的首位是不同的。那么4位格雷码判断满状态就可以分为3步:首先后两位相同,然后前两位的异或值相等,最后首位不同。

    展开全文
  • 本文介绍一种充分利用FPGA内部的RAM资源,在FPGA内部实现异步FIFO模块的设计方法。这种异步FIFO比外部FIFO 芯片更能提高系统的稳定性。  1 FIFO的基本结构和工作原理  FIFO(First In First Out)是一种采用环形...
  • 在视音频嵌入解嵌系统中,嵌入音频、音频解嵌与音频转换成音频帧标准格式输出都是工作在不同的时钟频率下的。...针对这个问题,本文介绍了一种适合音频解嵌的高效异步FIFO,对通用异步FIFO进行了一些改
  • 大侠们,江湖偌大,有缘相见,欢迎一叙,今天来聊一聊在数字电路设计中,基于FPGA的异步FIFO的实现,在很多厂商的开发软件中都有异步FIFO IP核,为安全起见推荐使用IP核定制FIFO,本文的目的只是作为思路参考。...

    大侠们,江湖偌大,有缘相见,欢迎一叙,今天来聊一聊在数字电路设计中,基于FPGA的异步FIFO的实现,在很多厂商的开发软件中都有异步FIFO IP核,为安全起见推荐使用IP核定制FIFO,本文的目的只是作为思路参考。

    首先,我们来了解一下什么事FIFO,FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,它与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

     

    分类以及用途

    可分为同步FIFO和异步FIFO,同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作;异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

    异步FIFO读写分别采用相互异步的不同时钟。在现代集成电路芯片中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步FIFO是这个问题的一种简便、快捷的解决方案,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据。

    对于不同宽度的数据接口也可以用FIFO,例如单片机8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。

     

    FIFO的常见参数

    FIFO的宽度:即FIFO一次读写操作的数据位;

    FIFO的深度:指的是FIFO可以存储多少个N位的数据(假设宽度为N)。

    满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。

    空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。 

    读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。

    写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

     

    读写指针的工作原理

    写指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为0)。

    读指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0)。

     

    FIFO的“空”/“满”检测

    FIFO设计的关键:产生可靠的FIFO读写指针和生成FIFO“空”/“满”状态标志。

    当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时,或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,如下图所示:

    当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:

    为了区分到底是满状态还是空状态,可以采用以下方法:

    在指针中添加一个额外的位(extra bit),当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针。

    如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。

    如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空;

     

    二进制FIFO指针的考虑

    将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。

     

    使用gray码进行对比,如何判断“空”与“满”

    使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。

    对于“空”的判断依然依据二者完全相等(包括MSB);

    而对于“满”的判断,如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:

    1.wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。

    2.wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。

    3.剩下的其余位完全相等。

     

    总体实现

    系统的总体框图如下:

    1) 顶层模块

    2)DualRAM模块

    3)同步模块

    a

    b

    4)空判断逻辑

    5)满判断逻辑

     

    大侠们,江湖偌大,继续闯荡!

    展开全文
  • 异步FIFO,Verilog源码

    2018-10-31 10:22:54
    异步FIFO,Verilog源码实现异步FIFO异步FIFO的原理,
  • 异步fifo设计,仿真 前面研究了同步fifo,并进行了仿真验证,有关同步fifo问题可以参考本人上一篇博客,本次主要对异步fifo进行设计仿真验证。 与同步fifo相比,异步fifo主要不同之处在于读写时钟不同,因此异步...

    异步fifo设计,仿真

    前面研究了同步fifo,并进行了仿真验证,有关同步fifo问题可以参考本人上一篇博客,本次主要对异步fifo进行设计仿真验证。
    与同步fifo相比,异步fifo主要不同之处在于读写时钟不同,因此异步fifo需要处理的问题较为复杂,通常需要处理注意的问题点有以下几点:

      1. 不同时钟域之间信号的同步化处理。
      1. 异步fifo的空状态与满状态的判断。

    fifo主要有两个目的,速度匹配或者数据宽度匹配。需要进行满状态,空状态的判断,实现复杂。

    异步fifo框图

    异步fifo

    不同时钟域之间信号的同步化处理。

    针对读写时钟域的不同采用寄存器打两拍来实现同步化处理。另外由于二进制在两个时钟域之间传递,考虑到这个问题将二进制转换为gray(格雷码),由于gray
    相邻两位之间只有一位不同,因此在不同域之间传递减小了出错的概率。(两种方式结合都是为了较小亚稳态)

    二进制转换为gray方法

    在这里插入图片描述
    verilog 实现二进制转gray

    //---------------gray----------
    assign r_gray = (r_reg2 >> 1) ^r_reg2;
    assign w_gray = (w_reg2 >> 1) ^w_reg2;

    判断fifo的空满状态

    判断fifo的状态需要考虑几个问题:

    • 1当读时钟快于写时钟的时候如何考虑;
    • 2当读时钟慢于写时钟的时候如何考虑;
    • 3何时将不同的时钟同步到另一个时钟域;
    • 4如何用格雷码进行fifo状态的判断;
      以上几个问题可以总结为,需要判断哪个状态就将另一个时钟同步过来,例如判断fifo的满状态,因为fifo的状态影响到fifo的写功能,因此我们需要在写时钟域进行判断,即将读始终同步到写时钟域。另外在判断fifo状态时会出现虚空虚满的状态,但是不影响实际的功能。

    verilog代码

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: zhangsy
    // 
    // Create Date: 2019/08/05 11:58:12
    // Design Name: 
    // Module Name: asynfifo
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module asynfifo#(
    									parameter data_depth = 4,
    									parameter data_width = 4,
    									parameter ptr_width  = 2
    								)
    								(
    								
    									input w_clk,r_clk,
    									input write,read,
    									input rst_n,
    									input [data_width-1:0] data_in,
    									output [data_width-1:0] data_out,
    									output  valid,
    									output full,
    									output emty
    								);
    ///
    //name              enable
    //write               H
    //read                H
    //rst_n               L
    //valid               H
    //full                H
    //emty                H
    /
    reg [data_width-1:0]fifo [data_depth-1:0];
    reg [ptr_width:0] r_ptr;
    wire [ptr_width:0]r_ptr_g;
    reg [ptr_width:0] w_ptr;
    wire [ptr_width:0] w_ptr_g;
    //****************update r_ptr************
    always @ (posedge r_clk)
    	if(!rst_n)
    		r_ptr <= 2'd0;
    	else if(read && !emty )
    		r_ptr <= r_ptr + 1'b1;
    	assign r_ptr_g = (r_ptr >>1) ^r_ptr;
    //****************update w_ptr************
    always @ (posedge w_clk)
    	if(!rst_n)
    		w_ptr <= 2'd0;
    	else if(write && !full )
    		w_ptr <= w_ptr + 1'b1;	
    		
    assign w_ptr_g = (w_ptr >>1) ^w_ptr;
    
    //---------------------------------read-------------
    //******************synchronization w_ptr***********
    reg[ptr_width:0] w_reg1,w_reg2;
    wire [ptr_width:0] w_gray;
    reg [data_width-1:0] dout;
    reg valid_r;
    always @ (posedge r_clk)
    	if(!rst_n)
    		{w_reg1,w_reg2} <= 2'b00;
    	else
    		{w_reg1,w_reg2} <= {w_ptr,w_reg1};
    		
    always @(posedge r_clk)
    	if(!rst_n)
    		begin
    			dout <= 'h0;
    			valid_r <= 1'b0;
    		end
    	else if(read && !emty)
    		begin
    			dout <= fifo[r_ptr[ptr_width-1-:ptr_width]];
    			valid_r <= 1'b1;
    		end 
    		else
    		begin
    			dout <= 'h0;
    			valid_r <= 1'b0;
    		end
    //---------------------------------write-------------
    //******************synchronization r_ptr***********
    reg[ptr_width:0] r_reg1,r_reg2;
    wire [ptr_width:0] r_gray;
    
    always @ (posedge w_clk)
    	if(!rst_n)
    		{r_reg1,r_reg2} <= 2'b00;
    	else
    		{r_reg1,r_reg2} <= {r_ptr,r_reg1};
    
    always @ (posedge w_clk)
    	 if(write && !full)
    		begin
    			fifo[w_ptr[ptr_width-1-:ptr_width]] <= data_in;
    		end 
    
    //---------------gray----------
    assign r_gray = (r_reg2 >> 1) ^r_reg2;
    assign w_gray = (w_reg2 >> 1) ^w_reg2;	
    
    assign full =  ({~w_ptr_g[ptr_width:ptr_width-1],w_ptr_g[ptr_width-2:0]}== r_gray);	
    assign emty =  (w_gray == r_ptr_g);
    assign data_out = dout;
    assign valid = valid_r;
    endmodule
    
    
    

    仿真结果

    在这里插入图片描述

    激励文件

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: zhangsy
    // 
    // Create Date: 2019/08/05 13:03:14
    // Design Name: 
    // Module Name: simtxt
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module simtxt();
    reg w_clk,r_clk;
    reg write,read;
    reg rst_n;
    reg [3:0] data_in;
    wire  [3:0] data_out;
    wire  valid;
    wire  full;
    wire  emty;
    initial
    begin
        w_clk = 0;
        r_clk = 0;
        rst_n = 0;
         #80
           rst_n = 1;
           write =0;
           read = 0;
           #20
           write = 1;
          // #20
           data_in = 4'd1;
            #20
              data_in = 4'd2;
               #20
                 data_in = 4'd3;
                  #20
                    data_in = 4'd4;
            
              #20
                write =0;
                
              read =1;
              #200;
              read = 0;
              #40
             write = 1;
                 // #20
                  data_in = 4'd6;
                   #20
                     data_in = 4'd7;
                    // read = 1;
                      #20
                        data_in = 4'd8;
                         #20
                           data_in = 4'd9;
                            read = 1;
               #200
                          //    write = 1;
                             
                             // #20read
                              data_in = 4'd5;
                               #20
                                 data_in = 4'd5;
                                  #20
                                   read =0;
                                    data_in = 4'd4;
                                     #20
                                       data_in = 4'd4;
                                
                                 #20
                                   write =0;
                                 read =1;  
    end			
    always #10 w_clk = ~w_clk;
    always #20 r_clk = ~r_clk;						
    asynfifo asynfifo_inst(                                          
                    .w_clk,
                    .r_clk,
                    .write,
                    .read,
                    .rst_n,
                    .data_in,
                    .data_out,
                    .valid,
                    .full,
                    .emty
                );
    endmodule
    
    

    实际设计中需要注意的问题

    • 1考虑格雷码的对称性,在进行读写指针读取数据时应该去除最高位否则会影响fifo的读写功能。
    • 2在进行fifo状态判断时要注意时钟域的转换
    • 3fifo满状态的判断是将写指针的最高位与次高位取反,在与读指针进行判断,同理将读指针高两位取反判断亦可,具体参考博主程序设计
    • 4fifo空状态的判断为读写指针完全相等时即为空。
      github
    展开全文
  • 异步FIFO设计的源代码,工程,仿真波形 verilog
  • 设计是基于Verilog的异步FIFO设计,所需的RAM由IP core例化而来,不是自己设计的,因而时序性要好。同时读写位宽不一样。写位宽为8bit,读位宽为32bit。
  • 无论是数据通讯或者SOC(包括FPGA或者ASIC设计设计,跨时钟域(clock domain crossing)处理都是一件让人很头疼的事情,无论是在设计的前端或者步入设计的后端,都没有很好的工具去做保证,只能靠设计人员的经验...
  • 一、亚稳态状态 对于使用上升沿触发的触发器来说, 建立时间(Setup Time)是在时钟上升沿到来之前,触发器数据保持稳定的最小时间; 而保持时间(Hold Time)是在时钟上升沿到来之后,触发器数据还应该保持的最小时间。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,582
精华内容 8,632
关键字:

异步fifo设计