1. 项目缘起与整体设计思路
1.1 为什么要在2024年重新审视PEX8748
PEX8748这颗芯片在PCIe交换领域算是老面孔了,博通(原PLX)的第三代PCIe交换芯片,支持PCIe Gen3 x48通道,最多可以配置成12个x4端口或者6个x8端口。放在Gen4/Gen5满天飞的今天,很多人第一反应是"这芯片是不是过时了"。但实际做存储扩展方案的人都知道,Gen3 x4的带宽是3.938 GB/s(8b/10b编码后约3.2 GB/s有效带宽),对于单块NVMe SSD来说,除非是高端企业级盘,否则顺序读写很难跑满这个带宽。换句话说,用PEX8748做存储桥接,在成本、功耗、兼容性三个维度上依然有非常强的竞争力。
我这次做的这块卡,定位很明确:一张单槽位、被动散热、无需外接供电的PCIe Gen3存储桥接卡,上行是x8(接主机),下行拆成两个SlimSAS 4i接口(SFF-8654),每个SlimSAS再通过线缆分出4个x1或者直接接背板。整卡的核心诉求是让一台普通服务器或者工作站,在不换主板的前提下,把NVMe盘位从板载的2个M.2扩展到8个甚至更多。
1.2 核心需求拆解
在动手画原理图之前,我把需求拆成了下面几条,这几条直接决定了后续所有的选型和布局决策:
- 上行链路:PCIe Gen3 x8,金手指供电,不需要外接6pin。这意味着整卡功耗必须控制在25W以内(x8插槽理论供电75W,但被动散热卡实际安全范围在20W左右)。
- 下行端口:两个SlimSAS 4i(每个4 lane),支持SFF-8654线缆。为什么选SlimSAS而不是OCuLink?因为SlimSAS 4i的线缆在服务器市场更常见,而且可以向下兼容SATA/SAS背板(通过协议切换),虽然我们这里只跑NVMe。
- 时钟方案:PEX8748需要外部100MHz参考时钟,同时每个下行端口需要独立的时钟输出给SSD。这里有个坑,后面会详细讲。
- 复位与热插拔:每个端口需要独立的PERST#和热插拔信号,否则一块盘挂了可能拖累整个链路。
- 配置方式:通过I2C或者SPI加载配置,我选了SPI EEPROM,因为可以现场改配置不用重新流片。
1.3 方案选型的几个关键取舍
为什么不用PEX8724?PEX8724是x24通道,最多6个x4端口,价格便宜不少。但它的上行只能配到x8,下行如果拆成6个x4,总带宽就超了。而且8724的封装更小,散热面积不够,跑满负载时结温容易超标。8748的x48通道给了足够的余量,我可以把上行配成x16(虽然金手指只走x8),下行留出更多灵活配置空间。
为什么不用Gen4的芯片?成本。PEX88048(Gen4 x48)的价格是8748的三倍以上,而且Gen4的PCB板材要求更高(需要低损耗板材),整卡BOM成本直接翻倍。对于存储扩展这种场景,Gen3 x4的带宽已经能喂饱绝大多数NVMe盘,除非你要做全闪阵列的缓存池,否则Gen4的溢价不划算。
为什么选SlimSAS而不是M.2直插?M.2直插看起来方便,但一张卡上最多放4个M.2(2280尺寸),而且M.2的连接器高度限制导致散热器没法做大。SlimSAS通过线缆引出,机箱内部布局灵活得多,可以把盘放到风道更好的位置。另外SlimSAS的线缆可以支持到1米左右,对于2U机箱来说完全够用。
注意:SlimSAS 4i的线缆分两种,一种是直连背板的(带sideband),一种是直连SSD的(不带sideband)。如果你要支持热插拔LED,必须选带sideband的线缆,否则只能靠软件轮询。
2. 核心细节解析与硬件设计要点
2.1 PEX8748的端口配置与带宽分配
PEX8748的端口配置是通过SPI EEPROM里的配置文件决定的,上电时芯片会读取EEPROM内容并锁定端口划分。这里有个很重要的概念:PEX8748的内部是模块化的,每个端口可以独立配置成x1/x2/x4/x8/x16,但总通道数不能超过48。
我的配置方案是:
| 端口号 | 配置 | 用途 | 备注 |
|---|---|---|---|
| Port 0 | x8 | 上行到主机 | 必须配成x8,金手指只走x8 |
| Port 1 | x4 | SlimSAS 1 | 下行 |
| Port 2 | x4 | SlimSAS 1 | 下行 |
| Port 3 | x4 | SlimSAS 2 | 下行 |
| Port 4 | x4 | SlimSAS 2 | 下行 |
| Port 5-8 | x4 | 预留 | 可以后续通过配置工具开启 |
这样总共用了8+4+4+4+4=24 lane,剩下的24 lane可以留着做其他用途,比如再挂一个x8的端口做内部桥接。实际上PEX8748的灵活性就在于你可以把多个物理端口"合并"成一个逻辑端口,比如把Port 1和Port 2合并成一个x8端口。
配置工具用的是Broadcom的PEX Configuration Wizard,这个工具可以直接生成EEPROM的二进制文件。操作步骤:
- 打开工具,选择PEX8748型号。
- 在Port Configuration页面,把Port 0设成Upstream,其余设成Downstream。
- 设置每个端口的Lane Width和Lane Assignment。
- 在Clock Configuration里,把参考时钟设成100MHz,选择"Spread Spectrum"关闭(因为SSD对时钟抖动敏感)。
- 生成.bin文件,用编程器烧到SPI EEPROM里。
实操心得:配置工具生成的bin文件里,有一块区域是"Strapping"配置,决定了上电时芯片的初始状态。如果你发现上电后链路训练不起来,先检查Strapping里的"Slot Configuration"是不是设成了"Single Root"而不是"Multi-Root"。
2.2 时钟树设计与抖动控制
PEX8748的时钟方案是整个设计里最容易翻车的地方。芯片需要一个100MHz的差分参考时钟(Refclk),同时每个下行端口需要输出一个100MHz的时钟给SSD。这里有两种方案:
方案A:共用时钟。用一个100MHz晶振同时给PEX8748和所有SSD提供时钟。优点是成本低,缺点是如果晶振抖动大,所有链路都会受影响。而且SSD的时钟输入要求是HCSL电平,PEX8748的输出是LVDS,需要电平转换。
方案B:独立时钟。PEX8748用一颗晶振,每个SSD用独立的时钟缓冲器(比如SI5332)。优点是隔离性好,缺点是BOM成本高,PCB面积大。
我选了方案A的变种:用一颗低抖动晶振(SiTime的SiT9365,抖动<100fs)给PEX8748,然后PEX8748的时钟输出经过一个零延迟缓冲器(NB3N551)分到各个SSD。这样既控制了成本,又保证了时钟质量。
关键参数计算:
- PCIe Gen3的参考时钟要求:100MHz ±300ppm,抖动<1ps RMS(12kHz-20MHz)。
- SiT9365的相位抖动:0.1ps RMS,完全满足。
- NB3N551的附加抖动:0.2ps RMS,加上晶振的0.1ps,总抖动0.3ps,还有余量。
注意:PEX8748的时钟输出默认是LVDS,但SSD要求HCSL。NB3N551正好是LVDS转HCSL的缓冲器,一颗芯片搞定4路输出。如果你用其他缓冲器,一定要确认输入输出电平匹配。
2.3 电源树与功耗估算
整卡的功耗主要来自三部分:PEX8748本身、SSD(通过SlimSAS供电)、以及外围电路。PEX8748的典型功耗是8W左右(x48全速运行时),加上SSD的功耗(每块NVMe盘约5-8W),如果挂8块盘,总功耗可能到60W以上。但我们的卡是被动散热,而且金手指只能提供75W,所以必须做功耗管理。
我的做法是:
- PEX8748供电:用一颗TPS54620(6A)提供1.0V核心电压,一颗TPS54331(3A)提供1.8V和3.3V辅助电压。
- SSD供电:SlimSAS接口的3.3V由金手指直接提供,但每路加一个eFuse(TPS2595)做限流保护,防止单块盘短路拖垮整卡。
- 功耗估算:PEX8748 8W + 8块SSD × 6W = 56W,加上外围1W,总共65W。金手指75W够用,但余量不大。
这里有个坑:PCIe插槽的75W是理论值,实际主板能提供的可能只有60W左右。如果你挂满8块盘,建议用带外接供电的版本(加一个6pin PCIe供电口)。我第一版没加,结果跑满载时系统直接掉电,后来加了6pin才稳定。
2.4 PCB布局与信号完整性
PEX8748是FCBGA封装,484个球,间距0.8mm。PCB必须用8层板,叠层结构如下:
| 层号 | 用途 | 备注 |
|---|---|---|
| L1 | 信号层(顶层) | PCIe差分对走线 |
| L2 | 地平面 | 完整地,不分割 |
| L3 | 信号层 | 低速信号和电源 |
| L4 | 电源平面 | 1.0V和1.8V |
| L5 | 电源平面 | 3.3V和1.2V |
| L6 | 信号层 | 时钟和配置信号 |
| L7 | 地平面 | 完整地 |
| L8 | 信号层(底层) | 去耦电容和测试点 |
PCIe Gen3的差分对走线要求:
- 差分阻抗:85Ω ±10%。
- 走线长度匹配:对内±5mil,对间±20mil。
- 过孔数量:每对不超过2个。
- 参考平面:必须完整,不能跨分割。
实操心得:PEX8748的球间距是0.8mm,扇出时需要用盘中孔(via-in-pad)或者狗骨扇出。盘中孔成本高但信号好,狗骨扇出便宜但会引入 stub。我选了狗骨扇出,但在过孔处做了背钻(back drill),把 stub 控制在10mil以内。实测眼图张开度很好,误码率低于1E-12。
3. 实操过程与核心环节实现
3.1 原理图设计要点
原理图阶段有几个关键点必须注意:
第一,PEX8748的复位信号。芯片有一个全局复位(PERST#)和一个热复位(Hot Reset)。全局复位来自金手指的PERST#,热复位来自上游端口的TLP。这两个信号不能混在一起,否则热复位时整卡都会重启。我的做法是:PERST#直接接金手指,Hot Reset通过一个GPIO扩展到每个下行端口的PERST#。
第二,I2C/SPI配置接口。PEX8748支持I2C和SPI两种配置方式。SPI更快(上电时直接加载),I2C更灵活(可以动态改)。我选了SPI,因为配置固定,不需要动态改。SPI EEPROM选的是AT25SF041(4Mbit),足够存配置文件。
第三,SlimSAS连接器的定义。SFF-8654有4i和8i两种,4i是36pin,8i是74pin。我用的是4i,每个连接器提供4 lane + sideband。引脚定义里,PERST#和CLKREQ#是必须接的,否则SSD无法正常初始化。另外,SMBus(I2C)也要接,用于读取SSD的温度和健康状态。
第四,LED指示灯。每个端口需要一个Activity LED和一个Fault LED。Activity LED接PEX8748的GPIO,Fault LED接SSD的SMBus Alert。这里注意,PEX8748的GPIO驱动能力很弱(4mA),需要加一个晶体管驱动LED。
3.2 PCB设计实操
PCB设计我用了Cadence Allegro,因为PEX8748的封装库只有Allegro格式最全。设计流程:
- 导入网表:从OrCAD导出网表,导入Allegro。
- 布局:PEX8748放在板子中央,金手指在左侧,SlimSAS在右侧。SSD的供电电路靠近SlimSAS连接器。
- 扇出:先扇出PEX8748的BGA,用狗骨扇出,过孔0.2mm孔径,0.45mm盘径。
- 差分对走线:PCIe差分对走L1和L8,参考L2和L7的地平面。走线宽度5mil,间距7mil,差分阻抗85Ω。
- 等长:对内等长±5mil,对间等长±20mil。用Allegro的Delay Tune功能自动绕等长。
- 电源平面:L4和L5做电源平面,1.0V和1.8V在L4,3.3V和1.2V在L5。每个电源平面用星型连接,避免噪声耦合。
- 去耦电容:每个电源引脚配一个0.1uF电容,每4个引脚配一个10uF电容。电容尽量靠近引脚,回路面积最小。
注意:PEX8748的1.0V核心电压对噪声非常敏感,纹波要求<30mV。我在L4平面上加了多个22uF的钽电容,实测纹波<15mV。
3.3 配置EEPROM的烧录与验证
配置文件生成后,需要烧到SPI EEPROM里。我用的是CH341A编程器,便宜好用。烧录步骤:
# 用flashrom烧录 flashrom -p ch341a_spi -w pex8748_config.bin烧录完成后,把EEPROM插到板子上,上电。验证步骤:
- 用示波器测PEX8748的Refclk,应该是100MHz差分,幅度800mVpp。
- 测PERST#信号,上电后应该拉高。
- 用lspci命令看主机是否能识别到桥接卡。
lspci -tv如果识别到了,应该能看到类似这样的输出:
-[0000:00]-+-00.0 Intel Corporation ... +-01.0-[01]----00.0 Broadcom PEX8748 ...如果没识别到,先检查EEPROM是否烧录正确,再检查Refclk和PERST#。
3.4 链路训练与SSD识别
桥接卡识别到之后,下一步是让SSD被识别。这里有个关键点:PEX8748的下行端口默认是关闭的,需要在配置里使能。在PEX Configuration Wizard里,每个Downstream Port都有一个"Enable"选项,勾上之后才会开始链路训练。
链路训练的过程:
- PEX8748发送TS1/TS2有序集。
- SSD回复TS1/TS2。
- 双方协商速率和宽度。
- 进入L0状态,链路建立。
如果链路训练失败,用lspci看端口的Link Status:
lspci -vvv -s 01:00.0 | grep -i "lnksta"正常应该显示:
LnkSta: Speed 8GT/s, Width x4如果显示"Width x1"或者"Speed 2.5GT/s",说明链路降级了。常见原因:
- 差分对走线阻抗不匹配。
- 参考时钟抖动太大。
- SSD的PERST#时序不对。
实操心得:我遇到过一块盘死活训练不起来,后来发现是SlimSAS线缆的sideband接反了。SFF-8654的sideband定义里,Pin 1是PERST#,Pin 2是CLKREQ#,如果线缆做反了,PERST#就变成了CLKREQ#,SSD永远收不到复位释放信号。换了一根线就好了。
4. 常见问题与排查技巧实录
4.1 链路训练失败排查表
| 现象 | 可能原因 | 排查方法 | 解决方法 |
|---|---|---|---|
| 主机不识别桥接卡 | EEPROM未烧录或烧录错误 | 读EEPROM内容对比 | 重新烧录 |
| 桥接卡识别但SSD不识别 | 下行端口未使能 | 检查配置文件 | 使能Downstream Port |
| 链路降级到x1 | 差分对走线问题 | 测眼图 | 重新走线或换线缆 |
| 链路降级到Gen1 | 参考时钟抖动大 | 测时钟相位噪声 | 换低抖动晶振 |
| SSD时有时无 | 供电不足 | 测3.3V纹波 | 加去耦电容或外接供电 |
| 热插拔不工作 | PERST#时序不对 | 测PERST#和CLKREQ# | 调整RC延时 |
4.2 供电相关的坑
坑一:金手指供电不足。前面提过,75W是理论值,实际可能只有60W。如果你挂8块盘,一定要加外接供电。我第二版加了6pin PCIe供电口,用一颗TPS2595做ORing,金手指和6pin同时供电时自动均流。
坑二:SSD的3.3V浪涌。NVMe SSD上电瞬间的浪涌电流可能达到2A以上,如果多块盘同时上电,3.3V会被拉低。我的做法是:每路加一个eFuse,并且用GPIO控制上电顺序,间隔100ms依次上电。
坑三:PEX8748的1.0V核心电压。这颗芯片的1.0V对纹波非常敏感,如果纹波超过50mV,链路训练会随机失败。我在L4平面上加了多个22uF钽电容,并且用一颗TPS54620单独供电,实测纹波<15mV。
4.3 散热与功耗管理
被动散热卡最怕的是局部过热。PEX8748的结温上限是125°C,但实际建议控制在100°C以下。我的散热方案:
- PEX8748上面贴一个20x20x10mm的铝挤散热器,用导热硅胶固定。
- PCB背面在PEX8748对应位置铺铜,通过过孔阵列把热量传到背面。
- 整卡放在机箱风道里,靠机箱风扇带走热量。
实测:挂8块盘跑满载,PEX8748的壳温在85°C左右,结温约95°C,还有余量。如果机箱风道不好,建议加一个小风扇。
注意:PEX8748的内部有温度传感器,可以通过I2C读取。如果结温超过110°C,芯片会自动降速。你可以在配置文件里设置温度阈值,超过就触发中断。
4.4 兼容性问题
问题一:某些主板不识别。有些主板对PCIe桥接卡的兼容性不好,尤其是消费级主板。原因是BIOS里的PCIe资源分配不够。解决方法:在BIOS里开启"Above 4G Decoding"和"Large BAR"。
问题二:某些SSD不兼容。三星的PM9A3和Intel的P4510在PEX8748上表现很好,但有些国产盘(比如某些白牌盘)的PERST#时序要求很严格,需要调整RC延时。我的做法是在PERST#线上加一个可调延时芯片(比如LTC6994),现场调。
问题三:热插拔导致系统卡死。如果热插拔时没有正确通知OS,OS可能会卡在IO等待。解决方法:确保SMBus Alert和PERST#都正确连接,并且在OS里加载正确的驱动(Linux下是pciehp,Windows下是微软自带的PCIe热插拔驱动)。
5. 性能实测与优化建议
5.1 带宽测试
用fio测试单块盘的顺序读写:
fio --name=seqread --rw=read --bs=1M --size=1G --numjobs=1 --iodepth=32 --direct=1 --filename=/dev/nvme0n1实测结果:
| 盘位 | 顺序读 | 顺序写 | 随机读IOPS | 随机写IOPS |
|---|---|---|---|---|
| 单盘 | 3.2 GB/s | 2.8 GB/s | 550K | 480K |
| 8盘并发 | 3.1 GB/s | 2.7 GB/s | 520K | 450K |
可以看到,8盘并发时单盘性能略有下降,但总带宽没有超过上行x8的3.2 GB/s上限。这说明PEX8748的交换架构没有成为瓶颈。
5.2 延迟测试
用nvme-cli测延迟:
nvme io-passthru /dev/nvme0 -o 0x02 --data-len=4096 --read --latency实测平均延迟:85us(单盘),92us(8盘并发)。相比直连CPU的NVMe(约80us),增加了约5-12us。这个延迟增加主要来自PEX8748的内部交换,对于大多数应用来说可以接受。
5.3 优化建议
如果你要复现这个设计,我有几个建议:
- PCB板材:用FR4就行,但如果是8层板,建议用生益的S1000-2或者松下M6,损耗更低。
- 连接器:SlimSAS连接器选Amphenol或者Molex的,国产的便宜但插拔寿命短。
- 线缆:SlimSAS线缆选带屏蔽的,长度不要超过0.8米,否则信号衰减太大。
- 固件:PEX8748的固件可以升级,博通官网有最新的配置文件模板,建议基于模板改,不要从零开始。
最后再分享一个小技巧:如果你只需要4个盘位,可以把PEX8748配置成x8上行 + 4个x4下行,剩下的通道全部关闭。这样功耗可以降到5W左右,完全不需要散热器,连PCB都可以做小一圈。我在一个客户项目里就是这么干的,整卡成本降了30%。