当前位置:   article > 正文

跨时钟域方法(同步器、异步FIFO、边沿检测器、脉冲同步器、同步FIFO)_同步器 跨时钟域

同步器 跨时钟域

目录

1、跨时钟域方法的原因

2、跨时钟处理的两种思路

3、跨时钟域分类——单比特信号跨时钟

3.1.1慢时钟———快时钟。(满足三边沿准则,有效事件可以被安全采样)

3.1.2慢时钟———快时钟。(不满足三边沿准则,有效事件可以被安全采样)

3.2.1有效事件传输背景下确保有效事件的数量定义一致。(如何确保跨时钟前后单电平对应单事件?)

 3.2.1.1边沿检测电路

3.2.2.2脉冲同步器(快时钟--慢时钟)

3.3多有效可控事件背景下使用反馈机制

3.4单bit信号跨时钟方法总结

4、跨时钟域信号的分类——多比特数据信号。

4.6异步FIFO

4.5 同步FIFO

5异步FIFO

5.6.1格雷码

1、跨时钟域方法的原因

  异步时钟信号直接传输在信号跳变时违背本地时钟域的时序要求(建立时间约束,保持时间约束),容易产生亚稳态,无法确定亚稳态何时结束以及结束时保持在何种状态上。

2、跨时钟处理的两种思路

  • 对代跨时钟的信息先缓存,然后由另一时钟域接口取出 ————异步FIFO
  • 速度缓慢、可控的信号(配置信号,使能en、指示flag)我们对其进行一些处理。例如拉长持续时间,然后直接传输过去,亚稳态的问题通过同步器解决。适用于低速信号传输。

3、跨时钟域分类——单比特信号跨时钟

3.1.1慢时钟———快时钟。(满足三边沿准则,有效事件可以被安全采样)

           接收时钟的频率是发送时钟的1.5倍以上,跨时钟信号的最小持续时间必然跨越接受时钟说的三个相邻时钟边沿,经过同步器该有效事件可以被安全传输。

3.1.2慢时钟———快时钟。(不满足三边沿准则,有效事件可以被安全采样)

        接收时钟的频率小于发送时钟的1.5倍,将有效事件的持续时间拉长满足三边沿准则,然后再经过同步器传输。

  1. always@(posedge clk_a or negedge arst)//(D触发器)(打拍可拉长持续时间)
  2. if(!arst)
  3. data_reg <=0;
  4. else
  5. data_reg <=data_in;
  6. always@(posedge clk_b or negedge brst)//同步器(时钟域为将要同步的时钟域)(两个连续的D触发器)
  7. if(!brst)
  8. begin
  9. data_breg <=0;
  10. data_breg2<=0;
  11. end
  12. else
  13. begin
  14. data_breg <=data_reg;
  15. data_breg2<=data_breg2;
  16. end

