当前位置:   article > 正文

亚稳态及跨时钟域处理_跨时钟域同步打两拍亚稳态

跨时钟域同步打两拍亚稳态

一、亚稳态:

1.亚稳态出现的原因

        数据传输中不满足触发器的Tsu(建立时间)和Th(保持时间),或者复位过程中复位信号的释放相对于有效时钟沿的恢复时间(recovery time)不满足,就可能产生亚稳态。

情形一(不满足建立时间和保持时间)

情形二(复位信号释放实际不满足)

在同源时钟下,时钟驱动寄存器的信号改变在保持时间之后;

在同源时钟下,时钟判断、检测寄存器的数值都是在刚开始建立时间时。

2.消除亚稳态

通过打拍消除亚稳态

三级寄存器消除亚稳态

PS:但是为什么第二级寄存器还是可能会产生亚稳态呢?

由于振荡时间Tmet是受到很多因素影响的,所以Tmet时间又长有短,所以当Tmet时间长到大于一个采集周期后,那第二级寄存器就会采集到亚稳态。

(不用二级)

由上面两个图可知,我们一般采用三级同步寄存器来增加系统的鲁棒性。

当异步信号不是一组数据,或者信号量较少,那就需要对异步信号进行同步处理,例如对一个异步脉冲信号进行采集,只要脉冲信号变化发生在时钟Tsu和Th窗口内,那就很可能会产生亚稳态,亚稳态产生的概率大概为:

                                              概率 = (建立时间 + 保持时间)/ 采集时钟周期

3.解决亚稳态的方法:

    1.降低系统时钟的频率(采样周期时间增大)
    2.缩小亚稳态窗口(缩小Tsu和Th),取决于FPGA工艺
    3.采用CDC跨时钟域处理

        a).单bit信号:

  • 从慢时钟域到快时钟域(同步寄存器):脉冲宽度会改变,但不影响同步结果
  • 从快时钟域到慢时钟域:1.(展宽+同步)2.(脉冲电平检测+双触发器同步+边沿检测)
  • 任意时钟域

        b).多bit信号:

下面着重介绍常用的单bit信号消除亚稳态的方法:

二、CDC跨时钟域处理(Verilog代码实现带仿真)

1.慢时钟域 跨 快时钟域:打两拍

1.如果慢时钟是clk_slow,快时钟是clk_fast,那么从clk_slow打出的单bit信号宽度至少是clk_fast周期的1.5倍,否则认为是从快到慢;

2.从clk_slow同步到clk_fast,从clk_slow打出的信号必须经过clk_slow打过,否则组合逻辑输出会有毛刺,被clk_fast采到,会增大clk_fast时钟域第一级触发器出现亚稳态的概率,从而增大同步器出现亚稳态的概率;如下图所示:

  1. `timescale 1ns / 1ps
  2. module cdc_slow2fast(
  3. input i_clk_s,
  4. input i_rst_s,
  5. input i_pulse_s,
  6. input i_clk_f,
  7. input i_rst_f,
  8. output o_pulse_f
  9. );
  10. reg[1:0] r_pulse_s_d;
  11. assign o_pulse_f = r_pulse_s_d[1];
  12. always@(posedge i_clk_f,negedge i_rst_f)begin
  13. if(!i_rst_f)
  14. r_pulse_s_d <= 'd0;
  15. else
  16. r_pulse_s_d <= {r_pulse_s_d[0],i_pulse_s};
  17. end
  18. endmodule

 tb文件

  1. `timescale 1ns / 1ps
  2. `define CLK_FAST_PERIOD 35
  3. `define CLK_SLOW_PERIOD 100
  4. module cdc_tb();
  5. reg i_clk_s ;
  6. reg i_rst_s ;
  7. reg i_pulse_s ;
  8. reg i_clk_f ;
  9. reg i_rst_f ;
  10. wire o_pulse_f ;
  11. cdc_slow2fast cdc_slow2fast_u0(
  12. .i_clk_s (i_clk_s ),
  13. .i_rst_s (i_rst_s ),
  14. .i_pulse_s (i_pulse_s),
  15. .i_clk_f (i_clk_f ),
  16. .i_rst_f (i_rst_f ),
  17. .o_pulse_f (o_pulse_f)
  18. );
  19. initial begin
  20. i_clk_s = 0;
  21. forever
  22. #(`CLK_SLOW_PERIOD/2) i_clk_s = ~i_clk_s;
  23. end
  24. initial begin
  25. i_clk_f = 0;
  26. forever
  27. #(`CLK_FAST_PERIOD/2) i_clk_f = ~i_clk_f;
  28. end
  29. initial begin
  30. i_rst_s = 0;
  31. i_rst_f = 0;
  32. @(posedge i_clk_s);
  33. i_rst_s = 1;
  34. @(posedge i_clk_f);
  35. i_rst_f = 1;
  36. i_pulse_s = 0;
  37. repeat(5)@(posedge i_clk_s);
  38. i_pulse_s = 1;
  39. repeat(5)@(posedge i_clk_s);
  40. i_pulse_s = 0;
  41. repeat(5)@(posedge i_clk_s);
  42. $stop;
  43. end
  44. endmodule

