2. 复位电路设计原则:复位信号的电平与边沿、复位信号的去抖与同步
大家好,我是你们的FPGA讲师。今天咱们聊聊复位电路设计里最基础、也最容易翻车的两个点:电平与边沿,以及去抖与同步。
说实话,我见过不少新手,甚至一些老工程师,在复位电路上栽跟头。我自己刚入行那会儿,也吃过亏——有一次调试一块Lattice的板子,复位信号没处理好,结果系统上电后死活不工作,查了整整两天,最后发现是复位信号毛刺惹的祸。从那以后,我对复位电路的设计就格外上心。
好,咱们直接进入正题。
2.1 复位信号的电平与边沿
复位信号,说白了就是告诉FPGA:“嘿,从头开始!” 但怎么告诉它,这里头有讲究。
复位信号通常有两种形式:电平复位和边沿复位。
- 电平复位:靠信号的电平高低来触发复位。比如高电平复位,就是复位信号拉高时,系统复位;拉低后,系统开始工作。
- 边沿复位:靠信号的跳变沿来触发复位。比如上升沿复位,就是信号从低变高的那一瞬间,系统复位。
你可能会问:“这两种有啥区别?我该用哪种?”
嗯,这里我分享一个经验:在FPGA设计中,我强烈建议使用电平复位。为什么?
核心原因:电平复位对毛刺的容忍度更高。
边沿复位对毛刺极其敏感。一个窄脉冲毛刺,如果刚好落在时钟沿附近,就可能被当成有效的复位边沿,导致系统误复位。而电平复位呢?只要毛刺宽度不够,它根本不会触发复位。说白了,电平复位更“皮实”。
我记得有一次,一个同事用边沿复位做设计,仿真怎么都过不了。我过去一看,发现是复位信号上有个几纳秒的毛刺,刚好被时钟采样到了。换成电平复位后,问题立马解决。
当然,也不是说边沿复位一无是处。它有个优点:复位动作快。如果你对复位速度有极致要求,比如某些高速接口的初始化,边沿复位可能更合适。但代价就是,你得额外做去抖处理。
咱们用个表格总结一下:
| 特性 | 电平复位 | 边沿复位 |
|---|---|---|
| 抗毛刺能力 | 强 | 弱 |
| 复位速度 | 较慢(需保持电平) | 快(一个边沿即可) |
| 设计复杂度 | 低 | 高(需去抖) |
| 推荐场景 | 绝大多数FPGA设计 | 高速、对复位时序敏感的场景 |
2.2 复位信号的去抖
去抖,说白了就是把复位信号上的毛刺和抖动滤掉。
你想想看,复位信号通常来自按键、上电检测电路,或者外部控制器。这些信号在传输过程中,难免会引入噪声。如果不做去抖,一个毛刺就可能让系统误复位。
去抖的方法有很多,我常用的有两种:
- 硬件去抖:在PCB上加RC滤波电路。简单粗暴,但不够灵活。
- 软件去抖:在FPGA内部用计数器或状态机实现。我个人更推荐这种方式,因为可以灵活调整去抖时间。
这里我给出一个Lattice平台上常用的去抖模块代码示例:
// 复位信号去抖模块
// 输入:clk, rst_n (原始复位信号)
// 输出:rst_debounced (去抖后的复位信号)
module debounce_rst (
input wire clk,
input wire rst_n,
output reg rst_debounced
);
// 去抖计数器,假设时钟频率为50MHz,去抖时间为10ms
// 计数器最大值 = 50MHz * 10ms = 500,000
parameter DEBOUNCE_CNT_MAX = 500_000;
reg [19:0] cnt; // 20位计数器,足够计数到500,000
reg rst_sync;
// 同步原始复位信号到时钟域
always @(posedge clk) begin
rst_sync <= rst_n;
end
// 去抖逻辑
always @(posedge clk) begin
if (rst_sync == 1'b0) begin
// 复位信号有效,开始计数
if (cnt < DEBOUNCE_CNT_MAX)
cnt <= cnt + 1;
else
cnt <= cnt; // 保持最大值
end else begin
// 复位信号无效,清零计数器
cnt <= 0;
end
end
// 输出去抖后的复位信号
always @(posedge clk) begin
if (cnt == DEBOUNCE_CNT_MAX)
rst_debounced <= 1'b0; // 复位有效
else
rst_debounced <= 1'b1; // 复位无效
end
endmodule
小提示:去抖时间不是越长越好。太长了,系统上电后要等很久才能开始工作;太短了,又可能滤不掉毛刺。我一般建议设置在5ms到20ms之间,具体看你的应用场景。
2.3 复位信号的同步
同步,是复位电路设计的另一个关键点。
为什么要同步?因为复位信号通常是异步的——它可能在任何时刻跳变。而FPGA内部是同步时序逻辑,如果异步复位信号直接接入触发器,很容易导致亚稳态。
亚稳态是什么?说白了,就是触发器输出既不是0也不是1,而是一个不确定的状态。这个状态可能会传播到后续逻辑,导致整个系统行为异常。
我曾经在一个项目中,因为没做复位同步,结果系统偶尔会莫名其妙地死机。查了很久才发现,是异步复位信号导致了亚稳态。从那以后,我养成了一个习惯:所有异步复位信号,必须先同步,再使用。
同步的方法很简单:用两级触发器打两拍。
// 异步复位同步模块
// 输入:clk, async_rst_n (原始异步复位信号)
// 输出:sync_rst_n (同步后的复位信号)
module sync_rst (
input wire clk,
input wire async_rst_n,
output reg sync_rst_n
);
reg sync_ff1;
always @(posedge clk) begin
sync_ff1 <= async_rst_n;
sync_rst_n <= sync_ff1;
end
endmodule
警告:千万不要只用一级触发器做同步!一级触发器无法完全消除亚稳态。两级是最低要求,如果时钟频率很高,我建议用三级。
2.4 知识体系总览
为了让你更直观地理解本章的知识结构,我画了一张图:
这张图把本章的三个核心知识点串起来了。你仔细看看,它们之间是环环相扣的:电平与边沿决定了复位信号的“性格”,去抖负责“净化”它,同步则确保它“安全”地进入FPGA内部。
好了,关于复位电路的设计原则,咱们就聊到这儿。记住:电平复位、软件去抖、两级同步,这三板斧用好了,你的复位电路基本就稳了。