亚稳态的分析与处理

描述

引言

  本文主要介绍了亚稳态的分析与处理。

一、亚稳态的相关概念

  亚稳态(Metastability)是指 D 触发器无法在某个规定的时间段内(决断时间)达到一个可确认的状态(0 或 1),进而处于一个振荡的不确定状态。

  亚稳态的具体表现是当一个 D 触发器进入亚稳态时,既无法预测该单元的输出电平(不确定是 0 还是 1),也无法预测何时输出才能够稳定下来(通常情况下,一个时钟、或者两个时钟的时间之内可以返回稳态)。在这个达到稳定之前的时间内,D 触发器输出一些中间级电平,或者可能处于振荡状态,或者可能会沿着信号通道上的各个 D 触发器级联式传播下去,最终导致系统的崩溃。

  亚稳态的产生原因是 D 触发器的建立时间和保持时间在时钟上升沿左右定义了一个时间窗口(亚稳态窗口)(较新的逻辑器件会有较小的亚稳态窗口),在这段时间内,输入信号和时钟都应该保持不变。如果 D 触发器的输入数据在这个时间窗口内发生变化(数据更新),那么就违反了建立时间和保持时间的要求,从而产生了时序违规(Timing Violation)。此时 D 触发器内部的一个节点(一个内部节点或者要输出到外部节点)可能会在一个电压范围内浮动(徘徊在一个中间电平状态),无法确定最终是稳定在逻辑 0 或者是逻辑 1 的状态,而在这段时间里,数据输出端 Q 为毛刺、振荡状态,而不是等于数据输入端 D 的值。

  亚稳态的随机输出表现为 D 触发器输出端 Q 在时钟上升沿之后,比较长的一段时间处于不确定的状态,在这段时间里 Q 端在 0 和 1 之间处于振荡状态,而不是等于数据输入端 D 的值,这段时间称为决断时间。经过决断时间之后 Q 端将稳定为 0 或 1 ,但是具体是 0 或 1 却是随机的,与输入没有必然关联。亚稳态无法根除,但是可以减小亚稳态发生的概率。

二、亚稳态的解决方法

  • 降低系统时钟频率;
  • 提高时钟信号边沿变化速度(这个取决于晶振、器件、工艺等等);
  • 用反应更快的 DFF;
  • 引入同步机制,防止亚稳态的传播;
  • 相位控制技术,PLL 控制分频与相位。

三、亚稳态的解决案例

  对于以上的第 4 点,有多级 D 触发器级联处理方式(节拍),可对异步信号进行同步处理。可以看出,前面基于时钟域 Clk_a 的信号发送过来,在基于时钟域 Clk_b 的时钟下进行采样,可能会出现采样时钟上升沿刚好出现在数据变化的那一刻,于是,就会产生亚稳态。

  当第一个寄存器发生亚稳态后,经过 Tmet 的振荡稳定后,第二级、或者第三级寄存器就能采集到一个稳定的值,避免了亚稳态跟随着电路一直传递下去,从而最终导致的系统的崩溃。

时钟

打两拍电路图

时钟

打两拍时序图

时钟

打两拍时序图

四、亚稳态的 Verilog 代码(打两拍、慢到快)

reg [1:0] signal_r; // 两级缓冲器、两级寄存器,打完两拍之后,才可以进行组合逻辑的操作

always @(posedge clkb or negedge rst_n)begin
    if(!rst_n)
        signal_r <= 2'b00;
    else begin
        signal_r <= {signal_r[0], signal_in};//signal_in 是基于 clka 的
    end
end

assign  signal_out = signal_r[1];

五、亚稳态的 Verilog 代码(打两拍、快到慢)

module Sync_Pulse(  // clka 下生成展宽信号,展宽信号同步到 clkb,再同步回 clka
 input  clka,
 input  clkb,
 input  rst_n,
 input  pulse_ina, // clka下的脉冲;
 output pulse_outb,// 上升沿检测;
 output signal_outb// clkb下的脉冲;
);

reg signal_a;
reg [2:0] signal_b_r;
reg [1:0] signal_a_r;

//-------------------------------------------------------
// 在 clka 下,生成展宽信号 signal_a
always @(posedge clka or negedge rst_n)begin
    if(!rst_n)
        signal_a <= 1'b0;
    else if(pulse_ina)     // 检测到输入信号 pulse_ina 被拉高,则拉高 signal_a
        signal_a <= 1'b1;
    else if(signal_a_r[1]) // 检测到反馈信号 signal_a_r[1] 被拉高,则拉低 signal_a
        signal_a <= 1'b0;
    else
        signal_a <= signal_a;
end
//-------------------------------------------------------
// 在 clkb 下打三拍,前两个用于同步 signal_a,后一个用于生成脉冲信号、// 输出信号
always @(posedge clkb or negedge rst_n)begin
    if(!rst_n)
        signal_b_r <= 3'b000;
    else
        signal_b_r <= {signal_b_r[1:0], signal_a};
end
assign pulse_outb = ~signal_b_r[2] & signal_b_r[1];// 判断上升沿;
assign signal_outb = signal_b_r[2];
// 不需要用到跳变沿的来自同一时钟域的输入,没有必要对信号进行寄存;
// 需要用到跳变沿的来自同一时钟域的输入,寄存一次即可;
// 需要用到跳变沿的来自不同时钟域的输入,需要用到 3 个触发器,前两个用// 于同步,第 3 个触发器的输出和第 2 个的输出经过逻辑门来判断跳变沿。
//-------------------------------------------------------
// 在 clka 下打两拍,采集 signal_b_r[2],生成 signal_a_r[1] 用于反馈拉// 低 signal_a
always @(posedge clka or negedge rst_n)begin
    if(!rst_n)begin
        signal_a_r <= 2'b00;
    end
    else begin
        signal_a_r <= {signal_a_r[0], signal_b_r[2]};
    end
end

endmodule
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分