从网上看到了一系列十分不错的数字IC笔试面试必考点文章,特地搬运分享给各位论坛的坛友,共同学习,共同进步。来源于:新芯设计公众号
引言
一、亚稳态的相关概念
二、亚稳态的解决方法
三、亚稳态的解决案例
四、亚稳态的
Verilog
代码(打两拍、慢到快)
五、亚稳态的
Verilog
代码(打两拍、快到慢)
引言
本文主要介绍了亚稳态的分析与处理。
一、亚稳态的相关概念
亚稳态(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 代码(打两拍、慢到快)
Plaintext 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 代码(打两拍、快到慢)
Plaintext 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 |