赞
踩
数据传输中不满足触发器的Tsu(建立时间)和Th(保持时间),或者复位过程中复位信号的释放相对于有效时钟沿的恢复时间(recovery time)不满足,就可能产生亚稳态。
情形一(不满足建立时间和保持时间)
情形二(复位信号释放实际不满足)
在同源时钟下,时钟驱动寄存器的信号改变在保持时间之后;
在同源时钟下,时钟判断、检测寄存器的数值都是在刚开始建立时间时。
通过打拍消除亚稳态
三级寄存器消除亚稳态
PS:但是为什么第二级寄存器还是可能会产生亚稳态呢?
由于振荡时间Tmet是受到很多因素影响的,所以Tmet时间又长有短,所以当Tmet时间长到大于一个采集周期后,那第二级寄存器就会采集到亚稳态。
(不用二级)
由上面两个图可知,我们一般采用三级同步寄存器来增加系统的鲁棒性。
当异步信号不是一组数据,或者信号量较少,那就需要对异步信号进行同步处理,例如对一个异步脉冲信号进行采集,只要脉冲信号变化发生在时钟Tsu和Th窗口内,那就很可能会产生亚稳态,亚稳态产生的概率大概为:
概率 = (建立时间 + 保持时间)/ 采集时钟周期
a).单bit信号:
b).多bit信号:
下面着重介绍常用的单bit信号消除亚稳态的方法:
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时钟域第一级触发器出现亚稳态的概率,从而增大同步器出现亚稳态的概率;如下图所示:
- `timescale 1ns / 1ps
- module cdc_slow2fast(
- input i_clk_s,
- input i_rst_s,
- input i_pulse_s,
- input i_clk_f,
- input i_rst_f,
- output o_pulse_f
- );
-
- reg[1:0] r_pulse_s_d;
- assign o_pulse_f = r_pulse_s_d[1];
-
- always@(posedge i_clk_f,negedge i_rst_f)begin
- if(!i_rst_f)
- r_pulse_s_d <= 'd0;
- else
- r_pulse_s_d <= {r_pulse_s_d[0],i_pulse_s};
- end
- endmodule

tb文件
- `timescale 1ns / 1ps
- `define CLK_FAST_PERIOD 35
- `define CLK_SLOW_PERIOD 100
- module cdc_tb();
- reg i_clk_s ;
- reg i_rst_s ;
- reg i_pulse_s ;
- reg i_clk_f ;
- reg i_rst_f ;
- wire o_pulse_f ;
-
- cdc_slow2fast cdc_slow2fast_u0(
- .i_clk_s (i_clk_s ),
- .i_rst_s (i_rst_s ),
- .i_pulse_s (i_pulse_s),
- .i_clk_f (i_clk_f ),
- .i_rst_f (i_rst_f ),
- .o_pulse_f (o_pulse_f)
- );
-
- initial begin
- i_clk_s = 0;
- forever
- #(`CLK_SLOW_PERIOD/2) i_clk_s = ~i_clk_s;
- end
-
- initial begin
- i_clk_f = 0;
- forever
- #(`CLK_FAST_PERIOD/2) i_clk_f = ~i_clk_f;
- end
-
- initial begin
- i_rst_s = 0;
- i_rst_f = 0;
- @(posedge i_clk_s);
- i_rst_s = 1;
- @(posedge i_clk_f);
- i_rst_f = 1;
- i_pulse_s = 0;
- repeat(5)@(posedge i_clk_s);
- i_pulse_s = 1;
- repeat(5)@(posedge i_clk_s);
- i_pulse_s = 0;
- repeat(5)@(posedge i_clk_s);
- $stop;
- end
- endmodule

