PCIe基础回顾:从拓扑到物理层的完整链路
各位同学,今天我们聊聊PCIe的基础。说实话,很多做SSD固件的工程师,对PCIe的理解往往停留在「能跑就行」的层面。但如果你真想深入NVMe协议,PCIe的这几个层次——拓扑结构、事务层、数据链路层、物理层——必须吃透。
我个人习惯把PCIe比作一个快递系统。你想想看,CPU发一个读命令给SSD,就像寄一个包裹。这个包裹怎么封装、怎么运输、怎么确认送达,就是PCIe各层干的事。
一、PCIe拓扑结构:点对点的连接方式
PCIe和传统的PCI总线最大的区别是什么?
传统PCI是共享总线架构。所有设备挂在一根总线上,谁想通信得先抢总线使用权。这就像一条单车道,所有车挤在一起,效率可想而知。
PCIe不一样。它是点对点的拓扑结构。每个设备都有自己的专用通道,直接和Root Complex(根复合体)或Switch相连。
核心概念:
- Root Complex (RC):系统的老大,连接CPU、内存和PCIe总线。我习惯叫它「总调度中心」。
- Endpoint (EP):终端设备,比如你的NVMe SSD、显卡、网卡。
- Switch:相当于一个PCIe交换机,把一条上游链路扩展成多条下游链路。注意,Switch不是路由器,它只是透明转发。
- Bridge:用于连接PCIe和传统PCI总线,现在用得少了。
我在项目中遇到过一个问题:一个NVMe SSD插在Switch下游,性能始终上不去。后来发现是Switch的端口配置不对,导致链路宽度协商成了x2而不是x4。嗯,这种坑踩过一次就记住了。
下面这张图是我画的PCIe拓扑结构示意,帮你快速建立整体认知:
二、事务层(Transaction Layer):TLP的封装与解析
事务层是PCIe协议栈里最「上层」的一层。说白了,它负责把CPU或设备要传输的数据,打包成事务层包(TLP)。
TLP长什么样?我直接给你看格式:
TLP格式(简化版):
+--------+--------+--------+--------+--------+--------+
| 前缀 | 头部 | 数据 | ECRC | | |
| (可选) | (12B/16B) | (0-4KB) | (可选) | | |
+--------+--------+--------+--------+--------+--------+
头部里最关键的是什么呢?
- Fmt/Type:告诉接收方这是个什么类型的包。是读请求?写请求?还是完成包?
- Requester ID:谁发的这个包。Bus:Device:Function,简称BDF。
- Tag:事务标签。我发一个读请求,SSD回一个完成包,Tag必须对上。否则乱套了。
- Address/Length:要读写的地址和数据长度。
避坑指南:我曾经在调试NVMe驱动时,发现读命令总是超时。抓了PCIe分析仪一看,原来是Tag管理出了问题——驱动把同一个Tag发了两遍,SSD那边直接懵了。记住:Tag是事务的唯一标识,绝对不能重复使用,直到收到对应的完成包。
事务层还负责流量控制(Flow Control)。每个接收端会告诉发送端:「我的缓冲区还能收多少个TLP」。发送端收到这个信息后,才会继续发。这就像你给朋友寄快递,先问一句「你家里还有地方放吗?」
三、数据链路层(Data Link Layer):DLLP与可靠传输
数据链路层夹在事务层和物理层之间。它的核心任务就一个:保证TLP能可靠地从A点传到B点。
怎么保证?两个手段:
- ACK/NAK协议:发送端发一个TLP,接收端必须回一个DLLP(数据链路层包)说「收到了」或者「没收到,重发」。这就像你寄了快递,对方签收了给你回个短信。
- CRC校验:每个TLP在数据链路层会被加上一个LCRC(链路层CRC)。接收端收到后重新算一遍,对不上就NAK。
数据链路层还维护一个序列号。每个TLP都有一个递增的序列号,接收端靠这个检测有没有丢包。我见过一个案例:某款SSD在高温下偶尔丢包,就是数据链路层的序列号处理逻辑有bug,导致重传机制失效。
注意:数据链路层的重传是硬件自动完成的,软件完全感知不到。这也是PCIe能做到「可靠传输」的原因。但如果你在固件里发现某个DMA传输偶尔数据错乱,别急着怀疑数据链路层——先查查你的DMA描述符是不是写错了。
四、物理层(Physical Layer):电气特性与链路训练
物理层是最底层,负责真正的比特传输。它又分成两个子层:
- 电气子层:处理电压、阻抗、时钟恢复这些硬件细节。
- 逻辑子层:处理8b/10b编码(Gen1/Gen2)或128b/130b编码(Gen3+)、加扰、去扰。
物理层有一个非常重要的过程叫链路训练(Link Training)。每次上电或复位后,PCIe设备之间会通过物理层的LTSSM(链路训练状态机)协商链路参数:
| LTSSM状态 | 说明 | 我踩过的坑 |
|---|---|---|
| Detect | 检测对端是否存在 | —— |
| Polling | 交换训练序列,协商位宽和速率 | —— |
| Configuration | 分配链路编号,完成链路初始化 | 曾经遇到Switch配置错误,导致链路卡在Configuration状态 |
| L0 | 正常工作状态,可以收发TLP | —— |
| L0s/L1 | 低功耗状态,省电用的 | SSD在L1状态下唤醒延迟过大,导致IO超时 |
物理层的速率演进也很重要。从Gen1的2.5GT/s到Gen5的32GT/s,每一代速率翻倍。但注意,速率越高,信号完整性越难做。我调试Gen4 SSD时,PCB走线稍微长一点,链路就训练不到Gen4速率,只能降级到Gen3。
核心总结:
- 事务层:打包TLP,定义事务类型(读、写、完成)。
- 数据链路层:加序列号、加CRC、ACK/NAK重传,保证可靠。
- 物理层:比特传输、链路训练、速率协商。
这三层各司其职,共同构成了PCIe的完整通信链路。你写NVMe驱动时,大部分时间打交道的是事务层(构造TLP),但出了问题往往要往下层查。
好了,PCIe基础就回顾到这里。这些内容虽然基础,但非常重要。后面讲NVMe队列和命令提交时,你会反复用到今天讲的概念。
公众号:蓝海资料掘金营,微信deep3321