
板子拿回来的当天晚上我就把串口线怼上了。PM40028这颗PCIe Gen4 Switch芯片的调试口在UART0板上用CH340G转USB电脑上开着串口调试助手115200、8N1按下电源开关3.3V、1.8V、1.0V、0.9V四路电源按顺序爬升整板电流也正常。可串口助手界面干干净净一个字节都没吐出来。更诡异的是电源、时钟、复位看着全对SPI Flash却一动不动——芯片既没死也没活就像僵在那里等什么人发话。这篇文章就把这次从boot引脚设置到串口无输出的完整踩坑过程记录下来适合正在做PCIe Switch相关板卡的硬件和固件工程师参考。1. 这块板卡为什么需要一颗PCIe Gen4 Switch1.1 项目背景把CPU的x16拆成三个独立NVMe端口我们做的是一个存储扩展卡挂在服务器主板上给系统额外扩展出三个U.2 NVMe盘位。主CPU的PCIe根端口是x16如果直接把三块盘接到一个x16上就得做bifurcation把x16拆成x4x4x4x4但这依赖CPU平台对bifurcation的支持程度而且每个盘都要单独做热插拔电源管理和错误隔离根端口根本管不过来。用PCIe Switch是最省心的方案PM40028的上行口接CPU的x16下行口拆成三个x4每个x4接一个U.2盘位。这样CPU看到的就是一个标准PCIe设备盘的插拔、复位、电源控制全交给Switch来处理。PCIe Gen4单条lane速率是16GT/s用128b/130b编码实际有效带宽大约1.969GB/sx16上行总带宽超过31GB/s给三块Gen4 SSD跑满绰绰有余。1.2 PM40028的选型理由与板级架构选PM40028主要是看中它的28条lane配置灵活刚好可以做成x16上行加三个x4下行一颗芯片解决所有问题。同类的候选还有Broadcom PEX880xx系列和Microchip Switchtec系列但PM40028在成本、供货和固件功能上更合适我们这批货的定位。它支持标准PCIe枚举、热插拔事件上报、SMBus带外管理还有一套完整的串口调试命令这在后续调链路时非常重要。板卡架构不复杂主机端x16连接器进PM40028上行口下行三个x4走内层差分线到三个U.2连接器PM40028的启动固件存在一颗W25Q128JV128Mbit SPI NOR Flash里上电时芯片自己从Flash读固件板上的复位时序由一个电源时序控制器产生REFCLK 100MHz差分时钟从主板引入或由板载时钟buffer分配调试串口UART0通过CH340G转USB引到面板。1.3 上电前的核对清单我们以为万无一失投板之前我们按照参考设计核过好几遍电源rails按数据手册顺序接、REFCLK加了交流耦合电容、PERST#时序由时序控制器保证、SPI Flash提前用烧录器写好了固件。boot引脚的设置也按参考设计配了——BOOT_SEL[1]上拉到3.3VBOOT_SEL[0]下拉到GND10k电阻。上电前我还专门用万用表量了一遍这两个引脚一个3.29V一个0.00V心想这肯定没毛病。现在回头看恰恰是这几颗10k电阻和一个不起眼的滤波电容把整个首版调试节奏拖慢了整整两天。2. Boot引脚设置第一道坎就把我们卡住了2.1 什么是strap引脚为什么它只在复位释放瞬间有效Strap引脚也叫boot引脚、配置引脚是芯片上电时用来决定初始行为的一组引脚常见的有boot源选择、link宽度配置、lane翻转方向、console接口选择等。这类引脚和普通信号有个本质区别它只在芯片内部复位释放的那一瞬间被采样一次之后无论电平怎么变芯片内部已经锁存的值都不会再更新直到下一次复位。可以类比成电梯的叫梯按钮你只在进电梯前按一次进去之后按哪个楼层才是你真正要去的地方。strap引脚就是那个叫梯按钮按下瞬间的楼层电平决定了这次运行从哪一层开始。所以调strap问题的时候永远要记住一句话连续电平没有意义采样瞬间的电平才有意义。2.2 参考设计里的boot模式与我们的实现差异PM40028的Hardware User Guide里对BOOT_SEL[1:0]的编码我们用的这套参考设计是这样的BOOT_SEL[1]BOOT_SEL[0]启动模式10从本地SPI NOR Flash启动00等待主机通过SMBus下发固件11保留01保留所以正确配置应该是BOOT_SEL[1]为高、BOOT_SEL[0]为低也就是10。我们板子上的实现是BOOT_SEL[1]通过10k电阻上拉到3.3VBOOT_SEL[0]通过10k电阻下拉到GND同时在BOOT_SEL[1]上对地加了一个10nF的滤波电容。当时加这个电容是出于抗干扰的习惯——strap线在板内走线较长怕被旁边的高速信号串扰。这个习惯后来成了最大的雷。另外还有一个CONSOLE_IF strap引脚参考设计上明确写着要上拉到3.3V才能把调试console放到UART0。我们画板的时候觉得反正默认也应该能用就给它留了NC不接。这一留又埋了第二个雷。2.3 用万用表和示波器初步检查strap电平的差别上电前用万用表量strap电平量出来是好的BOOT_SEL[1]有3.29VBOOT_SEL[0]是0.00V完全符合预期。但万用表有个致命的盲区——它量的是几百毫秒时间尺度上的平均值看不到上电最初那几百微秒里发生了什么。正确的检查方法是用示波器把触发点设在复位信号PERST#或POR的上升沿同时探头打在strap引脚上观察复位释放瞬间该引脚到底处于什么电平。这一步我们当时没做结果多花了整整一天。后面章节会详细说为什么这个瞬间检查如此致命。3. 串口无输出的完整排查链路从电源查到SMBus3.1 症状描述与最开始的猜测症状就是标题说的串口完全无输出。打开串口调试助手用的是xcom后来也试了Linux下的minicom115200、8N1发送和接收都正常配置按下电源键接收区一个字节都没有。最开始我怀疑是USB转串口小板的问题——CH340G在Windows 11上有时候驱动不对设备管理器里甚至不识别换驱动、换COM口号折腾了二十分钟。换了一块FT232小板故障依旧。接着怀疑TX、RX是不是接反了或者CH340G的电平转换有问题。拿示波器直接点到PM40028的UART0_TX引脚上发现是一个稳定的3.3V高电平。UART空闲态本来就是高电平所以这个波形只能说明两种情况要么芯片已经配置好UART但没有数据要发要么这个引脚压根没有被配置成UART功能。这一下子信息量很低必须继续往上游查。3.2 电源与时序检查一切正常先把四条电源rail挨个量了一遍Rail目标电压实测电压纹波3V3_AUX3.3V3.31V24mV1V81.8V1.79V15mV1V0_CORE1.0V1.01V12mV0V9_SERDES0.9V0.90V11mV纹波和电压都在规格内上电顺序也和参考设计一致3V3_AUX先到1V8随后1V0_CORE和0V9_SERDES最后相邻间隔大约3ms没有反序、没有毛刺。整板电流大概1.4A符合PM40028内核加SerDes的典型功耗。电源这块可以排除。3.3 REFCLK与复位检查波形看着没问题PM40028的REFCLK是100MHz差分时钟用示波器看差分波形幅度大约700mVpp频率锁定在100MHz上上升沿干净没有明显抖动。虽然500MHz的示波器看不了PCIe Gen4那种精密眼图但REFCLK有没有起振、幅度够不够这个级别的检查足够了。复位信号PERST#也查了电源OK之后20ms拉高上升沿大约8µs没有误触发抖动。按参考设计这个参数完全合格。到这里电源、时钟、复位三大件全过了芯片却依然一声不吭。3.4 SPI Flash毫无动静关键转折点真正把方向扭转过来的是示波器点到SPI Flash的CS#引脚。上电之后CS#一直保持高电平CLK上也没有任何脉冲MISO和MOSI同样安静。也就是说PM40028从上电到我观察的这几秒钟里压根没有尝试去读Flash里的固件。这个现象直接推翻了Flash固件损坏导致起不来的猜测——芯片根本没去读Flash谈不上固件好坏。那么只剩下两种可能一是芯片本身没工作电源、时钟、复位没起来但前面检查全部通过了二是芯片工作了但boot模式不是本地SPI Flash启动而是进入了等待主机下发固件的SMBus host boot模式。第二种可能性我们当时第一反应是不信的因为万用表明明量到BOOT_SEL[1]是3.3V。3.5 挂上SMBus分析仪芯片其实活着为了验证芯片到底有没有活我拿了一台SMBus分析仪并联在PM40028的SMBus从站接口上观察上电后有没有总线活动。如果芯片进入SMBus host boot模式它理论上会对外呈现一个可访问的从站地址等待主站下发完整固件。实测发现SMBus上确实没有任何主站主动发起传输——因为根本没有主机去跟它通信于是芯片就一直挂在那个等待命令的状态里不读Flash、不初始化UART、不亮LED。这解释了所有现象芯片活着但在等一个永远不会来的主人。4. 真正的坑strap线上的RC延时和芯片的采样窗口4.1 为什么10k上拉和10nF滤波电容会惹祸回到BOOT_SEL[1]这根线。前面说了这根线上有一枚10nF对地电容是当初为了抗EMC干扰加上的。10k上拉电阻负责把引脚拉到高电平10nF电容负责把引脚上的干扰滤掉。单独看每个器件都没问题但组合在上电瞬间就变成了一个杀人组合。上电时3.3V rail爬升到稳定值BOOT_SEL[1]引脚要通过10k电阻给10nF电容充电电压才能从0V慢慢爬到3.3V。这个充电过程不是瞬间完成的而是遵循RC指数曲线。10k上拉和10nF电容组成的RC时间常数τ R × C 10kΩ × 10nF 100µs也就是说引脚电压要经过大约100µs才能充到3.3V的63.2%也就是大约2.09V要充到接近3.3V需要5τ也就是500µs。而PM40028内部逻辑在复位释放后的几十微秒内就会完成strap采样。以复位释放后50µs为例引脚电压按RC充电公式算V(t) Vcc × (1 - e^(-t/τ)) 3.3 × (1 - e^(-50/100)) 3.3 × 0.393 ≈ 1.30V1.30V对于典型VIH在2.0V上下的strap输入来说就是一个确定的逻辑低电平。芯片在50µs这个瞬间采到的BOOT_SEL[1]是0BOOT_SEL[0]本来就是0于是它认为自己收到的配置是00——SMBus host boot模式乖乖等主机下固件去了。4.2 计算过程复位释放后100µs引脚才勉强够到阈值再往后推一步芯片采样点如果正好落在复位释放后80µs引脚电压是V(80µs) 3.3 × (1 - e^(-80/100)) 3.3 × 0.551 ≈ 1.82V还是低于2.0V的VIH典型值。要到复位释放后100µs电压才勉强到2.09V卡在VIH边上。算上10k电阻的5%误差、10nF电容的20%误差以及不同板卡之间温度差异这个设计在采样窗口内几乎所有情况都会读到低电平。换句话说这不是一个偶尔踩雷的bug而是一个必现的配置错误只是它藏得太深不到上电瞬间看不出来。横向对比一下如果把上拉电阻改成2.2k、电容去掉只留100pF时间常数变成τ 2.2kΩ × 100pF 0.22µs同样在复位释放后10µs采样引脚早就充到接近3.3V了余量大到芯片怎么采样都不会出错。4.3 修复方案三个动作一起做确认根因之后改板上来不及我们先在现有板子上飞线验证做了三个动作把BOOT_SEL[1]的10k上拉电阻换成2.2k。参考设计本来推荐1k到4.7k我们当初图省事统一用了10k这次吃到教训。拆掉BOOT_SEL[1]上的10nF对地电容。实在想保留滤波最多用100pF不能再大。strap线属于上电瞬间就要稳定到位的信号任何对地电容都是在跟采样窗口抢时间。在电源时序控制器里把PERST#的延迟拉长到100ms。虽然外部复位延迟不一定直接影响芯片内部POR采样但对下游设备和整体上电时序更安全也能给strap线留出更充分的稳定时间。飞线改完再上电神奇的一幕出现了SPI Flash的CS#开始出现连续的读脉冲整颗W25Q128JV被正常访问UART0_TX也终于吐出了字符。第一个坑到此填完。4.4 修复后还踩的第二个坑console选择strap悬空本来以为到这里就结束了结果UART上出来的并不是完整的启动日志而是一堆乱码之后又归于沉寂。仔细一看Flash读取确实正常了说明芯片已经从本地SPI启动并执行固件但console没有正常输出到UART0。这次学聪明了直接回头查CONSOLE_IF这个strap引脚。它被我们留了NC芯片内部默认下拉默认值是console不走UART——固件启动了但日志发到了SMBus mailbox里UART0引脚保持着GPIO功能的高电平自然什么都打印不出来。用一根飞线把CONSOLE_IF拉到3.3V重新上电完整启动日志整整齐齐地出现在串口助手里。第二个坑告诉我们另一个道理所有strap引脚要么明确接高要么明确接低永远不要留NC赌默认值。芯片厂商的默认值往往是最安全但最少功能的配置跟你要的调试模式大概率不是一回事。5. 验证结果、完整启动日志与后续防坑清单5.1 修复后的正常启动日志长什么样两个strap都改对之后串口输出如下节选PM40028 Bootloader v1.04 PM40028 Management Firmware v2.3.0 Build time: 2024-05-16 08:22 SPI Flash: W25Q128JV 50MHz, 16MB Console: UART0, 115200, 8N1 SMBus slave address: 0x74 Port Width LaneRange Status 0(UP) x16 [0:15] Gen4 1 x4 [16:19] Gen4 2 x4 [20:23] Gen4 3 x4 [24:27] Gen4 Upstream port link training started...看到这些日志整个板子的状态才算真正掌握在手里。后面调PCIe链路训练、配置端口属性、观察Gen4协商速率全靠这个console。回头想那两天无输出的沉默根源其实就是几个电阻电容的取值和一个悬空的引脚。5.2 给下一版硬件和后续调试的注意事项这次踩完坑我把strap相关的检查项整理成了一张清单下一版投板之前必须逐项过检查项要求我们这次的教训strap上拉/下拉阻值1k~4.7k禁止10k10k配合寄生电容直接导致采样错误strap线对地电容尽量不加最多100pF10nF让电平在采样窗口内没有建立完成strap引脚终端每个strap必须显式接高/接低CONSOLE_IF悬空导致console被禁用strap检查手段示波器触发在复位上升沿测量万用表只能看稳态看不到采样瞬间复位释放时刻电源OK后留足strap稳定时间时序控制器里加100ms延迟更稳另外还有一条针对串口烧写失败的提醒如果你拿到一颗PM40028想通过串口/SPI烧写固件先确认芯片当前boot模式是本地Flash启动还是SMBus启动。很多串口烧写失败其实不是串口本身的问题而是芯片根本没进入允许烧写的模式或者console接口选择strap没拉对调试助手开了半天芯片压根没在听。5.3 关于PCIe Gen4 Switch调试的几个通用建议最后说几点这次调试之后沉淀下来的通用经验。PCIe Gen4跑在16GT/s上信号完整性压力比Gen3大很多但那是后话首版调试最先遇到的往往不是高速信号问题而是低速控制信号和strap配置问题。电源、时钟、复位、strap、Flash这五样东西任何一样不对芯片都可能表现得像完全没工作但实际原因千差万别。遇到串口无输出不要急着怀疑SerDes眼图先把scope触发在复位上升沿把每个strap在采样窗口内的实际电平拍下来比什么排查手段都管用。SMBus是非常好的第二调试通道。很多PCIe Switch即使起不来SMBus接口仍然有反应通过SMBus至少能确认芯片电源和内部逻辑是否活着。我们这次就是靠着SMBus上没人跟它说话、SPI Flash又没动静这两个现象才把问题锁定在boot模式上。还有一个小习惯我现在一直保留每次上电前拿示波器把关键strap在复位释放瞬间的电平存一张波形图和设计值对照存档。这样哪怕板子过了很长时间再出问题也能快速排除strap这个大类不用重新走一遍整套排查流程。毕竟板子沉默不可怕可怕的是你不知道它为什么沉默。