PCIe基础学习2:三层职责与三类包

协议栈全景:三层职责与三类包

PCIe 协议特性精讲 · 第 02 篇 | 基准:PCIe 5.0(Base Spec r5.0)| 内核:Linux v7.0.0
系列主线:39 篇,一篇一个协议特性——是什么、为什么、怎么工作、怎么测、坑在哪

一、三层协议栈:每层到底干什么

上一篇立了框架:事务层谈 TLP(语义),数据链路层谈 DLLP(可靠),物理层谈比特(信号)。这篇把每一层的职责边界划清楚,划清楚之后,出问题先判哪一层就有依据了。

1.1 事务层:把"要干什么"写成 TLP

事务层的核心职责是 TLP 的组装和拆解:设备核心要读一个地址,事务层把它打包成 MRd TLP;收到一个写请求,事务层解包交给设备核心。读写意图、地址、Tag、长度这些语义信息,全在这一层翻译成报文格式。

但组装拆解只是表面,事务层真正值钱的是三件事:

  • credit 流控:给对端报自己有多少接收缓冲(Header/Data credit),对端按 credit 决定能发多少 TLP。这是 PCIe 不发 retry 就能避免对端缓冲溢出的根子。
  • 排序规则:PCI/PCI-X 兼容的 producer/consumer 排序模型,加上 Relaxed Ordering、ID-Based Ordering 两个扩展。
  • 路由决策:TLP 头里带目标信息,Switch 靠它把包转到正确的出口。

我常跟人讲一句话:事务层管"邮局寄什么、往哪寄",它不知道也不关心底下电线长什么样。

1.2 数据链路层:给 TLP 上保险

DLL 夹在中间,职责一句话:把 TLP 可靠地搬过一条链路。具体手段三层:

  • 序列号(12 bit,0~4095 循环):发现"整个 TLP 丢了"
  • LCRC(32 bit):发现"TLP 坏了"
  • Ack/Nak + 重放:收方验不过就回 Nak,发方把 Retry Buffer 里的副本重发

这几个数字记牢:12 bit 序号、32 bit LCRC、重放最多 4 次失败就强制进 Recovery(REPLAY_NUM 计数)。重放风暴看到第 4 次失败掉链子,先怀疑信号质量,别去查协议逻辑。

DLL 还收发 DLLP:Ack/Nak、InitFC1/2、UpdateFC、电源管理请求,全是链路管理消息,8 字节固定,带 16 bit CRC。

1.3 物理层:把字节变成差分信号

物理层是所有"看不见的麻烦"所在。它分逻辑子块和电气子块:逻辑子块管加扰、编码(8b/10b 或 128b/130b)、并串转换、链路训练;电气子块管驱动、接收缓冲、阻抗匹配。

物理层对上层有个重要承诺:上层传下来的字节流,不管底层是 2.5 GT/s 还是 32 GT/s、NRZ 还是以后的 PAM4,DLL 和 TL 都不需要改。Spec 原文意思(中译):“未来的速率、编码或介质只会影响物理层的定义”()。这是 PCIe 从 Gen1 到 Gen5 速率涨 12.8 倍而软件生态不崩的结构性原因。

1.4 为什么要分三层:替换一层不重写全栈

分层不是 Spec 作者的洁癖,是工程取舍。三层各管一摊:TL 管"做什么"(语义),DLL 管"没丢没坏"(可靠性),PHY 管"比特到位"(物理传输)。好处是每一层只对上下邻居负责,换掉任何一层的实现,另外两层不用动——Gen3 换 128b/130b 编码的时候,TL 和 DLL 的报文格式一个字节都没改;将来换 PAM4,软件模型同样不受影响。

注意 Spec 自己点了一句:分层是架构描述,不约束具体实现。意思是硅片里三层可能是一坨逻辑,也可能拆成不同时钟域,Spec 不规定。但对验证工程师,这个模型就是排查地图:现象发生在哪层接口附近,就去哪层找证据。

二、三类包:谁在哪层跑

PCIe 里只有三类报文,分清它们各自的"活动范围",就是分层定位法的一半。Spec 用一句话把边界钉死了,原文意思(中译):

DLLPs are sent point-to-point, between the two components on one Link. TLPs are routed from one component to another, potentially through one or more intermediate components.

翻译成干活的话:

  • TLP 跨链路路由:从发起端到目标端,中间可以过 Switch 一级一级转发。事务层到事务层,端到端。
  • DLLP 仅在相邻两端:只在同一条 Link 的两个端口 DLL 之间交换,没有路由信息,Switch 不会转发它。抓包看到 DLLP 出现在不该出现的链路上,那是实现错误。
  • Ordered Set 只在 PHY 之间:TS1/TS2 训练序列、SKP 补偿符号,物理层本地消耗,连 DLL 都看不到。

