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内部。

好了,关于复位电路的设计原则,咱们就聊到这儿。记住:电平复位、软件去抖、两级同步,这三板斧用好了,你的复位电路基本就稳了。


专注资料整理