16 亚稳态原理和解决方案

1. 亚稳态原理

  • 亚稳态是指触发器无法在某个规定的时间段内到达一个可以确认的状态。
  • 在同步系统中,输入总是与时钟同步,因此寄存器的setup time和hold time是满足的,一般情况下是不会发生亚稳态情况的。
  • 在异步信号采集中,由于异步信号可以在任意时间点到达目的寄存器,所以无法保证满足目的寄存器Tsu和Th的要求,从而出现亚稳态

2. 异步信号用于边沿检测时出现亚稳态的波形分析

以外部按键key按下时是否出现下降沿为例:

按键未按下时为高电平,按下后为低电平,我们需要通过检测下降沿来判断按键是否按下,但由于亚稳态的存在,异步信号有可能检测不到,因此此处将分情况进行讨论。

2.1 未使用两级同步时正常情况与亚稳态情况

  1. 如果外部信号转变发生在建立保持时间之外(非时间上升沿处),则在外部信号变化的下一个时钟沿处,下降沿信号能被正常检测到。

  2. 如果外部信号转变发生在建立保持时间之内,出现了亚稳态现象,则在亚稳态结束后最终输入与输出有两种情况,输出不确定的0或1。

2.2 出现亚稳态后的两种情况

  1. 对于情况一,如果亚稳态最终稳定在高电平,则可以正常检测到下降沿

  2. 对于情况一,如果亚稳态最终稳定在低电平,那么将不能检测到下降沿信号

2.3 使用一级/两级同步后的波形分析

  1. 由于亚稳态持续时间小于一个时钟周期,使用一级同步后便可检测到下降沿,比原先的下降沿晚了一个时钟周期。

  2. 一般情况下,使用二级同步后可将单bit的亚稳态发生概率降低99%,所以一般会使用二级同步。

3. 按键消抖的代码修改(使用两级同步降低亚稳态的影响)

cpp 复制代码
module key_filter(
    clk,
    rstn,
    key,
//    key_p_flag,
//    key_r_flag,
    key_flag,
    key_state
);
    
    input clk;
    input rstn;
    input key;
//   output reg key_p_flag;
//   output reg key_r_flag;
    output reg key_flag;
    output reg key_state;

    
    //两级同步
    reg[1:0] sync_key;
    always@(posedge clk)
        sync_key <= {sync_key[0], key};       
        //key为D1,sync <= {Q2,Q1}不看取值,只看位置。
        //key存于sync[0], sync[0]存于sync[1]
        
    //边沿检测
    reg [1:0] r_key;
    always@(posedge clk)
        r_key <= {r_key[0], sync_key[1]}; 
        //r_key <= {Q4,Q3}
        //此处用了四个寄存器,比两级同步还多一级。
        //对于边沿检测,只用一个寄存器即可,我们用了两个
        //加上两级同步的两个寄存器,共四个寄存器。
        
//    reg [1:0] r_key;    
//    always@(posedge clk)begin
//        r_key[0] <= key;
//        r_key[1] <= r_key[0]; 
//    end
    wire nedge_key;
    wire pedge_key;
    assign nedge_key = (r_key == 2'b10);
    assign pedge_key = (r_key == 2'b01);
    
    
    reg [1:0]state;
    reg [19:0] cnt;
    always@(posedge clk or negedge rstn)
    if(!rstn)begin
        state <= 0;
        cnt <= 0;
//        key_p_flag <= 0;
//        key_r_flag <= 0;
        key_flag <= 0;
        key_state <= 1;
    end
    else
    case(state)
        0: 
        begin
//            key_r_flag <= 0;
            key_flag <= 0;
            if(nedge_key == 1) begin
                state <= 1;
            end
            else
                state <= 0;
        end 
        
        1:
        begin
            if((pedge_key == 1) && (cnt < 1000000 - 1))begin
                state <= 0;
                cnt <= 0;
            end
            else if((pedge_key == 0) && (cnt >= 1000000 - 1))begin
                state <= 2;
//               key_p_flag <= 1'd1;
                key_flag <= 1'd1;
                key_state <= 0;
                cnt <= 0;
            end
            else
                cnt <= cnt + 1'd1;
        end
        
        2:
        begin
//           key_p_flag <= 0;
            key_flag <= 0;
            if(pedge_key == 1)
                state <= 3;
            else
                state <= 2;
        end
        
        3:
        begin
            if((nedge_key == 1) && (cnt < 1000000 - 1))begin
                state <= 2;
                cnt <= 0;
            end
            else if((nedge_key == 0) && (cnt >= 1000000 - 1))begin
                state <= 0;
//                key_r_flag <= 1;
                key_flag <= 1'd1;
                key_state <= 1;
                cnt <= 0;
            end
            else
                cnt <= cnt + 1'd1;
         end
        
    endcase
    
endmodule
相关推荐
TIF星空30 分钟前
【使用 C# 获取 USB 设备信息及进行通信】
开发语言·经验分享·笔记·学习·microsoft·c#
AI算法工程师Moxi3 小时前
什么时候可以开始学习深度学习?
人工智能·深度学习·学习
jiedaodezhuti5 小时前
ElasticSearch重启之后shard未分配问题的解决
笔记·elasticsearch
z人间防沉迷k6 小时前
堆(Heap)
开发语言·数据结构·笔记·python·算法
z542968z6 小时前
Springboot3自定义starter笔记
笔记
丰锋ff7 小时前
操作系统学习笔记第3章 内存管理(灰灰题库)
笔记·学习
zly88653727 小时前
MMIO机制详解
fpga开发
jackson凌7 小时前
【Java学习笔记】equals方法
java·笔记·学习
虾球xz7 小时前
游戏引擎学习第282天:Z轴移动与摄像机运动
c++·学习·游戏引擎
.小墨迹8 小时前
Apollo学习——planning模块(3)之planning_base
linux·开发语言·c++·学习·自动驾驶