精华内容
下载资源
问答
  • 跨时钟域信号处理(二)——异步fifo的Verilog实现(附同步fifo的实现)
    千次阅读 多人点赞
    2019-11-03 15:51:50

    需要回答几个问题:

    1.什么是异步FIFO,异步FIFO有什么功能?
    跨时钟域的数据交换,防止亚稳态。

    2.在产生写满与读空信号时需要进行跨时钟域如何做的,且如何能正确指示空满状态?
    寄存器打两拍+格雷码。
    格雷码的具体作用1
    写读的地址是用二进制表示的,只是在将地址同步到对方的时钟域下得时候才会变成格雷码,因为格雷码相邻只有1位不同,即使在同步过程中同步错误,例如000->001,错误的结果仅仅为将原状态000同步过去,比如读同步到写,结果是在没满的时候会提前报写满,不会覆盖数据:<=满。读的情况一样。

    3.异步FIFO的写满与读空信号如何利用格雷码正确产生?

    格雷码的具体作用2
    另一方面就是如何判断空满,假设fifo空间有8个,0-7,假设读地址是0000,当写地址为8时,实际写地址为1000,对应格雷码是1100,这时候是写满了,读地址的格雷码0000,写地址格雷码1100,二者对应高位与次高位不同,其余位相同,标志写满。
    读空是二者格雷码完全相同,这个好理解:读是跟在写后面的,读得再快也是跟上了写,故而‘相同’。

    总结:格雷码的唯一目的就是即使在亚稳态进行读写指针抽样,也能进行正确的空满状态指示。

     

    原理框图:

    异步FIFO的Verilog设计[2]:

    module for_practice#(
    parameter N = 8,WD = 3
    )(
    input w_clk,
    input r_clk,
    input arst,
    input [N-1:0] i_dat,
    input i_wren,
    input i_rden,
    output [N-1:0] o_dat,
    output reg empty,       
    output reg full       
        );
    reg[N-1:0]fifo_mem[2**WD-1:0];
    wire [WD-1:0]wadd,radd;
    
    reg [WD:0] wbin,rbin,wgray,rgray;
    wire[WD:0] wbin_next,rbin_next;
    wire[WD:0] wgray_next,rgray_next;
    /*******************************************************/
    //产生写地址
    assign wbin_next  = wbin + (i_wren & ~full );
    assign wgray_next = (wbin_next>>1)^wbin_next;
    assign wadd = wbin[WD-1:0];
    always@(posedge w_clk or posedge arst)
    begin
        if(arst)    {wbin,wgray} <= 0;
        else        {wbin,wgray} <= {wbin_next,wgray_next};
    end
    //将读格雷地址同步到写时钟域下,进行写满判断
    reg [WD:0] rgray1,rgray2;
    always@(posedge w_clk or posedge arst)
    begin
        if(arst)    {rgray2,rgray1} <= 0;
        else        {rgray2,rgray1} <= {rgray1,rgray};
    end
    wire fullw;
    assign fullw = {wgray_next == {~rgray2[WD:WD-1],rgray2[WD-2:0]}};
    always@(posedge w_clk or posedge arst)
        if(arst)    full <= 0;
        else        full <= fullw;
    //写入数据
    always@(posedge w_clk)
        if(i_wren && !full)
            fifo_mem[wadd] <= i_dat ;
    
    /*******************************************************/
    //产生读地址
    assign rbin_next  = rbin + (i_rden & ~empty );
    assign rgray_next = (rbin_next>>1)^rbin_next;
    assign radd = rbin[WD-1:0];
    always@(posedge r_clk or posedge arst)
        if(arst)    {rbin,rgray} <= 0;
        else        {rbin,rgray} <= {rbin_next,rgray_next};
    //将写格雷地址同步到读时钟域下,进行读空判断
    reg [WD:0] wgray1,wgray2;
    always@(posedge r_clk or posedge arst)
        if(arst)    {wgray2,wgray1} <= 0;
        else        {wgray2,wgray1} <= {wgray1,wgray};
    wire emptyr;
    assign emptyr = {rgray_next == wgray2};
    always@(posedge r_clk or posedge arst)
        if(arst)    empty <= 0;
        else        empty <= emptyr;
    //读数据
    assign o_dat = fifo_mem[radd];
    /*******************************************************/
               
    endmodule 

    同步FIFO的Verilog设计

    思路就是将异步FIFO的跨时钟域的两拍寄存器与格雷码的部分去掉即可。

    //读写地址
    //当最高位相同,其余位相同认为是读空
    //当最高位不同,其余位相同认为是写满
    //同步fifo
    module syn_fifo#(
    parameter N = 8,WD = 3
    )(
    input clk,
    input arst,
    input [N-1:0] i_dat,
    input i_wren,
    input i_rden,
    output [N-1:0] o_dat,
    output reg empty,       
    output reg full 
    );
    reg[N-1:0]fifo_mem[2**WD-1:0];//可以在复位时进行初始化为0
    reg [WD:0] wbin,rbin;
    wire [WD:0] wbin_next,rbin_next;
    
    wire [WD-1:0]wadd,radd;
    wire fullw,emptyr;
    
    //产生写地址
    assign wbin_next = wbin +(i_wren & ~full );
    assign wadd = wbin[WD-1:0];
    always@(posedge clk , posedge arst)
    if(arst)    wbin <= 0;
    else        wbin <= wbin_next;
    
    //写入数据
    always@(posedge clk)
    if(i_wren && !full)     fifo_mem[wadd] <= i_dat ;
    
    //产生读地址
    assign rbin_next = rbin +(i_rden & ~empty );
    assign radd = rbin[WD-1:0];
    always@(posedge clk , posedge arst)
    if(arst)    rbin <= 0;
    else        rbin <= rbin_next;
    
    //读数据
    assign o_dat = fifo_mem[radd]; 
    
    //空满判断
    assign fullw = (wbin_next == {~rbin[WD],rbin[WD-1:0]});//注意是wbin_next 与现在的 rbin比
    assign emptyr = (wbin == rbin_next);                    //注意是rbin_next 与现在的 wbin比
    always@(posedge clk , posedge arst)
    if(arst)    { full,empty } <= 0;
    else        { full,empty } <= { fullw,emptyr }; 
    endmodule

    参考资料

    [1]异步fifo的设计(FPGA)

    [2]异步FIFO的Verilog实现

    更多相关内容
  • 异步fifo跨时钟域处理

    2011-11-30 15:08:16
    使用FPGA内部FIFO跨时钟域的信息处理,避免亚稳态的传播。
  • 三、设计要点解析 1、读空信号如何产生?写满信号如何产生? 读空信号:复位的时候,读指针和写指针相等,读空信号有效(这里所说的指针其实就是读地址、写...举个例子说明:假设要设计深度为 8 的异步FIFO,此时定义

    三、设计要点解析

    1、读空信号如何产生?写满信号如何产生?
    读空信号:复位的时候,读指针和写指针相等,读空信号有效(这里所说的指针其实就是读地址、写地址)
    当读指针赶上写指针的时候,写指针等于读指针意味着最后一个数据被读完,此时读空信号有效
    写满信号:当写指针比读指针多一圈时,写指针等于读指针意味着写满了,此时写满信号有效

    我们会发现 读空的条件是写指针等于读指针,写满的条件也是写指针等于读指针,到底如何区分呢?
    解决方法:将指针的位宽多定义一位
    举个例子说明:假设要设计深度为 8 的异步FIFO,此时定义读写指针只需要 3 位(2^3=8)就够用了,

    但是我们在设计时将指针的位宽设计成 4 位,最高位的作用就是区分是读空还是写满,具体理论 1 如下:
    当最高位相同,其余位相同认为是读空
    当最高位不同,其余位相同认为是写满
    注意:理论1试用的是二进制数之间的空满比较判断。

    但是这篇文章中确不是这样比较的,而是用的理论2,这里我解释一下
    由于文章在设计中判断是读指针是否等于写指针的时候,用的是读写指针的格雷码形式(为什么用格雷码后面解释),此时若用上面的理论1就会出问题,
    因为格雷码是镜像对称的,若只根据最高位是否相同来区分是读空还是写满是有问题的
    因此用格雷码判断是否为读空或写满时应使用理论 2,看最高位和次高位是否相等,具体如下:
    当最高位和次高位相同,其余位相同认为是读空
    当最高位和次高位不同,其余位相同认为是写满

    2、由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决?
    跨时钟域的问题:上面我们已经提到要通过比较读写指针来判断产生读空和写满信号
    但是读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,
    要是将读时钟域的读指针与写时钟域的写指针不做任何处理直接比较肯定是错误的,因此我们需要进行同步处理以后仔进行比较

    解决方法:两级寄存器同步 + 格雷码
    同步的过程有两个:
    (1)将写时钟域的写指针同步到读时钟域,将同步后的写指针与读时钟域的读指针进行比较产生读空信号
    (2)将读时钟域的读指针同步到写时钟域,将同步后的读指针与写时钟域的写指针进行比较产生写满信号

    同步的思想就是用两级寄存器同步,简单说就是打两拍

    只是这样简单的同步就可以了吗?no no no ,可怕的亚稳态还在等着你。
    我们如果直接用二进制编码的读写指针去完成上述的两种同步是不行的,使用格雷码更合适,为什么呢?
    因为二进制编码的指针在跳变的时候有可能是多位数据一起变化,如二进制的7–>8 即 0111 --> 1000 ,在跳变的过程中 4 位全部发生了改变,这样很容易产生毛刺,例如
    异步FIFO的写指针和读指针分属不同时钟域,这样指针在进行同步过程中很容易出错,比如写指针在从0111到1000跳变时4位同时改变,这样读时钟在进行写指针同步后得到的写指针可能是0000-1111的某个值,一共有2^4个可能的情况,而这些都是不可控制的,你并不能确定会出现哪个值,那出错的概率非常大,怎么办呢?

    到了格雷码发挥作用的时候了,而格雷码的编码特点是相邻位每次只有 1 位发生变化, 这样在进行指针同步的时候,只有两种可能出现的情况:1.指针同步正确,正是我们所要的;2.指针同步出错,举例假设格雷码写指针从000->001,将写指针同步到读时钟域同步出错,出错的结果只可能是000->000,因为相邻位的格雷码每次只有一位变化,这个出错结果实际上也就是写指针没有跳变保持不变,我们所关心的就是这个错误会不会导致读空判断出错?答案是不会,最多是让空标志在FIFO不是真正空的时候产生,而不会出现空读的情形。所以gray码保证的是同步后的读写指针即使在出错的情形下依然能够保证FIFO功能的正确性。在同步过程中的亚稳态不可能消除,但是我们只要保证它不会影响我们的正常工作即可。

    3、由于设计的时候读写指针用了至少两级寄存器同步,同步会消耗至少两个时钟周期,势必会使得判断空或满有所延迟,这会不会导致设计出错呢?
    异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域,所以在比较之前需要先将读写指针进行同步处理,
    将写指针同步到读时钟域再和读指针比较进行FIFO空状态判断,因为在同步写指针时需要时间,而在这个同步的时间内有可能还会写入新的数据,因此同步后的写指针一定是小于或者等于当前实际的写指针,所以此时判断FIFO为空不一定是真空,这样更加保守,一共不会出现空读的情况,虽然会影响FIFO的性能,但是并不会出错,同理将读指针同步到写时钟域再和写指针比较进行FIFO满状态判断,同步后的读指针一定是小于或者等于当前的读指针,所以此时判断FIFO为满不一定是真满,这样更保守,这样可以保证FIFO的特性:FIFO空之后不能继续读取,FIFO满之后不能继续写入。总结来说异步逻辑转到同步逻辑不可避免需要额外的时钟开销,这会导致满空趋于保守,但是保守并不等于错误,这么写会稍微有性能损失,但是不会出错。

    举个例子:大多数情形下,异步FIFO两端的时钟不是同频的,或者读快写慢,或者读慢写快,慢的时钟域同步到快的时钟域不会出现漏掉指针的情况,但是将指针从快的时钟域同步到慢的时钟域时可能会有指针遗漏,举个例子以读慢写快为例,进行满标志判断的时候需要将读指针同步到写时钟域,因为读慢写快,所以不会有读指针遗漏,同步消耗时钟周期,所以同步后的读指针滞后(小于等于)当前读地址,所以可能满标志会提前产生,满并非真满。进行空标志判断的时候需要将写指针同步到读指针 ,因为读慢写快,所以当读时钟同步写指针 的时候,必然会漏掉一部分写指针,我们不用关心那到底会漏掉哪些写指针,我们在乎的是漏掉的指针会对FIFO的空标志产生影响吗?比如写指针从0写到10,期间读时钟域只同步捕捉到了3、5、8这三个写指针而漏掉了其他指针。当同步到8这个写指针时,真实的写指针可能已经写到10 ,相当于在读时钟域还没来得及觉察的情况下,写时钟域可能偷偷写了数据到FIFO去,这样在判断它是不是空的时候会出现不是真正空的情况,漏掉的指针也没有对FIFO的逻辑操作产生影响。

    4、多位二进制码如何转化为格雷码
    二进制码转换成二进制格雷码,其法则是保留二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。
      转换示意图

    我再换种更简单的描述
    二进制数 1 0 1 1 0
    二进制数右移1位,空位补0 0 1 0 1 1
    异或运算 1 1 1 0 1

    这样就可以实现二进制到格雷码的转换了,总结就是移位并且异或,verilog代码实现就一句:assign wgraynext = (wbinnext>>1) ^ wbinnext;
    是不是非常简单。

    四、代码解析

    原文章中说明是设计深度为8的fifo,但是在阅读完代码后,发现实际上设计的是深度16的fifo。以下代码注释有所改动。

    异步FIFO的信号接口:
    wclk wrst_n winc wdata //写时钟、写复位、写请求、写数据 这几个与写有关的全部与wclk同步
    rclk rrst_n rinc rdata //读时钟、读 复位、读 请求、读 数据 这几个与读有关的全部与rclk同步
    wfull //写满 与wclk同步
    rempty // 读空 与rclk同步

    本次代码共分为6个module
    1、fifo.v 是顶层模块,作用是将各个小模块例化联系起来

    module fifo
    #(
      parameter DSIZE = 8,   //date size     
      parameter ASIZE = 4	 //adress size     fifo size:8*8
     ) 
     (
         output [DSIZE-1:0] rdata,  
         output             wfull,  
         output             rempty,  
         input  [DSIZE-1:0] wdata,  
         input              winc, wclk, wrst_n, 
         input              rinc, rclk, rrst_n
     );
    
      wire   [ASIZE-1:0] waddr, raddr;  
      wire   [ASIZE:0]   wptr, rptr, wq2_rptr, rq2_wptr;
    // synchronize the read pointer into the write-clock domain
      sync_r2w  sync_r2w
      (
                        .wq2_rptr    (wq2_rptr),
                        .rptr        (rptr    ),                          
                        .wclk        (wclk    ), 
                        .wrst_n      (wrst_n  )  
     );
    
    // synchronize the write pointer into the read-clock domain
      sync_w2r  sync_w2r 
      (
                       .rq2_wptr(rq2_wptr), 
                       .wptr(wptr),                          
                       .rclk(rclk),
                       .rrst_n(rrst_n)
     );
    
    //this is the FIFO memory buffer that is accessed by both the write and read clock domains.
    //This buffer is most likely an instantiated, synchronous dual-port RAM. 
    //Other memory styles can be adapted to function as the FIFO buffer. 
      fifomem 
      #(DSIZE, ASIZE)
      fifomem                        
      (
          .rdata(rdata), 
          .wdata(wdata),                           
          .waddr(waddr),
          .raddr(raddr),                           
          .wclken(winc),
          .wfull(wfull),                           
          .wclk(wclk)
      );
    
    //this module is completely synchronous to the read-clock domain and contains the FIFO read pointer and empty-flag logic.  
      rptr_empty
      #(ASIZE)    
      rptr_empty                          
      (
          .rempty(rempty),                          
          .raddr(raddr),                          
          .rptr(rptr),
          .rq2_wptr(rq2_wptr),                          
          .rinc(rinc),
          .rclk(rclk),                          
          .rrst_n(rrst_n)
      );
    
    //this module is completely synchronous to the write-clock domain and contains the FIFO write pointer and full-flag logic
      wptr_full 
      #(ASIZE)    
      wptr_full                         
      (
          .wfull(wfull),
          .waddr(waddr),  
          .wptr(wptr),
          .wq2_rptr(wq2_rptr),    
          .winc(winc),
          .wclk(wclk),        
          .wrst_n(wrst_n)
      );
      endmodule
    

    2.fifomem.v 生成存储实体,FIFO 的本质是RAM,因此在设计存储实体的时候有两种方法:用数组存储数据或者调用RAM的IP核

    module fifomem
    #(
        parameter  DATASIZE = 8, // Memory data word width               
        parameter  ADDRSIZE = 4  // 深度为8即地址为3位即可,这里多定义一位的原因是用来判断是空还是满
    ) // Number of mem address bits
    (
        output [DATASIZE-1:0] rdata, 
        input  [DATASIZE-1:0] wdata, 
        input  [ADDRSIZE-1:0] waddr, raddr, 
        input                 wclken, wfull, wclk
    );
     
    `ifdef RAM   //可以调用一个RAM IP核
    // instantiation of a vendor's dual-port RAM 
    my_ram  mem
          (
              .dout(rdata),
              .din(wdata),     
              .waddr(waddr),
              .raddr(raddr),   
              .wclken(wclken), 
              .wclken_n(wfull),
              .clk(wclk)
          );
      `else  //用数组生成存储体
     // RTL Verilog memory model
    localparam DEPTH = 1<<ADDRSIZE;   // 左移相当于乘法,2^4   。2^4即为16.这里就是生成了8*16的mem
    reg [DATASIZE-1:0] mem [0:DEPTH-1]; //生成2^4个位宽位8的数组
    assign rdata = mem[raddr];
    always @(posedge wclk)  //当写使能有效且还未写满的时候将数据写入存储实体中,注意这里是与wclk同步的
        if (wclken && !wfull)
            mem[waddr] <= wdata;
     `endif
     endmodule
    
    

    3、sync_r2w.v 将 rclk 时钟域的格雷码形式的读指针同步到 wclk 时钟域,简单来讲就是用两级寄存器同步,即打两拍

    module sync_r2w
    #(
        parameter ADDRSIZE = 4
    )
    (
        output reg [ADDRSIZE:0] wq2_rptr,   //读指针同步到写时钟域
        input      [ADDRSIZE:0] rptr,       // 格雷码形式的读指针,格雷码的好处后面会细说 
        input                   wclk, wrst_n
    );
     
    reg [ADDRSIZE:0] wq1_rptr;
     
      always @(posedge wclk or negedge wrst_n)   
          if (!wrst_n) begin
              wq1_rptr <= 0;          
              wq2_rptr <= 0;
          end           
          else begin        
              wq1_rptr<= rptr;
              wq2_rptr<=wq1_rptr;
          end          
      endmodule
    

    4、sync_w2r.v 将 wclk 时钟域的格雷码形式的写指针同步到 rclk 时钟域

    module sync_w2r
    #(parameter ADDRSIZE = 4)
    (
        output reg [ADDRSIZE:0] rq2_wptr, //写指针同步到读时钟域
        input      [ADDRSIZE:0] wptr,     //格雷码形式的写指针
        input                   rclk, rrst_n
    );
     
    reg [ADDRSIZE:0] rq1_wptr;
     
      always @(posedge rclk or negedge rrst_n)   
          if (!rrst_n)begin
              rq1_wptr <= 0;
              rq2_wptr <= 0;
          end 
          else begin
              rq1_wpt <= wptr;
              rq2_wptr <= rq1_wptr;
          end
            
    endmodule
    

    5、rptr_empty.v 将 sync_w2r.v 同步后的写指针与 rclk 时钟域的读指针进行比较生成都空信号

    module rptr_empty
    #(
        parameter ADDRSIZE = 4
    )
    (
        output reg                rempty, 
        output     [ADDRSIZE-1:0] raddr,  //二进制形式的读指针
        output reg [ADDRSIZE  :0] rptr,  //格雷码形式的读指针
        input      [ADDRSIZE  :0] rq2_wptr, //同步后的写指针
        input                     rinc, rclk, rrst_n
    );
      reg  [ADDRSIZE:0] rbin;
      wire [ADDRSIZE:0] rgraynext, rbinnext;
     // GRAYSTYLE2 pointer
     //将二进制的读指针与格雷码进制的读指针同步
      always @(posedge rclk or negedge rrst_n) 
          if (!rrst_n) begin
              rbin <= 0;
              rptr <= 0;
          end  
          else begin        
              rbin<=rbinnext; //直接作为存储实体的地址
              rptr<=rgraynext;//输出到 sync_r2w.v模块,被同步到 wrclk 时钟域
          end
      // Memory read-address pointer (okay to use binary to address memory)
      assign raddr     = rbin[ADDRSIZE-1:0]; //直接作为存储实体的地址,比如连接到RAM存储实体的读地址端。
      assign rbinnext  = rbin + (rinc & ~rempty); //不空且有读请求的时候读指针加1
      assign rgraynext = (rbinnext>>1) ^ rbinnext; //将二进制的读指针转为格雷码
      // FIFO empty when the next rptr == synchronized wptr or on reset 
      assign rempty_val = (rgraynext == rq2_wptr); //当读指针等于同步后的写指针,则为空。
      always @(posedge rclk or negedge rrst_n) 
          if (!rrst_n)
              rempty <= 1'b1; 
          else     
              rempty <= rempty_val;
     
    endmodule
    

    6、wptr_full.v 将 sync_r2w.v 同步后的读指针与wclk 时钟域的写指针进行比较生成写满信号

    module wptr_full
    #(
        parameter ADDRSIZE = 4
    ) 
    (
        output reg                wfull,   
        output     [ADDRSIZE-1:0] waddr,
        output reg [ADDRSIZE  :0] wptr, 
        input      [ADDRSIZE  :0] wq2_rptr,
        input                     winc, wclk, wrst_n
    );
      reg  [ADDRSIZE:0] wbin;
      wire [ADDRSIZE:0] wgraynext, wbinnext;
      // GRAYSTYLE2 pointer
      always @(posedge wclk or negedge wrst_n)   
          if (!wrst_n)
              {wbin, wptr} <= 0;   
          else         
              {wbin, wptr} <= {wbinnext, wgraynext};
      // Memory write-address pointer (okay to use binary to address memory) 
      assign waddr = wbin[ADDRSIZE-1:0];
      assign wbinnext  = wbin + (winc & ~wfull);
      assign wgraynext = (wbinnext>>1) ^ wbinnext; //二进制转为格雷码
      //-----------------------------------------------------------------
      assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]}); //当最高位和次高位不同其余位相同时则写指针超前于读指针一圈,即写满。后面会详细解释。
      always @(posedge wclk or negedge wrst_n)
          if (!wrst_n)
              wfull  <= 1'b0;   
          else     
              wfull  <= wfull_val;
     
      endmodule
    

    7、测试文件

    `timescale 1ns /1ns 
    
    module test();
    reg  [7:0] wdata;
    reg           winc, wclk, wrst_n; 
    reg           rinc, rclk, rrst_n;
    wire [7:0] rdata;  
    wire           wfull;  
    wire          rempty;  
    
    fifo
    
    u_fifo (
                   .rdata(rdata),  
                   .wfull(wfull),  
                   .rempty(rempty),  
                   .wdata (wdata),  
                   .winc  (winc), 
                   .wclk  (wclk), 
                   .wrst_n(wrst_n), 
                   .rinc(rinc), 
                   .rclk(rclk), 
                   .rrst_n(rrst_n)
     );
    localparam CYCLE = 20;
    localparam CYCLE1 = 40;
    
    
    
            //时钟周期,单位为ns,可在此修改时钟周期。
         
                //生成本地时钟50M
                initial begin
                    wclk = 0;
                    forever
                    #(CYCLE/2)
                    wclk=~wclk;
                end
                initial begin
                    rclk = 0;
                    forever
                    #(CYCLE1/2)
                    rclk=~rclk;
                end
    
                //产生复位信号
                initial begin
                    wrst_n = 1;
                    #2;
                    wrst_n = 0;
                    #(CYCLE*3);
                    wrst_n = 1;
                end
                
                 initial begin
                    rrst_n = 1;
                    #2;
                    rrst_n = 0;
                    #(CYCLE*3);
                    rrst_n = 1;
                end
    
                always  @(posedge wclk or negedge wrst_n)begin
                    if(wrst_n==1'b0)begin
                        winc <= 0;
                        rinc <= 0;
                    end
                    else begin
                        winc <= $random;
                        rinc <= $random;
                    end
                end
    
                always  @(posedge rclk or negedge rrst_n)begin
                    if(rrst_n==1'b0)begin                  
                        rinc <= 0;
                    end
                    else begin                
                        rinc <= $random;
                    end
                end
    always@(*)begin
      if(winc == 1)
        wdata= $random ;
      else
        wdata = 0;
    end  
    endmodule
    
    展开全文
  • 单比特信号的跨时钟域传输3.多比特信号的跨时钟域传输 1.CDC简介 含义:Clock Domain Conversion(时钟域转换) 场景:跨时钟域传输(数据从ck_a 传输至 ck_b) 存在问题:亚稳态现象 含义:触发器无法在某个规定的...

    1.CDC简介

    含义:Clock Domain Conversion(时钟域转换)
    场景:跨时钟域传输(数据从ck_a 传输至 ck_b)

    在这里插入图片描述

    存在问题:亚稳态现象

    • 含义:触发器无法在某个规定的时间段内到达一个可以确认的状态(0/1)
    • 原因:采样信号在触发器的建立时间或保持时间内发生变化
    • 结果:输出有一段时间的不稳定状态(后果:不定态继续向下级传输)
      在这里插入图片描述

    2.单比特信号的跨时钟域传输

    慢时钟域到快时钟域

    • 方案:两级同步
    • 实现:在这里插入图片描述
    • 结果:在这里插入图片描述

    3.多比特信号的跨时钟域传输

    AsyncFIFO

    • 目的:解决异步时钟多bit数据传输问题+收发数据匹配
    • 关键:通过读写地址使用格雷码转换,完成数据跨时钟域传输存在亚稳态状况下的读写指针抽样,从而产生正确的空满状态
    • 实现:
    1.单独异步读写(无需空满信号)不涉及跨时钟域问题
    写入数据存入fifo 地址递增 读取时从fifo顺序读出
    在这里插入图片描述
    2.空满信号产生
    cdc问题:由于cdc 亚稳态处理存在随机性(多bit 同时变化时,需考虑第一拍是否采集到数据变化)
    解决:采用Gray使前后地址维持1bit变动 cdc后 完成地址比对
    空: 在这里插入图片描述
    满: 在这里插入图片描述
    展开全文
  • 设计一个异步FIFO,深度为8,位宽也是8. 代码是学习Simulation and Synthesis Techniques for Asynchronous FIFO Design Clifford E. Cummings, Sunburst Design, Inc.这篇文章的 一、FIFO介绍 FIFO是英文First I

    本文大部分内容来自https://www.cnblogs.com/aslmer/p/6114216.html

    本文首先对异步 FIFO 设计的重点难点进行分析
    最后给出详细代码
    设计一个异步FIFO,深度为8,位宽也是8.
    代码是学习Simulation and Synthesis Techniques for Asynchronous FIFO Design Clifford E. Cummings, Sunburst Design, Inc.这篇文章的

    一、FIFO介绍

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

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

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

    分类

    同步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)

    2.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为空;

    3. 二进制FIFO指针的考虑

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

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

    对于“空”的判断依然依据二者完全相等(包括MSB);
      而对于“满”的判断,则会产生问题。
    因为格雷码是镜像对称的,若只根据最高位是否相同来区分是读空还是写满是有问题的。
    绿色框起来的是0–15的格雷码,用红线将格雷码分为上下两部分
    通过观察格雷码相邻位每次只有1位发生变化,且上下两部分,除了最高位相反,其余位全都关于红线镜像对称,
    7 --> 8 ,格雷码从 0100 --> 1100 ,只有最高位发生变化其余位相同
    6 --> 9 , 格雷码从 0101 --> 1101 , 只有最高位发生变化其余位相同

    试想如果读指针指向 8,写指针指向 7 ,我们可以知道此时此刻并不是读空状态也不是写满状态
    但是如果在此刻套用以上理论来判断,看会出现什么情况,我们来套一下
    7的格雷码与8的格雷码的最高位不同,其余位相同,所以判断出为写满。这就出现误判了,同样套用在 6 和 9,5 和 10等也会出现误判。

    因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:

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

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 异步FIFO跨时钟域亚稳态如何解决?

    千次阅读 2018-09-18 08:14:00
    跨时钟域的问题:前一篇已经提到要通过比较读写指针来判断产生读空和写满信号,但是读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域的写...
  • 异步fifo的verilog实现

    2020-07-08 15:38:34
    该资源是实现了的通过异步fifo进行跨时钟域传输的vivado工程,在不同的时钟域进行fifo数据读写,并用读写地址的格雷码判断fifo空满产生空满标志。工程代码基于vivado2017.4,并在modelsim10.6上仿真成功。工程中附有...
  • 跨时钟域 全同步设计:1个时钟 全异步设计:没有时钟(存在少) 全局异步,局部同步设计:不可避免,单一时钟不能满足设计需求 多时钟域设计不可避免问题:亚稳态,同步失败 同步化:同步器;保持寄存器和握手(效率...
  • 摘 要:数据流在不同时钟域间的传递一直是集成电路芯片设计中的一个重点问题。本文通过采用异步FIFO的方式给出了这个问题的一种解决方法,并采用Verilog 硬件描述语言通过前仿真和逻辑综合完成设计。 关键词:异步...
  • 异步FIFO跨时钟域,写的快、读的慢

    千次阅读 2020-10-23 10:34:17
    异步FIFO跨时钟域问题,读写时钟不同,不能确定,写满信号和读空信号,采用的FIFO策略是,先写满,写的过程通过帧拉高,帧数据有效等来进入写的状态机,确定写的数量并缓存计数,确定写满。 写满信号到来之后,输出...
  • 异步FIFO跨时钟域,写的慢,读的快

    千次阅读 2020-10-23 10:25:45
    异步FIFO跨时钟域问题,读写时钟不同,不能确定,写满信号和读空信号,采用的FIFO策略是,先写满,写的过程通过帧拉高,帧数据有效等来进入写的状态机,确定写的数量并缓存计数,确定写满。 写满信号到来之后,输出...
  • FPGA跨时钟域处理方法

    2021-07-17 20:19:16
    前提条件是 快时钟域到慢时钟域,原因有两个,1)只有快时钟域到慢时钟域,才能保证慢时钟域的脉冲信号能被快时钟域采样到。2)两级寄存器的主要作用是消除亚稳态(不能完全消除亚稳态,但可以使亚稳态出现的概率...
  • ...异步FIFO 结构及FPGA 设计 吴... 单片机及嵌入式系统应用,2000摘要:首先介绍异步FIFO的概念、应用及其结构,然后分析实现异步FIFO的难点问题及其解决办法;在传统设计的基础上提出一种新颖的电路结构并对...
  • 异步FIFO 二进制实现指针的问题 同步指针的影响 格雷码实现指针 空满标志的产生 代码实现 1.二进制实现指针的问题 由于空满标志的产生,需要比较写指针与读指针是否相等。而写指针在写时钟域下,读指针在读...
  • 空满标志通过比较读写指针来生成,异步FIFO读写指针在不同的时钟域下产生,所以要进行同步处理,要把读指针传递到写时钟域,把写指针传递到读时钟域,这是因为读时钟域实时生成空标志来控制FIFO不空读,写时钟域...
  • 跨时钟域处理异步FIFO.docx
  • FPGA跨时钟域异步FIFO设计,Vivado仿真工程
  • 如apb,ahb,iic中的不同时钟,处理跨时钟域的数据有单bit和多bit之分,而打两拍的方式常见于处理单bit数据的跨时钟域问题。打两拍本质就是定义两级寄存器对数据进行延拍。 不同时钟域之间的信息交流需要时钟的同步化...
  • 如果你写过异步FIFO,格雷码的传输就已经包括慢时钟域到快时钟域以及快时钟域到慢时钟域处理方法了,自己之前的异步FIFO由于理解没到位,快时钟域到慢时钟域处理也是打拍,原因是快时钟域的时钟频率不够快。...
  • 原文链接:跨时钟域传输的黄金搭档:异步FIFO与格雷码 ———————————————— 异步FIFO里为什么要用格雷码? 技术面被问到这个问题。 最初学习信号跨时钟域传输的时候,只是想当然地认为多比特信号在...
  • 现对FIFO处理跨时钟域突发数据的能力进行分析: 在数据传输中,数据可能到达某个时钟域的间隔是完全随机的,有时候或许会面临一个很大的突发数据块。这种情况下,处在另一个时钟域的接收设备只能以指定的速率来处理...
  • 用途 同步 FIFO 常用于同步时钟的数据缓存, 异步 FIFO 常用于跨时钟域的数据信号的传递 思路 FIFO结构图   我们首先来了解一下FIFO的常见参数: FIFO 的宽度: FIFO 一次读写操作的数据位 N FIFO 的深度: FIFO ...
  • 1.什么是亚稳态? 触发器无法在某一个规定时间段内产生一个确定的状态。 原因:由于触发器的建立时间和保持时间不满足,当触发器进入亚稳态,使得无法预测该...(4)改善时钟质量,用边沿变化快速的时钟信号消除;
  • CDC:跨时钟域处理

    千次阅读 多人点赞 2020-12-10 17:10:23
    前言 CDC(clock domain crossing)检查(跨时钟域的检查)是对电路设计中同步... 在当今的电路设计中我们通常使用多级同步,异步FIFO,握手等同步设计来保证亚稳态信号不会在电路中无序的传播。 但同步设计中,同步处
  • 2 多比特信号跨时钟域问题_异步FIFO 2 异步FIFO 原理图 异步FIFO的设计主要有5部分组成: FIFO Memory:双口RAM存储数据 sync_r2w:同步器,同步读数据指针到写时钟域 sync_w2r:同步器,同步写数据指针到读...
  • 跨异步时钟域的6种方法

    千次阅读 2021-05-25 17:26:46
    该方法只用于慢到快时钟域的1bit信号传递。 module ff2( input clk0,//10M input din, input clk1,//100M output dout ); reg din_r=1'd0; reg r0=1'd0; reg r1=1'd0; assign dout = r1; always@(posedge ...
  • 跨时钟域处理

    2022-04-26 08:06:54
    目录跨时钟域处理1.单bit时钟(控制信号)2.多bit时钟(数据信号)参考文章 跨时钟域处理 传输信号分为控制信号和数据信号 1.单bit时钟(控制信号) 慢到快:由于快时钟域的可以采集到慢时钟域的信号,所以两级同步...
  • 关于异步FIFO时钟同步问题分析

    千次阅读 2020-07-23 11:00:16
    对于fifo来说,无论是写地址在读时钟下同步,还是读地址在写时钟下同步,被同步地址都会是延迟两个时钟周期之前的地址,所以在进行比较判断的时刻,参与判断的同步地址总是小于等于当前时刻的真实地址。那么, 对于...
  • 单bit 源时钟域打一拍,目的时钟域打两拍或者更多拍 多bit fifo方法,原理是格雷码指针判断空满,深入了解,可以分析一下源目的时钟分别是快或慢的情况。架构简单,设计review快,质量可靠。 所有异步处理机制,都多...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,602
精华内容 3,840
关键字:

异步fifo跨时钟域处理

友情链接: chap04.rar