通过仿真,我们可以看出同步脉冲宽度宽度变了,但不影响我们的最初的本质,同步信号。
当试图将控制信号从较快的时钟域传递到较慢的时钟域时,会出现此规则的例外情况,
控制信号必须比较慢时钟的周期时间更宽(根据快慢时钟的频率比)。如果控制信号仅在一个快速时钟周期内被断言,则控制信号可以在较慢时钟的上升沿之间上下波动,而不会被捕获到较慢时钟域。
我们通过对快时钟域的脉冲信号进行展宽,仿真中我们的快时钟频率为100,慢时钟为35,所以我们需要对输入得快时钟信号展宽3倍才能满足慢时钟域能够完全采集到。
- `timescale 1ns / 1ps
- module fast2slow_cdc(
- input i_clk_f ,
- input i_rst_f ,
- input i_pulse_f ,
- input i_clk_s ,
- input i_rst_s ,
- output o_pulse_s
- );
-
- reg[2:0] r_pulse_f_d;
- reg[1:0] r_pulse_s_d;
- //***************** 展宽+同步 *************\\
- wire wide_pulse;
- //展宽信号通过原始输入信号和打拍信号进行或来展宽(主要依据快时钟和慢时钟的频率之比)
- assign wide_pulse = r_pulse_f_d[0]|r_pulse_f_d[1]|r_pulse_f_d[2];
- assign o_pulse_s = r_pulse_s_d[1];
-
- always @(posedge i_clk_f or negedge i_rst_f) begin
- if(!i_rst_f)
- r_pulse_f_d <= 'd0;
- else
- r_pulse_f_d <= {r_pulse_f_d[1:0],i_pulse_f};
- end
- always @(posedge i_clk_s or negedge i_rst_s) begin
- if(!i_rst_s)
- r_pulse_s_d <= 'd0;
- else
- r_pulse_s_d <= {r_pulse_s_d[0],wide_pulse};
- end
- endmodule

tb文件
- `timescale 1ns / 1ps
- `define CLK_FAST_PERIOD 35
- `define CLK_SLOW_PERIOD 100
- module cdc_tb();
- //******************** slow2fast s1*************************\\
- reg i_clk_f ;
- reg i_rst_f ;
- reg i_pulse_f ;
- reg i_clk_s ;
- reg i_rst_s ;
- wire o_pulse_s ;
- fast2slow_cdc fast2slow_cdc_u0(
- .i_clk_f (i_clk_f ),
- .i_rst_f (i_rst_f ),
- .i_pulse_f (i_pulse_f ),
- .i_clk_s (i_clk_s ),
- .i_rst_s (i_rst_s ),
- .o_pulse_s (o_pulse_s )
- );
-
- initial begin
- i_clk_s = 0;
- forever
- #(`CLK_SLOW_PERIOD/2) i_clk_s = ~i_clk_s;
- end
-
- initial begin
- i_clk_f = 0;
- forever
- #(`CLK_FAST_PERIOD/2) i_clk_f = ~i_clk_f;
- end
-
- initial begin
- i_rst_s = 0;
- i_rst_f = 0;
- @(posedge i_clk_s);
- i_rst_s = 1;
- @(posedge i_clk_f);
- i_rst_f = 1;
- i_pulse_f = 0;
- repeat(5)@(posedge i_clk_f);
- i_pulse_f = 1;
- @(posedge i_clk_f);
- i_pulse_f = 0;
- repeat(5)@(posedge i_clk_f);
- $stop;
- end
- endmodule

