第2章 PCIe拓扑与分层结构
记得我刚开始接触 PCIe 时,最头疼的就是搞不清设备之间到底怎么连的。后来一位老前辈跟我说了一句大实话:“别把 PCIe 想得太玄乎,它就是一套快递系统。” 这句话让我豁然开朗。Root Complex 相当于总仓库,Endpoint 是收件人,Switch 就是个。这么一比喻,拓扑结构就清楚了。
不过,光知道拓扑还不够。你拿协议分析仪抓到的报文,到底在哪个层级产生的?为什么有的包丢了不会重传,有的包丢了就得重来?这些问题的答案,都藏在分层模型里。今天我们就把这两件事彻底聊透。
2.1 拓扑:谁跟谁说话
PCIe 系统是典型的树形拓扑。根节点只有一个,叫 Root Complex(RC)。下面是各种 Switch、Bridge,最末端是 Endpoint(EP)。我画了个图,你看一眼就明白了。
Completion Timeout。所以拓扑结构一定要跟你选型的带宽匹配,别光看“能识别”,要看“跑不跑得动”。
2.2 Root Complex:系统的大管家
Root Complex 是 PCIe 体系里的“交通指挥中心”。CPU 发出的内存访问指令,必须经过 RC 转成 PCIe 事务层包(TLP),才能发给外设。反过来,外设发来的 DMA 数据,也要靠 RC 转换成内存写指令。
一般 x86 平台的 RC 都集成在 CPU 里,比如 Intel 的 PCH 或 AMD 的芯片组。ARM 平台的 RC 则在 SoC 内部。我习惯先把 RC 的配置空间读一遍,尤其是 Bus Number 和 Device Number 的分配方式。因为这直接决定了枚举顺序。
Secondary Bus Number 配错了,导致下游设备根本没被正确发现。所以分析仪抓包时要是发现链路层都 Link Up 了,但 TLP 层死活没动静,第一件事就是检查 RC 的配置空间。
2.3 Switch:流量转发的中枢
Switch 看起来像个简单的数据转发器,但实际上它承担了 路由 和 仲裁 两件事。每个端口都可以独立配置 Bus Number 范围。当收到的 TLP 的目标 Bus Number 落在某个下游端口的管理范围内时,Switch 就会转发过去。
举个例子:
- 上游端口:Bus 0
- 下游端口 1:Bus 1 – 4
- 下游端口 2:Bus 5 – 8
如果 RC 发了一个目标 Bus=3 的 TLP,Switch 一看,这是端口 1 的辖区,转过去。就这么简单。
2.4 Endpoint:功能的承载者
Endpoint 是实际干活儿的。它可能是个 NVMe SSD,网卡,或者 FPGA 加速卡。作为硬件验证的人,我最关心的是 Endpoint 的 Bar 空间 映射和 中断 行为。
每个 Endpoint 都必须实现至少 1 个 Bar(Base Address Register),用于告诉 RC:“你要找我的数据,往这个地址写就行”。调试时,我习惯先把每个 Endpoint 的 Bar 地址读出来,确认地址范围和类型(Memory 空间还是 IO 空间)。
另外,MSI/MSI-X 中断的配置也是 Endpoint 的重灾区。我见过很多 FPGA 设计的 Endpoint,Bar 空间配对了,但 MSI 的 Message Address 没写对,导致中断永远发不出去。
2.5 分层模型:从 TLP 到物理层的三驾马车
PCIe 的分层模型分三层:事务层 (Transaction Layer)、数据链路层 (Data Link Layer)、物理层 (Physical Layer)。每一层都干自己那摊事,说白了就是各司其职。
| 层级 | 核心职责 | 生成/处理的包 | 调试时最常看到的错误 |
|---|---|---|---|
| 事务层 | 构造/解析 TLP,处理事务 | MemRd, MemWr, CplD, CfgRd | Unsupported Request (UR), Completer Abort (CA) |
| 数据链路层 | 链路管理,ACK/NAK 重传 | DLLP (ACK, NAK, InitFC1/2) | CRC 错误,重传超时,链路状态抖动 |
| 物理层 | 串并转换,信号编码 | Ordered Sets (TS1/TS2, SKP, EIOS) | Lane 失锁,极性反转,De-emphasis 不匹配 |
简单来说,事务层 决定“发什么内容”,数据链路层 保证“内容不能丢”,物理层 负责“怎么把 0/1 送出去”。我调试时看协议分析仪,看 TLP 是查功能问题,看 DLLP 是查可靠性问题,看物理层 Ordered Sets 是查信号完整性。
2.6 事务层:TLP 的结构与类型
TLP 是 PCIe 通信的真正单位。它的核心结构是:
+--------+--------+------------+--------------+------------+
| 前缀 | 头标 | Data Payload | ECRC | |
| (可选) | (12-16B)| (0-4096B) | (可选 4B) | |
+--------+--------+------------+--------------+------------+
最常见的几种 TLP 类型:
- Memory Read (MemRd):从外设地址空间读取数据。没有 Data Payload。
- Memory Write (MemWr):向外设地址空间写入数据。带 Data Payload。
- Completion with Data (CplD):对于 Read 请求,必须返回 CplD。写请求一般不需要。
- Configuration Read/Write (CfgRd/CfgWr):用于枚举和配置 RC 及 Switch。
Max_Payload_Size。
2.7 数据链路层:ACK/NAK 与重传
数据链路层的核心是 ACK/NAK 机制。事务层丢一个 TLP 下来,数据链路层会给它加一个 Sequence Number (SeqNum) 和 LCRC。接收端收到后,检查 LCRC 对不对。对的,回 ACK。错的,回 NAK,要求重传。
你可能会想:“那重传会不会影响性能?” 影响不大。因为发送端会维护一个 重传缓冲区 (Replay Buffer),最多放几十个待确认的 TLP。收到 ACK 就释放,收到 NAK 就立即重放缓冲区里的所有 TLP(通过 Replay Timer 触发)。
2.8 物理层:训练与链路初始化
物理层是链路能跑起来的根基。上电后,物理层先经历一个 Link Training and Status State Machine (LTSSM) 的过程。从 Detect 状态一路走到 L0(正常工作状态)。如果中间某个步骤卡住了,比如 Polling.Confg 超时,链路就上不去。
协议分析仪物理层抓得最细的数据是 Ordered Sets:
- TS1/TS2:用于链路训练和位锁定。
- SKP:用于时钟补偿,调整 bit 偏移。
- EIOS/EIE:用于进入或退出电气空闲状态。
我记得做 Gen4 信号调试时,卡在 Polling.Active 状态出不来,后来发现是发送端的 De-emphasis 系数没配对,物理层接收端根本识别不到有效信号。换了电阻网络才搞定。
2.9 小结
今天把 PCIe 的拓扑和分层结构捋了一遍。记住三件事:
- 拓扑 决定流量路径,RC、Switch、EP 各司其职,调试先从 Bus/Device 枚举查起。
- 分层 决定问题域:TLP 查功能,DLLP 查重传,Phy 查信号。
- 分析仪 是你最好的朋友,学会在不同层过滤数据、观察分布,解决问题快一倍。
嗯,下一站在哪里?先把这些记扎实了,以后调试时你会感谢今天自己的基本功。