1. 为什么复位设计值得单独写一篇文章做芯片设计的人尤其是刚入行做数字前端的朋友通常会把大部分精力放在功能逻辑、流水线效率、低功耗策略这些“看得见算得着”的地方。复位电路这种基础得不能再基础的东西往往被放到“先把功能跑通后面再说”的优先级里。但实际项目中我见过太多模块调不通、芯片回来跑飞、甚至流片后靠改金属层才救回来的案例根因都出在复位上。复位的本质是什么一句话让芯片里所有时序单元从不确定状态进入确定状态。芯片上电瞬间寄存器、SRAM、触发器里的值都是随机的如果这些随机值被功能逻辑当成有效数据去处理轻则状态机跑进非法状态重则总线冲突、漏电、芯片直接不工作。复位电路就是给整个芯片一个“出厂设置”让所有模块从一个已知且安全的起点开始跑。但“给一个起点”这件事在实际工程里远没有听起来那么简单。你的复位信号应该同步于时钟还是异步于时钟复位释放的瞬间如果刚好撞上时钟沿寄存器采集到的“复位已撤销”信号是0还是1一个大的SoC里几百万个触发器靠一根线直接连过去到达时间能差出几个周期这些不是教科书里那种“接一个按键加一个电容”就能糊弄过去的而是会直接决定芯片能不能在全部工艺角、全温度范围下稳定启动的硬问题。我最早意识到复位问题严重性是在做一个MCU项目的时候。功能仿真怎么跑都过前仿、后仿、网表仿真都绿油油结果芯片回来之后十个芯片里有三个在低温下概率性启动失败。后来一追发现是复位释放路径上没有做时序收敛复位撤销信号到达各个触发器的时刻和时钟沿产生了竞争。那段排查经历让我花了不少时间也让我下定决心把复位设计从头到尾系统梳理一遍。这篇文章就围绕“异步复位同步释放”和“复位树构建”这两条主线展开适合数字前端工程师、芯片验证工程师也适合对复位设计原理感兴趣的学生和刚入行的新人。内容不会太学术但该讲清楚的原理一点都不会省。2. 三种复位策略的取舍2.1 同步复位省心但没那么省资源同步复位顾名思义复位信号必须配合时钟沿一起生效。也就是说复位的置位和撤销都是相对于时钟沿来说的复位信号本身不直接驱动触发器的异步端而是作为数据路径上的一个逻辑条件。RTL写法很直接always (posedge clk) begin if (!rst_n) begin q 1b0; end else begin q d; end end这段代码综合出来之后复位信号会进入触发器数据输入端的组合逻辑跟D端数据做一个二选一。这样做的优点很突出复位行为天然和时钟对齐不存在复位释放导致亚稳态的问题。因为释放的时候复位信号作为数据路径的一部分受建立时间和保持时间约束的保护。后端做时序约束的时候也比较简单把它当作普通数据路径来约束和处理就行了。但它有几个绕不开的缺点。第一个是面积和功耗代价。因为复位进入了数据路径每个触发器前面都得多一组逻辑门对面积敏感的设计来说白白浪费资源。功耗上因为数据路径翻转率本身高复位作为数据路径的一部分也会增加这部分翻转的功率。第二个问题是异步信号进同步逻辑需要额外处理。如果你的复位源本身是异步的比如外部按键复位、看门狗超时复位必须在模块内部先做两级同步器否则就是一个标准的CDC违例。第三个问题是所谓“复位传播”慢。同步复位需要时钟存在才能完成复位如果时钟停了复位永远也进不去。在有些需要“时钟还没起来就要把关键寄存器钉死在安全值”的场景下同步复位根本做不到。2.2 异步复位直截了当但释放有坑异步复位就是直接把复位信号接到触发器的异步复位端通常是CDN端低有效。RTL写法always (posedge clk or negedge rst_n) begin if (!rst_n) begin q 1b0; end else begin q d; end end综合工具这时候会把rst_n映射到触发器的异步复位引脚不需要额外组合逻辑面积和延迟都比同步复位有优势。而且复位不依赖时钟只要复位有效不管时钟有没有、时钟跑多快触发器一律被清零。这个特性在芯片刚上电、时钟PLL还没锁定的阶段是保命的。但是异步复位最经典的坑就是复位释放de-assertion时的亚稳态问题。回想一下异步复位只要保持有效触发器就一直处于复位态这时候复位什么时候撤除、撤除瞬间时钟在什么相位都不影响复位状态本身。问题的关键在于释放的一瞬间如果复位信号在时钟有效沿附近释放触发器的异步端从“有效”变成“无效”而这个变化又正好处在时钟沿的建立保持窗口里触发器输出可能进入亚稳态——既不是0也不是1也可能震荡最后收敛到哪个值完全不可预测。更麻烦的是因为复位信号到每个触发器的物理距离不同同一个复位源释放时有的触发器已经看到“复位撤销”了有的还认为“复位还在有效”。这种复位偏移reset skew会导致整个芯片在退出复位的那个周期里状态是混乱的。功能上表现为什么都可能状态机跳到未定义状态总线进入高阻与驱动冲突的竞争态甚至直接死机。所以异步复位用得好是利器用得糙就是定时炸弹。关键点就在于怎么处理“释放”这个动作这就是异步复位同步释放要解决的问题。2.3 异步复位同步释放业界主流做法的核心原理异步复位同步释放英文叫 Asynchronous Assert, Synchronous De-assert或者常见的缩写 Async Reset Synchronized Release。它的核心思想用一个词概括就是“各取所长”复位的建立assertion走异步路径复位信号一旦有效立即通过触发器的异步端生效不管时钟有没有、时钟是否稳定。这样保证了最快响应和最小电路开销。复位的释放de-assertion走同步路径在释放之前先让复位信号经过两级同步触发器让它和时钟域对齐再统一释放到各个功能触发器。标准电路结构如下module reset_sync #( parameter NUM_STAGES 2 ) ( input wire clk, input wire arst_n, // 异步复位输入原始复位源 output wire rst_n_out // 同步释放后的复位输出 ); reg [NUM_STAGES-1:0] rst_n_sync; always (posedge clk or negedge arst_n) begin if (!arst_n) begin rst_n_sync b0; // 异步置位 end else begin rst_n_sync {rst_n_sync[NUM_STAGES-2:0], ~arst_n}; end end assign rst_n_out rst_n_sync[NUM_STAGES-1]; endmodule实际上标准的异步复位同步释放结构是“异步复位同步释放”或者更严格的“异步置位同步移出”。以下是更经典的双触发器结构reg rst_n_r1, rst_n_r2; always (posedge clk or negedge arst_n) begin if (!arst_n) begin rst_n_r1 1b0; end else begin rst_n_r1 1b1; end end always (posedge clk or negedge arst_n) begin if (!arst_n) begin rst_n_r2 1b0; end else begin rst_n_r2 rst_n_r1; end end assign rst_n_out rst_n_r2;注意这两级触发器的复位端依然连的是原始异步复位信号所以复位有效时它们会立即被拉低输出也是低整个功能模块被复位。当异步复位释放时第一级触发器进入“等待下一个时钟沿置1”的状态第二级同样两级链保证释放动作只在时钟沿之后才传播出去从而杜绝了复位信号释放沿和时钟沿竞争的问题。为什么必须两级而不是一级这是经典的CDC同步器思路。异步信号进入同步时钟域第一级触发器存在亚稳态风险第二级的作用就是给第一级的亚稳态足够长的收敛时间。一级同步器在复位随机释放面前失效率仍然大到不可接受。两级之后只要第一级的亚稳态在一个周期内收敛第二级就能采到稳定值MTBF平均无故障时间就非常高了。三级在部分对安全要求极高的场景会使用常规设计里两级足够。从行为上理解这个电路相当于一个“异步置位、同步撤除”的边沿触发器。它被置位不需要时钟但撤销必须等时钟。这样的设计同时满足了“复位必须立刻生效”和“释放必须避开竞争”这两个看似矛盾的需求。2.4 三种方案关键指标对比指标同步复位异步复位异步复位同步释放复位生效是否依赖时钟依赖时钟必须存在且稳定不依赖立刻生效不依赖立刻生效资源开销面积/功耗较高复位逻辑进入数据路径较低使用DFF原生异步端较低功能DFF同样用异步端仅额外两级同步器释放风险亚稳态/复位偏移低受时序约束保护高释放与时钟竞争低释放通过两级同步器对齐时序约束复杂度简单当作数据路径复杂需要reset recovery/removal约束适中也需要recovery/removal约束但释放路径被同步抗毛刺能力较好不直接驱动异步端较差毛刺直接导致误复位较差但可缓解毛刺仍可能误触发需要额外滤波典型应用场景模块级弱复位、无时钟域问题的小设计简单模块复位、对面积敏感且复位释放风险可控的设计SoC主流选择几乎覆盖所有数字模块复位实践里我基本只在两种情况下用纯同步复位一种是模块内部自己产生的、明确与时钟域对齐的复位信号另一种是极小的模块几十个触发器没有异步复位源进入同步复位写得顺。一旦设计规模上来复位源是外部引脚或跨域信号基本都采用异步复位同步释放。3. 复位释放的时序本质recovery 与 removal 检查3.1 为什么释放路径也是一条寄存器到寄存器的路径很多前端工程师对复位信号的处理有误解以为复位信号不是常规数据信号不需要做时序检查。在用异步复位同步释放之后释放路径上其实已经产生了一条从“同步器内部触发器”到“功能触发器异步端”的路径。虽然这些寄存器的异步端不参与计算但复位释放信号到达时刻如果与时钟沿距离过近就会引发前面提到的亚稳态问题。这里有两个关键时序参数必须理解恢复时间Recovery Time类比于建立时间。它定义的是在时钟有效沿到来之前异步复位信号必须从无效状态转变撤销为有效状态的最小提前量。也就是说释放动作不能紧贴着时钟沿来必须提前一段时间稳定下来。移除时间Removal Time类比于保持时间。它定义的是在时钟有效沿之后异步复位信号必须保持有效状态的最小时间。换句话说时钟沿到了之后复位信号不能立刻撤掉得稳定保持一段时间否则沿后面采样到的状态无法保证。这两个时序参数在标准单元库里都有定义通常以触发器的lib文件约束存在。异步复位同步释放电路做的事情本质上是把“复位释放这个异步事件”重新同步到了时钟域使得它和时钟沿之间的关系可以满足recovery和removal检查。3.2 约束这样写才不留坑设计里有专门的复位同步器模块之后对释放路径的约束通常有两种做法。我在不同项目里都试过简单说说各自的用法。一种做法是把复位同步器输出的复位信号与第一级时钟沿之间的路径用set_false_path约束掉。理由是同步器输出的复位信号是异步释放的时序工具并没有必要去修一个实际不存在的功能路径。但这种约束有一个前提就是你必须极度确信同步器逻辑没有其他路径能遇到真正的时序问题。比如如果你把复位同步器的第二级触发器输出接到另一个触发器的数据端那条路径就不该被false path误伤。另一种更严谨的做法是对复位释放路径使用专用的reset约束。SDC里对应的命令是set_reset_signal -type sync -active low -domain reset_domain -hierarchy ...不过这个命令主要用于指导综合布局布线工具把复位信号当作复位类信号处理并非所有工具都友好。实战中我更推荐在复位同步器模块上加set_false_path -from约束# 复位同步器输出的释放路径锁定到功能触发器的异步端 set_false_path -from [get_pins reset_sync_inst/rst_n_r2_reg/C] -to [get_pins .../CDN]但这条约束之后需要单独对复位同步器内部做recovery和removal检查。更准确的做法是直接把释放路径上需要的recovery/removal时序约束加在同步器寄存器上让工具去优化同步器输出到各功能触发器异步端的树形布线延迟。实际项目里我习惯同时做两件事对复位同步器内部的寄存器设置完整的set_clock_groups -asynchronous划分避免工具把同步器和功能逻辑混在同一个时钟域里乱优化。同步器到功能触发器异步端的路径用set_false_path屏蔽数据时序检查同时额外报告这些引脚的recovery/removal检查结果人工确认余量。芯片设计工具的约束写法各家有不少细节差异但原理是一样的释放路径要保证“复位释放”这个信号到达所有功能触发器的时刻离各自时钟沿都有足够距离。3.3 多时钟域场景下的处理一个大的SoC不可能只有一个时钟域。CPU核、总线、外设、内存控制器各自跑在不同频率甚至完全异步的时钟下。复位信号要怎么分配常见的方案是全局复位源先做一次异步复位同步释放输出一个“全局复位已释放”信号然后各时钟域分别对该信号再做一次本域的同步释放。也就是说复位同步器本质上要为每个时钟域做一份副本。时序上需要注意如果全局复位释放信号在时钟域A的同步器里已经变为“已释放”状态但时钟域B的同步器还没有看到这个变化这个时候两个时钟域模块的复位释放时刻就是不同的。这个偏移有没有关系要分情况。如果两个域之间在复位释放后的第一个周期就已经有通信那就要小心了。比如域A释放后立刻去访问域B的寄存器而域B还在复位态那么从域A视角读到的全是0可能会被当成有效数据。解决这个问题有两种思路按依赖顺序释放先释放给所有模块供时钟/电源管理使用的“基础设施复位域”再释放功能逻辑域最后释放与外界通信的IO域。每一个域的释放都等待其依赖的下游域已经稳定释放。在跨域路径上加同步器对于存在跨域通信的接口在接口上正常做异步FIFO或握手同步这样即使两个域复位释放有先后也不会把复位态误判为有效数据。实际项目中我倾向于把“复位顺序”做成可控的配置项。复位管理器Reset Manager用状态机控制不同域的释放时序每个域释放之间的间隔是固定周期数这个间隔要覆盖路径上最长同步器的稳定时间一般加上个10~20个周期的余量就非常稳了。同时跨域接口按常规CDC设计这样无论复位顺序怎么配置数据都不会错乱。4. 复位树构建从单点同步到全芯片分配4.1 复位信号为什么也需要“树”做过后端物理实现的朋友都知道时钟树。时钟信号从时钟源出发经过一系列缓冲器分发给所有触发器时钟端保证时钟到达各个触发器的偏差skew在可控范围内。复位信号面临的问题和时钟信号有相似之处但也有本质区别。相似之处在于复位信号也是一个单源、多负载的信号。一个SoC里可能有几十万甚至上百万个触发器需要复位一个复位源的驱动能力不可能直接拉到这么多负载上必须通过缓冲器逐级扇出。就像一条水管要给全城供水中间得有泵站和管道网络。区别在于时钟树需要满足严格的skew要求因为所有触发器共享同一个时钟沿skew过大会直接造成建立/保持违例。而复位树呢如果使用的是异步复位同步释放方案实际上“各个触发器何时进入复位态”并不要求完全一致——异步复位只要有效就会立刻生效早一个周期晚一个周期触发复位在复位期间没有功能性影响。真正关键的是释放时刻的一致性。如果有的触发器已经释放有的还在复位那么在这段不一致的窗口内已释放的触发器开始用“复位后初始值”参与逻辑而未释放的触发器还在复位态两个状态就会互相打架。这个窗口就是“释放偏移”。所以复位的异步建立允许复位树有较大的插入延迟差异但释放的一致性要求所有触发器从“复位态”到“释放态”的时间尽量对齐。这种不对称的要求决定了复位树的结构不能完全照抄时钟树。4.2 复位缓冲器选型这个细节最容易翻车复位树里使用的缓冲器有一个非常容易踩的坑普通缓冲器在复位阶段自己也需要复位吗实际上复位树上的缓冲器只是逻辑门不需要复位。但如果你用的缓冲器是带复位的寄存器型单元比如做流水线用的flop那就会出问题这些寄存器自己也需要复位信号而它们的复位信号又来自于这棵复位树——这会导致先有鸡还是先有蛋的死循环。因此构建复位树时必须选用纯组合逻辑的缓冲器buffer或反相器对inverter pair绝不能选带时序逻辑的单元。哪怕只是很小的局部复位分支这个原则都不能破。另外缓冲器的驱动强度和树的层级结构要根据负载数量、布线长度、目标频率来计算。我的一般做法是先用综合工具报告复位网络的扇出情况对于扇出超过工具建议值的节点手动插入buffer tree或者用综合工具自带的set_clock_tree_options类似的命令对复位树单独做树形优化。4.3 全局复位树与局部复位树的分层设计现在主流的SoC复位架构基本是三层结构第一层芯片级复位源。外部复位引脚、上电复位POR、看门狗复位、软件复位等统一进入复位管理器。第二层复位管理器Reset Manager。它负责做异步复位同步释放并按设计好的顺序生成各域复位信号比如rst_cpu_n、rst_bus_n、rst_periph_n、rst_io_n。第三层各模块内部的局部复位树。每个模块拿到本域复位信号后通常在模块入口再做一次本地同步释放然后生成模块内部使用的rst_n再通过buffer树驱动到模块内各个触发器。为什么一定要在模块入口再做一次同步释放因为全局复位信号在到达模块入口时经过了漫长的全局复位树插入延迟较大信号边沿也可能变慢。如果再直接驱动模块内部的触发器释放沿的质量很难保证。模块入口的本地同步器相当于把“长距离传输的信号”重新对齐到本模块时钟域这样从模块内部的视角看复位释放就像一个本地信号后端约束容易收敛得多。还有一种设计是把第一层和第二层合并即全局复位源直接同步释放不再经过复位管理器。但从可维护性和可控性角度我还是强烈推荐做一个独立的复位管理器。调度顺序、可配置延迟、状态监控这些功能等到芯片调试阶段你就会发现有多重要。一次流片回来发现某个外设复位时序不对如果复位管理器支持寄存器配置释放间隔一条软件命令就解决了而不是再改版。4.4 复位树上的毛刺、倾斜和电源域问题复位树有一个时钟树不太容易遇到的问题复位信号上的毛刺glitch。异步复位只要毛刺幅度够深、宽度够宽触发器的异步端就可能被误触发导致寄存器随机复位。毛刺来源通常是跨电源域信号或者信号走线太长、相邻信号翻转耦合。处理毛刺有几种常见手段在复位同步器前面加一个毛刺滤波器glitch filter通常是一个RC滤波器配合施密特触发器或者数字上做连续N拍采样连续N拍都是有效电平才认为复位有效。对进入芯片的异步复位引脚约束输入延迟并在IO处加同步器逻辑。物理上让复位树走线远离高速翻转的总线降低耦合。关于复位倾斜reset skew前面已经讲了释放一致性是最主要的约束。后端实现时复位树虽然不像时钟树那样要严格balance但对于释放路径上的关键节点还是应该让工具对复位树的末端做balance处理。在Place Route阶段我会对全局复位树的末端buffer设置set_dont_touch防止工具为了修时序把它挪走或删除然后在signoff阶段专门报一下各模块入口复位信号的到达时间差目标是把最差释放偏移控制在几个时钟周期内。对大多数设计来说这个量级的偏移完全无害因为模块内触发器再次通过本地同步器对齐了。4.5 功耗和低功耗模式下的复位处理现代芯片几乎都有低功耗模式复位设计和低功耗的交互是一个容易被忽略的点。典型场景在休眠模式下模块时钟被关闭CLKGATE甚至电源域被关断Power Gating此时复位信号怎么办如果是时钟关闭但电源不关的模块复位信号释放时模块内触发器没有时钟复位释放的同步就无从谈起。这种情况下需要确保模块在时钟恢复之前保持复位有效时钟恢复并稳定之后再释放复位。这个顺序通常由电源管理单元PMU配合时钟管理单元CCU通过状态机来保证。如果是电源关断的模块复位信号在电源域关断期间应该保持有效重新上电后等电源稳定再释放。这里需要特别注意隔离单元isolation cell和电平转换器level shifter的配置关断域的复位信号如果要输出到常开域必须经过隔离单元否则浮动电压会漏电或影响常开域逻辑。低功耗模式下的复位释放顺序我通常设计成和上电复位释放顺序一样的机制只是触发源从POR变成了“唤醒事件”。这段逻辑放在PMU里和功能逻辑彻底分开用常开域的独立小状态机实现防止功能逻辑在休眠时把PMU带乱。5. 实际项目中踩过的复位坑和排查链路怎么描述“排查复位移除时序问题”的过程呢最好的方式是把完整的排查链路写清楚让大家在自己的项目里遇到类似现象时知道从哪下手。5.1 现象芯片概率性启动失败低温更明显那次MCU项目的现象是某些芯片在低温低于0°C下约30%的概率无法正常启动。示波器抓外部晶振波形和复位引脚波形看起来都正常。串口没有任何输出JTAG链能连上但CPU核停在未知状态。第一反应是查时钟因为晶振在低温下起振困难是常见问题。但示波器显示晶振振幅正常PLL锁定信号也正常。接着怀疑电源用高带宽示波器测内核电压的纹波没发现明显异常。然后转向检查复位释放。在这款芯片里全局复位源经过一个外部复位引脚和内部POR然后进复位管理器生成rst_cpu_n等域复位信号。我们把复位信号用逻辑分析仪长时间抓取同时配合一个GPIO输出指示“CPU已经完成初始化”。结果发现复位释放后CPU初始化代码执行到一半某些外设寄存器读取的值和预期不符导致初始化流程跑飞。5.2 排查链路从前仿环境补测试用例在功能仿真环境里通常的复位测试只是简单地拉低复位、拉高复位、跑代码很少会对“复位释放沿相对时钟沿相位”做穷举。但真实芯片上每次上电复位释放的相位都是随机的和时钟沿的关系不可控。我后来做的一件事是在验证环境中加入了对复位释放相位的扫描测试让复位释放时刻相对时钟沿在一个周期内以1/20周期步进遍历然后跑一遍完整的启动序列检查初始化结果是否一致。这一扫果然找到了多个模块存在对复位释放相位敏感的问题。修复方法是调整这些模块内部的复位同步器以及修正某些模块入口复位信号的约束让释放路径的时序余量更充足。这个排查经历给我的最大启发是功能验证通过不等于复位没问题复位释放相位的鲁棒性必须通过针对性测试来覆盖。验证工程师看到这篇内容建议检查一下自己的验证环境中是否有这样的扫描用例没有的话可以补一个。5.3 现象看门狗复位后系统状态不确定另一个常见坑是看门狗复位。小型MCU项目里看门狗超时后会产生复位信号。如果这个复位信号直接接入复位管理器那么看门狗复位的释放路径和上电复位的释放路径是一样的理论上应该没问题。但实际项目中看门狗复位往往还需要保留某些调试信息比如复位原因寄存器这些信息是用普通寄存器存储的如果它们也被看门狗复位“一把清掉”调试数据就没了。解决办法是把“复位原因寄存器”放在一个独立的小模块里使用不同的复位信号只受POR复位而不受看门狗复位影响。这个模块的复位移除路径也要单独约束防止它在其他模块还在复位时被错误访问。这类问题不是RC电路能解决的是芯片架构层面的决策哪些逻辑应该被哪些复位信号复位。我的经验是在设计初期就把复位域定义清楚和时钟域一样重视。一个寄存器只能归属于一个复位域如果它需要受多个复位源控制通常的做法是在软件层面管理而不是在硬件上叠加多个复位信号。5.4 现象复位信号毛刺导致的间歇性误复位还有一个项目功耗不高但芯片在强电磁干扰环境下会偶发复位。这个现象排查起来最难因为它不是每次复现频率也极低可能跑几小时才出现一次。我们用片上调试接口在复位发生前记录关键信号到RAM然后通过JTAG读出。最终定位到是外部复位引脚上的一个毛刺。毛刺本身只有几百皮秒宽但恰好超过了触发器的异步端最小脉冲宽度要求触发了异步复位。解决手段包括在外部复位引脚输入路径上增加毛刺滤波器以及重新规划PCB上复位引脚的走线让它远离大电流开关节点。这个案例说明即使是“外部简单RC复位电路”这种看起来很成熟的设计放到复杂电磁环境里也可能出问题。6. 一些值得带走的实操经验这篇文章聊了异步复位同步释放的原理也聊了复位树的物理设计考量。最后分享几个我实际项目中沉淀下来的、常规文档里不会写的小经验。第一复位信号命名一定要带后缀。rst_n是低有效rst是高有效如果用reset这种模棱两可的名字综合工具默认的映射行为可能会让你在检查网表时浪费大量时间。我习惯在模块端口命名时就明确标注arst_n表示异步复位低有效srst_n表示同步复位低有效rst_n表示模块统一复位。看代码的人一眼就能知道这个复位是异步同步释放出来的还是普通同步复位。第二复位同步器模块不要被综合工具优化掉。因为异步复位同步释放电路的结构很固定工具有时会“聪明”地把它和功能逻辑合并导致等效电路不符合预期。我通常会对复位同步器模块设置set_dont_touch确保它的结构在综合、布局布线全流程中都保持不变。代价是这部分电路无法被工具优化面积略有增加但换来的确定性和可调性远远值得。第三复位释放顺序要在设计文档里画清楚。做大型SoC时复位域之间的关系、释放时序、依赖关系一定要有明确的文档最好配一张时序图。芯片调试阶段几个模块的复位顺序搞反问题会以非常奇怪的方式出现排查起来特别痛苦。这些文档可能在芯片回来之前看起来没什么用但真到了调试阶段你会感谢当时的自己。第四不要轻易在RTL里手动插入buffer树。很多前端工程师看到复位扇出太大就手动在RTL里加buffer这其实是后端工具的活。你手动加的buffer在后端布局时可能被移动甚至删除而且可读性很差。正确做法是在综合约束里约束复位信号的max_fanout或者在后端专门针对复位信号做树形优化。第五复位信号和时钟信号一样需要一个“clean”的开始。如果芯片没有PORPower-On Reset电路外部复位引脚必须保证在上电后维持足够长的低电平时间直到电源电压稳定。很多“芯片上电后概率性死机”的问题最后查出来的根因都是POR时间不够复位提前释放。电源管理芯片PMU的复位输出延时可以解决这个问题或者用外部复位IC的电源监控功能。说实话复位设计在芯片设计的教科书里往往只有一两页但它牵扯到的时序、物理实现、低功耗、验证策略每一项都值得认真对待。尤其对做SoC和MCU的工程师来说把复位架构想清楚比多做几个功能模块更能决定芯片的稳定性和可调试性。希望这篇文章能把一些实践经验传递给正在做相关工作的读者少走几步我走过的弯路。