一、亚稳态:
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时钟域第一级触发器出现亚稳态的概率,从而增大同步器出现亚稳态的概率;如下图所示:
`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
通过仿真,我们可以看出同步脉冲宽度宽度变了,但不影响我们的最初的本质,同步信号。
2.快时钟域 跨 慢时钟域
当试图将控制信号从较快的时钟域传递到较慢的时钟域时,会出现此规则的例外情况,
控制信号必须比较慢时钟的周期时间更宽(根据快慢时钟的频率比)。如果控制信号仅在一个快速时钟周期内被断言,则控制信号可以在较慢时钟的上升沿之间上下波动,而不会被捕获到较慢时钟域。
2.1 展宽+同步解决
我们通过对快时钟域的脉冲信号进行展宽,仿真中我们的快时钟频率为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:根据理论分析,由于我们的展宽信号是很简答的逻辑或产生的,若信号打拍次数过多,在不理想的脉冲边沿进行逻辑或很容易产生毛刺,会使得不稳定,所以我们采用下面的的方法。
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_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.3 展宽信号+握手协议
结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:如有雷同,纯属抄袭!(借鉴颇多)