2. 异步复位同步释放:经典双寄存器同步器的原理、Verilog实现、时序约束要求
好,咱们接着聊复位。上一章我讲了异步复位和同步复位各自的优缺点,你可能会问:有没有一种方案,能把两者的好处都占了?
答案是肯定的。这就是我今天要重点讲的——异步复位同步释放。说白了,就是复位信号异步生效,但释放(撤除)的时候,让它跟时钟同步。这个思路,我在好几个量产项目里都用过,效果很稳。
2.1 为什么需要同步释放?
先想想看,异步复位最大的问题是什么?
是复位释放时刻的不确定性。如果复位信号在时钟沿附近撤除,就可能产生亚稳态。寄存器可能采到0,也可能采到1,甚至可能震荡。这会导致整个系统行为不可预测。
我遇到过最头疼的一次:板子跑着跑着,偶尔会莫名其妙地死机。查了三天,最后发现是复位释放路径上有个寄存器进入了亚稳态,把状态机带飞了。从那以后,我对复位释放的时序格外敏感。
异步复位同步释放,就是来解决这个问题的。它让复位释放的时刻,严格对齐到时钟沿。这样,所有寄存器都能在同一个时钟周期内,稳定地退出复位状态。
2.2 经典双寄存器同步器原理
实现这个方案,核心就是两个寄存器串联。我画了个图,你一看就明白。
原理其实很简单:
- 异步复位信号
rst_async_n直接接到两个寄存器的异步复位端(低电平有效)。所以复位一拉低,两个寄存器立刻清零,不需要等时钟。 - 复位释放时,
rst_async_n变高。第一个寄存器(reg1)的D端是1,但它要等下一个时钟沿才能采到。第二个寄存器(reg2)再等一个时钟沿,输出才变高。 - 最终输出的
rst_sync_n,释放时刻完全同步于时钟上升沿。
核心要点:复位异步生效,释放同步生效。这样既保证了复位的即时性,又避免了亚稳态。
2.3 Verilog实现
代码其实很短。我个人习惯把同步器封装成一个模块,方便复用。
module rst_sync (
input wire clk, // 系统时钟
input wire rst_async_n, // 异步复位输入(低有效)
output wire rst_sync_n // 同步复位输出(低有效)
);
// 两级同步寄存器
reg reg1, reg2;
always @(posedge clk or negedge rst_async_n) begin
if (!rst_async_n) begin
reg1 <= 1'b0;
reg2 <= 1'b0;
end else begin
reg1 <= 1'b1;
reg2 <= reg1;
end
end
assign rst_sync_n = reg2;
endmodule
嗯,这里要注意:
- 两个寄存器都用异步复位,所以敏感列表里要写
posedge clk or negedge rst_async_n。 - 复位时,两个寄存器都清零。释放后,reg1先变1,reg2再变1。这样输出就延迟了两个时钟周期。
- 如果你需要高电平有效的复位,把取反逻辑加在输入或输出端即可。
我的习惯:在顶层模块里只例化一个同步器,然后把 rst_sync_n 接到所有子模块的复位端。这样整个芯片的复位释放是统一的,不会出现有的模块已经释放了,有的还在复位中。
2.4 时序约束要求
代码写完了,但如果不加约束,工具可能不会按你期望的方式去优化。我曾经就吃过这个亏——仿真没问题,上板就出问题。后来发现是综合工具把同步器的路径优化掉了。
所以,时序约束一定要加。主要有两条:
| 约束类型 | 命令 | 说明 |
|---|---|---|
| 伪路径 | set_false_path -from [get_ports rst_async_n] -to [get_pins reg1/D] |
异步复位信号到第一级寄存器的D端,不需要时序检查 |
| 异步复位端约束 | set_false_path -to [get_pins reg1/CLR] |
复位信号到寄存器的异步复位端,也不做时序检查 |
为什么这么加?
rst_async_n是异步信号,它和时钟没有固定的相位关系。如果让工具去分析这条路径,它会报一大堆违例,但其实这些违例是假的。- 我们真正关心的是第二级寄存器(reg2)的输出。它已经同步到时钟域了,所以后续路径需要正常做时序分析。
注意:千万不要把整个同步器都设成伪路径。我见过有人图省事,直接把 rst_sync_n 输出到所有寄存器的路径都设成 false path。这样做,复位释放后的时序就没人管了,很容易出问题。
2.5 实际项目中的避坑指南
最后,分享几个我在项目中踩过的坑:
- 同步器位置要放对。 同步器一定要放在时钟树的根部,靠近PLL或者时钟管理模块。如果放在远端,时钟偏斜可能会让两级寄存器采到不同的时钟沿。
- 不要用组合逻辑。 同步器的输出不要经过任何组合逻辑再接到寄存器的复位端。组合逻辑会引入毛刺,可能导致寄存器误复位。
- 多时钟域要分别处理。 如果你的设计有多个时钟域,每个时钟域都要有自己的同步器。不能用一个同步器的输出去复位所有时钟域的寄存器。
- 仿真时要检查。 我曾经在仿真里看到复位释放后,reg1和reg2之间有一个时钟周期的延迟。这是正常的。但如果延迟超过一个周期,就要检查是不是时钟有问题。
好了,关于异步复位同步释放,核心内容就这些。你想想看,这个方案是不是既简单又实用?我个人觉得,它是FPGA复位设计里最值得掌握的技术之一。
一句话总结:异步复位保证响应速度,同步释放保证系统稳定。双寄存器同步器,用两个触发器换一个可靠的复位信号,这笔买卖很划算。
公众号:蓝海资料掘金营,微信deep3321