
做FPGA设计复位电路几乎每个工程都会用到。很多人觉得复位嘛不就是给个低电平或者高电平把寄存器清零写代码的时候加个if(rst)就完事了。但真到了板子调试阶段随机出现的死机、状态机跑飞、数据通路错乱追来追去发现根子出在复位释放那一刻——亚稳态把系统坑了。这个问题说实话不算新鲜但踩坑的人还是一茬接一茬。今天就把异步复位释放时亚稳态这个事掰扯清楚顺便给几个靠谱的解决思路。一、异步复位为什么容易出亚稳态异步复位最大的问题在于复位释放也就是rst从有效变无效的那个沿跟时钟沿之间没有任何固定的时间关系。如果复位释放的时刻刚好落在时钟沿附近的建立/保持时间窗口内触发器就进入了亚稳态——输出既不是0也不是1而是卡在中间一个不确定的电平上而且这个状态持续多久谁也说不准。这跟异步复位的进入不一样。复位有效的时候不管时钟什么状态触发器都会被强制拉到已知状态这个没问题。问题出在离开复位的那一刻——释放沿跟时钟沿撞上了。亚稳态的后果看具体场景。如果只是某个数据寄存器抖了一下可能就是一帧数据出错但如果复位释放时状态机的寄存器进了亚稳态那状态机可能跳到一个非法状态整个系统就卡死了。更麻烦的是这种问题是随机的复现概率低排查起来很头疼。二、异步复位同步释放——最经典的解决方案这个方案的核心思路很简单异步复位、同步释放。也就是说复位进入的时候仍然是异步的保证及时响应但释放的时候要跟时钟同步避免释放沿落在时钟的建立保持窗口里。实现方式通常用一个两三级的触发器链来同步复位释放信号always (posedge clk or negedge rst_n) begin if (!rst_n) begin rst_sync1 1b0; rst_sync2 1b0; end else begin rst_sync1 1b1; rst_sync2 rst_sync1; end end // 用 rst_sync2 作为系统复位信号第一级触发器可能进亚稳态但经过一两个时钟周期的沉淀之后第二级或第三级触发器的输出基本稳定了。两级同步能解决大部分场景如果是高可靠性设计可以加到三级。三、几个容易被忽略的细节1. 同步后的复位仍然是异步复位有个常见的误解以为用了同步释放电路之后整个系统就变成同步复位了。其实不是。rst_sync2对下游逻辑来说仍然是一个异步复位信号——只不过它的释放沿已经跟时钟对齐了。所以下游寄存器的always块里还是要写成异步复位的写法posedge clk or negedge rst_sync2。2. 复位网络的扇出问题同步释放电路输出的rst_sync2要接到系统中所有需要复位的寄存器上。在大设计中这个信号的扇出可能非常大导致布线延迟不一致——有的寄存器先收到释放有的后收到这就相当于不同的模块在不同时刻退出了复位状态。解决这个问题的办法是插入BUF或者用多个同步器分别驱动不同模块的复位。FPGA厂商一般也会提供专门的BUFG资源来处理高扇出复位信号。3. 上电复位和热复位的区别上电时FPGA内部有个全局的初始化过程GSR所有寄存器都会被初始化到指定值这个是FPGA架构保证的。但热复位运行中拉低rst_n再释放就不一样了没有GSR帮你兜底完全靠你的复位逻辑。所以热复位场景下异步复位同步释放电路就更关键了。四、还有别的方案吗如果你的设计对复位没有严格要求——比如所有寄存器都有确定初始值、状态机用了独热编码且有非法状态恢复逻辑——那完全不用异步复位也行纯同步复位或者干脆不复位。Xilinx和Intel的FPGA都支持寄存器初始化很多简单设计确实不需要额外的复位信号。但如果你的设计里有时序要求严格的控制逻辑、需要快速响应外部复位请求那异步复位同步释放还是最稳妥的选择。有个经验可以分享做项目的时候把复位策略想清楚再动手写代码。别等到调试阶段才发现复位有问题那时候排查的成本比设计阶段加个同步器高太多了。本文由凡亿教育整理发布