SoC设计中的RDC陷阱:异步复位亚稳态问题与同步释放方案

发布时间:2026/9/19 19:11:07
SoC设计中的RDC陷阱:异步复位亚稳态问题与同步释放方案
1. 从一个被反复触发的诡异Bug说起如果你在SoC前端设计这行干过几年大概率遇到过这样一种现象芯片回来之后大部分功能都正常偏偏在某些特定场景下——比如上电初始化、低功耗唤醒、或者某个外设突然被复位——系统会随机性地跑飞而且复现概率极低可能几千次才出一次。你拿波形去抓发现某个寄存器的值莫名其妙变成了一个既不是0也不是1的中间态或者状态机跳到了一个根本不存在的状态。更让人头疼的是这种问题在仿真阶段几乎抓不到因为仿真器默认是理想模型不会模拟复位释放时刻的亚稳态传播。这个问题的根源十有八九出在RDC上。RDC是Reset Domain Crossing的缩写中文一般叫“复位域交叉”。它和大家熟悉的CDCClock Domain Crossing时钟域交叉是一对孪生兄弟但RDC的受重视程度远远不如CDC。很多团队在项目初期会花大量精力做CDC检查、加同步器、写约束但RDC往往被忽略直到流片回来出了问题才开始排查。而RDC引发的问题恰恰是那种“平时不显山露水一出事就是大事”的类型。这篇文章想聊的就是SoC设计中异步复位导致的亚稳态问题以及如何通过合理的RDC设计来规避这些陷阱。我会从复位域的基本概念讲起拆解异步复位为什么会产生亚稳态然后给出几种经过实际项目验证的复位同步方案最后分享一些在RDC检查工具使用和约束编写上的实操经验。不管你是刚接触SoC前端设计的新人还是已经做过几个项目的老手希望这些内容都能帮你少踩几个坑。2. 复位域交叉到底在“交叉”什么2.1 复位域和时钟域不是一回事很多人第一次听到RDC这个词会下意识地把它和CDC混为一谈。确实两者都涉及“跨域”的问题但它们的本质区别在于CDC关心的是信号从一个时钟域传到另一个时钟域时由于时钟相位和频率的差异导致的采样不确定而RDC关心的是信号从一个复位域传到另一个复位域时由于复位释放时刻的差异导致的采样不确定。打个比方。CDC就像两个人用不同的节奏打拍子一个人要把球扔给另一个人如果扔的时机不对对方可能接不住。RDC则是两个人本来在同一个节奏下打球但其中一个人突然被叫停又重新开始而另一个人不知道他什么时候重新开始结果球传过去的时候对方还没准备好。在SoC里复位域通常和时钟域是绑定的但并不是一一对应。一个时钟域里可能有多个复位域比如一个模块有全局复位和局部软复位一个复位域也可能跨越多个时钟域比如一个全局复位信号同时复位了APB总线和AXI总线上的模块。这种复杂的对应关系就是RDC问题的温床。2.2 异步复位释放时刻的“灰色地带”要理解RDC为什么会导致亚稳态得先搞清楚异步复位的工作机制。异步复位的特点是复位有效通常为低电平或高电平时不管时钟是什么状态触发器立刻被强制到复位值复位释放时触发器需要在下一个时钟有效沿才能恢复正常工作。问题就出在“复位释放”这个动作上。假设复位信号在时钟上升沿附近释放触发器的复位端从有效变为无效此时触发器的内部状态处于一个不确定的区域——它既不是完全复位状态也不是完全正常工作状态。如果复位释放时刻离时钟有效沿太近触发器的输出就可能进入亚稳态也就是输出既不是0也不是1而是在两者之间振荡一段时间最终随机稳定到某个值。这个“太近”的范围就是触发器的复位恢复时间Recovery Time和复位移除时间Removal Time。Recovery Time指的是复位释放到下一个时钟有效沿之间必须保持的最小时间Removal Time指的是时钟有效沿到复位释放之间必须保持的最小时间。如果这两个时间不满足触发器就可能进入亚稳态。在同一个复位域内所有触发器同时复位、同时释放只要复位释放满足时序要求就不会有问题。但当信号从一个复位域传到另一个复位域时两个域的复位释放时刻可能不同步接收端的触发器可能在发送端还没完全退出复位状态时就开始采样采到的就是一个不确定的值。2.3 一个典型的RDC故障场景举个具体的例子。假设一个SoC里有两个模块模块A在复位域RST_A下模块B在复位域RST_B下。模块A产生一个valid信号给模块B模块B用这个valid信号来使能某个数据通路。RST_A和RST_B来自不同的复位源释放时刻相差几个时钟周期。在RST_A释放后、RST_B还没释放的这段时间里模块A已经开始正常工作valid信号可能已经拉高。但模块B还处于复位状态它的触发器被强制复位valid信号在模块B内部被忽略。当RST_B释放时如果释放时刻恰好靠近时钟有效沿模块B的触发器可能进入亚稳态采到的valid值不确定。如果采到1模块B会误以为数据有效开始处理一个可能还没准备好的数据如果采到0模块B会漏掉一个本来应该处理的请求。无论哪种情况都是功能性Bug。更隐蔽的情况是valid信号本身经过了一个多级触发器同步器。很多人以为加了同步器就万事大吉了但如果同步器的复位域和发送端不同同步器本身也可能在复位释放时进入亚稳态导致同步失败。3. 异步复位同步释放最常用的解法及其局限3.1 基本电路结构说到解决异步复位亚稳态问题最经典的方案就是“异步复位同步释放”Asynchronous Reset Synchronous Release。这个电路的核心思想是复位有效时异步拉低保证复位立即生效复位释放时通过两级触发器同步到目标时钟域保证释放时刻与时钟对齐。典型的电路结构是这样的复位输入信号先经过一个两级触发器链触发器的时钟是目标时钟域的时钟复位端接复位输入信号本身。第一级触发器的输入接常数1或VDD第二级触发器的输入接第一级输出第二级输出就是同步后的复位释放信号。同时复位输入信号直接接到触发器的异步复位端保证复位有效时立即拉低输出。这个电路的工作逻辑是当复位输入有效时两个触发器被异步复位输出为0当复位输入释放时第一个触发器在下一个时钟上升沿采样到1第二个触发器再下一个时钟上升沿采样到1输出在第二个时钟沿之后释放。这样复位释放就与时钟对齐了避免了亚稳态。3.2 为什么两级触发器就够了有人会问为什么是两级而不是三级或更多这涉及到MTBFMean Time Between Failures的计算。同步器的级数越多亚稳态传播到输出端的概率越低但面积和延迟也越大。对于大多数SoC设计来说两级触发器已经能把MTBF做到足够高通常几十年甚至几百年才出一次问题。MTBF的计算公式大致是MTBF e^(t/τ) / (T0 × fclk × fdata)其中t是留给亚稳态稳定的时间一个时钟周期减去建立时间τ和T0是触发器的工艺参数fclk是时钟频率fdata是异步信号的变化频率。从公式可以看出时钟频率越高、异步信号变化越频繁MTBF越低。在先进工艺下τ值变小MTBF也会降低。所以到了5nm、3nm节点有些团队会考虑用三级同步器。3.3 这个方案不是万能的异步复位同步释放虽然经典但它有一个重要的前提同步器本身的复位必须和发送端的复位是同一个复位域或者至少是同步释放的。如果同步器的复位域和发送端不同同步器在复位释放时同样可能进入亚稳态那这个同步器就形同虚设了。另一个局限是这个方案只解决了复位释放的同步问题没有解决复位域之间的信号传输问题。如果模块A在复位释放后立刻给模块B发信号而模块B的复位还没释放那模块B仍然可能采到不确定的值。所以除了加同步器还需要在协议层面做握手确保接收端已经退出复位状态后再接收数据。还有一个容易被忽略的点同步器链上的触发器本身也需要复位。如果同步器没有复位上电时触发器的初始状态不确定可能导致复位释放信号出现毛刺。所以同步器的复位端必须接一个可靠的复位源。4. RDC检查工具能帮你做什么不能帮你做什么4.1 主流RDC检查工具的能力边界现在主流的EDA工具都支持RDC检查比如Synopsys的SpyGlass RDC、Cadence的Conformal RDC、Mentor的Questa RDC等。这些工具的基本原理是识别设计中的复位域分析跨复位域的信号路径检查这些路径上是否有同步器或握手逻辑如果没有就报warning或error。工具能帮你做的主要是结构性检查哪些信号跨了复位域、跨域路径上有没有同步器、同步器的结构是否正确、复位域的定义是否完整。这些检查对于大型SoC设计来说非常有必要因为靠人工review很难覆盖所有跨域路径。但工具不能帮你做的是判断某个跨域路径是否真的需要同步。有些跨域信号是静态配置信号复位释放后不会变化或者变化时接收端已经退出复位状态这些路径可能不需要同步器。工具会一律报出来你需要根据实际场景判断哪些是真正的风险哪些可以waive掉。4.2 复位域定义的准确性决定检查质量RDC检查的第一步是定义复位域。工具需要知道每个触发器的复位端接的是哪个复位信号以及这个复位信号的释放是否与某个时钟同步。如果复位域定义不准确检查结果要么漏报要么误报。在实际项目中复位域定义通常通过SDC或专门的RDC约束文件来描述。你需要明确指定每个复位信号的源、每个复位域的时钟、复位释放的同步方式、跨域路径的同步策略等。这些约束的编写质量直接决定了RDC检查的有效性。我见过一些项目RDC约束写得很粗糙只定义了顶层复位没有细分模块级的复位域结果工具报了一大堆跨域路径但大部分都是误报。后来花了两周时间重新梳理复位域把约束写细误报率从70%降到了10%以下。所以RDC检查不是跑一下工具就完事前期的复位域梳理和约束编写才是重头戏。4.3 工具报出来的问题怎么分类处理工具报出来的RDC问题大致可以分为三类第一类是真正的风险路径跨域信号在接收端复位释放前后可能变化且没有同步器。这类问题必须修要么加同步器要么改协议做握手要么调整复位释放顺序。第二类是伪风险路径跨域信号在接收端复位释放时已经稳定或者接收端在复位释放后不会立即采样该信号。这类问题可以通过加约束或waive来处理但需要设计者给出充分的理由。第三类是工具误报比如工具没有识别出某个同步器结构或者复位域定义有误导致路径被错误分类。这类问题需要修正约束或工具配置。处理这三类问题的顺序很重要。先修第一类再评估第二类最后处理第三类。不要一上来就waive那样可能漏掉真正的风险。5. 复位域规划的几个实战原则5.1 复位域数量不是越多越好有些设计者喜欢把复位域切得很细每个模块一个复位域觉得这样控制灵活。但复位域越多RDC路径就越多同步器就越多面积和验证复杂度都上去了。而且复位域之间的释放顺序很难保证容易引入新的RDC问题。我的经验是复位域的数量应该和电源域、时钟域的数量相匹配不要为了灵活而过度细分。一般来说一个SoC里复位域的数量控制在时钟域数量的1.5倍以内比较合理。如果发现复位域太多可以考虑合并一些释放顺序相同、没有跨域交互的复位域。5.2 复位释放顺序要有明确规划当SoC里有多个复位域时复位释放顺序必须有明确的规划。通常的原则是先释放核心和总线再释放外设先释放发送端再释放接收端。这样可以保证接收端在退出复位状态时发送端已经稳定不会采到不确定的值。但有时候这个顺序很难保证比如两个模块互相发送数据谁先释放都不对。这时候就需要在协议层面做握手或者用一个全局的复位释放信号统一控制。复位释放顺序的规划要在架构阶段就确定不能等到RTL写完再补。因为一旦RTL结构定了再调整释放顺序可能需要改很多模块。5.3 软复位和硬复位的RDC处理差异SoC里通常有硬复位和软复位两种。硬复位是上电复位或外部复位释放时刻由外部条件决定软复位是寄存器控制的复位释放时刻由软件写入决定。软复位的RDC处理和硬复位不同。硬复位的释放时刻是随机的必须加同步器软复位的释放时刻是软件控制的通常与时钟同步但如果软复位信号跨了时钟域仍然需要同步。而且软复位释放后软件需要等待一段时间才能访问被复位的模块这个等待时间要写进软件驱动里。我见过一个项目软复位释放后软件立刻去读被复位模块的寄存器结果读到了不确定的值。后来在驱动里加了几十个时钟周期的延时才解决。所以软复位的RDC问题不仅影响硬件还影响软件。6. 从RTL到约束一个完整的RDC排查流程6.1 第一步梳理复位域清单在跑RDC工具之前先手工梳理一份复位域清单。清单里要包含复位信号名、复位源、复位类型硬复位/软复位、所属时钟域、释放同步方式、影响的模块列表。这份清单不需要很精确但要有因为它能帮你快速定位问题。当工具报出某个跨域路径时你可以对照清单判断这个路径是否真的跨了复位域以及跨域的方式是否合理。梳理清单的过程也是发现问题的过程。有时候你会发现某个复位信号的源不明确或者某个模块的复位域定义有歧义这些问题在跑工具之前解决掉能省很多时间。6.2 第二步编写RDC约束RDC约束的核心是告诉工具哪些信号是复位信号、每个复位域的时钟是什么、跨域路径的同步策略是什么。以SpyGlass为例RDC约束通常包括# 定义复位信号 reset -name RST_A -active low reset -name RST_B -active low # 定义复位域 reset_domain -name DOMAIN_A -reset RST_A -clock CLK_A reset_domain -name DOMAIN_B -reset RST_B -clock CLK_B # 定义跨域同步策略 rdc_sync -from DOMAIN_A -to DOMAIN_B -type async_reset_sync约束写完后先跑一遍检查看看有没有语法错误和明显的漏定义。然后根据报错逐步完善。6.3 第三步分析报告并分类工具跑完后会生成一份报告列出所有跨域路径和检查结果。你需要逐条分析把问题分类。分析时重点关注跨域信号的功能是什么、在接收端复位释放时是否会变化、接收端是否有同步器、同步器的结构是否正确、同步器的复位域是否匹配。对于每条路径都要给出明确的处理意见修、waive、还是需要进一步确认。不要留模糊地带否则问题会一直挂着。6.4 第四步修复和验证修复RDC问题通常有几种方式加同步器、改协议做握手、调整复位释放顺序、加约束waive。加同步器是最直接的方式但要注意同步器的复位域必须和发送端匹配否则同步器本身会成为新的RDC问题。改协议做握手更彻底但改动量大适合在架构阶段做。调整复位释放顺序需要改复位控制逻辑可能影响多个模块。加约束waive适合伪风险路径但要有充分理由。修复后要重新跑RDC检查确认问题真的解决了没有引入新的问题。然后跑一遍功能仿真确认修复没有影响正常功能。7. 几个容易踩的坑和我的应对经验7.1 同步器被综合工具优化掉这是一个非常隐蔽的坑。你写了一个两级同步器综合工具在优化时发现第一级触发器的输出没有被其他逻辑使用只驱动了第二级触发器于是把第一级优化掉了同步器变成了一级。一级同步器的MTBF远低于两级亚稳态概率大大增加。避免这个坑的方法是在同步器上加dont_touch或preserve属性告诉综合工具不要优化。或者在代码里加一个(* keep true *)的注释。不同工具的属性名不同需要查手册确认。7.2 复位释放时的毛刺异步复位同步释放电路在复位释放时如果复位输入信号本身有毛刺可能导致同步器输出出现毛刺。虽然同步器能滤掉一些毛刺但如果毛刺出现在时钟有效沿附近仍然可能被采到。解决方法是在复位输入信号上加滤波电路或者用施密特触发器整形。对于片内复位通常问题不大对于片外复位建议加RC滤波和施密特触发器。7.3 仿真抓不到RDC问题前面提到过仿真器默认是理想模型不会模拟复位释放时的亚稳态。所以即使RTL有RDC问题功能仿真也可能全过。要抓RDC问题需要用带时序信息的仿真或者在仿真中注入亚稳态模型。有些仿真器支持define或plusarg来开启亚稳态模拟但配置比较复杂。更实际的做法是依赖RDC静态检查工具在RTL阶段就把问题找出来。7.4 跨复位域的总线协议信号总线协议信号如AXI的valid/ready跨复位域时处理起来特别麻烦。因为valid和ready是握手信号如果两个信号跨域的方式不一致可能导致死锁或数据丢失。我的经验是跨复位域的总线信号最好在协议层做隔离。比如在复位域边界加一个桥接模块桥接模块的复位域和接收端一致发送端的信号先同步到桥接模块的时钟域再由桥接模块转发给接收端。这样可以把RDC问题集中到一个模块里处理避免分散在各处。7.5 低功耗唤醒时的RDC问题低功耗设计里电源域关断再唤醒时复位释放的时序和正常上电不同。电源域关断时域内的触发器状态丢失唤醒时复位释放和电源稳定的时序关系可能不满足要求导致RDC问题。处理低功耗唤醒的RDC问题需要在电源管理单元里做专门的复位释放控制确保电源稳定后再释放复位且释放时刻与时钟同步。这部分通常需要和低功耗验证团队紧密配合。8. 写在最后的一些个人体会RDC这个问题说大不大说小不小。它不像CDC那样每个项目都会重点检查但一旦出问题排查起来非常痛苦。因为RDC问题往往是概率性的复现困难定位困难修复后验证也困难。我的建议是在项目初期就把RDC检查纳入流程不要等到流片前才想起来。复位域规划要在架构阶段做RDC约束要在RTL阶段写检查要在综合前跑。这样即使有问题也有足够的时间修。另外RDC检查工具只是辅助不能完全依赖。工具能帮你找结构性问题但判断某个路径是否真的需要同步还需要设计者对电路功能有深入理解。所以花时间理解复位域的工作原理比学会用工具更重要。最后分享一个小技巧在RTL里给每个跨复位域的信号加一个命名前缀比如rdc_这样在代码review和工具检查时都能快速识别。这个习惯我坚持了好几年确实能减少遗漏。