仿真结果如下:
PS:根据理论分析,由于我们的展宽信号是很简答的逻辑或产生的,若信号打拍次数过多,在不理想的脉冲边沿进行逻辑或很容易产生毛刺,会使得不稳定,所以我们采用下面的的方法。
该方法逻辑运算只涉及了一次边沿检测(逻辑异或),相对于方法一,很大程度上增加了稳定性。
- `timescale 1ns / 1ps
- module fast2slow_cdc(
- input i_clk_f ,
- input i_rst_f ,
- input i_pulse_f ,
- input i_clk_s ,
- input i_rst_s ,
- output o_pulse_s
- );
-
- //***************** 脉冲电平检测+双触发器同步+ 边沿检测 *************\\
- reg r_pulse_f;
- reg[2:0] r_pulse_f_d;
- always @(posedge i_clk_f or negedge i_rst_f) begin
- if(!i_rst_f)
- r_pulse_f <= 'd0;
- else if(i_pulse_f)
- r_pulse_f <= ~r_pulse_f;
- else
- r_pulse_f <= r_pulse_f;
- end
- always @(posedge i_clk_s or negedge i_rst_s) begin
- if(!i_rst_s)
- r_pulse_f_d <= 'd0;
- else
- r_pulse_f_d <= {r_pulse_f_d[1:0],r_pulse_f};
- end
-
- assign o_pulse_s = (!r_pulse_f_d[2] & r_pulse_f_d[1]) || (r_pulse_f_d[2] & !r_pulse_f_d[1]) ;
- endmodule

tb文件
-
- initial begin
- i_rst_s = 0;
- i_rst_f = 0;
- i_pulse_f = 0;
- @(posedge i_clk_s);
- i_rst_s = 1;
- @(posedge i_clk_f);
- i_rst_f = 1;
- repeat(5)@(posedge i_clk_f);
- i_pulse_f = 1;
- @(posedge i_clk_f);
- i_pulse_f = 0;
- repeat(5)@(posedge i_clk_f);
- i_pulse_f = 1;
- @(posedge i_clk_f);
- i_pulse_f = 0;
- repeat(5)@(posedge i_clk_f);
- $stop;
- end

仿真波形
PS:这个方法也有一个bug:如果快时钟域的两个相邻的输入信号间隔仍快于慢时钟频率,那样对于慢时钟域不一定能采集到。因此最稳妥使用握手协议+同步的方法来实现单Bit的跨时钟域处理 。
结2.1和2.2的两种方法,并在此基础之上进行优化,我们将快时钟域的输入信号进行展宽,展宽信号的长度(下降沿)根据握手协议来决定(拉低)。
大致原理可参考下图:
- `timescale 1ns / 1ps
- module fast2slow_cdc(
- input i_clk_f ,
- input i_rst_f ,
- input i_pulse_f ,
- input i_clk_s ,
- input i_rst_s ,
- output o_pulse_s
- );
- reg r_wide_pulse_f ;
- reg[1:0] r_pulse_s_d ;
- reg[1:0] r_pulse_f_d ;
-
- always @(posedge i_clk_f or negedge i_rst_f) begin
- if(!i_rst_f)
- r_wide_pulse_f <= 'd0;
- else if(r_pulse_f_d[1] == 1'd1)
- r_wide_pulse_f <= 'd0;
- else if(i_pulse_f)
- r_wide_pulse_f <= 'd1;
- else
- r_wide_pulse_f <= r_wide_pulse_f;
- end
-
- always @(posedge i_clk_s or negedge i_rst_s) begin
- if(!i_rst_s)
- r_pulse_s_d <= 'd0;
- else
- r_pulse_s_d <= {r_pulse_s_d[0],r_wide_pulse_f};
- end
- always @(posedge i_clk_f or negedge i_rst_f) begin
- if(!i_rst_f)
- r_pulse_f_d <= 'd0;
- else
- r_pulse_f_d <= {r_pulse_f_d[0],r_pulse_s_d[1]};
- end
-
- assign o_pulse_s = !r_pulse_s_d[1] & r_pulse_s_d[0];
- endmodule

tb文件
- initial begin
- i_rst_s = 0;
- i_rst_f = 0;
- i_pulse_f = 0;
- @(posedge i_clk_s);
- i_rst_s = 1;
- @(posedge i_clk_f);
- i_rst_f = 1;
- repeat(5)@(posedge i_clk_f);
- i_pulse_f = 1;
- @(posedge i_clk_f);
- i_pulse_f = 0;
- repeat(15)@(posedge i_clk_f);
- i_pulse_f = 1;
- @(posedge i_clk_f);
- i_pulse_f = 0;
- repeat(5)@(posedge i_clk_f);
- $stop;
- end

仿真结果
通过以上的CDC跨时钟域处理,我们最后可以完美解决单BIT信号的跨时钟处理。
PS:如有雷同,纯属抄袭!(借鉴颇多)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。