4、复位与初始化问题:复位信号毛刺、异步复位同步释放、初始化顺序错误、看门狗误触发
复位和初始化,听起来像是基本功对吧?
但我跟你说,这恰恰是上板后最容易翻车的地方。我见过太多项目,仿真跑得飞起,一上板就各种诡异现象——要么系统起不来,要么跑着跑着突然复位。说白了,都是复位和初始化埋的雷。
4.1 复位信号毛刺——你以为复位了,其实没有
先说说复位毛刺。这个问题很隐蔽。
你想想看,如果复位信号上有一个窄脉冲毛刺,刚好在时钟沿附近出现,会发生什么?
有些寄存器被复位了,有些没被复位。整个系统的状态就乱了。
我在项目中遇到过一块板子,每次用手摸一下复位按键附近,系统就重启。查了半天,发现是按键抖动加上走线耦合,给FPGA送了一串毛刺。
怎么解决?
- 硬件滤波: 复位引脚加RC低通滤波,时间常数选1~10ms
- 软件去抖: 在FPGA内部做连续采样,连续N个周期为低才认为是复位
- 施密特触发器: 如果板级允许,用施密特触发器芯片做整形
4.2 异步复位同步释放——这个真不是玄学
好,毛刺问题解决了。但还有一个更常见的坑:异步复位。
很多工程师喜欢直接用异步复位,因为简单嘛——复位信号一来,寄存器立刻清零。但问题出在复位释放的时候。
如果复位释放时刻刚好靠近时钟沿,有些寄存器会认为复位已经结束,有些还认为在复位中。这叫复位撤销的亚稳态。
我建议的做法是:异步复位,同步释放。
// 异步复位同步释放的典型写法
reg rst_sync1, rst_sync2;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rst_sync1 <= 1'b0;
rst_sync2 <= 1'b0;
end else begin
rst_sync1 <= 1'b1;
rst_sync2 <= rst_sync1;
end
end
// 用 rst_sync2 作为内部复位
always @(posedge clk or negedge rst_sync2) begin
if (!rst_sync2) begin
// 复位逻辑
end else begin
// 正常逻辑
end
end
你看,复位信号进来时是异步的,直接清零。但释放时,经过两级同步,保证所有寄存器都在同一个时钟沿退出复位。嗯,这个写法我用了十年,从来没出过问题。
4.3 初始化顺序错误——谁先谁后,有讲究
接下来聊初始化顺序。这个在复杂系统里特别常见。
比如你有DDR、有以太网PHY、有ADC,上电后谁先初始化?
我曾经遇到一个项目,FPGA先初始化了,然后才去配PHY。结果PHY还没准备好,FPGA已经发了配置命令,全丢了。系统跑起来后网络死活不通。
正确的初始化顺序一般是这样:
- 电源稳定: 等所有电源轨都OK
- 时钟稳定: PLL锁定、晶振起振
- 外部器件初始化: PHY、ADC、DDR等先配好
- FPGA内部状态机启动: 等外部都就绪了,再开始干活
- 看门狗喂狗: 最后再开看门狗,避免初始化期间误触发
我个人习惯写一个初始化状态机,每个步骤完成后给一个done信号,串行推进。这样逻辑清晰,调试也方便。
4.4 看门狗误触发——你还没喂它,它就咬人了
最后说说看门狗。这东西本来是保护系统的,但用不好反而添乱。
最常见的场景:系统上电后,看门狗先启动了,但主控还在初始化,没来得及喂狗。结果看门狗超时,直接复位。系统陷入「上电→复位→上电→复位」的死循环。
怎么避免?
- 延迟启动: 看门狗在初始化完成后再使能
- 长超时时间: 初始化阶段用较长的超时时间,比如10秒
- 硬件使能引脚: 如果看门狗芯片有EN引脚,用FPGA的GPIO控制它
4.5 本章小结——一张图说清楚
好了,四个问题讲完了。我画了一张图,帮你把整个复位与初始化的流程串起来:
这张图把整个流程串起来了。你照着这个顺序做,复位和初始化的问题基本都能规避。
嗯,这一章就到这里。复位和初始化看似基础,但往往是系统稳定性的第一道关卡。把这里做好了,后面的调试会顺畅很多。