2. PCIe协议核心概念精讲

PCIe 的分层结构是理解整条互连链路的起点。它把一次数据搬运拆成三个职责清晰、彼此解耦的层次:事务层(Transaction Layer)负责“要做什么”,数据链路层(Data Link Layer)负责“可靠地送到对端”,物理层(Physical Layer)负责“把比特真正发出去”。PolarFire 器件内置的 PCIe 硬核把这套协议栈固化在硅片上,用户逻辑只需面对事务层接口(AXI4 或本地接口),但调试时却必须能看懂三层各自的报文形态,否则一旦链路训练失败或 TLP 被丢弃,就无从下手。

三层分工与报文封装关系

发送方向自顶向下逐层加头:事务层根据请求类型生成 TLP(Transaction Layer Packet),包含 Header、可选 Data Payload 和可选 ECRC;数据链路层在 TLP 前后追加 Sequence Number 与 LCRC,形成 DLLP 承载的帧;物理层再把帧按 Lane 做字节条带化(Byte Striping),加上帧起始/结束符号,经 8b/10b 或 128b/130b 编码后串行发出。接收方向完全对称,逐层剥离并校验。理解这个“加头—校验—剥离”的对称过程,是读懂任何 PCIe 抓包数据的前提。

三层之间还有一类不属于 TLP 的特殊报文——Ordered Set(有序集合)。它由物理层直接产生和消费,不经过事务层,典型成员包括 TS1/TS2(用于链路训练与速率协商)、SKP OS(用于时钟补偿,接收端弹性缓冲溢出前必须插入或删除)、EIEOS(电气空闲退出)、以及 SDS(速率切换)。Ordered Set 是链路能否建立和维持的“底层语言”,链路训练失败时,第一件要看的就是 TS1/TS2 是否成功交换。

PCIe 三层分工与报文封装(发送方向) 事务层 TL 生成 TLP:Header + Data Memory / IO / Config / Msg 可选 ECRC 校验 面向“事务语义” 数据链路层 DLL 加 Seq Num + LCRC ACK / NAK 重传 DLLP:Ack/Nak/PM/Flow 面向“可靠传输” 物理层 PHY Byte Striping 分 Lane 加 Framing 起止符 8b/10b 或 128b/130b 面向“比特传输” TLP 在线路上的实际封装(以 32 位 Memory Write 为例) Seq Num TLP Header Fmt/Type/Len/Addr Data Payload 0~4KB ECRC LCRC ← 链路层加 事务层加 → Ordered Set(物理层专用,不经事务层) TS1 / TS2 SKP OS EIEOS SDS 用于链路训练、时钟补偿、速率切换 配置空间与 BAR 映射 Vendor/Device ID Command/Status BAR0~BAR5 基址寄存器 Capabilities → 决定 TLP 路由

TLP 格式要点

TLP 的 Header 长度由 Fmt 字段决定:Fmt=00 为 3DW(12 字节)头,Fmt=01 为 4DW(16 字节)头,Fmt=10/11 表示带数据。Type 字段区分 Memory Read/Write、Config Read/Write、Completion、Message 等。以 32 位 Memory Write 为例,Header 中携带目标地址和 Length(以 DW 为单位,最大 1024 DW 即 4KB)。PolarFire 的 AXI 桥在把 AXI 写突发转成 TLP 时,会自动按 4KB 边界拆分,这一点在写地址跨页时尤其要注意。

Completion TLP 是另一个高频对象。它携带 Requester ID、Tag、Completer ID、Status 以及返回数据。Tag 用于把 Completion 与原始请求配对,PolarFire 硬核支持的 Tag 数量决定了同时未完成的 Non-Posted 请求上限。如果 Tag 耗尽,新的读请求会被阻塞,表现为读延迟突然增大——这是排查性能问题时常见的“隐形瓶颈”。

DLLP 与重传机制

DLLP 只有 8 字节(含 2 字节 CRC),常见类型有 Ack、Nak、PM(电源管理)、Flow Control(FC1/FC2/FC3 信用更新)。发送端每发一个 TLP 就递增 Sequence Number,接收端校验 LCRC 后回 Ack;若 LCRC 错误则回 Nak,发送端从重传缓冲区重发。PolarFire 的重传缓冲区深度是固定资源,链路误码率高时缓冲区会频繁回卷,吞吐明显下降。因此观察 DLLP 中 Nak 的比例,是判断物理层信号质量的一个间接指标。

配置空间与 BAR 机制

每个 PCIe 功能都有 4KB 配置空间,前 256 字节是标准头,之后是 Capability 链。BAR(Base Address Register)是其中最关键的部分:系统上电枚举时,配置软件向 BAR 写入全 1,读回后根据不可写位判断该 BAR 请求的地址空间大小和对齐要求,再分配一段系统物理地址写回。BAR 的 bit0 为 0 表示 Memory BAR,为 1 表示 IO BAR;Memory BAR 的 bit2 为 1 表示 64 位地址,此时 BAR0 与 BAR1 联合使用。

在 PolarFire 设计中,用户逻辑通过 AXI 接口暴露的地址窗口最终会映射到某个 BAR。例如把 BAR0 配置为 64 位、请求 1MB 空间,则主机侧 lspci -vv 会显示 Region 0: Memory at 0x... (64-bit, non-prefetchable) [size=1M]。主机对该窗口的读写会被转换成 Memory TLP,经事务层路由到 PolarFire 内部对应的 AXI 从端口。若 BAR 大小与实际逻辑窗口不匹配,会出现“地址能读但数据错位”或“访问越界无响应”两类典型故障。

调试建议:链路训练阶段先确认 TS1/TS2 是否完成交换(LTSSM 进入 L0);枚举阶段用 lspci -vv 核对 BAR 大小与 64 位标志;数据阶段用协议分析仪抓 TLP,重点看 Fmt/Type、Length、Tag 与 Completion Status。

三层协同的一个完整读事务

主机发起一次 Memory Read:事务层生成 3DW 或 4DW 的 Read Request TLP,Tag 分配为 0x1A;数据链路层加 Seq=0x20 与 LCRC 后交给物理层;物理层条带化到各 Lane 发出。PolarFire 收到后逐层校验,事务层解析地址命中 BAR0 窗口,从用户逻辑取回数据,生成带相同 Tag 的 Completion with Data(CplD)TLP 返回。主机收到 CplD 后按 Tag 匹配,完成一次读。若中途 LCRC 出错,接收端回 Nak,发送端重传,主机侧只看到延迟增加而不会看到数据错误——这正是分层校验的价值所在。