尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

同步复位与异步复位:从晶体管到芯片设计的关键抉择

同步复位与异步复位:从晶体管到芯片设计的关键抉择 开头这几年我面试数字IC工程师几乎每次都会问一个“基础”问题同步复位和异步复位的区别是什么大部分人能说出“异步复位不看时钟同步复位要看时钟”但再追问一句“那你项目里为什么这么选”十个人里有两三个能聊清楚就已经不错了。说实话这个知识点表面上是触发器结构问题背后牵扯的是整个复位架构、时序收敛、DFT设计甚至流片后的稳定性和抗干扰能力。你可能写过无数个always (posedge clk or negedge rst_n)但未必认真想过这一行代码在晶体管层面到底发生了什么。所以我决定把从晶体管到触发器、再到同步/异步复位这个问题彻底拆开讲清楚“为什么芯片设计必须区分两者”也把我在实际项目里踩过的坑、用过的技巧一起倒出来。这篇文章适合刚入门的学生、做FPGA开发想转ASIC的工程师以及那些写RTL多年但想弄明白底层原理的人。内容不偏理论考古重心放在设计决策上——你读完至少应该能在下个项目里自信地告诉同事这里为什么用同步复位那里为什么用异步复位同步释放。1. 内容整体设计与思路拆解1.1 晶体管的开关本质如何决定触发器行为一切逻辑电路的大前提就一句话晶体管是电压控制的开关。CMOS工艺里NMOS在栅极为高电平时导通、低电平时关断PMOS恰好相反。于是我们要实现任何数字逻辑本质上就是在组合一堆“开关”让某个节点在有条件的情况下被拉到电源或地被拉到地没有条件时就保持原来的电平。这个“保持”就是存储的核心。但是单个反相器或者组合逻辑节点并没有保持能力——你输入一变输出就跟着变。真正能存储一位信息的器件是抓住了电容和反馈这两个物理事实构建出来的。触发器里最常见的是D触发器由两个锁存器级联主锁存器和从锁存器组成。锁存器内部用传输门或三态门把信号“关”进一个节点再用反馈回路稳住电平时钟就是控制这个“关门放狗”动作的总开关。理解了这层再看复位就顺了。复位本质上就是“强制让存储节点变成某个确定值”。但如果不去复位上电后触发器内部节点进入什么状态是未知的——可能高、可能低、甚至可能是介于高低之间的亚稳态。芯片里的状态机要是从这种随机状态启动整个系统就可能跑飞。所以复位信号不是可选项而是必需品。1.2 触发器的“记忆”来自哪里不少人学触发器时背过真值表D端是什么时钟沿来了Q端就变成什么。但一旦问他“为什么这个电路能记住原来的值”就答不出来了。这里我习惯用一个生活类比来解释锁存器就像一个装了液压关门器的大门你推它时钟沿它就按你的方向转但如果你不推门自动弹回原来的位置。这个“弹回”的力在电路里就是反馈环路的保持能力。具体到D触发器的结构主锁存器在时钟低电平时采样、从锁存器在时钟变高时锁存两级交替工作。任何复位的实现方式本质上都是在修改这个反馈环路的行为同步复位相当于在D输入路径上插一个选择器复位时强制MUX选通“0”或“1”异步复位则直接对锁存器内部节点动手用一个额外的晶体管把存储点强行拉到电源或地完全不管时钟当前是什么状态。这也是为什么“必须区分同步/异步复位”的第一层理由两种方式在晶体管层面的介入位置不同导致的时序特性、面积开销和设计约束完全不同。不搞清这一点你写的RTL在综合后的行为可能和你想象的根本不是一回事。2. 核心细节解析与实操要点2.1 同步复位的设计逻辑同步复位标准RTL写法是always (posedge clk) begin if (rst_n 1b0) q 1b0; else q d; end综合工具看到这种写法会把它映射成在D端加一个2选1MUX。复位有效时MUX选“0”或“1”作为D输入时钟沿到来时写入触发器。换句话说同步复位信号和普通数据信号在硬件上没什么区别只是它控制的是MUX的选择端而不是触发器的内部节点。这么设计有它的好处抗毛刺能力很强。因为复位信号只在时钟沿被采样两个时钟沿之间出现的任何毛刺只要没有撞上时钟沿就不会被捕获。时序约束简单清晰。复位信号的路径和普通数据路径一样只需满足setup/hold即可。对DFT友好。扫描模式下可以把复位信号当作普通控制信号处理不需要额外的旁路逻辑。缺点也很明显复位信号必须持续有效至少一个时钟周期以上才能被可靠采样这要求复位脉冲宽度必须大于时钟周期如果是多时钟域下用同步复位还要考虑跨时钟域问题。另外MUX增加了D端路径的延迟对高速设计来说可能成为时序收敛的关键路径。2.2 异步复位的设计逻辑异步复位标准RTL写法是always (posedge clk or negedge rst_n) begin if (rst_n 1b0) q 1b0; else q d; end综合工具会把它映射成在触发器的内部存储节点上直接加复位晶体管。复位信号一到不管时钟是高是低存储节点立刻被拉到确定电平。这个晶体管通常是放在反相器反馈回路上的一个NMOS或PMOS用来强制改写节点状态。异步复位的核心优势是不需要等时钟。对于需要快速断电或紧急关断的系统异步复位能保证在若干纳秒内让所有触发器进入已知状态。此外因为异步复位不需要在D端额外加MUX触发器的D到Q延迟理论上可以更小对关键路径有时反而是利好前提是你不做复位同步处理。但异步复位的风险业界讲了一万遍也不嫌多复位信号作为异步输入任何毛刺都可能触发复位噪声容限差。复位释放release那一刻如果恰好靠近时钟沿触发器可能进入亚稳态导致恢复时间recovery或移除时间removal违例。大规模芯片中异步复位信号的到达时间难以严格一致不同触发器可能出现不同拍释放复位导致芯片“起跑即乱”。2.3 两者本质对比一张表说清对比维度同步复位异步复位触发时机仅在时钟有效沿采样复位复位信号一到立刻生效晶体管位置D端MUX触发器内部存储节点抗毛刺能力强毛刺很难撞上时钟沿弱毛刺可能直接触发复位时序约束只需setup/hold需recovery/removal约束面积开销增加MUX和布线资源节省D端MUX但增加内部晶体管对时钟依赖必须有时钟才能复位无时钟也能复位DFT友好度友好容易控制和观察不友好异步端需要旁路典型应用数据路径、多数控制逻辑上电初始化、紧急复位、低功耗模式这个表我建议你们收藏起来。实际项目里设计规范通常会在架构阶段就定好复位的使用策略绝不允许同一个模块里混用两种复位风格——混用的后果我在第4章会讲一个真实案例。3. 为什么芯片设计必须区分从实际项目角度看3.1 复位树和时钟树的关系不少人觉得复位就是一根全局信号拉低就全都归零拉高就全都开始跑。真正做芯片才明白大规模芯片里复位信号不是一根线而是一整棵“树”——从复位源出发经过缓冲器树形分发覆盖到每一个触发器的复位端。这和时钟树的道理是一样的你不可能让同一个源信号直接驱动十万个触发器负载太重信号边沿会变得很缓必须分级加buffer保证每个叶子节点的复位信号到达时间和边沿斜率都满足要求。而同步复位和异步复位对复位树的要求是完全不同的。异步复位不依赖时钟看起来省事但恰恰因为这样它对复位信号到达每一个触发器的时间一致性要求极高。如果复位释放时刻有偏差有的先被释放、有的还被锁住那么整个电路会在“非全局同步”的状态下开始跑状态机可能进入非法状态。相比之下同步复位把复位信号当作普通数据来处理所有触发器都在同一个时钟沿采样复位天然规避了“释放不同步”的问题——只要复位信号满足setup/hold系统的恢复节奏就是一致的。3.2 异步复位可能引起的亚稳态问题亚稳态是数字电路设计里最需要敬畏的现象之一。简单说就是触发器的输出端在一个不确定的时间窗口内既不是逻辑0也不是逻辑1而是一个中间电压并且可能持续较长一段时间才稳定下来。对后级逻辑来说这个中间电压可能导致后级触发器的输入同时满足“开关阈值”从而把不确定性像病毒一样传播出去。异步复位和亚稳态的纠缠集中在复位释放边沿。假设复位信号在时钟上升沿之前一点点释放撤除那么触发器内部节点正在被“释放”和“采样新数据”两股力量拉扯这个时刻如果违反了recovery时间要求输出就可能进入亚稳态。更麻烦的是复位树在不同触发器之间的延迟差会让一部分触发器先释放、一部分后释放某种情况下甚至出现某个触发器在释放边沿上采样了一个未稳定的状态。业界解决这个问题的标准手段叫作“异步复位同步释放”。做法是先把异步复位信号用两级触发器同步到时钟域用同步后的信号去驱动实际的复位树。这样对外部世界来说复位是异步的——随时可以触发但对系统内部触发器来说复位释放时刻是经过同步器对齐的——所有触发器在同一拍被释放不会出现亚稳态窗口。3.3 同步复位的“代价”不只是面积既然异步复位有这么多坑那全部用同步复位是不是就万事大吉也不是。首先同步复位要求复位信号宽度至少大于一个时钟周期否则触发器采样不到。这不只是面积问题还是功能正确性问题。你的系统如果支持非常窄的复位脉冲例如来自外部按键去抖、看门狗引脚等同步复位可能直接吞掉这个复位事件。虽然可以在复位路径上做脉冲展宽电路但这又增加了额外的设计工作。其次多时钟域下同步复位处理起来很麻烦。每个时钟域都需要自己的同步复位逻辑如果模块跨时钟域且使用不同时钟沿复位信号需要分别同步。有人图省事直接把全局复位信号当成普通信号接进每个模块的同步复位逻辑结果在时钟频率差异大的域之间复位释放时刻出现了“争抢”系统状态零散地“苏醒”状态机里的状态还停留在复位之前的非法组合——这种bug查起来极其痛苦。第三DFT和ATPG对同步复位更友好这点没错但意味着你在综合时通常需要额外插入一条扫描复位路径否则测试模式下无法将触发器初始化。而插入这条路径的过程本身又可能引入新的时序问题。4. 实操过程与核心环节实现4.1 复位同步器结构标准的异步复位同步释放这是我个人最喜欢的解决方案也是大多数工业级SoC的标准做法。核心结构是两级触发器加一个AND门或OR门取决于复位极性。代码大概长这样module rst_sync ( input wire clk, input wire rst_n_async, // 原始异步复位低有效 output wire rst_n_sync // 同步后的复位 ); reg rst_n_meta; reg rst_n_sync_reg; always (posedge clk or negedge rst_n_async) begin if (!rst_n_async) begin rst_n_meta 1b0; rst_n_sync_reg 1b0; end else begin rst_n_meta 1b1; rst_n_sync_reg rst_n_meta; end end assign rst_n_sync rst_n_sync_reg; endmodule我在项目里一般会把两级同步器的输出再接一个AND门和原始异步复位相与后生成最终复位信号目的前面说过缩窄异步释放窗口避免同步器自身进入亚稳态后输出不确定电平导致复位树上有毛刺。4.2 关键参数与设计决策这里有一个很多人第一次做时容易疏忽的点异步复位同步释放之后的复位信号不能再被当成异步信号来用。它实际上是“异步产生、同步释放”的混合信号。设计时需要明确以下几个参数同步器级数通常用2级。如果是超高频时钟例如大于1GHz或者你对外部复位信号质量很不放心可以用3级付出的代价是复位释放延迟多一个周期。复位极性绝大多数芯片使用低电平复位。原因是低电平复位正好利用NMOS下拉能力强、综合工具实现清零/置位寄存器更方便的特点。如果你在高电平复位和低电平复位之间切换一定要检查综合库里的DFF是否有对应的置位/清零端。复位树的扇出复位信号的扇出通常比时钟信号还要大。建议在综合阶段单独为复位树设置max_fanout约束并在布局规划时把复位缓冲器分配到各个供电区域的角落避免长距离跑线。4.3 时序分析与约束写法以下是SDC里和复位相关的几组典型约束项目里直接抄# 异步复位信号设置为false path不做setup/hold检查 set_false_path -from [get_ports rst_n] -to [all_registers] # 复位同步器第一级触发器设为异步寄存器 set_async_false_path -from [get_pins rst_sync/rst_n_meta_reg/D] -to [get_pins rst_sync/rst_n_meta_reg/Q] # 复位释放路径检查recovery/removal set_recovery -fall_from [get_ports rst_n] -to [get_clocks clk] 0.5 set_removal -fall_from [get_ports rst_n] -to [get_clocks clk] 0.5看到这里的同学应该注意到了异步复位虽然可以设为false path但不是完全不管。你真正要检查的是recovery和removal——复位释放沿和时钟采样沿之间的最小时间间隔。很多新人在跑signoff时看到recovery违例完全不知道怎么办其实检查一下是不是复位树路径上有太多buffer、或者复位信号的到达时间和时钟沿太接近基本都是这些问题。4.4 我做过的一个实际项目为什么最后选了混合策略去年做一颗MCU级别的小芯片整颗芯片有大约20万触发器的规模。架构阶段同事坚持全部用异步复位理由是上电初始化快。我接过来看了眼复位树延迟发现最远的触发器和最近的触发器之间到达时间差能达到近1ns而时钟周期是2ns——这1ns的差直接让一半芯片在复位释放后第一个周期就出现了寄存器状态不一致。最后方案是全局用异步复位同步释放模块内部的数据路径全部用同步复位逻辑也就是内部复位信号必须是同步后的那个版本只有少量真正需要紧急关断的安全链路保留了纯异步复位功能并且专门给它们做了独立复位树。这样既有全局快速初始化的好处又避免了大规模异步复位释放不一致的坑。5. 常见问题与排查技巧实录5.1 复位毛刺导致误复位现象芯片正常运行时偶发出现寄存器被莫名其妙清零但用示波器抓复位引脚什么都看不到。排查过程这类问题多半是异步复位路径上串入了毛刺由于异步复位敏感性极高即使是几个ns的尖峰也可能触发复位。我遇到过一种情况是复位信号与某个高速翻转信号在芯片内部并行走线串扰耦合出毛刺。解决办法是复位信号不能和时钟、高频数据线长距离并行走线在复位树上加RC滤波如果工艺允许或者干脆改用同步复位一劳永逸。5.2 复位释放违例recovery违例现象signoff时序报告里出现recovery违例。排查过程先用report_timing -recovery看关键路径在哪。大部分情况是复位树上的buffer链太长或者复位同步器的第二级输出直接驱动的触发器太多。解决思路在复位树中间插入更多级的buffer但注意别让复位信号到达时间超出时钟域范围把复位同步器输出复制多份不同副本驱动不同区域的复位树。还有一个技巧把复位树的叶子节点做成“局部异步复位、全局同步释放”的混合结构降低对单一同步器的扇出压力。5.3 部分触发器没有复位端怎么办现象综合时发现某些触发器没有复位端或者库里的标准单元不支持异步复位。排查过程我看到很多设计规范明确规定“所有寄存器必须复位”但库里的某些高速DFF可能只提供同步复位版本。这时有两种可行选择如果模块逻辑允许就统一用同步复位如果必须异步复位就要更换单元库或者用多个标准单元组合替代。最重要的一点是保持风格统一千万不要一个模块里一半触发器用异步复位、一半用同步复位最后综合工具会给你生成一张“混合复位检查”报错的网表改起来非常痛苦。5.4 DFT扫描模式下异步复位处理现象ATPG跑不出来报很多寄存器不可控制。排查过程异步复位端的寄存器在测试模式下必须能被旁路控制否则测试向量根本无法将这些寄存器初始化到已知状态。标准做法是给异步复位端加测试旁路MUX让测试模式下的复位信号强制来自扫描使能控制的测试复位网络。如果你在综合时没有做这一步后面ATPG一定会帮你“深刻认识到”这个教训。5.5 常见问题速查表问题可能原因推荐排查方向运行中偶发寄存器被清空异步复位路径毛刺查串扰、查电源噪声、查复位树走线复位释放后状态机跑飞复位释放不同步检查复位树延迟差、加复位同步器signoff出现recovery违例复位树路径过长/负载过大优化复位树buffer、复制同步器输出ATPG覆盖率低异步复位端不可控加测试旁路MUX、统一复位策略低功耗唤醒后逻辑混乱复位在低功耗模式下被截断检查隔离单元、复位域划分上电瞬间偶发死机复位信号边沿过缓增加上电复位POR电路、展宽复位脉冲5.6 一个让我记忆犹新的案例之前有颗芯片所有模块都是同步复位唯独一个电源管理子模块用了异步复位。电源管理模块掉电时其他模块已经关闭只有它还在工作结果就是那根异步复位线在电源关断过程中出现了一段“中间电平”直接导致其他已进入休眠状态的模块被错误唤醒。后来我们在电源管理模块的复位输出端加了一个“复位控制寄存器”——掉电前先写该寄存器的值去阻塞外部异步复位信号只有系统软件确认所有模块已进入低功耗状态后才允许断电。这个设计后来被写进了我们团队的低功耗设计checklist里。遇到跨电源域、跨电压域的复位设计一定要考虑复位信号在电源切换时的行为。结尾这次把从晶体管到触发器、再从触发器到同步/异步复位的逻辑链完整捋了一遍。我个人在这上面也算交了足够多的学费早年做一块板级FPGA调试时就因为复位释放时机差了几纳秒导致状态机在复位后进入了一个没有任何代码定义的态整整查了两天才定位到问题根源。所以后来每次做芯片我都会在架构阶段就问自己三个问题这个复位信号从哪来它会跨几个时钟域有没有触发器在复位释放边沿可能被采到搞清楚这三件事复位设计基本就不会出大问题。最后再分享一个小技巧如果你在项目里发现了复位相关的疑难bug不要急着对着网表查先翻一翻寄存器传输级RTL里的复位写法看是不是不小心在某个always块里把复位信号放进了敏感列表之外的地方。很多“诡异”复位问题本质上就是代码里同步异步混用导致的综合结果和仿真结果不一致。把RTL里的复位逻辑写清楚、写规范比事后加一堆约束都管用。
返回列表