
简介PEX8749-BA/CA 数据手册面向PCIe交换芯片选型、硬件设计与底层驱动开发人员提供PCI Express Gen 3多根交换机的架构、寄存器与配置参考。文档围绕Multi-Root多主机共享、DMA直接内存访问、18端口拓扑与高通道带宽管理展开并说明DMA降低CPU负担的方式同时给出Gen 3 8GT/s速率、寄存器偏移、JTAG标识及修订历史等工程细节可服务于数据中心、服务器、网络设备和存储系统的I/O扩展与调试。压缩包内含1个PDF文件约18.06MB适合本地检索、归档和随工程资料留存目前已有1747人学习下载。读者可据此了解芯片特性、端口划分、DMA机制与寄存器定义理清多主机访问、带宽分配和版本差异辅助完成方案评估、原理图设计、链路带宽排查及多主机设备共享问题分析也可作为驱动开发和硬件调试阶段的案头参考。1. 一块 48 通道的 PCIe Switch 数据手册什么时候你会真的翻开它板子插上电上游根端口认到了lspci里却只看到一个孤零零的桥下面挂着的加速卡、NVMe 一个都不出来或者链路倒是起来了协商结果停在 x4、2.5 GT/s换卡换线换槽位都没用。这类问题查到最后十有八九要回到那颗 PCI Express Gen 3 交换机PCIe Switch本身——PEX874948 条 Lane、18 个端口、支持多根Multi-Root和 DMA 的那一颗。数据手册 v1.2 覆盖 BA 和 CA 两个硅版本从 I/O 信号定义、复位时序、EEPROM 加载一路写到后面十几章的寄存器偏移表。它适合三类人画原理图、定 strap 的硬件工程师写配置 EEPROM 和上电流程的固件工程师以及要在几台主机之间做资源分区的系统软件。不适合想找一份“五分钟点亮教程”的人通篇没有一个字的示例代码。2. 48 条 Lane 怎么切成 18 个端口Station、端口配置与虚拟交换机的读法拿到手册先别翻寄存器表先把第 4 章到第 5 章的架构图啃下来。同一颗片子18 个端口不是唯一的切法切法由 strap 引脚在上电那一刻决定软件事后改不了。搞不清这一层后面所有寄存器都读不懂——你会以为某个端口没枚举出来是驱动问题实际是它压根没被切出来。2.1 Lane、Port、Station 三层结构先对齐Lane 是最小物理单位一对差分线Gen 3 下跑 8 GT/s128b/130b 编码扣掉开销单向实际可用约 985 MB/s。Port 是一条逻辑链路宽度可以是 x1、x2、x4、x8、x16宽度必须是 2 的幂一条 Port 内部不能出现“x3 这种”宽度。Station 是把若干 Lane 圈在一起、再往里切 Port 的单元。手册里带 STNx 前缀的 strap 和复位信号都是按 Station 生效的。这一点非常关键STNx_PERST#只复位某个 Station不是整颗片子STNx_PORTCFGx只决定这个 Station 内部的端口切法。一颗片子上多个 Station 可以配成完全不同的端口形态这在做混合业务背板时很有用。由此带来一个必然结果48 条 Lane 分到各 Station 之后每个 Station 内部切完 Port 剩下的零头 Lane 就只能空着。想凑满 18 个端口意味着大量 Port 是 x1 或 x2 的宽度手册的端口配置表就是干这个的——它告诉你哪一档配置能切出几个端口、每个多宽。2.2 端口配置编号0 到 7 里v1.2 只剩 1 到 6端口配置是用 strap 引脚电平编码的选哪一档芯片上电时就把 Lane 按预设的形态切好。手册的修订记录里有一行很容易被跳过v1.2 删除了端口配置 0 和 7。这意味着如果手上的板子是从更早的设计改过来的strap 电阻还按配置 0 接芯片读到的就是一个已经作废的编码行为会退回到默认档或者干脆不训练链路现象是这条链路上什么都没有。排查思路很朴素但很多板子就栽在这先翻原理图找到STRAP_STNx_PORTCFGx那几个引脚把上下拉电阻的取值抄出来编码成 0/1 序列再对照手册第 21 章的 strap 表。对不上就改电阻别在软件里找补。中文章节里要确认的事在手册里查哪里典型翻车点这颗片子有几个 Station第 4 章架构图以为整颗只有一个 Station复位拉错引脚每个 Station 能切几档配置第 5 章端口配置表用了已删除的配置 0 / 7每个 Port 的宽度上限端口配置表对应的行规划了 x16实际该档位只给到 x8strap 引脚的上电采样时刻第 21 章 strap 时序上拉太弱采样到中间电平2.3 多根与虚拟交换机一根板子上的几台“主机”怎么分家Multi-Root 的实现方式是虚拟交换机Virtual Switch手册里写作 VS。一个 VS 有自己的上行端口、自己的配置空间视图主机 A 挂在 VS0 的上行口上就只看得到 VS0 下游的设备看不见 VS1 下面的东西。这在多主机存储、双控设备里是标准玩法。三个必须记住的边界条件。第一VSx_PERST#是每个 VS 独立的复位信号复位的粒度不是整颗片子。第二端口可以在 VS 之间迁移Port Migration但手册明确要求迁移之前必须先把经过源 VS 的流量停掉不停就迁表现就是数据串到另一台主机上去。第三NT 端口Non-Transparent Port是跨域通信的通道NT0 和 NT1 这两个端口可以落在同一个 Station 里也可以分在不同 Station 里灵活度高但也意味着地址规划要自己兜底。还有一个细节值得单独拎出来扩展 ROMExpansion ROM只在 NT0 端口上可用。做固件的时候如果指望在 NT1 上挂一段 option ROM会发现根本没这个空间。2.4 用 lspci 把拓扑对到手册的图上手册的框图是逻辑视图板子上的真实拓扑得自己拉出来。先用树形输出看整体再逐个端口核对链路状态。# 1. 打印整棵 PCIe 拓扑树交换机会呈现为一串桥节点 lspci -tv # 2. 上游端口的链路能力与实际协商结果两者要分开看 lspci -vvv -s 01:00.0 | grep -E LnkCap:|LnkSta:|DevSta: # 3. 逐个下游端口确认有没有设备、速率宽度是否达到预期 for d in 02:00.0 03:00.0 04:00.0 05:00.0; do echo $d lspci -vv -s $d | grep -E LnkSta: doneLnkCap只说明这颗芯片和这条链路“支持到哪一档”LnkSta才是“现在跑在哪一档”。两者不一致时先怀疑物理层连接器、背板走线、参考时钟。两者一致但都低于预期那就是端口配置本身只切了这么宽回去查 strap。DevSta里的 Correctable Error 计数也值得顺手看一眼链路训练阶段如果有大量可纠正错误说明信号质量已经在临界点上跑压力测试迟早翻车。3. 上电那几百毫秒复位时序、strap 引脚与串行 EEPROM 加载上电到链路训练完成这段时间手册里散在第 5 章、第 6 章、第 21 章。三块内容必须一起看因为它们互相咬合复位放早了EEPROM 加载不完整strap 接错了EEPROM 里的配置再好也没用EEPROM 镜像格式对不上加载模式前面全白做。3.1 复位与参考时钟谁先谁后有硬要求顺序是参考时钟稳定、PERST#释放、内部初始化、串行 EEPROM 加载、链路训练。手册第 5 章有一节专门讲复位与时钟的初始化时序里面给了参考时钟稳定到PERST#释放之间的最小时间。这条要求在实际板子上极容易被压缩。电源芯片的 PG 信号一到就拉高PERST#看起来响应快实际上参考时钟的 PLL 还没锁住链训在某些批次上成功、某些批次上失败而且换台机器就复现不了属于标准的玄学问题。稳妥做法是给PERST#加一段 RC 延迟或者直接用电源的 PG 加一级延时电路把余量留足。另一个坑是PERST#的复位来源混用。有些设计把整板的复位和STNx_PERST#短接在一起单 Station 出问题时一复位就把整颗片子连带其它 Station 全带走了正在跑的业务直接断。既然手册把 Station 级复位单独做出来就别省那几颗电阻。3.2 strap 引脚上电采样一次之后软件改不回来strap 相关的信号在手册第 21 章v1.2 改过好几个的描述几个高频的STRAP_I2C_SMBUS_CFG_EN#、STRAP_I2C_SMBUS_EN、STRAP_MGMT_PORT_EN决定管理接口走 I2C 还是 SMBus以及是否启用管理端口。STRAP_REFCLK_SSC_ENxv1.2 把它的作用范围从 SerDes quad 改成了按 Port 关联。如果原理图还是照旧版把几个 quad 绑一起可能给不该开 SSC 的端口开了扩频链路能起但误码率上不去。STRAP_STNx_PORTCFGx端口切法前面已经说过。这些引脚都是上电采样一次然后锁存。想换管理接口、想换端口切法只能动板子。软件侧唯一能做的是读回来确认实际采样值别假设自己配对了。3.3 串行 EEPROM 加载Base Mode 和 Virtual Switch Mode 的格式不一样第 6 章把加载分成两个场景。Base Mode 是一套配置打天下单主机场景用这个。Virtual Switch Mode 下每个 VS 有自己的配置区镜像里还要额外带NTx_PERST#的控制位。两个 v1.2 新增的说明做固件的人必须知道。其一A30h[2]这一位在 NT 模式使能时应该置 1。其二A30h[27]的默认值改过而且当 Virtual Switch 模式和 NT 模式同时开启时这个值可能会变。第二条的实际含义是同一个 EEPROM 镜像不能无脑复制到另一种工作模式的板子上加载完读回来可能和你写进去的不一样。同理BA 和 CA 两个硅版本的寄存器默认值有不少差异。修订记录里列了一串比如74h[11:10]在 CA 上默认是10b禁用 ASPM L0s 支持BA 上是11b204h[15:0]在 CA 上默认置 1开启错误注入的推断模式BDCh里 Port x 的信号检测电平默认值也改成了011b。拿 BA 的镜像刷 CA 的板子最轻的结果是 ASPM 行为不符合预期最重的是信号检测阈值不对长链路上直接训练不起来。3.4 镜像解析脚本把 offset/value 对还原成能 include 的头文件调试阶段经常要拿 EEPROM 里的实际内容和设计值做 diff肉眼对十六进制不现实。下面这段把镜像里成对的偏移, 值记录抽出来生成一张 C 表方便和驱动里的默认值逐条比对。#!/usr/bin/env python3 把串行 EEPROM 镜像里的 (offset, value) 记录转成 C 头文件。 记录区的起始位置、每条记录的字节序、头部长度都以手册第 6 章 Serial EEPROM Load 的描述为准。Base Mode 和 Virtual Switch Mode 的头部定义不同这里只处理已经确认过格式的记录区。 import struct import sys REC_SIZE 8 # 4 字节偏移 4 字节值手册若定义为 3 字节字段需同步修改 def parse(path): with open(path, rb) as f: blob f.read() records [] pos 0 # 逐条向后读剩余长度不足一条记录就停止避免读到填充区 while pos REC_SIZE len(blob): offset, value struct.unpack_from(II, blob, pos) records.append((offset, value)) pos REC_SIZE return records def emit(records, out_path): with open(out_path, w) as f: f.write(/* 由 EEPROM 镜像自动生成勿手改 */\n) f.write(#ifndef PEX8749_EEPROM_CFG_H\n#define PEX8749_EEPROM_CFG_H\n\n) f.write(struct pex8749_cfg { unsigned int off; unsigned int val; };\n\n) f.write(static const struct pex8749_cfg pex8749_cfg_tbl[] {\n) for off, val in records: f.write( { 0x%08X, 0x%08X },\n % (off, val)) f.write(};\n\n#endif /* PEX8749_EEPROM_CFG_H */\n) if __name__ __main__: recs parse(sys.argv[1]) emit(recs, sys.argv[2]) print(records: %d % len(recs))REC_SIZE是最容易出错的地方——不同加载模式下记录长度不一定都是 8 字节写完先拿一份已知正确的镜像跑一遍记录条数和总长度能对上再往下走。生成的头文件建议直接进版本管理每次改 EEPROM 都重新生成一次、看 diff比对着二进制文件猜要靠谱得多。如果 EEPROM 在板子上能通过 SMBus 直接读连拆焊都省了# 扫描 SMBus配置 EEPROM 通常落在 0x50 段 i2cdetect -y 3 # dump 前 256 字节和手里的镜像做 diff i2cdump -y 3 0x50 b | head -20注意i2cdump是读操作安全i2cset是写动手前先i2cget把原值读出来存一份EEPROM 的擦写寿命经不起反复试错。4. DMA 与 NT 端口数据路径怎么组织带宽怎么测才算数4.1 DMA 引擎省掉的是什么没省掉的是什么DMA 让数据在主机内存和交换机侧之间直接搬不经过 CPU 的拷贝循环。省掉的是 CPU 占用和中断次数不是总线带宽——总线带宽该多少还是多少48 条 Lane 单向加起来大约 47 GB/s这是物理上限DMA 只是让你更接近它。实际做的时候有三个参数绕不开。缓冲区对齐按 1 MiB 对齐能避免描述符被拆分拆分一次就多一次事务开销。缓冲区大小要和链路上协商的 Max Payload Size、Max Read Request Size 配合缓冲区远大于 MPS 时硬件会拆成多个 TLP看起来是“DMA 慢”实际是参数没配平。描述符环深度太浅会在高并发下频繁等中断太深则首包延迟变差。4.2 NT 端口与 A-LUT地址转换靠什么匹配跨域访问的路径是主机 A 发起访问地址落在 NT 端口上NT 端口查地址查找表A-LUTAddress Lookup Table把地址翻译成目标域里的地址再转发出去。v1.2 对 A-LUT 的改动不小明确扩展了入方向ingress的支持第 16 章专门加了一节讲入方向的地址查找。A-LUT 是有限条目资源规划地址段时得算清楚每个条目覆盖多大的地址窗口、总共需要几条、留多少余量给后续扩容。表项配少了超过窗口的访问直接落空现象是“一部分地址能访问、另一部分不行”很容易被误判成驱动 bug。还有一个边界扩展 ROM 只在 NT0 端口上可用NT1 没有。做双 NT 端口的对称设计时这一条会让两边能力不对等得在软件侧补齐。4.3 带宽与延迟的打点程序手册给的是理论值板子上能跑出多少得自己测。别用dd读裸设备那种方式测出来的是文件系统缓存的速度。写一个最小化的打点程序只测 DMA 搬运这一段/* 最小 DMA 吞吐打点反复搬同一块缓冲区只统计搬运耗时 */ #include stdio.h #include stdlib.h #include string.h #include time.h #define BUF_BYTES (64UL 20) /* 64 MiB远大于任何一级缓存 */ #define ROUNDS 32 static double now_s(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, ts); return ts.tv_sec ts.tv_nsec / 1e9; } int main(void) { void *host NULL; /* 1 MiB 对齐避免描述符跨界被硬件拆分 */ if (posix_memalign(host, 1 20, BUF_BYTES) ! 0) { return 1; } memset(host, 0x5a, BUF_BYTES); double t0 now_s(); for (int i 0; i ROUNDS; i) { /* 换成实际的 DMA 提交入口等待完成后再进下一轮 */ /* device_dma_xfer(host, BUF_BYTES, DMA_TO_HOST); */ } double t1 now_s(); double gbps (double)BUF_BYTES * ROUNDS * 8 / (t1 - t0) / 1e9; printf(%.2f Gbps\n, gbps); free(host); return 0; }跑之前先把链路状态确认一遍LnkSta不是 8 GT/s 的话测出来的数字没有参考意义。跑的时候用perf stat看 CPU 占用如果 CPU 在满负荷跑说明 DMA 根本没生效数据还是走 memcpy 路径。延迟的话把BUF_BYTES降到 4 KiB 再跑一轮单次往返时间就是延迟的粗测值——注意这只是粗测精确值要靠硬件时间戳。5. 上电枚举不出来的排查清单五个现象、五个根因5.1 现象上游端口能认到下游一个设备都没有原因通常是端口配置没有生效或者下游端口根本没被切出来。v1.2 删掉了端口配置 0 和 7板子如果还是按旧设计接 strap芯片读到的编码无效该 Station 的端口全部不出现上游自然只能看到一颗光杆交换机。解决先用lspci -tv确认桥下面确实没有子节点再翻原理图核对STRAP_STNx_PORTCFGx的电阻编码对照手册第 21 章的 strap 表重算一遍。改完电阻重新上电别指望软复位能生效——strap 只在上电采样。5.2 现象链路起来了但降级到 x4 或者 2.5 GT/s原因分两类。物理侧是连接器、背板走线、参考时钟质量不达标配置侧是这个端口本来就只切了这么宽。区分方法很简单看LnkCap和LnkSta。两个都低是配置问题LnkCap高、LnkSta低是物理问题。物理侧先查参考时钟的 SSC 配置。v1.2 把STRAP_REFCLK_SSC_ENx从按 quad 关联改成了按 Port 关联老原理图如果还按 quad 绑可能给不该开 SSC 的端口开了扩频或者反过来。解决是把 strap 改成按端口独立控制逐端口验证。5.3 现象EEPROM 有时候能加载有时候加载不了原因多半在 I2C 的上拉电阻上。v1.2 明确改过I2C_SCL0、I2C_SDA0、I2C_SCL1、I2C_SDA1的上拉要求和外围电路说明。按旧版手册选的阻值在温度和批次波动下会落到临界区表现就是“十次里能成七八次”非常折磨人。解决按 v1.2 的要求重算上拉阻值同时用示波器看上升沿。另外确认STRAP_I2C_SMBUS_EN的状态和实际上挂的器件类型匹配——管理接口走 I2C 还是 SMBus时序要求不一样。5.4 现象虚拟交换机之间的流量串了原因通常有两个。一是端口迁移Port Migration的时候没有把源 VS 的流量停干净手册第 14 章明确要求迁移前先停流量不停就迁正在飞的 TLP 会落到错误的域里。二是 A-LUT 的地址窗口规划有重叠跨域访问落到了不该落的条目上。解决迁移流程里加一步显式的流量静默等所有 outstanding 事务回完再动配置。地址窗口做一张表落盘重启后比对别靠记忆维护。5.5 现象DMA 带宽只有理论值的三成先排除测量误差缓冲区是不是太小、有没有被缓存吸收、CPU 是不是在忙等。排除之后查三个参数缓冲区是否 1 MiB 对齐、Max Payload Size 和缓冲区大小是否匹配、描述符环深度是否够。还有一个容易被忽略的点链路上如果有可纠正错误在持续增长硬件会反复重传带宽看着就是上不去。用lspci -vv看DevSta里的 Correctable Error 计数跑一轮压力测试前后各读一次涨得快就是信号完整性问题回去查物理层别在软件里调参数。6. 把数据手册当寄存器字典用改一个 strap 的完整走法手册十几章的寄存器偏移表正确的用法不是通读是当字典查。我一般按这个顺序走一遍能把大部分配置错误挡在前面。第一步定位。先在目录里找到功能对应的章节再在该章的寄存器映射表里定位偏移最后确认位域。比如要改 ASPM L0s 的支持行为落到74h[11:10]。第二步查默认值和硅版本差异。同一个偏移在 BA 和 CA 上默认值可能不同74h[11:10]就是典型BA 默认11bCA 默认10b。不查这一条写进去的值和预期对不上会怀疑是自己写错了。偏移位域v1.2 中的关键变化什么时候要动它74h[11:10]CA 默认改为10b禁用 ASPM L0s排查链路功耗与唤醒异常108h整体随硅版本变化用于识别 BA / CA上电第一件事确认片子版本204h[15:0]CA 默认置 1开启错误注入推断做错误注入测试前先确认A30h[2]NT 模式使能时应置 1启用 NT 端口时A30h[27]VS 与 NT 同时开启时值可能变化双模式共存场景760h[16]CA 上重新定义为 ECAM 访问使能需要 ECAM 访问全部寄存器时第三步分清 strap 和寄存器。strap 是上电采样一次就锁死的寄存器是随时可写的。想在运行时改端口切法改寄存器没用只能改板子。这一条踩过一次之后就不会再踩了。第四步留回滚路径。改之前把原值读出来存文件改完立刻读回确认。EEPROM 里的内容尤其要备份写坏了还能用编程器烧回去没有备份就只能对着手册一行行推。从那以后我每次碰这类交换芯片上电第一件事都是读108h确认硅版本再读一遍 strap 相关引脚的实际采样状态然后才动任何配置。这个习惯帮我省掉了至少两次“明明按手册配的却不生效”的深夜排查。希望帮到你。本文还有配套的精品资源点击获取