这张图的判断价值在验证时特别大:抓包工具过滤三类包,先看它们在什么位置出现。Ordered Set 刷屏说明链路在训练(LTSSM 没到 L0);DLLP 正常但没 TLP,说明流控门禁没过;TLP 在但数据错,说明问题出在事务层内部或端到端路径。一个包位置,直接指向一层。

三、层间接口:门禁和帧化

层与层之间不是"递个文件"这么简单,接口上有两件必须知道的事:TL/DLL 之间有流控门禁,DLL/PHY 之间是帧化字节流。

3.1 TL/DLL 接口:FC 门禁

TL 发 TLP 前要过 DLL 的"门":链路初始化时,双方用 InitFC1/InitFC2 DLLP 交换 credit 额度,InitFC2 交换完,TL 才允许往 DLL 送 TLP。credit 用完了,TL 就得停发,等 UpdateFC 补额度。

验证含义:链路 L0 了但 CfgRd 超时,先抓包看有没有 InitFC2。只有 InitFC1 没有 InitFC2,就是 FC 初始化没完成,TLP 全被门禁挡在发端,跟物理层一点关系没有。这个判断我一年能用上好几回。

3.2 DLL/PHY 接口:帧化字节流

DLL 和 PHY 之间传的是带边界的字节流:DLL 给 PHY 的每个 TLP/DLLP 都标了边界信息(帧化),PHY 原样把字节串搬过去,不解析内容。PHY 只认"这是一串字节,边界在哪",不知道里面是读还是写。

反过来,PHY 往 DLL 交数据时,要带上物理层发现的错误指示(比如帧错误)。这一层接口决定了:物理层的错(失锁、帧错误)会向上报给 DLL,但物理层永远不会去解释 TLP 内容

四、版本差异:6.4 的 Flit 模式动了什么

基准钉在 5.0,但验证 6.0 产品前,这一节必须看。6.4 引入 Flit Mode,分层职责有实打实的变化:

维度r5.0(本系列基准)r6.4 Flit Mode验证含义
传输单位TLP/DLLP 独立封包256B Flit 统一承载(236B TLP + 6B DLP + 8B CRC + 6B ECC)抓包工具必须支持 Flit 解码
完整性机制LCRC + 序号 + Ack/Nak 重放Flit 内 CRC + FEC(ECC) + Flit 序号,重放在 Flit 粒度误码观测点从 Ack/Nak 计数变成 FEC/Flit Replay 计数
DLLP独立 8B 帧嵌入 Flit 的 DLP 区,无独立帧DLLP 统计口径变化

两个细节值得单独说:

  • Ack/Nak 只在 NFM(Non-Flit Mode)保留。Flit 模式下的链路层重传是 Flit 粒度,TLP 级 Ack/Nak 概念消失。
  • Flit 固定 256 字节(Spec 原文 “The flit size is 256 Bytes”),TLP 是变长的,所以要切分/拼装进固定槽位——这也是 6.0 抓包解析和 5.0 思路完全不同的根。

7.0 在分层职责上等于 6.4 加 128 GT/s,本篇主题没有功能变化。

五、Capability 锚点:PCIe Cap 0x10

说了半天层,每层状态在配置空间里有对应寄存器,这就是本系列的 Capability 锚点。PCI Express Capability 结构(Capability ID = 10h)是每个 PCIe 设备的必修课——它在配置空间的位置不固定,靠 0x34 能力指针链定位,lspci 里显示为 Express (v2)。其中两个寄存器跟分层定位直接相关:

  • Device Status(Offset 0Ah):bit0 = Correctable Error Detected,bit1 = Non-Fatal Error Detected,bit2 = Fatal Error Detected,bit3 = Unsupported Request Detected,bit5 = Transactions Pending(还有 Non-Posted 请求没完成)。设备层的错误账本,任何层出问题最终都可能在这里留痕。
  • Link Status(Offset 12h):bit3:0 = Current Link Speed(协商速率),bit9:4 = Negotiated Link Width(协商宽度),bit13 = Data Link Layer Link Active(DL_Active,DLL 状态机的活信号)。链路层的体检报告:速率、宽度、DL 状态一屏看完。

配套的 Link Control(Offset 10h)有两个常用位:bit5 = Retrain Link(写 1 强制重训,进 Recovery),bit6 = Common Clock Configuration(时钟配置,改了要重训)。

验证含义:lspci 里看到 LnkSta: Speed 16GT/s, Width x16,读的就是 Link Status;看到 DevSta: CorrErr+ NonFatalErr- 这类,读的是 Device Status。链路速率不对看 LnkSta,错误归谁看 DevSta 加 AER,两本账别混。实际输出长这样:

