亚稳态及跨时钟域处理

一、亚稳态:

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:如有雷同,纯属抄袭!(借鉴颇多)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值