3. TLP包结构基础:TLP组成、Fmt/Type字段详解与路由方式
TLP(Transaction Layer Packet)是PCIe事务层与数据链路层之间交换的基本信息单元,也是驱动开发者最常打交道的协议层数据格式。理解TLP的组成、头部字段含义以及路由机制,是后续解析RC端TLP日志、调试FPGA多队列中断的基石。
3.1 TLP包的宏观组成
下图展示了TLP在PCIe协议栈中的位置及其内部结构。TLP在事务层生成,向下传递给数据链路层添加序列号和LCRC,再经物理层编码后发送到链路上。
3.2 TLP头格式详解
TLP头是解析TLP的核心,其长度根据TLP类型不同分为3DW(12字节)、4DW(16字节)或5DW(20字节)。下图展示了通用TLP头的字段布局(以4DW为例):
3.3 Fmt/Type字段详解
Fmt[1:0]和Type[4:0]是TLP头中最关键的两个字段,它们共同编码了TLP的类型、是否带数据、以及头部长度。下表列出了最常见的组合:
| Fmt[1:0] | Type[4:0] | TLP类型 | 头长度 | 数据负载 | 典型用途 |
|---|---|---|---|---|---|
| 00 | 00000 | MRd(存储器读) | 3DW/4DW | 无 | RC读取EP的BAR空间 |
| 01 | 00000 | MRdLk(存储器读锁定) | 3DW/4DW | 无 | 已废弃,不建议使用 |
| 10 | 00000 | MWr(存储器写) | 3DW/4DW | 有 | RC写数据到EP的BAR空间 |
| 00 | 00100 | IORd(IO读) | 3DW | 无 | 访问IO空间(x86平台) |
| 10 | 00100 | IOWr(IO写) | 3DW | 有 | 访问IO空间(x86平台) |
| 00 | 01010 | CfgRd0(配置读0) | 3DW | 无 | 读取EP的配置空间(Bus=0) |
| 10 | 01010 | CfgWr0(配置写0) | 3DW | 有 | 写入EP的配置空间(Bus=0) |
| 00 | 01011 | CfgRd1(配置读1) | 3DW | 无 | 读取EP的配置空间(Bus≠0) |
| 10 | 01011 | CfgWr1(配置写1) | 3DW | 有 | 写入EP的配置空间(Bus≠0) |
| 00 | 10011 | Msg(消息) | 4DW | 无 | 中断、错误、电源管理等 |
| 10 | 10011 | MsgD(消息带数据) | 4DW | 有 | 带数据的消息(如PME_Turn_Off) |
| 01 | 00000 | Cpl(完成) | 3DW | 无 | 对MRd的响应(无数据) |
| 11 | 00000 | CplD(完成带数据) | 3DW | 有 | 对MRd的响应(含读取数据) |
- Fmt[1:0] = 00:无数据,3DW头(仅用于MRd、IORd、CfgRd、Cpl)
- Fmt[1:0] = 01:无数据,4DW头(用于64位地址的MRd、MRdLk)
- Fmt[1:0] = 10:有数据,3DW头(用于MWr、IOWr、CfgWr、MsgD)
- Fmt[1:0] = 11:有数据,4DW头(用于64位地址的MWr、MsgD)
3.4 TLP路由方式
PCIe支持三种TLP路由方式,用于将TLP从发送端传递到正确的目标设备。路由方式由TLP头中的字段隐式或显式指定。
3.4.1 地址路由(Address Routing)
地址路由是最常用的路由方式,用于存储器事务(MRd/MWr)和IO事务(IORd/IOWr)。TLP头中包含目标地址,交换机(Switch)根据地址查找路由表,将TLP转发到对应的下游端口。
- 32位地址:使用3DW头,地址位于DW1(Byte 4-7)。
- 64位地址:使用4DW头,低32位在DW1,高32位在DW3。
- 路由依据:交换机根据地址落在哪个下游端口的BAR范围内进行转发。
3.4.2 ID路由(ID Routing)
ID路由用于配置事务(CfgRd/CfgWr)和消息事务(Msg/MsgD)。TLP头中包含目标设备的Bus Number(总线号)、Device Number(设备号)和Function Number(功能号),即BDF。
- 配置事务:目标BDF直接放在TLP头的DW1中(Bus[7:0]、Device[4:0]、Function[2:0])。
- 消息事务:目标BDF放在DW1中,但消息路由还依赖消息路由表(Message Routing Table)。
- 路由依据:交换机根据配置空间中的总线号范围(Secondary Bus Number、Subordinate Bus Number)决定转发路径。
3.4.3 隐式路由(Implicit Routing)
隐式路由仅用于消息事务(Msg/MsgD),且目标不是某个具体设备,而是RC本身、所有设备或上游设备。TLP头中不包含显式的地址或BDF,而是通过Type字段中的Routing[2:0]子字段(Type[2:0])来指示路由行为。
| Routing[2:0] | 路由行为 | 典型消息 | 说明 |
|---|---|---|---|
| 000 | 路由到RC | INTx中断、错误消息 | 消息最终到达Root Complex |
| 001 | 路由到所有设备 | PME_Turn_Off、热插拔 | 广播到所有下游设备 |
| 010 | 路由到上游设备 | Vendor Defined | 向父级设备发送 |
| 011 | 路由到本地(自身) | 调试消息 | 不转发,由接收端自身处理 |
| 1xx | 保留 | — | 未使用 |
3.5 实战:如何从TLP头中解析路由方式
在驱动调试中,当你从RC端捕获到一个TLP时,可以通过以下步骤快速判断其路由方式:
- 读取Fmt[1:0]和Type[4:0]:确定TLP类型(MRd、MWr、CfgRd、Msg等)。
- 判断路由方式:
- 如果Type为存储器事务或IO事务 → 地址路由,解析DW1中的地址。
- 如果Type为配置事务 → ID路由,解析DW1中的Bus/Device/Function。
- 如果Type为消息事务 → 检查Type[2:0](Routing字段):
- 000 → 隐式路由到RC
- 001 → 隐式路由到所有设备
- 010 → 隐式路由到上游
- 其他 → 可能使用ID路由(消息路由表)
- 验证数据负载:根据Fmt[0]判断是否带数据(Fmt[0]=1表示有数据负载)。
“存IO看地址,配置看BDF,消息看路由,完成看请求者ID。”
存储器/IO事务 → 地址路由;配置事务 → ID路由;消息事务 → 隐式路由或ID路由;完成(Cpl/CplD)→ 根据请求者ID原路返回。
3.6 本章小结
| 知识点 | 关键内容 | 调试价值 |
|---|---|---|
| TLP组成 | 前缀(可选) + TLP头(必选) + 数据(可选) + ECRC(可选) | 识别TLP边界,区分头和数据 |
| Fmt/Type字段 | 共同决定TLP类型、头长度、是否带数据 | 快速分类TLP,判断事务方向 |
| 地址路由 | 存储器/IO事务,目标=地址,交换机BAR匹配 | 分析DMA、BAR访问、MSI中断地址 |
| ID路由 | 配置/消息事务,目标=BDF,交换机Bus范围匹配 | 枚举设备、配置空间读写 |
| 隐式路由 | 消息事务,目标由Type[2:0]指定 | INTx中断、错误、电源管理消息 |