4、复位可靠性验证:复位毛刺过滤、复位抖动消除、复位完成检测电路设计
复位信号,说白了就是整个系统的“总开关”。
这个开关要是不可靠,系统上电后可能疯跑,也可能卡死。我见过不少项目,功能仿真跑得飞起,一上板子就出问题,最后查来查去,都是复位惹的祸。
今天咱们就聊聊,怎么把复位信号“洗干净”,让它老老实实干活。
4.1 复位毛刺过滤
先说说毛刺。你想想看,复位信号从板级进来,经过连接器、走线、可能还有电平转换芯片,一路颠簸。外部干扰、电源噪声,都可能在复位线上“种”出毛刺。
毛刺有多宽?可能就几纳秒。但FPGA是边沿触发的,一个毛刺就可能让整个设计误复位。
我个人习惯的做法,是用一个简单的移位寄存器来做“数字滤波器”。
// 复位毛刺过滤电路
// 连续采样N拍,全部为低电平才认为是有效复位
module glitch_filter #(
parameter FILTER_CNT = 4 // 过滤深度,建议4~8
)(
input wire clk,
input wire rst_async, // 原始异步复位
output wire rst_sync // 过滤后复位输出
);
reg [FILTER_CNT-1:0] shift_reg;
always @(posedge clk) begin
shift_reg <= {shift_reg[FILTER_CNT-2:0], rst_async};
end
// 全部为0才输出有效复位
assign rst_sync = (shift_reg == {FILTER_CNT{1'b0}}) ? 1'b0 : 1'b1;
endmodule
这个电路的核心思想很简单:连续采样4拍,如果4拍都是低电平,才认为复位真的来了。中间任何一拍跳高了,计数器清零,重新开始。
我在项目中遇到过一种情况:外部复位芯片输出有毛刺,大概2~3ns宽。用4拍过滤(时钟50MHz,一拍20ns),毛刺根本穿不过去。效果立竿见影。
4.2 复位抖动消除
抖动和毛刺不一样。毛刺是窄脉冲,抖动是复位信号在跳变沿附近来回震荡。
为什么会这样?
最常见的原因是机械开关。比如板子上的复位按键,按下和释放时,金属触点会弹跳几次。这个过程可能持续几毫秒甚至十几毫秒。如果不处理,FPGA会看到一连串的复位脉冲。
消除抖动,我推荐用“延时采样法”。说白了,就是等信号稳定了再采样。
// 复位抖动消除电路
// 检测到下降沿后,等待稳定时间,再采样
module debounce #(
parameter STABLE_CNT = 100_000 // 10ms @ 10MHz时钟
)(
input wire clk,
input wire rst_in,
output wire rst_out
);
reg [1:0] sync_reg;
reg [31:0] cnt;
reg rst_debounced;
// 两级同步,消除亚稳态
always @(posedge clk) begin
sync_reg <= {sync_reg[0], rst_in};
end
// 抖动消除逻辑
always @(posedge clk) begin
if (sync_reg[1] != rst_debounced) begin
cnt <= cnt + 1;
if (cnt == STABLE_CNT - 1) begin
rst_debounced <= sync_reg[1];
cnt <= 0;
end
end else begin
cnt <= 0;
end
end
assign rst_out = rst_debounced;
endmodule
这个电路的工作流程:
- 先做两级同步,把异步信号同步到时钟域
- 检测到电平变化后,启动计数器
- 计数器满(比如10ms),才确认电平真的变了
- 中间任何一次电平回跳,计数器清零重来
我曾经在一个工业控制项目里吃过亏。客户反映设备偶尔会莫名其妙复位,查了三天,最后发现是面板上的复位按键弹跳时间长达15ms。我原来只等了5ms,改成20ms后,问题彻底消失。
4.3 复位完成检测电路
复位信号释放了,是不是就万事大吉了?
不一定。你想想看,复位释放后,PLL需要锁定时间,DDR需要初始化时间,某些IP核也需要自检时间。如果主逻辑在复位释放后立即开始工作,很可能读到的是无效数据。
所以,我们需要一个“复位完成检测”电路。它的作用是:等所有模块都准备好后,再给主逻辑一个“可以开始工作”的信号。
// 复位完成检测电路
// 等待多个条件同时满足
module rst_complete_detector (
input wire clk,
input wire rst_n, // 全局复位
input wire pll_locked, // PLL锁定信号
input wire ddr_ready, // DDR初始化完成
input wire eth_ready, // 以太网PHY就绪
output wire sys_rst_n // 系统复位完成
);
reg [3:0] ready_cnt;
reg sys_rst_n_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ready_cnt <= 0;
sys_rst_n_reg <= 0;
end else begin
// 所有条件满足后,延迟4拍输出
if (pll_locked && ddr_ready && eth_ready) begin
if (ready_cnt < 4)
ready_cnt <= ready_cnt + 1;
else
sys_rst_n_reg <= 1;
end else begin
ready_cnt <= 0;
sys_rst_n_reg <= 0;
end
end
end
assign sys_rst_n = sys_rst_n_reg;
endmodule
这个电路的设计思路:
- 收集各个模块的“就绪”信号(PLL锁定、DDR就绪等)
- 所有条件满足后,再延迟几个时钟周期
- 最后才释放系统复位
为什么要延迟几拍?
嗯,这里要注意:各个模块的就绪信号可能不是严格同步的。延迟几拍,是为了确保所有信号都已经稳定。我一般习惯延迟4~8拍,够用又不拖沓。
4.4 知识体系总览
上面讲了三个电路,它们的关系是这样的:
从图上可以看得很清楚:原始复位信号经过三级处理——毛刺过滤、抖动消除、完成检测——最终输出一个干净、可靠的复位信号。每一级解决不同的问题,缺一不可。
- 毛刺过滤:解决外部干扰引起的窄脉冲问题
- 抖动消除:解决机械开关弹跳引起的多次触发问题
- 完成检测:解决各模块初始化时序不同步的问题
这三个电路,我建议作为每个FPGA设计的“标配”。别嫌麻烦,复位搞好了,后面能省80%的调试时间。我曾经在一个项目里偷懒没加毛刺过滤,结果板子一上电就随机复位,查了整整一周。从那以后,我再也不敢省这一步了。
公众号:蓝海资料掘金营,微信deep3321