Capabilities: [40] Express (v2) Legacy Endpoint, MSI 00
    DevSta: CorrErr+ NonFatalErr- FatalErr- UnsuppReq- AuxPwr+ TransPend-
    LnkCap: Port #0, Speed 32GT/s, Width x16, ASPM not supported
    LnkSta: Speed 16GT/s, Width x16, TrErr- Train- SlotClk+ DLActive+ BWMgmt- ABWMgmt-

注意 LnkSta 行里的 DLActive+:它就是 Link Status 的 bit13,链路层报"我可以传了"。Train- 对应 bit11(LTSSM 不在 Configuration/Recovery)。这些位每个都能对应到一层,分层定位法到寄存器层面就是查这几位。

六、三表:分层定位第一查

分层定位法的完整落地,一张故障表 + 一张测试表 + 一张判断表:

症状可能根因(哪一层)可观测证据
链路起不来 / 枚举不到设备PHY:训练失败(无 REFCLK/信号坏)LTSSM 卡 Detect/Polling;LinkUp=0
L0 但 CfgRd 超时TL/DLL:FC 初始化未完成(TLP 被门禁挡住)抓包只有 InitFC1 无 InitFC2;无 TLP;DL_Up=0
TLP 有 LCRC 错误不断重放DLL:信号劣化/串扰误码抓包 Ack/Nak 交替、重放风暴;dmesg 无报错
数据错但 LCRC 全过TL 内部:Switch 转发错误 / ECRC 未覆盖内部路径ECRC 使能后注入能抓到;AER 报 ECRC 错误
抓包只见 Ordered Set 无 TLPPHY:卡训练协商(Config/Recovery)LTSSM 非 L0;TS1/TS2 持续交换

测试方法(按排查顺序):

手段命令/工具预期
分层定位第一查lspci -vvv(LnkSta)+ 协议分析仪三层同时观测:PHY 看 LTSSM、DLL 看 Ack/Nak 计数、TL 看 TLP 类型统计
三类包分别过滤协议分析仪(挂 RP 口或链路中)Ordered Set(训练)/ DLLP(链路管理)/ TLP(事务)分开统计
ECRC 覆盖验证两端开 ECRC + 注入翻转翻转包在端点被拒且 AER 上报 ECRC——证明内部转发路径无错
DLL 误码率观测分析仪 Ack/Nak 统计 + 灌流量 ≥1hNak/重放率在预期 BER 内;异常升高指向信号完整性而非协议

判断依据(三态 + 两路径):

判据期望值/特征反例特征
三态齐备L0 + DL_Up=1 + InitFC2 交换完 → 可传 TLPDL_Up=0 时 TL 还发 TLP = 协议违例
三类包位置OS 只在 PHY 间;DLLP 只在相邻端口 DLL 间;TLP 可跨多链路DLLP 出现在 Switch 内部转发 = 实现错误
层间接口信号TL/DLL = 带 FC 门禁的 TLP 流;DLL/PHY = 帧化字节流层间顺序颠倒(FC 未完成就收 TLP)
错误上报路径链路错(LCRC)DLL 自愈不上报 TL;端到端错(ECRC/UR)走 AER链路误码被当 UR 上报 = 判错层,先查信号完整性

七、观测命令:三行命令对三层

1. 链路状态(物理层 + 链路层体检)

lspci -vvv -s 01:00.0 | grep -E "LnkSta|LnkCap"

LnkSta: Speed 16GT/s, Width x16 是协商结果;LnkCap: Speed 32GT/s 是能力上限。能力是 32 协商是 16,先怀疑训练降速(贯穿本系列的经典案例,第 21/22 篇展开)。

2. 设备错误状态(事务层 + 端到端账本)

lspci -vvv -s 01:00.0 | grep -E "DevSta|CorrErr|UncorrErr"

DevSta: CorrErr+ NonFatalErr- FatalErr- UR- 一行看全设备层错误位。UR 出现先问"谁发的什么请求",别急着骂对端。

3. 链路层活动(DL_Active 信号)

setpci -s 01:00.0 CAP_EXP+0x12.w   # Link Status 寄存器

Link Status 的 bit13(0x2000)就是 DL_Active,链路层说"我能传了"的信号。枚举不到设备时,这一步能分清"物理层没起来"还是"链路层没就绪"。


系列首发:CSDN「PCIe 协议特性精讲」| 基准 PCIe 5.0(Base Spec r5.0)| 内核 Linux v7.0.0
数字均以 Base Spec r5.0 为准(6.4/7.0 差异单列),错误欢迎评论区指出,勘误会更新在文末。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值