转载:FPGA设计的“打拍(寄存)”和“亚稳态” 到底是什么?

本文转载自CSDN博主「孤独的单刀」的原创文章,原文链接:https://blog.csdn.net/wuzhikaidetb/article/details/119619162

1、前言

  可能很多FPGA初学者在刚开始学习FPGA设计的时候(当然也包括我自己),经常听到类似于“这个信号需要打一拍、打两拍(寄存),以防止亚稳态问题的产生”这种话,但是对这个打拍和亚稳态问题还是一知半解,接下来结合一些资料谈下自己的理解。


2、触发器的建立时间和保持时间

  时序电路的基础是触发器(FF、Flip-Flop),触发器正常工作需要满足建立时间和保持时间的时序要求。

建立时间Tsu(set up time):是指在触发器的时钟信号上升沿到来以前,数据稳定不变的时间,如果建立时间不够,数据将不能在这个时钟上升沿被稳定的打入触发器,Tsu就是指这个最小的稳定时间。
保持时间Th(hold time):是指在触发器的时钟信号上升沿到来以后,数据稳定不变的时间,如果保持时间不够,数据同样不能被稳定的打入触发器,Th就是指这个最小的保持时间

3、亚稳态

  亚稳态 (Metastability),如果数据传输中不满足触发器的Tsu和Th,就可能产生亚稳态。此时触发器输出端Q在有效时钟沿之后比较长的一段时间处于不确定的状态,在这段时间里Q端在0和1之间处于振荡状态,而不是等于数据输入端D的值。这段时间称为 决断时间Tmet(resolution time)。经过Tmet之后Q端将稳定到0或1上,但是稳定到0或者1,是随机的,与输入没有必然的关系。

  亚稳态振荡时间 Tmet 关系到后级寄存器的采集稳定问题,Tmet 影响因素包括:器件的生产工艺、温度、环境以及寄存器采集到亚稳态里稳定态的时刻等。甚至某些特定条件,如干扰、辐射等都会造成 Tmet 增长。

  只要系统中有异步元件,亚稳态就是无法避免的,亚稳态主要发生在异步信号检测、跨时钟域信号传输以及复位电路等常用设计中。由于产生亚稳态后,寄存器Q端输出在稳定下来之前可能是毛刺、振荡、固定的某一电压值。在信号传输中产生亚稳态就会导致与其相连的其他数字部件将作出不同的判断,有的判断到“1”,有的判断到“0”,有的也进入了亚稳态,数字部件就会逻辑混乱。


4、如何防止亚稳态

  首先,在同步系统中,输入信号总是由系统时钟同步,能够达到寄存器的时序要求,所以亚稳态肯定不会发生。在异步系统的信号输出过程中,如果无法满足建立时间和保持时间的要求则会发生亚稳态。预防亚稳态的方法就是将输入信号(单bit信号)打拍,也就是在要使用的时钟域下,将信号寄存。

  rx是相对于时钟域sys_clk的异步信号,rx_reg1、rx_reg2分别是rx在时钟域sys_clk打一拍(寄存一次,也可以理解为延迟一个时钟周期 )、打两拍(寄存两次,也可以理解为延迟两个时钟周期)的信号。可以看到rx_reg1可能还存在低概率的亚稳态现象,当然rx_reg2虽然在示意图里是稳定的,不过实际过程中也仍然存在亚稳态发生的概率。

  单bit信号从慢速时钟域同步到快速时钟域需要使用打两拍的方式消除亚稳态。 第一级寄存器产生亚稳态并经过自身后可以稳定输出的概率为 70%~80%左右,第二级寄存器可以稳定输出的概率为 99%左右,后面再多加寄存器的级数改善效果就不明显了,所以数据进来后一般选择打两拍即可。这里注意,该方法仅仅适用单bit信号从慢速时钟域同步到快速时钟域,单bit信号从快速时钟域同步到慢速时钟域还仅仅使用打两拍的方式则会漏采数据。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这里是一个简单的自动寄存柜的C语言实验示例: ```c #include <stdio.h> #include <stdlib.h> #define CABIN_NUM 10 #define CABIN_SIZE 20 char cabin[CABIN_NUM][CABIN_SIZE]; int occupied[CABIN_NUM] = {0}; int find_cabin() { for (int i = 0; i < CABIN_NUM; i++) { if (occupied[i] == 0) { return i; } } return -1; } void show_cabins() { printf("Cabin Status:\n"); for (int i = 0; i < CABIN_NUM; i++) { printf("%d: %s\n", i, cabin[i]); } printf("\n"); } int main() { int cmd, cabin_num; char item[CABIN_SIZE]; while (1) { printf("1: Store item\n"); printf("2: Retrieve item\n"); printf("3: Show cabin status\n"); printf("4: Exit\n"); printf("Command: "); scanf("%d", &cmd); switch (cmd) { case 1: cabin_num = find_cabin(); if (cabin_num == -1) { printf("No available cabin!\n"); } else { printf("Input item: "); scanf("%s", item); strcpy(cabin[cabin_num], item); occupied[cabin_num] = 1; printf("Item %s stored in cabin %d.\n", item, cabin_num); } break; case 2: printf("Input cabin number: "); scanf("%d", &cabin_num); if (cabin_num < 0 || cabin_num >= CABIN_NUM) { printf("Invalid cabin number!\n"); } else if (occupied[cabin_num] == 0) { printf("This cabin is empty!\n"); } else { printf("Item %s retrieved from cabin %d.\n", cabin[cabin_num], cabin_num); strcpy(cabin[cabin_num], ""); occupied[cabin_num] = 0; } break; case 3: show_cabins(); break; case 4: exit(0); default: printf("Invalid command!\n"); } } return 0; } ``` 该示例中定义了一个大小为10的寄存柜数组,每个寄存柜的大小为20。程序提供了存储物品、取回物品、显示寄存柜状态和退出程序等四个命令。在存储物品时,程序会寻找第一个空闲的寄存柜,并将物品存放在其中。在取回物品时,程序需要输入要取回物品所在的寄存柜编号。程序还提供了一个显示所有寄存柜状态的函数show_cabins(),用于方便用户查看当前寄存柜的使用情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值