通过仿真,我们可以看出同步脉冲宽度宽度变了,但不影响我们的最初的本质,同步信号。

2.快时钟域 跨 慢时钟域

当试图将控制信号从较快的时钟域传递到较慢的时钟域时,会出现此规则的例外情况,
控制信号必须比较慢时钟的周期时间更宽(根据快慢时钟的频率比。如果控制信号仅在一个快速时钟周期内被断言,则控制信号可以在较慢时钟的上升沿之间上下波动,而不会被捕获到较慢时钟域。

2.1 展宽+同步解决

我们通过对快时钟域的脉冲信号进行展宽,仿真中我们的快时钟频率为100,慢时钟为35,所以我们需要对输入得快时钟信号展宽3倍才能满足慢时钟域能够完全采集到。 

  1. `timescale 1ns / 1ps
  2. module fast2slow_cdc(
  3. input i_clk_f ,
  4. input i_rst_f ,
  5. input i_pulse_f ,
  6. input i_clk_s ,
  7. input i_rst_s ,
  8. output o_pulse_s
  9. );
  10. reg[2:0] r_pulse_f_d;
  11. reg[1:0] r_pulse_s_d;
  12. //***************** 展宽+同步 *************\\
  13. wire wide_pulse;
  14. //展宽信号通过原始输入信号和打拍信号进行或来展宽(主要依据快时钟和慢时钟的频率之比)
  15. assign wide_pulse = r_pulse_f_d[0]|r_pulse_f_d[1]|r_pulse_f_d[2];
  16. assign o_pulse_s = r_pulse_s_d[1];
  17. always @(posedge i_clk_f or negedge i_rst_f) begin
  18. if(!i_rst_f)
  19. r_pulse_f_d <= 'd0;
  20. else
  21. r_pulse_f_d <= {r_pulse_f_d[1:0],i_pulse_f};
  22. end
  23. always @(posedge i_clk_s or negedge i_rst_s) begin
  24. if(!i_rst_s)
  25. r_pulse_s_d <= 'd0;
  26. else
  27. r_pulse_s_d <= {r_pulse_s_d[0],wide_pulse};
  28. end
  29. endmodule

 tb文件

  1. `timescale 1ns / 1ps
  2. `define CLK_FAST_PERIOD 35
  3. `define CLK_SLOW_PERIOD 100
  4. module cdc_tb();
  5. //******************** slow2fast s1*************************\\
  6. reg i_clk_f ;
  7. reg i_rst_f ;
  8. reg i_pulse_f ;
  9. reg i_clk_s ;
  10. reg i_rst_s ;
  11. wire o_pulse_s ;
  12. fast2slow_cdc fast2slow_cdc_u0(
  13. .i_clk_f (i_clk_f ),
  14. .i_rst_f (i_rst_f ),
  15. .i_pulse_f (i_pulse_f ),
  16. .i_clk_s (i_clk_s ),
  17. .i_rst_s (i_rst_s ),
  18. .o_pulse_s (o_pulse_s )
  19. );
  20. initial begin
  21. i_clk_s = 0;
  22. forever
  23. #(`CLK_SLOW_PERIOD/2) i_clk_s = ~i_clk_s;
  24. end
  25. initial begin
  26. i_clk_f = 0;
  27. forever
  28. #(`CLK_FAST_PERIOD/2) i_clk_f = ~i_clk_f;
  29. end
  30. initial begin
  31. i_rst_s = 0;
  32. i_rst_f = 0;
  33. @(posedge i_clk_s);
  34. i_rst_s = 1;
  35. @(posedge i_clk_f);
  36. i_rst_f = 1;
  37. i_pulse_f = 0;
  38. repeat(5)@(posedge i_clk_f);
  39. i_pulse_f = 1;
  40. @(posedge i_clk_f);
  41. i_pulse_f = 0;
  42. repeat(5)@(posedge i_clk_f);
  43. $stop;
  44. end
  45. endmodule

仿真结果如下:

PS:根据理论分析,由于我们的展宽信号是很简答的逻辑或产生的,若信号打拍次数过多,在不理想的脉冲边沿进行逻辑或很容易产生毛刺,会使得不稳定,所以我们采用下面的的方法。 

2.2 脉冲电平检测+双触发器同步+ 边沿检测

该方法逻辑运算只涉及了一次边沿检测(逻辑异或),相对于方法一,很大程度上增加了稳定性。

  1. `timescale 1ns / 1ps
  2. module fast2slow_cdc(
  3. input i_clk_f ,
  4. input i_rst_f ,
  5. input i_pulse_f ,
  6. input i_clk_s ,
  7. input i_rst_s ,
  8. output o_pulse_s
  9. );
  10. //***************** 脉冲电平检测+双触发器同步+ 边沿检测 *************\\
  11. reg r_pulse_f;
  12. reg[2:0] r_pulse_f_d;
  13. always @(posedge i_clk_f or negedge i_rst_f) begin
  14. if(!i_rst_f)
  15. r_pulse_f <= 'd0;
  16. else if(i_pulse_f)
  17. r_pulse_f <= ~r_pulse_f;
  18. else
  19. r_pulse_f <= r_pulse_f;
  20. end
  21. always @(posedge i_clk_s or negedge i_rst_s) begin
  22. if(!i_rst_s)
  23. r_pulse_f_d <= 'd0;
  24. else
  25. r_pulse_f_d <= {r_pulse_f_d[1:0],r_pulse_f};
  26. end
  27. assign o_pulse_s = (!r_pulse_f_d[2] & r_pulse_f_d[1]) || (r_pulse_f_d[2] & !r_pulse_f_d[1]) ;
  28. endmodule

tb文件 

  1. initial begin
  2. i_rst_s = 0;
  3. i_rst_f = 0;
  4. i_pulse_f = 0;
  5. @(posedge i_clk_s);
  6. i_rst_s = 1;
  7. @(posedge i_clk_f);
  8. i_rst_f = 1;
  9. repeat(5)@(posedge i_clk_f);
  10. i_pulse_f = 1;
  11. @(posedge i_clk_f);
  12. i_pulse_f = 0;
  13. repeat(5)@(posedge i_clk_f);
  14. i_pulse_f = 1;
  15. @(posedge i_clk_f);
  16. i_pulse_f = 0;
  17. repeat(5)@(posedge i_clk_f);
  18. $stop;
  19. end

仿真波形

 

PS:这个方法也有一个bug:如果快时钟域的两个相邻的输入信号间隔仍快于慢时钟频率,那样对于慢时钟域不一定能采集到。因此最稳妥使用握手协议+同步的方法来实现单Bit的跨时钟域处理 。

2.3 展宽信号+握手协议

结2.1和2.2的两种方法,并在此基础之上进行优化,我们将快时钟域的输入信号进行展宽,展宽信号的长度(下降沿)根据握手协议来决定(拉低)。

大致原理可参考下图:

  1. `timescale 1ns / 1ps
  2. module fast2slow_cdc(
  3. input i_clk_f ,
  4. input i_rst_f ,
  5. input i_pulse_f ,
  6. input i_clk_s ,
  7. input i_rst_s ,
  8. output o_pulse_s
  9. );
  10. reg r_wide_pulse_f ;
  11. reg[1:0] r_pulse_s_d ;
  12. reg[1:0] r_pulse_f_d ;
  13. always @(posedge i_clk_f or negedge i_rst_f) begin
  14. if(!i_rst_f)
  15. r_wide_pulse_f <= 'd0;
  16. else if(r_pulse_f_d[1] == 1'd1)
  17. r_wide_pulse_f <= 'd0;
  18. else if(i_pulse_f)
  19. r_wide_pulse_f <= 'd1;
  20. else
  21. r_wide_pulse_f <= r_wide_pulse_f;
  22. end
  23. always @(posedge i_clk_s or negedge i_rst_s) begin
  24. if(!i_rst_s)
  25. r_pulse_s_d <= 'd0;
  26. else
  27. r_pulse_s_d <= {r_pulse_s_d[0],r_wide_pulse_f};
  28. end
  29. always @(posedge i_clk_f or negedge i_rst_f) begin
  30. if(!i_rst_f)
  31. r_pulse_f_d <= 'd0;
  32. else
  33. r_pulse_f_d <= {r_pulse_f_d[0],r_pulse_s_d[1]};
  34. end
  35. assign o_pulse_s = !r_pulse_s_d[1] & r_pulse_s_d[0];
  36. endmodule

 tb文件

  1. initial begin
  2. i_rst_s = 0;
  3. i_rst_f = 0;
  4. i_pulse_f = 0;
  5. @(posedge i_clk_s);
  6. i_rst_s = 1;
  7. @(posedge i_clk_f);
  8. i_rst_f = 1;
  9. repeat(5)@(posedge i_clk_f);
  10. i_pulse_f = 1;
  11. @(posedge i_clk_f);
  12. i_pulse_f = 0;
  13. repeat(15)@(posedge i_clk_f);
  14. i_pulse_f = 1;
  15. @(posedge i_clk_f);
  16. i_pulse_f = 0;
  17. repeat(5)@(posedge i_clk_f);
  18. $stop;
  19. end

 仿真结果

 通过以上的CDC跨时钟域处理,我们最后可以完美解决单BIT信号的跨时钟处理。

PS:如有雷同,纯属抄袭!(借鉴颇多)

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

闽ICP备14008679号