3.2.1有效事件传输背景下确保有效事件的数量定义一致。(如何确保跨时钟前后单电平对应单事件?)

  • 脉冲同步电路
  • 边沿检测电路

 3.2.1.1边沿检测电路

       对一个持续电平的上升沿或者下降沿进行检测,并将检测后的电平作为有效事件,就可以做到无论跨时钟输出电平持续多少个cycle,有效检测电平只持续一个cycle,边沿检测常适用于慢时钟到快时钟。

   要实现边沿检测,最直接的想法是用两级寄存器,第二级寄存器锁存住某个时钟上升沿到来时的输入电平,第一级寄存器锁存住下一个时钟沿到来时的输入电平,如果这两个寄存器锁存住的电平信号不同,就说明检测到了边沿,具体是上升沿还是下降沿可以通过组合逻辑来实现。如下图所示:(思想:延迟一个寄存器

  1. //边沿检测电路
  2. //2014/12/10
  3. module edge_cap
  4. (
  5. input clk, rst_n,
  6. input pulse,
  7. output pos_edge,
  8. output neg_edge
  9. );
  10. reg pulse_r1, pulse_r2;
  11. always @ (posedge clk or negedge rst_n)
  12. if(!rst_n)
  13. begin
  14. pulse_r1 <= 1'b0;
  15. pulse_r2 <= 1'b0;
  16. end
  17. else
  18. begin
  19. pulse_r1 <= pulse;
  20. pulse_r2 <= pulse_r1;
  21. end
  22. assign pos_edge = (pulse_r1 && ~pulse_r2) ?1:0;
  23. assign neg_edge = (~pulse_r1 && pulse_r2) ?1:0;
  24. endmodule

 异步信号同步化,一般采用多加一级寄存器的方法来减小亚稳态的发生概率,如下图所示:

  1. //异步信号边沿检测电路,三级寄存器实现
  2. //2014/12/08
  3. module edge_cap
  4. (
  5. input clk, rst_n,
  6. input pulse,
  7. output pos_edge,
  8. output neg_edge
  9. );
  10. reg pulse_r1, pulse_r2, pulse_r3;
  11. always @ (posedge clk or negedge rst_n)
  12. if(!rst_n)
  13. begin
  14. pulse_r1 <= 1'b0;
  15. pulse_r2 <= 1'b0;
  16. pulse_r3 <= 1'b0;
  17. end
  18. else
  19. begin
  20. pulse_r1 <= pulse;
  21. pulse_r2 <= pulse_r1;
  22. pulse_r3 <= pulse_r2;
  23. end
  24. assign pos_edge = (pulse_r2 && ~pulse_r3) ?1:0;
  25. assign neg_edge = (~pulse_r2 && pulse_r3) ?1:0;
  26. endmodule

3.2.2.2脉冲同步器(快时钟--慢时钟)

        对于快时钟域单电平脉冲信号跨时钟到慢时钟域常使用脉冲同步器电路,脉冲同步器在异步时钟域时钟频率彼此差距较大的场景下能节省触发器资源。快时钟域脉冲持续时间无法满足三边沿准则,需要通过翻转电路拉长脉冲电平以保证有效事件被采样,在接收时钟通过边沿检测回复原单电平脉冲。


 

  1. `timescale 1ns/1ns
  2. module pulse_detect(
  3. input clk_fast ,
  4. input clk_slow ,
  5. input rst_n ,
  6. input data_in ,
  7. output dataout
  8. );
  9. reg data_level,data_level1,data_level2,data_level3;//翻转电路
  10. always @(posedge clk_fast or negedge rst_n)
  11. if(!rst_n)
  12. data_level<=0;
  13. else
  14. data_level<= (data_in)? ~data_level : data_level;
  15. always @(posedge clk_slow or negedge rst_n)//同步器
  16. if(!rst_n)
  17. begin
  18. data_level1<=0;
  19. data_level2<=0;
  20. end
  21. else
  22. begin
  23. data_level1<=data_level;
  24. data_level2<=data_level1;
  25. end
  26. always @(posedge clk_slow or negedge rst_n)// D触发器
  27. if(!rst_n)
  28. data_level3<=0;
  29. else
  30. data_level3<=data_level2;
  31. assign dataout= data_level2^data_level3;
  32. endmodule

3.3多有效可控事件背景下使用反馈机制

        现有多个连续的有效事件需要进行跨时钟,单个事件的发起时刻是可控的,这时建议使用反馈机制保证各有效事件跨时钟传输的安全性。

3.4单bit信号跨时钟方法总结

  1.  1.在单有效事件传输背景下,首先要确认是否需要用单周期脉冲表示单有效事件若没有要求,传输信号满足三边沿准则后可经同步器直接传输
  2.     2.在单有效事件传输背景下,对于满足三边沿准则的慢时钟域到快时钟域的跨时钟信号可以优先考虑边沿检测电路,其他其他情况下可使用脉冲同步器(快时钟到慢时钟)
  3.     3.边沿检测电路与脉冲同步器的思路类似,先保证跨时钟信号满足三边沿准则以传递有效状态,然后通过边沿检测获取单周期脉冲,二者的区别在于边沿检测电路的信号事先满足三边沿准则,脉冲同步器的信号需要翻转电路产生长电平,因此也可以考虑将不满足三边沿准则的短脉冲本地打拍取逻辑或输出得到满足三边沿准则的长电平信号再打拍来跨时钟,并在异步时钟域取上升沿检测信号,这种方式代码写起来简单但可能导致资源消耗过多 。(寄存器延迟一个时钟比较。
  4.     4.在多有效可控事件传输的背景下,可考虑对以上电路加入反馈控制
  5.     5.多有效不可控事件传输的背景下,如发生两有效事件无时钟间隔连发,则上述跨时钟方法无法保证有效状态的传递与有效事件数量的识别,此时只能引入缓存机制,采用如DPRAM或者异步FIFO的方式保证数据的安全传输

  1. 4、跨时钟域信号的分类——多比特数据信号。

  2. FIFO

    1. 4.1FIFO定义

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

      1. 4.2、FIFO功能
        FIFO存储器是系统的缓冲环节,如果没有FIFO存储器,整个系统就不可能正常工作,它主要有几方面的功能:

        1)对连续的数据流]进行缓存,防止在进机和存储操作时丢失数据;

        2)数据集中起来进行进栈和存储,可避免频繁的总线操作,减轻CPU的负担;

        3)允许系统进行DMA操作,提高数据的传输速度。这是至关重要的一点,如果不采用DMA操作,数据传输将达不到传输要求,而且大大增加CPU的负担,无法同时完成数据的存储工作。

        • 4.3、FIFO分类
                  FIFO的分类根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。同步FIFO和异步FIFO如图所示,从图中可以看到,同步FIFO 具有一个独立的时钟端口 clock,因此所有的输入输出信号都同步于 clock 信号。而在异步FIFO 结构中,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟 wrclk,所有与读相关的信号都是同步于读时钟 rdclk。

                  同步 FIFO 常用于同步时钟的数据缓存,异步 FIFO 常用于跨时钟域的数据信号的传递,例如时钟域 A 下的数据 data1 传递给异步时钟域 B,当 data1 为连续变化信号时,如果直接传递给时钟域 B 则可能会导致收非所送的情况,即在采集过程中会出现包括亚稳态问题在内的一系列问题,使用异步 FIFO 能够将不同时钟域中的数据同步到所需的时钟域中。

        • 4.4FIFO的几个重要参数

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

          FIFO 的深度:FIFO 可以存储多少个宽度为 N 位的数据。

          空标志:对于双时钟 FIFO 又分为读空标志 rdempty 和写空标志 wrempty。FIFO 已空或将要空时由 FIFO

          的状态电路送出的一个信号,以阻止 FIFO 的读操作继续从 FIFO 中读出数据而造成无效数据的读出。

          满标志:对于双时钟 FIFO 又分为读满标志 rdfull 和写满标志 wrfull。FIFO 已满或将要写满时由 FIFO

          的状态电路送出的一个信号,以阻止 FIFO 的写操作继续向 FIFO 中写数据而造成溢出。

          读时钟:读 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。

          写时钟:写 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。


      2. 4.5 同步FIFO

        •    模块主要分为读/写指针、读写指针的比较逻辑和RAM存储。
          1. `timescale 1ns/1ns
          2. /**********************************RAM************************************/
          3. module dual_port_RAM #(parameter DEPTH = 16,
          4. parameter WIDTH = 8)(
          5. input wclk
          6. ,input wenc
          7. ,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。
          8. ,input [WIDTH-1:0] wdata //数据写入
          9. ,input rclk
          10. ,input renc
          11. ,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。
          12. ,output reg [WIDTH-1:0] rdata //数据输出
          13. );
          14. reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
          15. always @(posedge wclk) begin
          16. if(wenc)
          17. RAM_MEM[waddr] <= wdata;
          18. end
          19. always @(posedge rclk) begin
          20. if(renc)
          21. rdata <= RAM_MEM[raddr];
          22. end
          23. endmodule
          24. /**********************************SFIFO************************************/
          25. module sfifo#(
          26. parameter WIDTH = 8,
          27. parameter DEPTH = 16
          28. )(
          29. input clk ,
          30. input rst_n ,
          31. input winc ,
          32. input rinc ,
          33. input [WIDTH-1:0] wdata ,
          34. output reg wfull ,
          35. output reg rempty ,
          36. output wire [WIDTH-1:0] rdata
          37. );
          38. reg [$clog2(DEPTH):0] waddr,raddr;
          39. dual_port_RAM #(.DEPTH (DEPTH),
          40. .WIDTH (WIDTH))
          41. dual_port_RAM (
          42. .wclk (clk ),
          43. .wenc (wenc ),
          44. .waddr (waddr),
          45. .wdata (wdata),
          46. .rclk (clk ),
          47. .renc (renc ),
          48. .raddr (raddr),
          49. .rdata (rdata)
          50. );
          51. always @(posedge clk or negedge rst_n)//读数据计数加一
          52. if(!rst_n)
          53. raddr<=0;
          54. else if(renc)
          55. raddr<=raddr+1'd1;
          56. else
          57. raddr<=raddr;
          58. always @(posedge clk or negedge rst_n)//写数据计数加一
          59. if(!rst_n)
          60. waddr<=0;
          61. else if(wenc)
          62. waddr<=waddr+1'd1;
          63. else
          64. waddr<=waddr;
          65. always @(posedge clk or negedge rst_n) //判断空满状态
          66. if(!rst_n)begin
          67. wfull<=0;
          68. rempty<=0;
          69. end
          70. else begin
          71. wfull<=(waddr==raddr+DEPTH);
          72. rempty<=(waddr==raddr);
          73. end
          74. assign wenc = winc && !wfull;
          75. assign renc = rinc && !rempty;
          76. endmodule

          5.6异步FIFO

          •         异步FIFO主要是由双端口存储器、写指针产生逻辑、读指针产生逻辑及空满标志产生逻辑4部分组成。读写操作是由两个完全不同时钟域的时钟所控制。在写时钟域部分,由写指针所产生逻辑生成写端口所需要的写地址和写控制信号;在读时钟域部分,由读指针产生逻辑生成读断口所需要的读地址和读控制信号;在空满标志产生部分,通常是把写指针与读指针相互比较产生空满标志。

            1. 双口RAM,用于数据的存储。(RAM中的指针的地址取FIFO中的地址的除地址第一位外的地址,因为FIFO中的第一位是标志位)

            2. 数据写入控制器,在wenc信号的使能下,数据写入控制器使RAM读入数据,同时数据写地址指针加一。

            3. 数据读取控制器,在renc信号的使能下,数据读出控制器使RAM读出数据,同时数据读地址指针加一。

            4. 读指针同步器:使用写时钟的两级触发器采集读指针,输出到数据写入控制器。

            5. 写指针同步器: 使用读时钟的两级触发器采集写指针,输出到数据读取控制器。

                    本题解采用的空满判断的方式是用格雷码的比较来产生空满信号,同时产生的空满信号,会与输入的winc,rinc输入的是能信号,共同控制数据是否写入和读出。

            •         
          1. `timescale 1ns/1ns
          2. /***************************************RAM*****************************************/
          3. module dual_port_RAM #(parameter DEPTH = 16,
          4. parameter WIDTH = 8)(
          5. input wclk
          6. ,input wenc
          7. ,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。
          8. ,input [WIDTH-1:0] wdata //数据写入
          9. ,input rclk
          10. ,input renc
          11. ,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。
          12. ,output reg [WIDTH-1:0] rdata //数据输出
          13. );
          14. reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
          15. always @(posedge wclk) begin
          16. if(wenc)
          17. RAM_MEM[waddr] <= wdata;
          18. end
          19. always @(posedge rclk) begin
          20. if(renc)
          21. rdata <= RAM_MEM[raddr];
          22. end
          23. endmodule
          24. /***************************************AFIFO*****************************************/
          25. module asyn_fifo#(
          26. parameter WIDTH = 8,
          27. parameter DEPTH = 16
          28. )(
          29. input wclk ,
          30. input rclk ,
          31. input wrstn ,
          32. input rrstn ,
          33. input winc ,
          34. input rinc ,
          35. input [WIDTH-1:0] wdata ,
          36. output wire wfull ,
          37. output wire rempty ,
          38. output wire [WIDTH-1:0] rdata
          39. );
          40. /**********************addr bin gen*************************/
          41. //RAM读写地址的变化
          42. reg [ADDR_WIDTH:0] waddr_bin;
          43. reg [ADDR_WIDTH:0] raddr_bin;
          44. parameter ADDR_WIDTH = $clog2(DEPTH);
          45. always@(posedge rclk or negedge rrstn)//counter read
          46. if(!rrstn)
          47. raddr_bin<=0;
          48. else if(renc)
          49. raddr_bin<=raddr_bin+1'd1;
          50. else
          51. raddr_bin<=raddr_bin;
          52. always@(posedge wclk or negedge wrstn)//counter write
          53. if(!wrstn)
          54. waddr_bin<=0;
          55. else if(wenc)
          56. waddr_bin<=waddr_bin+1'd1;
          57. else
          58. waddr_bin<=waddr_bin;
          59. assign wenc = winc && !wfull;
          60. assign renc = rinc && !rempty;
          61. /**********************addr gray gen*************************/
          62. //二进制转换为格雷码
          63. wire [ADDR_WIDTH:0] waddr_gray;
          64. wire [ADDR_WIDTH:0] raddr_gray;
          65. reg [ADDR_WIDTH:0] wptr;
          66. reg [ADDR_WIDTH:0] rptr;
          67. assign waddr_gray = waddr_bin ^ (waddr_bin>>1);//二进制转换为格雷码
          68. assign raddr_gray = raddr_bin ^ (raddr_bin>>1);//二进制转换为格雷码
          69. always @(posedge rclk or negedge rrstn)//打一拍避免冲突与竞争
          70. if(!rrstn)
          71. rptr<=0;
          72. else
          73. rptr<=raddr_gray;
          74. always @(posedge wclk or negedge wrstn)//打一拍避免冲突与竞争
          75. if(!wrstn)
          76. wptr<=0;
          77. else
          78. wptr<=waddr_gray;
          79. /**********************syn addr gray*************************/
          80. //同步器打两拍
          81. reg [ADDR_WIDTH:0] wptr_buff;
          82. reg [ADDR_WIDTH:0] wptr_syn;
          83. reg [ADDR_WIDTH:0] rptr_buff;
          84. reg [ADDR_WIDTH:0] rptr_syn;
          85. always@(posedge rclk or negedge rrstn)
          86. //同步器:格雷码写地址同步至读时钟域
          87. if(!rrstn)
          88. begin
          89. wptr_buff<=0;
          90. wptr_syn<=0;
          91. end
          92. else
          93. begin
          94. wptr_buff<=wptr;
          95. wptr_syn<=wptr_buff;
          96. end
          97. always@(posedge wclk or negedge wrstn)// 格雷码读地址同步至写时钟域
          98. if(!wrstn)
          99. begin
          100. rptr_buff<=0;
          101. rptr_syn<=0;
          102. end
          103. else
          104. begin
          105. rptr_buff<=rptr;
          106. rptr_syn<=rptr_buff;
          107. end
          108. /**********************full empty gen*************************/
          109. //空满状态判断
          110. assign wfull = (wptr == {~rptr_syn[ADDR_WIDTH:ADDR_WIDTH-1],rptr_syn[ADDR_WIDTH-2:0]});
          111. assign rempty = (rptr == wptr_syn);
          112. /**********************RAM*************************/
          113. wire [ADDR_WIDTH-1:0] waddr;
          114. wire [ADDR_WIDTH-1:0] raddr;
          115. assign waddr = waddr_bin[ADDR_WIDTH-1:0];
          116. assign raddr = raddr_bin[ADDR_WIDTH-1:0];
          117. dual_port_RAM #(.DEPTH(DEPTH),
          118. .WIDTH(WIDTH)
          119. )dual_port_RAM(
          120. .wclk (wclk),
          121. .wenc (wenc),
          122. .waddr(waddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。
          123. .wdata(wdata), //数据写入
          124. .rclk (rclk),
          125. .renc (renc),
          126. .raddr(raddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。
          127. .rdata(rdata) //数据输出
          128. );
          129. endmodule

          5.6.1格雷码

          •         

            采用格雷码的原因:单bit翻转

                 每两个相邻编码之间只有一位是不同的,并且对于N位格雷码,当从最高位编码(对应二进制2^N -1)跳转到最低位编码(对应0)时也只会发生1bit跳转。单bit跳转意味着格雷码在通过二级同步器跨时钟时,输出不会出现不可控的中间状态,只能是正确的更新状态或者保持原来的状态。

          • 二进制码与gray码的转换关系

            • ​​​​​​​​​​​​​​二进制转换成格雷码:  assign    gray  =  (bin >>1^bin;

            • 格雷码转换成二进制码:

                   bin[N-1]  = gray[N-1];

                  for(i=0;i<(N-1);i+1)begin

                         bin[i] = ^(gray[N-1:0]>>i);  

                  end

          • ​​​​​​​

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/839154
推荐阅读
相关标签
  

闽ICP备14008679号