树莓派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 M | NVMe SSD | 体积小,免线缆,但需要 3.3V 供电 |
| M.2 Key E | WiFi 网卡 | 尺寸更小,但 PCIe lane 通常只有 x1 |
| Mini 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.1V,SSD 直接不认。
3. PCIe 枚举过程与 ASM1184e 的配置空间
3.1 枚举的基本流程
PCIe 枚举是系统启动时由 RC(Root 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 里加一行dtparam=pcie=on才能使能。这个坑在官方文档里写得不明显,但社区里讨论很多。
3.3 配置空间里的关键寄存器
ASM1184e 的配置空间里,有几个寄存器值得关注:
- Link Control Register:可以查看和设置链路速率、ASPM 电源管理策略。如果设备不稳定,可以尝试关闭 ASPM。
- Link Status Register:显示当前链路速率和宽度,确认是否协商到了 Gen2 x1。
- Secondary Bus Reset:可以通过这个位对下游总线做复位,调试时很有用。
在 Linux 下可以用setpci命令读写这些寄存器。比如查看链路状态:
setpci -s 01:00.0 CAP_EXP+12.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 1,2.5G 网卡接 Port 2,SATA 控制器接 Port 3,这样系统盘能优先拿到带宽。ASM1184e 内部有仲裁机制,但具体策略不可配,所以物理端口的选择会影响实际表现。
4.2 实测数据:不同组合的吞吐表现
我用 CM4 + ASM1184e 载板做了一组测试,设备组合和实测吞吐如下:
| 设备组合 | 顺序读 | 顺序写 | 备注 |
|---|---|---|---|
| 单 NVMe | 380MB/s | 350MB/s | 接近理论上限 |
| NVMe + 2.5G 网卡 | 280MB/s | 260MB/s | 网卡空闲时 |
| NVMe + 2.5G 网卡 | 180MB/s | 160MB/s | 网卡跑满 2.5G |
| 双 NVMe | 190MB/s | 170MB/s | 两盘同时读写 |
| NVMe + SATA 控制器 + 网卡 | 150MB/s | 140MB/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_aspm=off关闭 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里加一行:
dtparam=pcie=on如果要强制 Gen2 速率,可以加:
dtparam=pcie_gen=2但要注意,强制 Gen2 可能会让一些信号质量不好的板子枚举失败。如果遇到问题,可以先设成 Gen1 确认能认到设备,再逐步提高到 Gen2。
7.2 用 lspci 和 setpci 做快速诊断
lspci -vv能看到设备的详细配置空间,包括链路状态、电源管理、中断分配。重点看 LnkCap 和 LnkSta 两行,确认速率和宽度。setpci可以读写配置空间,调试时很有用。比如手动触发下游总线复位:
setpci -s 01:00.0 BRIDGE_CONTROL=40:40这个命令会把 Secondary Bus Reset 位置 1,复位下游设备。等几秒后再写回 0,设备会重新枚举。
7.3 内核参数调整
如果遇到 PCIe 设备不稳定,可以尝试在内核命令行加:
pcie_aspm=off pci=noaerpcie_aspm=off关闭 ASPM 电源管理,pci=noaer关闭高级错误报告。这两个参数能解决一部分兼容性问题,但会牺牲一些功耗优化。生产环境慎用,调试阶段可以试试。
7.4 热插拔的注意事项
PCIe 原生支持热插拔,但 ASM1184e 的热插拔需要额外的电源控制和存在检测信号。如果载板没做这些电路,热插拔可能导致系统崩溃。我的建议是,除非专门设计了热插拔电路,否则不要在运行时插拔 PCIe 设备。关机再插,虽然麻烦,但安全。
7.5 和 CM5 的对比
CM5 已经出来了,PCIe 升级到了 Gen3 x1,带宽翻倍到 1GB/s 左右。如果现在做新项目,预算允许的话建议直接上 CM5。但 CM4 的生态更成熟,载板设计资料多,ASM1184e 的参考设计也丰富。如果是学习或者小批量项目,CM4 还是够用的。等 CM5 的载板方案成熟了再迁移也不迟。
整体做下来,CM4 + ASM1184e 这套方案适合对带宽要求不高、但需要多设备 PCIe 扩展的场景。硬件设计上把电源、时钟、信号完整性这三块做好,软件上基本免驱,调试主要靠 lspci 和示波器。我个人的体会是,PCIe 扩展的坑大多在硬件层面,软件反而简单。把板子画好,比什么都重要。