PCIe基础回顾:从拓扑到物理层

讲NVMe之前,我觉得有必要先聊聊PCIe。毕竟NVMe就是跑在PCIe上面的协议,底层都不懂,上层肯定也玩不转。我见过不少工程师,NVMe命令背得滚瓜烂熟,一问PCIe事务层怎么工作的,立马卡壳。嗯,咱们今天就把这块补上。

PCIe拓扑结构:不是总线,是网络

传统PCI是共享总线架构,所有设备挂在一根线上,谁用谁申请。PCIe完全不一样,它用的是点对点连接。说白了,每个设备都有自己的专用通道,不用跟别人抢。

一个典型的PCIe系统包含这几个角色:

  • Root Complex(根复合体):CPU和内存的代言人,所有事务的起点
  • Switch(交换器):相当于网络交换机,把一条上游链路扩展成多条下游链路
  • Endpoint(端点):真正的设备,比如NVMe SSD、GPU、网卡
  • Bridge(桥):用来连接老式PCI/PCI-X设备,现在见得少了

我刚开始接触PCIe时,总觉得Switch就是个简单的信号中继器。后来调一个多NVMe SSD的系统才发现,Switch内部有复杂的路由逻辑,每个端口都有自己的VC(虚拟通道)和仲裁策略。选型选不好,性能直接腰斩。

关键点:PCIe拓扑是树形结构,从Root Complex出发,经过Switch,最终到达Endpoint。每个链路都是独立的,全双工通信。

Root Complex Switch Endpoint 1 (NVMe SSD) Endpoint 2 (GPU) Endpoint 3 (网卡) PCIe拓扑结构:点对点连接,树形层次 每个链路独立工作,全双工通信

事务层(TLP):PCIe的"快递包裹"

事务层是PCIe协议栈的最上层,负责生成和处理事务层包(TLP)。你可以把TLP想象成一个快递包裹——里面有收件地址、发件人、货物内容,还有校验信息。

TLP主要分这么几类:

TLP类型 用途 我见过的坑
Memory Read/Write 读写内存空间,NVMe主要用这个 地址对齐不对,性能直接掉一半
Configuration Read/Write 枚举和配置设备 配置空间访问超时,设备没响应
Message 中断、错误通知等 MSI-X配置错了,中断死活不来
Completion 对非Posted请求的回应 Completion超时,驱动直接挂掉

每个TLP都有固定的格式:起始的帧标记(STP)、序列号、Header、数据负载(可选)、CRC校验(ECRC/LCRC),最后是结束标记(END)。Header里最关键的是Fmt(格式)和Type(类型)字段,决定了这个包是干啥的。

我的经验:调试NVMe驱动时,我经常用逻辑分析仪抓TLP。有一次发现写请求的地址总是不对,查了半天,原来是DMA引擎的地址寄存器配置反了。TLP里的Address字段清清楚楚写着0xFFFF...,一眼就看出问题了。

数据链路层(DLLP):保证不出错

数据链路层夹在事务层和物理层中间,干的是脏活累活。它的核心职责就两个:可靠传输和流量控制。

DLLP(数据链路层包)比TLP小得多,主要用来做:

  • Ack/Nak协议:发送方每发一个TLP,接收方要回一个Ack或Nak。收到Nak就重传。我见过一个案例,链路信号质量差,Nak满天飞,吞吐量直接掉到原来的十分之一。
  • 流量控制(Flow Control):接收方告诉发送方自己还有多少缓冲区空间。发送方根据这个信息决定发多少数据。说白了就是"你慢点,我快撑不住了"。
  • 链路管理:链路的初始化、电源状态切换等。

数据链路层还负责给TLP加上序列号和LCRC(链路CRC)。序列号用来检测丢包,LCRC用来检测传输错误。这两个字段是硬件自动加的,软件完全感知不到。

注意:数据链路层的重传机制是硬件自动完成的,但重传次数多了会触发链路退化(Link Degradation)。我曾经遇到过一块NVMe SSD,因为PCB走线太长,信号衰减严重,链路从Gen4 x4自动降级到Gen3 x2,性能直接砍掉四分之三。

物理层:信号怎么跑

物理层是最底层,负责把数据变成电信号在差分对上传输。PCIe物理层分两部分:逻辑子层和电气子层。

逻辑子层干的事:

  • 8b/10b编码(Gen1/Gen2)或128b/130b编码(Gen3+):保证DC平衡,方便时钟恢复
  • 加扰(Scrambling):防止频谱尖峰,减少EMI
  • 链路训练(Link Training):上电后自动协商速率和宽度

电气子层就是实实在在的差分信号、阻抗匹配、预加重这些。嗯,这块我其实不太擅长,毕竟我是做协议和软件的,硬件细节交给硬件工程师去操心。

不过有一点我得提醒你:链路训练状态机(LTSSM)是物理层最复杂的部分。从Detect到Polling,再到Configuration、L0(正常工作),中间有十几个状态。如果链路训练失败,设备就认不到了。我调试过一块板子,NVMe SSD死活枚举不出来,最后发现是参考时钟的抖动太大,链路训练一直卡在Polling状态。

一句话总结:PCIe四层协议栈——事务层负责"干什么",数据链路层负责"保证干成",物理层负责"怎么干"。NVMe跑在事务层之上,用Memory Read/Write TLP来传输命令和数据。

好了,PCIe基础就回顾到这儿。这些概念后面讲NVMe队列、PRP、SGL时都会用到,你心里有个底就行。

可可精品资料库