PCIe 层次结构:事务层、数据链路层、物理层的功能与分工
我们在前一章聊过 PCIe 的基本概念。你可能会问:为什么一个总线协议要搞成三层?直接像 PCI 那样并口通信不更简单吗?
我刚开始接触 PCIe 时也这么想。后来在调试一块 FPGA 板卡时,发现链路偶尔丢包,但设备寄存器读出来全是 0x0 —— 那时候我才意识到,分层设计是为了把复杂问题拆解。每一层只管自己那一摊事,出了问题可以独立定位,互不干扰。
说白了,PCIe 的三层就像快递公司:
- 事务层(TL) —— 写字、打包、贴快递单
- 数据链路层(DLL) —— 装车、核对清单、确认签收
- 物理层(PL) —— 卡车、马路、交通信号
事务层(Transaction Layer)—— 生成与解析 TLP
事务层是最接近软件的一层。驱动发一个读配置空间的请求,CPU 写一个 Memory Write 命令,这些都先落到事务层。它负责把软件层的操作转化为 TLP(Transaction Layer Packet)。
我习惯把事务层看作“翻译官”。驱动告诉它“我想读设备 A 的 BAR0 空间”,它就把这个意图翻译成一个特定格式的 TLP,带上地址、长度、类型等信息,然后往下送。
- Memory Read / Write——最常见的读写操作
- IO Read / Write——兼容旧设备,新系统基本不用
- Configuration Read / Write——枚举阶段使用
- Message——中断、错误通知等
- Completion——用于带数据的读响应
事务层还有个关键功能:流量分类(TC)和虚拟通道(VC)。在高吞吐场景下,视频数据流和普通寄存器访问可以走不同的虚拟通道,避免互相阻塞。我在做视频采集卡驱动时就遇到过,如果不在事务层配好 TC,显示画面偶尔会卡顿,因为 DMA 和配置消息抢了同一条通道。
数据链路层(Data Link Layer)—— 可靠性保障
数据链路层在中间,夹在事务层和物理层之间。它的核心任务就一个:保证 TLP 传输的可靠性。
怎么保证?两个关键机制:
- ACK / NAK 重传 —— 发送方发出 TLP 后,接收方必须回复一个确认包。如果收到 NAK 或超时,发送方会重新发送。
- CRC 校验 —— 每个 TLP 尾部附带一个 LCRC(链路 CRC),物理层传完后数据链路层会校验。一旦发现错误,直接扔 NAK。
我当年调试一个 PCIe 交换机时,链路经常报“Bad TLP”,查了好多天。后来用协议分析仪一看,是物理层信号质量差导致偶尔误码,但数据链路层反复重传,最终超时断开。嗯,这种问题只能从底层修,光改驱动没用。
除了 TLP 的可靠性,数据链路层还负责 链路管理。比如链路初始化、电源状态转换(L0s / L1 / L2)、流控初始化等。这些通过 DLLP(Data Link Layer Packet) 来实现,DLLP 不暴露给事务层,是数据链路层自己的“内部信令”。
物理层(Physical Layer)—— 电信号的传输
物理层是离硬件最近的一层。它负责:
- 将数据链路层送来的字符流 串行化 为差分信号对(Tx+/Tx-)
- 在接收端做 时钟恢复 和 解串
- 处理电气特性(摆幅、预加重、均衡等)
- 完成链路的 训练与初始化(LTSSM)
物理层又分为两个子层:
| 子层 | 功能 | 示例 |
|---|---|---|
| 逻辑子层 | 8b/10b 编码(Gen1/2)或 128b/130b 编码(Gen3+),加扰 | 确保直流平衡,信号中不会出现长串 0 或 1 |
| 电气子层 | 差分驱动、接收均衡、阻抗匹配 | Per-lane 的 TX 摆幅可配置(如 800mV、400mV) |
物理层还有一个非常重要的状态机——LTSSM(Link Training and Status State Machine)。链路从复位开始,依次经历 Detect、Polling、Configuration、L0(正常工作)、Recovery、L0s/L1 等状态。我曾经在一次板卡调试中,发现链路始终卡在 Configuration 状态,测量时钟发现参考时钟抖动太大,导致锁相环失锁。换掉晶振后,LTSSM 顺利进入 L0,一切正常。
三层如何协作?
用一个典型的读操作来串讲一遍:
- 软件(驱动)向 Root Complex 的某个寄存器发起 Memory Read。
- 事务层构造一个 Memory Read TLP,包含地址、长度、Requester ID、Tag 等信息,交给数据链路层。
- 数据链路层给 TLP 加上序列号(Sequence Number)和 LCRC,然后放入发送缓冲区,同时启动重传定时器。
- 物理层把字符流编码、串行化,通过差分线对发出去。
- 接收端物理层解串、时钟恢复,把比特流还原成字符流,上交给数据链路层。
- 数据链路层检查 LCRC,如果正确则回复 ACK(DLLP),并将 TLP 送往事务层;如果错误则回复 NAK,请求重传。
- 事务层解析 TLP,找到目标 Endpoint,读取数据,然后生成一个 Completion TLP(带数据),再经过同样路径返回给发送端。
整个过程环环相扣。哪一层出问题,表现都不太一样。我整理了一个小经验:
- 数据全对但链路经常断开 -> 先查 LTSSM 状态,大概率是物理层电气问题
- 读数偶尔错但链路不丢 -> 查数据链路层 CRC 错误计数器(可通过 PCIe 扩展能力寄存器读取)
- 请求发出去了但收不到响应 -> 查事务层的 Tag 管理或 Completion Timeout
下面这张图能帮你直观理解三层的关系和数据流动方向:
说到底,这三层分工明确,各自独立却又紧密协作。做驱动开发时,你主要跟事务层打交道——构造请求、解析完成包。数据链路层和物理层虽然不直接暴露编程接口,但它们的健康状态直接影响性能。我建议所有做 PCIe 驱动的人,至少要知道怎么读链路状态寄存器和链路错误计数器。碰到疑难问题时,这些底层信息比猜测有用得多。
① 物理层:LTSSM 是否在 L0?链路宽度/速度是否正确?
② 数据链路层:有没有 CRC 错误?是否频繁重传?
③ 事务层:Completer 是否超时?Tag 是否耗尽?