
树莓派CM4 的 PCIe 扩展一直是 DIY 圈子里热度不减的话题。CM4 本身引出了一路 PCIe Gen2 x1 接口理论带宽 5GT/s实际可用吞吐在 400MB/s 上下这个数字放在今天不算亮眼但胜在原生、稳定、免驱。问题在于这一路 PCIe 只有一条 lane而实际项目里我们往往想同时挂 NVMe 固态、2.5G 网卡、甚至多口 SATA 控制器。这时候就需要 PCIe Switch 出场了ASM1184e 就是这类场景里被讨论最多的芯片之一。它是一颗 4 口 PCIe Gen2 交换芯片可以把上游的一路 x1 拆成下游四路 x1让 CM4 在有限的带宽里分身接多个设备。这篇内容我会从 CM4 的 PCIe 引出方式讲起拆解 ASM1184e 的工作原理、硬件设计要点、枚举过程中的坑以及实际跑起来之后带宽怎么分配、哪些设备组合最稳。适合已经上手 CM4、准备做载板或者 M.2 HAT 原型的硬件玩家和嵌入式开发者参考。1. CM4 那一路 PCIe 到底能干什么1.1 从连接器引脚说起CM4 的 PCIe 信号是通过板对板连接器引出的具体位置在高密度接口的特定引脚上包含一对发送差分对TX、一对接收差分对RX、参考时钟REFCLK以及复位信号PERST#。这里有个容易被忽略的点CM4 引出的 PCIe 参考时钟是 100MHz 的差分时钟标准 PCIe 设备都认这个时钟但如果你要接的是一块需要独立时钟的板卡就得额外处理。另外CM4 的 PCIe 复位信号是低电平有效设计载板时要注意上拉电阻和时序否则设备可能枚举不到。很多人第一次做 CM4 载板时会犯一个错误把 PCIe 的 TX 和 RX 接反。PCIe 是点对点全双工总线主机的 TX 要接设备的 RX主机的 RX 要接设备的 TX交叉连接是必须的。我见过不止一个案例板子打回来之后设备死活认不到查了半天才发现是差分对没交叉。所以画原理图的时候TX± 和 RX± 一定要用网络标号明确标注方向别靠记忆。1.2 Gen2 x1 的真实带宽账CM4 的 PCIe 控制器支持 Gen2 x1编码方式是 8b/10b所以理论有效带宽是 5GT/s × 0.8 ÷ 8 500MB/s。但实际跑下来受协议开销、TLP 包头、ACK/NAK 机制影响顺序读写能到 380~420MB/s 就算不错了。这个数字意味着什么一块普通的 NVMe SSD 顺序读能到 2000MB/s 以上挂在 CM4 上只能发挥不到五分之一。所以如果你只是想要高速存储CM4 的 PCIe 并不是最优解USB 3.0 或者直接上 CM5 会更合适。但 PCIe 的价值不在于峰值带宽而在于低延迟和直连 CPU 的总线结构。做网络转发、数据采集、实时控制这类场景PCIe 的确定性延迟比 USB 强太多。我实测过用 CM4 加 PCIe 网卡做软路由ping 延迟比 USB 网卡低 0.3~0.5ms抖动也小得多。这就是为什么大家明知道带宽有限还是愿意折腾 PCIe 扩展。1.3 什么场景值得上 PCIe Switch单设备场景其实不需要 Switch直接把 CM4 的 PCIe 接到目标设备就行。但以下几种情况Switch 就变得很有必要需要同时接 NVMe 和网卡且两者都要走 PCIe 而不是 USB想做多盘位 NAS用一颗 PCIe 转 SATA 控制器再加一块系统盘做 PCIe 设备测试平台需要热插拔多个设备载板要兼容多种配置不想为每种组合单独设计ASM1184e 的定位正好卡在这些需求上。它是 4 口 Switch上游 x1下游四路 x1支持 Gen2功耗不高外围电路相对简单最重要的是在 Linux 下免驱内核自带的 pcieport 驱动就能识别。这一点对嵌入式开发太关键了不用折腾厂商驱动省下大量调试时间。2. ASM1184e 芯片的脾气与硬件设计细节2.1 芯片基本架构与供电要求ASM1184e 是一颗 4 端口 PCIe Gen2 交换芯片采用 128 引脚 LQFP 封装工作温度范围覆盖商业级和工业级。它内部包含一个上游端口和四个下游端口每个端口都是独立的 x1 lane。芯片内部有 Packet Switch 逻辑负责 TLP 的路由和转发支持 Cut-Through 和 Store-and-Forward 两种转发模式默认是 Cut-Through延迟更低。供电方面ASM1184e 需要 1.0V 核心电压和 3.3V IO 电压部分版本还需要 1.8V 辅助电压。这里有个坑1.0V 核心电压的纹波要求很严一般要求小于 50mV如果电源设计偷懒用了普通的 LDO 或者开关电源纹波太大芯片可能能枚举但跑大数据量时会出错。我建议核心供电用低噪声 LDO或者至少加足够的去耦电容每个电源引脚配 0.1uF芯片附近再放一个 10uF 的钽电容。注意ASM1184e 的 1.0V 和 3.3V 上电时序没有严格要求但建议 3.3V 先上、1.0V 后上避免 IO 引脚在核心未就绪时出现倒灌电流。2.2 参考时钟与复位电路ASM1184e 需要外部提供 100MHz 差分参考时钟。CM4 已经引出了这个时钟可以直接用但要注意走线阻抗控制在 100Ω 差分长度尽量短避免过孔。如果载板上有多颗 PCIe 设备时钟走线要做等长处理偏差控制在 5mil 以内。复位信号 PERST# 是低电平有效ASM1184e 的复位引脚需要接到 CM4 的 PERST# 上同时加一个 10kΩ 上拉电阻到 3.3V。有些设计会加一个复位按钮或者 GPIO 控制的复位电路方便调试。我个人的习惯是留一个手动复位跳线因为 PCIe 设备有时候枚举失败重新上电比重新插拔方便得多。2.3 下游端口的连接器选型ASM1184e 的四个下游端口可以接不同的连接器。常见的选择有连接器类型适用设备特点M.2 Key MNVMe SSD体积小免线缆但需要 3.3V 供电M.2 Key EWiFi 网卡尺寸更小但 PCIe lane 通常只有 x1Mini PCIe老式网卡、SATA 控制器兼容性好但体积大PCIe x1 插槽标准扩展卡灵活但占板面积大FFC 排线座定制设备适合紧凑设计但信号完整性难保证选型的时候要考虑机械强度和信号完整性。M.2 连接器虽然方便但插拔寿命有限做测试平台的话建议用标准 PCIe 插槽。FFC 排线座看起来省空间但 PCIe Gen2 的 5GT/s 信号对排线长度和阻抗很敏感超过 10cm 就容易出错非必要不用。2.4 电源分配与限流设计四个下游端口如果都接设备总功耗可能超过 CM4 载板的供电能力。M.2 NVMe SSD 峰值功耗能到 5W 以上WiFi 网卡也要 2~3W四个口全插满就是十几瓦。CM4 本身的供电是通过载板输入的一般建议载板输入至少 5V/5A如果设备多还要更高。我的做法是给每个下游端口单独加限流开关比如 TPS2553 或者类似的负载开关芯片每路限制在 1.5A 左右。这样即使某个设备短路也不会把整个系统拉垮。另外3.3V 电源的走线要足够宽1oz 铜厚的话至少 40mil否则压降会很明显。我测过一块设计不良的板子空载 3.3V 正常插上 NVMe 之后掉到 3.1VSSD 直接不认。3. PCIe 枚举过程与 ASM1184e 的配置空间3.1 枚举的基本流程PCIe 枚举是系统启动时由 RCRoot Complex发起的配置空间扫描过程。CM4 的 PCIe 控制器作为 RC上电后会从 Bus 0、Device 0、Function 0 开始逐个读取配置空间的 Vendor ID 和 Device ID。如果读到有效值就分配 Bus 号和地址空间然后继续扫描下一级。ASM1184e 作为 Switch在枚举时表现为一个 PCIe-to-PCI 桥的结构。上游端口先被 RC 识别然后 Switch 内部的虚拟桥会触发下游端口的扫描。这个过程是递归的所以你会看到 lspci 输出里 ASM1184e 下面挂着几个设备Bus 号是递增的。3.2 常见枚举失败原因枚举失败是 CM4 PCIe 扩展里最常见的问题表现是 lspci 看不到设备或者只看到 Switch 看不到下游设备。根据我的排查经验原因大致分这几类物理连接问题差分对接反、虚焊、连接器接触不良。用万用表打一下 TX/RX 的通断确认没有短路或开路。参考时钟缺失CM4 的时钟输出没使能或者走线断了。用示波器测一下时钟引脚应该有 100MHz 差分信号。复位信号异常PERST# 一直处于复位状态或者上拉电阻没焊。测一下复位引脚电压正常应该是 3.3V。电源问题核心电压没起来或者纹波太大。测一下 1.0V 和 3.3V确认在规格范围内。配置电阻错误ASM1184e 有些配置引脚需要上拉或下拉来设定工作模式比如端口数量、转发模式等。查数据手册确认。我遇到过一次很隐蔽的情况板子单独供电时枚举正常但插上 CM4 之后就不行。后来发现是 CM4 的 PCIe 时钟输出默认关闭需要在 config.txt 里加一行dtparampcieon才能使能。这个坑在官方文档里写得不明显但社区里讨论很多。3.3 配置空间里的关键寄存器ASM1184e 的配置空间里有几个寄存器值得关注Link Control Register可以查看和设置链路速率、ASPM 电源管理策略。如果设备不稳定可以尝试关闭 ASPM。Link Status Register显示当前链路速率和宽度确认是否协商到了 Gen2 x1。Secondary Bus Reset可以通过这个位对下游总线做复位调试时很有用。在 Linux 下可以用setpci命令读写这些寄存器。比如查看链路状态setpci -s 01:00.0 CAP_EXP12.w这个命令会返回 Link Status 寄存器的值bit 0-3 是链路速率bit 4-9 是链路宽度。如果读出来是 0x11说明协商到了 Gen2 x1正常。3.4 枚举日志的解读Linux 启动时内核会把 PCIe 枚举过程打到 dmesg 里。正常情况你会看到类似这样的输出pcieport 0000:00:00.0: PME: Signaling with IRQ 38 pcieport 0000:00:00.0: AER: enabled with IRQ 38 pcieport 0000:01:00.0: bridge configuration invalid ([bus 02-00]), reconfiguring pcieport 0000:01:00.0: PCI bridge to [bus 02-05]如果看到bridge configuration invalid或者link training failed说明枚举过程中有问题。前者通常是 Switch 的下游总线号分配冲突后者是链路训练失败多半是信号完整性问题。4. 实际跑起来之后的带宽分配与设备组合4.1 四口共享 x1 上游的带宽现实ASM1184e 的上游只有 x1四个下游端口共享这 500MB/s 的理论带宽。这意味着如果同时跑两个 NVMe SSD每个只能分到 250MB/s 左右比单盘还慢。所以设备组合要讲究策略不能盲目堆数量。我的建议是把高带宽设备放在优先级最高的端口低带宽设备放在后面。比如 NVMe 系统盘接 Port 12.5G 网卡接 Port 2SATA 控制器接 Port 3这样系统盘能优先拿到带宽。ASM1184e 内部有仲裁机制但具体策略不可配所以物理端口的选择会影响实际表现。4.2 实测数据不同组合的吞吐表现我用 CM4 ASM1184e 载板做了一组测试设备组合和实测吞吐如下设备组合顺序读顺序写备注单 NVMe380MB/s350MB/s接近理论上限NVMe 2.5G 网卡280MB/s260MB/s网卡空闲时NVMe 2.5G 网卡180MB/s160MB/s网卡跑满 2.5G双 NVMe190MB/s170MB/s两盘同时读写NVMe SATA 控制器 网卡150MB/s140MB/s三设备并发从数据看单设备时能发挥 CM4 PCIe 的绝大部分性能但设备一多带宽分摊就很明显。所以如果你的应用对存储性能要求高建议只挂一块 NVMe其他设备走 USB 或者以太网。4.3 哪些设备组合最稳经过多次测试我发现以下几种组合稳定性最好NVMe WiFi 6 网卡WiFi 网卡带宽需求低对 NVMe 影响小适合做无线 NAS。NVMe 2.5G 网卡轻载网卡不跑满时NVMe 还能保持 280MB/s 左右。单 SATA 控制器 多盘SATA 控制器本身带宽需求不高多盘顺序读写能跑满 x1 上游。不太推荐的组合双 NVMe 同时高负载互相抢带宽体验很差。多口 2.5G 网卡 NVMe网卡跑满时 NVMe 几乎不可用。PCIe 转 USB 控制器 其他设备USB 控制器本身有延迟叠加 Switch 之后更明显。4.4 散热与长期稳定性ASM1184e 本身功耗不高满载大概 1W 左右但 NVMe SSD 发热很大。如果载板空间紧凑建议给 SSD 加散热片Switch 芯片下面铺铜散热。我测过一块没有散热措施的板子连续跑 2 小时之后 SSD 温度到 75°C开始降速。加了散热片之后稳定在 55°C性能不掉。另外CM4 的 PCIe 控制器在高温下也可能不稳定。如果载板放在密闭外壳里建议加一个小风扇或者至少留通风孔。长期跑的话温度每升高 10°C电子元件寿命大概减半这个账要算清楚。5. 调试过程中踩过的坑与排查链路5.1 设备时认时不认信号完整性问题有一块载板冷启动时能认到 NVMe但重启几次之后就找不到了。一开始怀疑是驱动问题换了内核版本、改了 config.txt都没用。后来用示波器测 PCIe 差分信号发现眼图很差上升沿有明显的振铃。查PCB发现差分对走线没有做阻抗控制而且有一段并行走线太长串扰严重。解决办法是重新设计走线差分对严格 100Ω 阻抗长度控制在 5cm 以内远离电源和时钟线。改板之后问题消失。这个坑告诉我PCIe Gen2 虽然速率不算高但信号完整性不能马虎尤其是经过连接器和排线之后。5.2 枚举到 Switch 但下游设备缺失另一种常见情况是 lspci 能看到 ASM1184e但下面的设备一个都没有。这种情况多半是下游端口的电源没起来或者复位信号没释放。我遇到过一次是下游端口的 3.3V 限流开关被触发因为 SSD 上电瞬间电流太大。把限流阈值从 1A 调到 1.5A 之后正常。还有一种可能是 Switch 的配置引脚设错了。ASM1184e 有几个引脚用来设定下游端口数量如果设成 2 口模式另外两个口就不工作。查数据手册确认引脚状态该上拉的上拉该下拉的下拉。5.3 带宽远低于预期如果设备能认到但测速只有 100MB/s 出头首先要确认链路速率。用lspci -vv查看 LnkSta如果显示 2.5GT/s 而不是 5GT/s说明只协商到了 Gen1。原因可能是信号质量差或者 Switch 配置成了 Gen1 模式。检查 ASM1184e 的速率配置引脚确认设成 Gen2。另一个原因是 ASPM 电源管理。有些设备在 ASPM 开启时会频繁进入低功耗状态导致吞吐下降。可以在内核启动参数里加pcie_aspmoff关闭 ASPM测一下是否有改善。如果改善了说明是 ASPM 兼容性问题可以在配置空间里针对特定设备关闭 ASPM。5.4 完整排查链路总结遇到 PCIe 扩展问题时我一般按这个顺序排查看电源测 1.0V、3.3V、1.8V 是否正常纹波是否超标。看时钟测 100MHz 差分时钟是否有输出幅度是否正常。看复位测 PERST# 是否释放电压是否到 3.3V。看链路用 lspci 确认链路速率和宽度是否协商到 Gen2 x1。看配置检查 ASM1184e 的配置引脚确认工作模式正确。看信号如果以上都正常但还不稳定用示波器看差分信号质量。看散热长时间跑测试确认温度在合理范围。这个顺序是从简单到复杂从电源到信号大部分问题在前三步就能定位。6. 从原型到量产载板设计的几点经验6.1 层叠结构与阻抗控制如果要做 CM4 ASM1184e 的载板建议至少用 4 层板。顶层走信号第二层完整地平面第三层电源底层走低速信号和电源。PCIe 差分对走在顶层参考第二层地平面阻抗控制在 100Ω±10%。如果预算允许用 6 层板更好可以把电源和地分开信号完整性更有保障。走线长度方面CM4 到 ASM1184e 的上游链路尽量短最好在 3cm 以内。下游端口到连接器的走线也要控制M.2 连接器的话 5cm 以内标准 PCIe 插槽可以放宽到 10cm。过孔尽量少每个差分对最多两个过孔而且过孔要加回流地孔。6.2 连接器与机械结构M.2 连接器的选型要注意高度和固定方式。有些连接器需要螺丝固定有些是卡扣式。如果载板要装在标准外壳里连接器高度要和外壳开孔匹配。我见过有人做完板子才发现 M.2 连接器太高外壳盖不上只能重新打板。另外CM4 的板对板连接器有高度选项常见的是 3mm 和 5mm。如果载板上元件多建议用 5mm 的留出足够的空间。但要注意连接器越高PCIe 信号走线越长信号完整性越难保证。这是个权衡需要根据实际情况取舍。6.3 测试点与调试接口做原型板的时候一定要留测试点。PCIe 的 TX、RX、时钟、复位这些关键信号都要引出测试点方便用示波器测量。我习惯在差分对旁边放两个小焊盘间距 1mm可以直接焊探针。另外留一个 UART 调试口CM4 的串口输出能看到内核启动日志排查问题很方便。如果空间允许可以加一个 PCIe 插槽的电源开关方便单独给下游设备断电。调试的时候经常需要重新枚举设备有独立开关就不用反复插拔了。6.4 成本与选型平衡ASM1184e 本身价格不算贵但外围电路和 PCB 成本不低。如果只是接一个设备完全没必要上 Switch直接走线就行。如果接两个设备可以考虑用两颗 PCIe 缓冲器或者简单的多路复用器成本更低。只有需要三个以上设备或者需要灵活配置的时候Switch 才划算。另外ASM1184e 有商业级和工业级版本温度范围不同价格差不少。如果只是室内使用商业级就够了。工业级适合户外或者车载场景但要注意 CM4 本身的工作温度范围别出现 Switch 能扛住但 CM4 扛不住的情况。7. 一些零散但有用的实操技巧7.1 config.txt 里的 PCIe 相关参数CM4 的 PCIe 默认是关闭的需要在/boot/config.txt里加一行dtparampcieon如果要强制 Gen2 速率可以加dtparampcie_gen2但要注意强制 Gen2 可能会让一些信号质量不好的板子枚举失败。如果遇到问题可以先设成 Gen1 确认能认到设备再逐步提高到 Gen2。7.2 用 lspci 和 setpci 做快速诊断lspci -vv能看到设备的详细配置空间包括链路状态、电源管理、中断分配。重点看 LnkCap 和 LnkSta 两行确认速率和宽度。setpci可以读写配置空间调试时很有用。比如手动触发下游总线复位setpci -s 01:00.0 BRIDGE_CONTROL40:40这个命令会把 Secondary Bus Reset 位置 1复位下游设备。等几秒后再写回 0设备会重新枚举。7.3 内核参数调整如果遇到 PCIe 设备不稳定可以尝试在内核命令行加pcie_aspmoff pcinoaerpcie_aspmoff关闭 ASPM 电源管理pcinoaer关闭高级错误报告。这两个参数能解决一部分兼容性问题但会牺牲一些功耗优化。生产环境慎用调试阶段可以试试。7.4 热插拔的注意事项PCIe 原生支持热插拔但 ASM1184e 的热插拔需要额外的电源控制和存在检测信号。如果载板没做这些电路热插拔可能导致系统崩溃。我的建议是除非专门设计了热插拔电路否则不要在运行时插拔 PCIe 设备。关机再插虽然麻烦但安全。7.5 和 CM5 的对比CM5 已经出来了PCIe 升级到了 Gen3 x1带宽翻倍到 1GB/s 左右。如果现在做新项目预算允许的话建议直接上 CM5。但 CM4 的生态更成熟载板设计资料多ASM1184e 的参考设计也丰富。如果是学习或者小批量项目CM4 还是够用的。等 CM5 的载板方案成熟了再迁移也不迟。整体做下来CM4 ASM1184e 这套方案适合对带宽要求不高、但需要多设备 PCIe 扩展的场景。硬件设计上把电源、时钟、信号完整性这三块做好软件上基本免驱调试主要靠 lspci 和示波器。我个人的体会是PCIe 扩展的坑大多在硬件层面软件反而简单。把板子画好比什么都重要。