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),毛刺根本穿不过去。效果立竿见影。

💡 小技巧: 过滤深度怎么选?我的经验是:时钟频率越高,过滤深度可以越大。100MHz时钟下,8拍过滤也就80ns延迟,完全可以接受。

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后,问题彻底消失。

🔑 关键点: 抖动消除的时间常数,一定要大于最坏情况下的弹跳时间。机械按键一般5~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拍,够用又不拖沓。

💡 经验之谈: 复位完成检测的“条件列表”一定要和系统架构师确认。漏掉一个条件,系统就可能跑飞。我见过最惨的一次,DDR初始化没完成就开始了读写,数据全乱了。

4.4 知识体系总览

上面讲了三个电路,它们的关系是这样的:

复位可靠性验证知识体系 原始复位信号 毛刺过滤 滤除窄脉冲干扰 抖动消除 消除机械弹跳 完成检测 等待所有模块就绪 可靠复位 三级处理流程说明 1. 毛刺过滤:用移位寄存器连续采样,滤除窄脉冲 2. 抖动消除:用延时计数器等待信号稳定,消除弹跳 3. 完成检测:收集各模块就绪信号,统一释放复位

从图上可以看得很清楚:原始复位信号经过三级处理——毛刺过滤、抖动消除、完成检测——最终输出一个干净、可靠的复位信号。每一级解决不同的问题,缺一不可。

🔑 总结一下:
  • 毛刺过滤:解决外部干扰引起的窄脉冲问题
  • 抖动消除:解决机械开关弹跳引起的多次触发问题
  • 完成检测:解决各模块初始化时序不同步的问题

这三个电路,我建议作为每个FPGA设计的“标配”。别嫌麻烦,复位搞好了,后面能省80%的调试时间。我曾经在一个项目里偷懒没加毛刺过滤,结果板子一上电就随机复位,查了整整一周。从那以后,我再也不敢省这一步了。


公众号:蓝海资料掘金营,微信deep3321