FPGA多路电源时序控制:上电顺序、PGOOD去抖与逆序下电

发布时间:2026/9/18 10:58:14
FPGA多路电源时序控制:上电顺序、PGOOD去抖与逆序下电 我手上这块板子一共六路电源1.0V 的 FPGA 内核、1.8V 的辅助电源、1.5V 的 DDR 主供电、0.75V 的 DDR 终端电压、3.3V 的 IO 和 1.2V 的一颗高速收发器供电。第一次上电时我图省事把六路的 EN 脚并在一起拉高结果板子啪一声一颗 DC-DC 直接进保护示波器抓到的浪涌电流峰值是稳态的三倍多。后来换了专用时序芯片又遇到 PGOOD 抖动把状态机带跑偏。折腾了两轮之后我干脆改用 FPGA 自己做多路电源时序控制把延时、去抖、故障回退、逆序下电全都捏在手里顺带还能通过 I2C 把每一路的电压电流回读上来做健康监控。这篇就围绕FPGA 多路电源时序控制模块这个主题把我从电源树梳理、方案选型、RTL 落地、时序约束、板级联调到踩坑排查的整条链路讲清楚适合正在做多轨供电板子、准备用 FPGA/CPLD 接管上电顺序的硬件和逻辑工程师参考。1. 上电顺序不是礼貌问题而是器件能不能活下来的问题很多刚接触多电源设计的人会有一个朴素的认知几路电源都是一起供电谁先谁后影响不大最多是时序图上不好看。这个想法在单轨板子上基本成立但在多轨的 SoC、FPGA、DDR 或者射频前端板子上顺序错了是会烧东西的而且往往是那种打样十块烧两块的随机故障特别难查。1.1 反灌是头号杀手CMOS 器件的 IO 引脚内部都有 ESD 保护二极管通常是一个从 IO 到电源轨的寄生通路。当某一路的电源轨还没建立而它相邻的另一路已经上电、并且有信号或者 IO 供电通过片内连线耦合过来时电流就会顺着这条二极管路径从一个有电的电源轨倒灌进一个没电的电源轨。这个倒灌电流有几个后果一是把未上电的电源轨慢慢拉到一个中间电位让器件处于半导通状态二是电流可能超过 ESD 二极管的承受能力时间长了就是永久性损伤三是在某些工艺下会触发寄生可控硅也就是常说的闩锁一旦触发就只能断电重来严重的直接烧毁。所以像 Xilinx 7 系列、Altera Cyclone 这类器件的手册里都会明确写清楚 VCCINT 和 VCCAUX 的上电先后关系以及各路之间允许的最大时间差这不是建议是硬要求。1.2 浪涌电流比顺序更容易被忽略即使顺序对了如果六路电源在同一时刻启动每一路输出电容的充电电流会叠加在一起。假设某一路输出电容 470µF、目标电压 1.0V、软启动时间 1ms那么仅这一路的平均充电电流就接近 0.5A六路同时来就是 3A 的瞬时冲击。前级电源、输入线缆、甚至 PCB 走线上的寄生电感都会在这个冲击下产生明显的电压跌落轻则触发前级的过流保护重则让整个系统反复重启。把六路上电时刻错开比如每路间隔 10~20ms峰值电流就会被摊平前级压力骤减。这也是时序控制除了保护器件之外的第二大价值把同时发生的能量冲击变成串行发生的小脉冲。我自己实测过六路错开之后输入 12V 母线上的电压跌落从 800mV 降到了 150mV 以内。1.3 下电顺序同样要有讲究上电顺序解决了很多人就把下电随手一放了。但下电如果顺序不对同样会触发反灌。常见的做法有两种一种是逆序下电也就是先关最后开的、后关最先开的让器件退出时电流路径始终是从有电轨流向地另一种是要求所有电源在极短时间内同时掉下去靠大电容储能保证掉电斜率一致。前者适合用数字状态机精确控制后者适合模拟方案但一致性很难保证。我在项目里最终采用的是逆序下电 掉电检测提前响应的组合。板子上的主 12V 一旦低于阈值FPGA 立刻收到掉电中断先把最上层的 IO 电源关掉再逐级往下关整个关断过程压缩在 5ms 内完成靠一颗 1000µF 的储能电容给 FPGA 自己续命保证在它彻底断电之前把六路全部关完。2. 板级视角电源树、负载开关与 PGOOD 的真实脾气写完第一版代码之后我才发现FPGA 里的状态机好写真正难的是板子上每一个被控对象的脾气都不一样。有的 EN 是高有效、有的是低有效有的 PGOOD 是开漏输出、有的是推挽有的 DC-DC 的 PG 在输出达到 90% 额定值时就拉高有的要等到 95%。这些差异不在代码里体现就会在板子上爆发。2.1 先把电源树画清楚做时序控制之前我建议先用一张表把所有电源轨的信息列全包括电压、电流、上游来源、使能极性、使能驱动电平、PGOOD 有无线号和极性、该路的下游负载是什么。这张表不只是给你自己看也是后面写 RTL 参数、写约束、写 testbench 的唯一依据。我项目里的表大致长这样电源轨电压上游来源EN 极性EN 驱动电平PGOOD下游负载RAIL01.0V12V→Buck高有效3.3V有开漏FPGA 内核RAIL11.8V12V→Buck高有效3.3V有推挽FPGA 辅助RAIL21.5V12V→Buck高有效3.3V有开漏DDR 主供电RAIL30.75V1.5V→LDO高有效3.3V无DDR 终端RAIL43.3V12V→Buck低有效3.3V有开漏IO、外设RAIL51.2V3.3V→LDO高有效3.3V无收发器这张表最容易被忽略的一列是PGOOD 有无。RAIL3 和 RAIL5 没有独立的 PGOOD 引脚它们的已就绪只能靠上游电源的 PGOOD 加上一段固定的建立延时来近似判断。这个近似值怎么取我的做法是查 LDO 手册里的软启动时间再留 50% 余量。比如手册写 1ms代码里就按 1.5ms 等待。2.2 PGOOD 为什么不干净PGOOD 看起来是个干净的逻辑信号但实际抓波形你会发现它在临界点附近是抖的。原因是 DC-DC 的 PG 比较器通常在一个窗口内翻转当输出电压刚刚爬过门限、还带着纹波和负载扰动时PG 会在一小段时间里反复跳变几次。如果状态机直接拿这个信号做下一步开始的条件就有可能在被判无效的那一拍错过触发或者在抖动的间隙里来回跳状态。解决办法有两层。硬件层面在 PGOOD 到 FPGA 的走线上加一颗 100nF 的滤波电容把高频毛刺先滤掉这一招成本极低但非常有效。逻辑层面必须做数字去抖也就是连续采样到高达到设定时长之后才认为这一路真正就绪。我在代码里用的去抖窗口是 10ms也就是在 50MHz 时钟下数 500000 个周期。这个值不是拍脑袋来的它要比 DC-DC 输出电压的建立时间小、比 PG 抖动持续时间大10ms 对大多数板级电源是合适的量级。2.3 负载开关和 DC-DC 的 EN 驱动方式不一样后级小电源轨比如某些 1.2V、1.8V 的负载开关或者 LDO的 EN 引脚内部通常是一个高阻输入靠外部电阻或者上游驱动决定默认状态。而大功率 DC-DC 的 EN 往往是精确的阈值输入还可能有内部上拉。这两种器件的驱动要求完全不同高阻 EN 的负载开关如果你用 FPGA 的 IO 直接驱动没问题但必须在 FPGA 未配置期间保证 EN 被外部下拉到确定电平否则器件会浮空开启把不该上的电先上了。有内部上拉的 DC-DCFPGA 未配置时 IO 是高阻上拉会把它拉到高电平同样会导致提前开启。这种情况要在 EN 脚外部加下拉电阻阻值要能压过内部上拉通常是 10kΩ 对 100kΩ 的组合。我第一版板子就吃了这个亏。FPGA 从配置到 IO 释放大概有几百毫秒这段时间里四路 DC-DC 的 EN 被内部上拉拉高全部提前上电顺序完全乱掉。后来在每一路 EN 上加了一颗 4.7kΩ 下拉同时在 FPGA 约束里把 EN 引脚在配置期的行为设置成驱动低才彻底解决。3. 方案选型专用时序芯片、CPLD 还是 FPGA确定要做时序控制之后第一件事是选实现载体。市面上主流有三条路专用电源时序芯片、CPLD、以及 FPGA。这三条路没有绝对优劣关键看你的板子上已经有什么、需要多复杂的逻辑。3.1 三种方案的适用边界维度专用时序芯片CPLDFPGA通道数通常 4~8 路视 IO 数量视 IO 数量延时精度由外部电容决定误差较大由晶振决定很准由晶振决定很准逻辑复杂度固定逻辑不可扩展中等高可加状态机、通信、监控是否可重配需要改外围可以可以板上已有器件需新增常已有常已有掉电行为由芯片自己决定需设计需设计监控与上报基本没有有限可以做 I2C、ADC 回读专用时序芯片的好处是简单、可靠、不占逻辑资源四路以内的简单系统用它最省事。但它的延时精度依赖外部 RC温漂和一致性都比较差而且一旦你需要在时序基础上加故障记录、电压回读、和主控通信它就无能为力了。CPLD 和 FPGA 的区别主要在资源和复杂度。如果板子上已经有一颗 FPGA而且这颗 FPGA 需要 3 路以上的电源时序那用 FPGA 的剩余逻辑去做边际成本几乎为零还省了一颗芯片和配套的 RC。我项目里就是这种情况FPGA 本身有大量空闲 IO 和逻辑直接把它变成时序控制器。3.2 FPGA 做时序的三个额外约束选 FPGA 不等于把代码写完就完事有三个约束必须在设计一开始就想清楚。第一FPGA 自己必须先活过来。时序控制器的前提是它自己已经上电并配置完成才能去控制别人。这就要求 FPGA 的供电必须来自一个永远先上的电源通常是输入 12V 直接下来的辅助 3.3V LDO或者由前级电源的 always-on 输出提供。如果 FPGA 的电源本身也在被控序列里就形成了死锁。第二配置完成之前 IO 是高阻这个问题前面讲过必须在外部加下拉并配合引脚约束。除此之外还要等 PLL 锁定、复位释放之后状态机才能启动否则计数器会跑飞。第三掉电时 FPGA 可能先掉。如果主 12V 一掉FPGA 的辅助电源也跟着掉那么它还没来得及关断被控电源就断电了EN 状态变成不确定负载开关可能随机开启。解决思路是给 FPGA 的辅助电源加一颗大储能电容并且在主电源掉电的早期就检测到并启动逆序关断抢在 FPGA 断电之前完成。3.3 模块划分把 FPGA 内部的时序控制逻辑拆开我分了六个部分时钟与复位管理、异步输入同步与去抖、参数化延时计数、主状态机、输出驱动与极性处理、故障处理与状态上报。这几个部分之间是单向依赖的状态机只消费已经同步并去抖完成的就绪信号输出驱动只消费状态机的状态编码职责很清晰。这样划分的好处是任何一路电源的时序参数变化只需要改一张参数表不用动状态机的结构。4. RTL 落地参数化延时、同步去抖与逆序下电这一节是整篇的核心我把关键代码和设计理由都摊开讲。代码基于 Verilog工具链用 Vivado 或者 Quartus 都可以仿真用 Vivado Simulator 或者 ModelSim 都行。4.1 延时参数怎么定计数器要多少位延时靠时钟计数实现所以要先把时间换算成周期数。假设系统时钟 50MHz周期 20ns。如果某一路要求上电后等待 20ms 再放下一步那么需要计数 20ms / 20ns 1000000 个周期也就是 100 万。计数器位宽要满足 2^N ≥ 10000002^20 1048576刚好够所以用 20 位。把这个换算整理成一张参数表每一路的本路使能到下一次动作的延时和等待 PGOOD 的超时都用周期数表达代码里用 parameter 定义。这样做的好处是将来时钟换成 25MHz 或者 100MHz只需要改一个 CLK_FREQ 参数所有延时自动跟着换算。// 时钟与时间参数 parameter CLK_FREQ_HZ 50_000_000; parameter DELAY_MS 20; // 路间延时单位毫秒 parameter PGOOD_DEB_MS 10; // PGOOD 去抖窗口 parameter TIMEOUT_MS 100; // 单路等待 PGOOD 的超时 localparam integer MS_TICKS CLK_FREQ_HZ / 1000; localparam integer DELAY_TICKS MS_TICKS * DELAY_MS; localparam integer DEB_TICKS MS_TICKS * PGOOD_DEB_MS; localparam integer TIMEOUT_TICKS MS_TICKS * TIMEOUT_MS;提示localparam 里的乘法是在综合时求值的常数表达式不会消耗逻辑资源也不会引入运行时计算可以放心用。4.2 异步输入的同步器和去抖PGOOD 和掉电检测信号都是异步输入直接进状态机一定会遇到亚稳态。标准做法是两级触发器串联打两拍。打两拍之后信号仍然可能有毛刺所以后面再跟一个去抖计数器连续数到 DEB_TICKS 个周期都是高才把同步后的有效标志拉起来一旦中间出现低计数器清零重来。// 两级同步器 reg pg_sync1, pg_sync2; always (posedge clk) begin pg_sync1 pg_raw; pg_sync2 pg_sync1; end // 去抖计数器 reg [31:0] deb_cnt; reg pg_stable; always (posedge clk) begin if (!rst_n) begin deb_cnt 32d0; pg_stable 1b0; end else if (pg_sync2) begin if (deb_cnt DEB_TICKS) begin deb_cnt DEB_TICKS; pg_stable 1b1; end else begin deb_cnt deb_cnt 1b1; end end else begin deb_cnt 32d0; pg_stable 1b0; end end这里有个细节去抖计数器在到达 DEB_TICKS 之后不再累加而是保持避免位宽溢出。另外 pg_stable 只用连续高作为进入条件退出条件我用的是同步后直接低也就是不做对称去抖。原因是安全侧考虑宁可误报故障也不能漏报掉电。4.3 主状态机的状态划分状态机我按上电链和下电链分了两组状态。上电链每一步做两件事把本路 EN 拉有效等本路 PGOOD 去抖完成或者超时下电链每一步按相反顺序把 EN 拉无效。中间插一个 FAULT 状态任何一步超时或者收到掉电中断都跳到 FAULT由 FAULT 统一走逆序关断。状态动作跳转条件IDLE所有 EN 无效PLL locked 且复位释放PWR0_ONRAIL0 使能PGOOD0 稳定PWR1_ONRAIL1 使能PGOOD1 稳定PWR2_ONRAIL2 使能PGOOD2 稳定 或 固定延时PWR3_ONRAIL3 使能固定延时PWR4_ONRAIL4 使能低有效PGOOD4 稳定PWR5_ONRAIL5 使能固定延时ALL_GOOD全部保持上报就绪收到掉电请求FAULT逆序关断关断完成回 IDLE以 PWR0_ON 为例代码里是这样一个结构进入该状态时启动一个等待计数器和超时计数器每拍判断 pg_stable如果在超时之前稳定了跳下一步如果超时计数器先到 TIMEOUT_TICKS 还没稳定直接跳 FAULT。这个超时分支是必须的否则某一路电源因为负载短路没起来状态机会永远卡住后面所有路都不上电系统表现就是完全没反应调试时会一头雾水。4.4 逆序下电的实现逆序下电最常见的做法是复用上电链的状态表用一个方向标志控制遍历顺序。但这样做会把状态机写得很绕容易出 bug。我后来改成了一个更直白的方式下电链单独一套状态从最高层的 RAIL5 开始关逐级往下每级之间也有延时延时参数复用同一张表。这样上电链和下电链在代码里是两段清晰的逻辑调试的时候一眼能看出顺序。// 下电链示例从 RAIL5 逆序关到 RAIL0 SHUT5: begin en_rail5 1b0; wait_delay; next SHUT4; end SHUT4: begin en_rail4 1b1; wait_delay; next SHUT3; end // RAIL4 低有效 SHUT3: begin en_rail3 1b0; wait_delay; next SHUT2; end SHUT2: begin en_rail2 1b0; wait_delay; next SHUT1; end SHUT1: begin en_rail1 1b0; wait_delay; next SHUT0; end SHUT0: begin en_rail0 1b0; next IDLE; end注意RAIL4 是低有效使能所以关断时输出要写 1 而不是 0。极性处理如果散落在状态机里很容易搞错我的做法是在输出端口统一加一级极性转换状态机内部一律用1 表示使能、0 表示关闭的逻辑语义最后再按各自的极性映射出去。4.5 输出端口的极性统一把极性转换独立出来是一个很小的重构但它能省掉大量调试时间。内部逻辑里所有 en_railX 都表示是否希望这一路工作是正逻辑端口输出时再根据一张极性表做映射。这样将来某一路换成了低有效的负载开关只需要改极性表的对应位状态机一行都不用动。// 极性映射pol1 表示高有效pol0 表示低有效 assign en_out[0] en_rail0 ^ ~POL[0]; assign en_out[1] en_rail1 ^ ~POL[1]; // ...其余各路同理5. 时序约束与 IO 电气细节让输出沿和 MOSFET 听话RTL 写对了不代表板子上就是对的中间还隔着综合、布局布线、IO 电气和 PCB 这四道关。这一节讲怎么把数字逻辑的正确落到板子的正确上。5.1 输出使能信号要不要做时序约束EN 信号是毫秒级的慢速控制信号切换频率极低从功能角度它根本不需要时序约束。但如果不约束静态时序分析工具仍然会尝试用系统时钟去检查它而它连的是一颗几十欧姆外接电阻加负载开关的输入电容路径延迟毫无规律工具很可能报一大堆无意义的违例把真正的问题淹没掉。我的做法是对这些控制输出统一加set_false_path声明它们不受时钟约束对异步输入 PGOOD 同样加set_false_path因为已经用两级同步器处理了跨时钟域工具不需要在这里折腾。同时给输出加一个set_output_delay的宽松值仅仅是为了让工具知道这些端口有负载还不至于完全忽略。# 异步输入配合两级同步器使用 set_false_path -from [get_ports {pgood_raw[*]}] -to [all_registers] # 慢速控制输出不做时序检查 set_false_path -from [all_registers] -to [get_ports {en_out[*]}] set_output_delay -clock [get_clocks sys_clk] -max 100.0 [get_ports {en_out[*]}]提示set_false_path是在明确知道这条路径不需要时序收敛时才用不能拿它当消违例的万能药。这里的依据是信号本身是毫秒级异步控制与时钟频率差了好几个数量级属于合理的例外。5.2 IO 电平要和被控器件的阈值匹配FPGA 的 IO bank 供电决定了输出高电平的幅度而被控器件的 EN 阈值决定了这个幅度够不够用。3.3V 的 bank 驱动 1.2V 阈值的 EN 完全没问题但如果反过来用 1.8V 的 bank 去驱动一个 2.0V 阈值的 EN就可能出现电平不够、器件不认的情况。这种情况下要么换 bank要么加电平转换。另外一个容易忽略的点是驱动能力。FPGA 的 IO 驱动电流通常有 4mA、8mA、12mA 几档可选驱动一个高阻的使能输入用 4mA 就够了驱动电流设太大反而会让输出沿变陡、引起振铃在长走线上辐射噪声。我的习惯是使能信号统一用最小档 4mA并在靠器件端加一颗 33Ω 的串阻把边沿稍微放缓一点。5.3 未配置期间的 IO 状态必须锁定这是我在板子上真正踩过的坑值得单独讲。FPGA 从上电到配置完成这段时间IO 处于高阻状态。如果这一段是高有效的负载开关高阻意味着 EN 电平不确定如果它有外部上拉就会被拉高导致电源提前上电顺序全乱。解决办法是三重保险一是在器件端加下拉电阻阻值要能压过任何外部上拉二是在综合工具的引脚约束里明确指定这些 EN 引脚在配置期间的行为三是在 FPGA 完成配置、复位释放之后的第一个状态 IDLE 里先主动把所有 EN 驱动到无效电平并保持一段时间确认所有电源都处于关闭状态再开始上电链。这三层叠加之后上电顺序才真正可控。5.4 时钟和复位的启动条件状态机的时钟来自 PLL而 PLL 从加电到锁定需要一段时间。如果时钟还没锁定计数器就跑不起来状态机会停在那里看起来像上电没反应。我的处理是把 PLL 的 locked 信号作为状态机的启动使能locked 为低时整个状态机停在 IDLE所有 EN 保持无效locked 拉高之后再经过一个去抖窗口确认稳定才允许状态机启动。复位信号本身也有讲究。外部复位是异步的进 FPGA 之后必须做同步释放否则复位释放的那一拍可能正好落在时钟边沿附近导致触发器进入亚稳态。标准做法是异步复位、同步释放复位断言用异步释放用两级触发器同步。6. 仿真与板级联调从波形到示波器的验证链路代码写完不仿真就上板基本等于赌博。时序控制这种一次做错就可能烧东西的模块仿真要覆盖得比常规逻辑更狠。我的验证分三层仿真、静态检查、板级实测。6.1 Testbench 要模拟 PGOOD 的坏脾气仿真里最容易被写成理想情况的就是 PGOOD。如果 testbench 里 PGOOD 是在 EN 拉高之后固定延时几十拍就稳定那么它检验不出任何去抖逻辑的价值。真正有用的 testbench 要模拟三种情况PGOOD 在稳定之前抖动几次检验去抖窗口是否能滤掉PGOOD 一直不拉高检验超时是否触发 FAULTPGOOD 先拉高又掉下来检验掉电响应是否及时。第二种和第三种情况必须单独写测试用例而不是指望运气。我在 testbench 里用 task 把每一路的 PGOOD 生成逻辑封装起来参数化控制抖动次数和超时这样一套 testbench 可以覆盖所有路。task pgood_gen(input integer rail, input integer delay_ns, input integer glitch_num); integer i; begin #(delay_ns); for (i 0; i glitch_num; i i 1) begin pgood_raw[rail] 1b1; #(200); pgood_raw[rail] 1b0; #(200); end pgood_raw[rail] 1b1; end endtask6.2 波形上要盯的四个点仿真跑起来之后波形上看什么我总结成四个必查点一是所有 EN 的上升沿之间的间隔是否等于设定的延时误差在一个时钟周期以内二是去抖逻辑在 PGOOD 抖动期间是否没有误判三是超时用例下状态机是否在超时时刻准确跳到 FAULT四是下电用例下 EN 的下降沿是否严格逆序。这四点过了功能层面基本没问题。6.3 板级调试单次触发和电流探头上板之后调试工具就变成示波器了。多路时序的调试有个技巧用示波器的单次触发模式触发源选第一路的 EN 上升沿然后把所有 EN 都抓到同一个屏幕上这样一眼就能看出顺序和间隔。四通道示波器不够用的话用逻辑分析仪抓数字信号同时用示波器的一两个通道盯模拟电源轨两边对照。浪涌电流的测量需要电流探头或者在被测电源的输入端串一个低阻值的采样电阻用差分探头测电阻两端电压。这一步很关键因为浪涌是时序控制最初要解决的问题之一如果错开之后峰值没降下来说明延时还不够或者前级软启动有问题。6.4 现象到原因的对照表板级调试最花时间的是看到现象猜不出原因。我把项目里遇到的问题整理成一张对照表以后遇到类似情况可以快速定位。现象可能原因排查手段顺序全乱几路同时上电配置期 EN 被上拉提前开启抓配置完成前后的 EN 波形状态机卡在某一步不动该路 PGOOD 一直没来或抖动抓 PGOOD 原始信号和去抖后信号上电后系统反复重启浪涌电流触发前级保护电流探头测输入峰值掉电时部分电源关不掉FPGA 先于被控电源掉电测 FPGA 辅助电源掉电时刻某一路偶尔上电失败延时余量不足或 PGOOD 抖动加大延时、加大去抖窗口上电瞬间烧器件反灌或闩锁核对器件手册的上电顺序要求7. 踩坑实录PGOOD 抖动、掉电失控与配置期盲区前面几节讲的是应该怎么做这一节讲我实际做错了什么。时序控制这种模块的价值很大程度上体现在它能不能在这些坑里活下来。7.1 第一坑PGOOD 抖动导致状态机来回跳最初的版本里我把 PGOOD 打两拍之后直接送进状态机的跳转条件没有去抖。结果板子在某些负载条件下RAIL2 的 PGOOD 在刚拉高的几十微秒内抖了三四次状态机被带着从 PWR2_ON 跳回 PWR1_ON再跳回来表现就是这一路的 EN 跟着抖输出电压也跟着抖最后前级进保护。排查过程是这样的先用示波器抓 PGOOD 原始波形确认它真的在抖再把去抖后的信号引到一个测试 IO 上对比两者确认问题后加上 10ms 去抖窗口现象消失。这个坑的教训是任何跨时钟域进来的信号同步只是第一步去抖才是让它可用的关键。7.2 第二坑掉电时 FPGA 先掉逆序关断没跑完第二个坑出现在掉电场景。主 12V 一断FPGA 的辅助 3.3V 也跟着掉FPGA 在几十毫秒内就失去工作能力而此时逆序关断才进行到第二步剩下四路电源的 EN 状态变成了不确定负载开关随机开启导致板子上电时的顺序被破坏甚至出现反向电流。解决分两步一是在 FPGA 的辅助电源上加了一颗 1000µF 的储能电容把 FPGA 的掉电时间拖后到 100ms 以上二是在主 12V 上加了掉电检测电路一旦电压低于阈值就立刻给 FPGA 一个中断状态机优先响应这个中断在储能电容耗尽之前完成整个逆序关断。实测下来从检测到掉电到六路全部关完大约 5ms储能电容还有大量余量。7.3 第三坑配置完成前 IO 高阻EN 提前开启这个坑前面提过但我要展开说排查过程因为它特别隐蔽。当时的现象是每次重新上电RAIL43.3V总是比 RAIL01.0V先起来而 3.3V 是 IO 电源按手册应该在后。第一反应是代码写反了但仿真完全正确。后来用示波器抓 FPGA 的配置完成信号和 RAIL4 的 EN发现在配置完成之前大约 200msEN 就已经被拉高了。这就锁定到配置期 IO 高阻 外部上拉这个问题上。原因是 RAIL4 的负载开关 EN 有一个 100kΩ 的外部上拉而 FPGA 的 IO 在配置期是高阻上拉就直接把它拉高了。加上 4.7kΩ 下拉之后问题解决之后每一路的 EN 都做了同样的处理同时把综合工具的引脚约束检查加入流程。7.4 第四坑延时不够浪涌还是没压下来前面提到把六路错开可以降低浪涌但具体错开多少要实测。我最初设置的间隔是 5ms结果示波器上看到输入 12V 的跌落仍然有 600mV。分析后发现问题在于RAIL0 和 RAIL1 的输出电容都很大5ms 的间隔还不够让第一路完全建立、充电电流降到接近零第二路就开始了两路的充电电流还是有重叠。把间隔加到 20ms 之后12V 母线上的跌落降到 150mV 以内。这里的判断依据是间隔要大于上一路的软启动时间加上一点余量而不是拍脑袋定一个数。软启动时间从 DC-DC 手册查得到余量我留的是 50%。7.5 第五坑上电顺序和手册要求相反最惊险的一次是顺序写反。当时 RAIL0 和 RAIL1 的先后关系我凭印象写成了先 1.8V 再 1.0V而手册明确要求内核电源先上。结果板子上电后1.8V 先建立1.0V 后建立两个电源之间有大约 15ms 的反灌窗口。虽然没有当场烧掉但连续上电几十次之后一颗器件的静态电流明显变大说明已经受了暗伤。这次的教训不是技术问题而是流程问题电源顺序的唯一依据是器件手册里那张上电时序表任何凭经验、凭印象、凭别的项目都这么干的做法都是危险的。我后来把每一路的上电顺序都写进了参数表的注释里并在板上标了序号任何人改代码之前必须核对手册。8. 进阶玩法电压回读、裕度调节与健康监控功能跑通之后我陆续加了一些进阶能力。这些不是必须的但它们体现了 FPGA 方案相对专用时序芯片的价值所在。8.1 用 I2C 把每一路的电压电流读回来项目里的几路 DC-DC 支持通过 I2C 或 PMBus 回读输出电压、输出电流和状态字。我在 FPGA 里加了一个 I2C 主机控制器定时比如每 100ms轮询每一路的输出电压把结果存到寄存器里。这样做的收益是系统在正常工作时可以实时知道每一路电源的健康状态一旦某一路电压出现偏移可以提前报警而不是等它真的掉下去才发现。写 I2C 主机的时候有几个细节要注意一是时钟频率标准模式 100kHz、快速模式 400kHz时序控制里我选 100kHz牺牲一点速度换取稳定性二是总线仲裁和 NACK 处理如果某个器件不应答要及时退出不能让状态机卡死三是上拉电阻的取值400kHz 下通常用 2.2kΩ 到 4.7kΩ具体要看总线电容。8.2 用 ADC 做模拟量监控如果某些电源轨不支持 I2C 回读可以在板子上加一颗多通道 ADC把每路的电压分压之后送进 ADC由 FPGA 驱动转换并读回结果。这样一来即便最初级的电源也能被监控。ADC 的驱动逻辑比 I2C 简单主要注意转换启动、忙标志等待和数据对齐这几步。采集到的数据可以和 I2C 读回的做交叉验证互为冗余。8.3 掉电前的日志写入时序异常往往发生在掉电瞬间而那一刻最需要的就是之前的现场。我加了一块小容量 EEPROM把每一路的状态、最后的跳转时间戳、故障码在运行中持续写入。掉电后重新上电从 EEPROM 里读出上一次的记录就能知道是超时、掉电还是人为关断导致的。这个小功能在排查偶发故障时价值极高比反复用示波器抓波形效率高得多。8.4 裕度测试与动态调节有些高可靠性场景需要做电压裕度测试也就是把每一路电源的输出电压在 ±5% 范围内拉偏看系统是否还能正常工作。支持 PMBus 的电源可以用一条命令进入裕度模式FPGA 只需要发这条命令就行。更进阶的是动态电压调节比如在低负载时降低内核电压省电这需要电源时序和电压调节顺序配合核心原则还是先建立电源、后改电压先恢复电压、后关电源顺序不能乱。8.5 冗余与看门狗如果这块板子的可用性要求很高可以给时序控制加一个看门狗。外部 MCU 定时给 FPGA 一个心跳信号FPGA 一旦在设定时间内没收到心跳就自动走逆序关断并进入安全状态。这样即使上位机死机电源也不会一直保持在上电状态。反向的MCU 也可以监控 FPGA 的状态上报一旦 FPGA 报告故障由 MCU 决定是重试还是保持关断。我自己在这套系统上还留了一个习惯所有关键状态都做成一个状态字通过一个简单的串行接口或者几个 GPIO 输出到板上的一排指示灯。别小看这几个灯板子出现问题的时候现场工程师看一眼灯的位置就能大致判断卡在哪一步比翻文档、找探头快得多。指示灯和日志配合起来基本能让大多数故障在一次上电里定位清楚。关于延时的精度还有一个实测体会值得分享。数字计数器的延时精度取决于晶振而普通板载晶振的初始精度通常在 ±20ppm 到 ±50ppm温漂再叠加几十 ppm。对于毫秒级的电源时序这个误差完全可以忽略。但如果将来把这个模块用到微秒级甚至更精细的场景就必须考虑晶振精度的影响必要时换用 TCXO。这也是我坚持用周期数而不是外部 RC来表达延时的原因前者可预测、可复现后者受温度和批次影响跨批次一致性很差。