Skip to content

Latest commit

 

History

History
491 lines (341 loc) · 29.5 KB

File metadata and controls

491 lines (341 loc) · 29.5 KB

第5章 TLP元素

来源: MindShare《PCI Express Technology 3.0》第5章
原文页码: 第169-214页
翻译日期: 2026-03-17


前一章回顾

前一章描述了功能通过基地址寄存器(Base Address Registers, BARs)请求地址空间(存储器地址空间或I/O地址空间)的目的和方法,以及软件如何必须在所有桥中设置基址/限制寄存器(Base/Limit registers)以将TLP从源端口路由到正确的目的端口。本章还讨论了PCI Express中TLP路由的一般概念,包括基于地址的路由、基于ID的路由和隐式路由。

本章内容

信息在PCI Express设备之间以数据包的形式传输。三大类数据包分别是事务层包(Transaction Layer Packets, TLPs)、数据链路层包(Data Link Layer Packets, DLLPs)和有序集(Ordered Sets)。本章描述了各种TLP的用途、格式和定义,以及相关字段的详细信息。DLLPs在第9章"DLLP元素"(第307页)中单独描述。

下一章预告

下一章讨论流量控制协议(Flow Control Protocol)的目的和详细操作。流量控制旨在确保发送方永远不会发送接收方无法接受的事务层包(TLP)。这可以防止接收缓冲区溢出,并消除了PCI风格低效操作(如断开连接、重试和等待状态)的需要。


5.1 基于数据包的协议介绍

5.1.1 概述

与并行总线不同,像PCIe这样的串行传输总线不使用控制信号来识别链路上在特定时间发生的情况。相反,它们发送的比特流必须具有预期的大小和可识别的格式,以便接收方能够理解其内容。此外,PCIe在数据包传输期间不使用任何即时握手信号。

除了逻辑空闲符号(Logical Idle symbols)和称为有序集(Ordered Sets)的物理层数据包外,信息在活动的PCIe链路上以称为数据包的基本块传输,这些数据包由符号组成。交换的两大类数据包是高层事务层包(TLPs)和称为数据链路层包(DLLPs)的低层链路维护数据包。这些数据包及其流向如图5-1所示。有序集也是数据包,但它们不像TLP和DLLP那样用起始和结束符号进行成帧。它们也不像TLP和DLLP那样进行字节条带化(byte striped)。有序集数据包而是在链路的所有通道(Lanes)上进行复制。

5.1.2 基于数据包协议的动机

使用基于数据包的协议有三个明显的优势,特别是在数据完整性方面:

1. 数据包格式定义明确

早期的总线(如PCI)允许传输不确定大小的数据,使得在传输结束前无法识别有效载荷边界。此外,任一设备都可以在传输完成前终止传输,这使得发送方难以计算和发送覆盖整个有效载荷的校验和或CRC。相反,PCI使用简单的奇偶校验方案,并在每个数据阶段进行检查。

相比之下,PCIe数据包具有已知的大小和格式。数据包头部指示数据包类型并包含必需和可选字段。头部字段的大小是固定的,除了地址可以是32位或64位。一旦传输开始,接收方不能暂停或提前终止它。这种结构化格式允许在TLP中包含信息以帮助可靠传输,包括成帧符号、CRC和数据包序列号。

2. 成帧符号定义数据包边界

在Gen1和Gen2操作模式下使用8b/10b编码时,每个发送的TLP和DLLP数据包都由起始和结束控制符号进行成帧,为接收方明确定义数据包边界。这比PCI和PCI-X有很大的改进,在PCI和PCI-X中,单个FRAME#信号的置位和复位表示事务的开始和结束。该信号(或任何其他控制信号)上的毛刺可能导致目标设备误解总线事件。PCIe接收方必须正确解码完整的10位符号后才能确定链路活动的开始或结束,因此意外或无法识别的符号更容易被识别和处理为错误。

对于Gen3使用的128b/130b编码,不再使用控制字符,也没有成帧符号。有关Gen3编码与早期版本差异的更多信息,请参见第12章"物理层-逻辑(Gen3)"(第407页)。

3. CRC保护整个数据包

与PCI在事务的地址和数据阶段使用的带外奇偶校验信号不同,PCIe的带内CRC值验证整个数据包的无错误传输。TLP数据包还具有由发送方数据链路层附加的序列号,以便如果在接收方检测到错误,可以自动重新发送有问题的数据包。发送方在重放缓冲区(Retry Buffer)中保存每个发送的TLP副本,直到它被接收方确认。这种TLP确认机制称为Ack/Nak协议(在第10章"Ack/Nak协议"(第317页)中描述),构成了链路级TLP错误检测和纠正的基础。这种Ack/Nak协议错误恢复机制允许在问题发生的位置或链路及时解决问题,但需要本地硬件解决方案来支持它。


5.2 事务层包(TLP)详解

在PCI Express中,高层事务起源于发送设备的核心逻辑,并在接收设备的核心逻辑处终止。事务层对这些请求进行操作,在发送方组装出站TLP,在接收方解释它们。在此过程中,每个设备的数据链路层和物理层也为最终的数据包组装做出贡献。

5.2.1 TLP组装与拆解

TLP在链路发送侧的组装和接收侧的拆解的一般流程如图5-2所示。现在让我们逐步了解从数据包创建到将其传递到接收方核心逻辑的整个过程。

事务层包组装和拆解的关键阶段如下所列。列表编号对应于图5-2中的编号。

发送方:

  1. 设备A的核心逻辑向其PCIe接口发送请求。如何完成这一点超出了规范或本书的范围。请求包括:

    • 目标地址或ID(路由信息)
    • 源信息,如请求者ID(Requester ID)和标签(Tag)
    • 事务类型/数据包类型(要执行的命令,如存储器读)
    • 数据有效载荷大小(如果有)以及数据有效载荷(如果有)
    • 流量类别(Traffic Class,用于分配数据包优先级)
    • 请求属性(No Snoop、Relaxed Ordering等)
  2. 基于该请求,事务层构建TLP头部,附加任何数据有效载荷,并可选地计算和附加摘要(端到端CRC,ECRC)(如果支持并已启用)。此时,TLP被放入虚拟通道(Virtual Channel)缓冲区。虚拟通道根据事务排序规则管理TLP序列,并在将TLP传递到数据链路层之前验证接收方是否有足够的流量控制信用量(flow control credits)来接受TLP。

  3. 当TLP到达数据链路层时,它被分配一个序列号,然后基于TLP内容和该序列号计算链路CRC。结果数据包的副本被保存在重放缓冲区中以防传输错误,同时它也被传递到物理层。

  4. 物理层执行多项操作以准备数据包进行串行传输,包括字节条带化(byte striping)、加扰(scrambling)、编码和位串行化。对于Gen1和Gen2设备,使用8b/10b编码时,控制字符STP和END被添加到数据包的两端。最后,数据包在链路上传输。在Gen3模式下,STP令牌被添加到TLP的前端,但END不被添加到数据包末尾。相反,STP令牌包含有关TLP数据包大小的信息。

接收方:

  1. 在接收方(本例中为设备B),为传输准备数据包所做的所有操作现在必须撤销。物理层反串行化比特流,解码结果符号,并取消字节条带化。控制字符在这里被移除,因为它们只在物理层有意义,然后数据包被转发到数据链路层。

  2. 数据链路层计算CRC并将其与接收到的CRC进行比较。如果匹配,则检查序列号。如果没有错误,则移除CRC和序列号,并将TLP传递到接收方的事务层,并通过返回Ack DLLP通知发送方接收良好。如果发生错误,则将返回Nak,发送方将重放其重放缓冲区中的TLP。

  3. 在事务层,TLP被解码,信息被传递到核心逻辑以采取适当的操作。如果接收设备是此数据包的最终目标,它会检查ECRC错误,并将任何相关的ECRC错误条件报告给核心逻辑(如果有的话)。

5.2.2 TLP结构

事务层包中每个字段的基本用法如表5-1所示。

表5-1: TLP头部类型字段定义事务变体

TLP组件 协议层 组件用途
头部(Header) 事务层 3或4个双字(12或16字节)。格式随类型变化,但头部定义参数,包括:
• 事务类型
• 目标地址、ID等
• 传输大小(如果有)、字节使能
• 属性
• 流量类别
数据(Data) 事务层 可选的1-1024 DW有效载荷,由字节使能或字节对齐的起始和结束地址限定。注意不能指定长度为零,但可以通过指定长度为1 DW且所有字节使能为零来近似零长度读(在某些情况下有用)。完成者返回的结果数据将是未定义的,但请求者不使用它,因此结果相同。
摘要/ECRC(Digest/ECRC) 事务层 可选。存在时,ECRC始终为1 DW大小。

5.3 通用TLP头部格式

5.3.1 概述

图5-3说明了通用TLP 4DW头部的格式和内容。在本节中,总结了几乎所有事务共有的字段。与特定事务类型相关的头部格式差异将在后面介绍。

5.3.2 通用头部字段摘要

表5-2总结了通用TLP头部中每个字段的大小和用途。注意,图5-3中标记为"R"的字段是保留字段,应设置为零。

表5-2: 通用头部字段摘要

头部字段 头部位置 字段用途
Fmt[2:0] (格式) Byte 0 Bit 7:5 这些位编码有关头部大小和数据有效载荷是否将成为TLP一部分的信息:
00b = 3DW头部,无数据
01b = 4DW头部,无数据
10b = 3DW头部,有数据
11b = 4DW头部,有数据
低于4GB的地址必须使用3DW头部。规范指出,如果将4DW头部用于低于4GB的地址且64位地址的高32位设置为零,则接收方行为未定义。
Type[4:0] (类型) Byte 0 Bit 4:0 这些位编码此TLP使用的事务变体。Type字段与Fmt[1:0]字段一起用于指定事务类型、头部大小以及是否存在数据有效载荷。详见第178页的"通用头部字段详解"。
TC[2:0] (流量类别) Byte 1 Bit 6:4 这些位编码要应用于此TLP及其相关完成(如果有)的流量类别:
000b = 流量类别0(默认)
...
111b = 流量类别7
TC 0是默认类别,而TC 1-7用于提供差异化服务。有关其他信息,请参见第247页的"流量类别(TC)"。
Attr[2] (属性) Byte 1 Bit 2 这第三个属性位指示是否为此TLP使用基于ID的排序。要了解更多信息,请参见第301页的"基于ID的排序(IDO)"。
TH (TLP处理提示) Byte 1 Bit 0 指示何时包含TLP提示,以让系统了解如何最好地处理此TLP。有关其用法的讨论,请参见第899页的"TPH(TLP处理提示)"。
TD (TLP摘要) Byte 2 Bit 7 如果TD = 1,则可选的4字节TLP摘要已作为ECRC值包含在此TLP中。
一些规则:
• 所有接收方必须根据此位检查摘要字段的存在。
• TD = 1但没有摘要的TLP被视为格式错误的TLP。
• 如果设备支持检查ECRC且TD=1,则必须执行ECRC检查。
• 如果设备在最终目的地不支持检查ECRC(可选),则必须忽略摘要。
有关此主题的更多信息,请参见第653页的"CRC"和第657页的"ECRC生成和检查"。
EP (数据中毒) Byte 2 Bit 6 如果EP = 1,则此数据附带的数据应被视为无效,尽管事务被允许正常完成。有关中毒数据包的更多信息,请参阅第660页的"数据中毒"。
Attr[1:0] (属性) Byte 2 Bit 5:4 Bit 5 = 宽松排序:当设置为1时,为此TLP启用PCI-X宽松排序。如果为0,则使用严格的PCI排序。
Bit 4 = 不窥探:当设置为1时,请求者指示此TLP不存在主机缓存一致性问题。因此系统硬件可以通过跳过此请求的正常处理器缓存窥探来节省时间。当为0时,需要PCI类型的缓存窥探保护。
AT[1:0] (地址类型) Byte 2 Bit 3:2 对于存储器和原子请求,此字段支持虚拟化系统的地址转换。转换协议在名为地址转换服务(Address Translation Services)的单独规范中描述,其中可以看到该字段编码为:
00 = 默认/未转换
01 = 转换请求
10 = 已转换
11 = 保留
Length[9:0] (长度) Byte 2 Bit 1:0
Byte 3 Bit 7:0
TLP数据有效载荷传输大小,以DW为单位。编码:
00 0000 0001b = 1DW
00 0000 0010b = 2DW
...
11 1111 1111b = 1023 DW
00 0000 0000b = 1024 DW
Last DW Byte Enables[3:0] (最后一个DW字节使能) Byte 7 Bit 7:4 这四个高有效的位与有效载荷最后一个双字中的字节一一映射。
Bit 7 = 1:最后一个DW中的字节3有效;否则无效
Bit 6 = 1:最后一个DW中的字节2有效;否则无效
Bit 5 = 1:最后一个DW中的字节1有效;否则无效
Bit 4 = 1:最后一个DW中的字节0有效;否则无效
First DW Byte Enables[3:0] (第一个DW字节使能) Byte 7 Bit 3:0 这四个高有效的位与有效载荷第一个双字中的字节一一映射。
Bit 3 = 1:第一个DW中的字节3有效;否则无效
Bit 2 = 1:第一个DW中的字节2有效;否则无效
Bit 1 = 1:第一个DW中的字节1有效;否则无效
Bit 0 = 1:第一个DW中的字节0有效;否则无效

5.3.3 通用头部字段详解

在以下各节中,我们描述图5-3中所示的每个TLP头部字段的详细信息。

头部类型/格式字段编码

表5-3总结了TLP头部类型和格式(Fmt)字段中使用的编码。

表5-3: TLP头部类型和格式字段编码

TLP FMT[2:0] TYPE[4:0]
存储器读请求 (MRd) 000 = 3DW, 无数据
001 = 4DW, 无数据
0 0000
存储器读锁定请求 (MRdLk) 000 = 3DW, 无数据
001 = 4DW, 无数据
0 0001
存储器写请求 (MWr) 010 = 3DW, 有数据
011 = 4DW, 有数据
0 0000
I/O读请求 (IORd) 000 = 3DW, 无数据 0 0010
I/O写请求 (IOWr) 010 = 3DW, 有数据 0 0010
配置类型0读请求 (CfgRd0) 000 = 3DW, 无数据 0 0100
配置类型0写请求 (CfgWr0) 010 = 3DW, 有数据 0 0100
配置类型1读请求 (CfgRd1) 000 = 3DW, 无数据 0 0101
配置类型1写请求 (CfgWr1) 010 = 3DW, 有数据 0 0101
消息请求 (Msg) 001 = 4DW, 无数据 1 0 rrr*
(见路由字段)
带数据消息请求 (MsgD) 011 = 4DW, 有数据 1 0rrr*
(见路由字段)
完成 (Cpl) 000 = 3DW, 无数据 0 1010
带数据完成 (CplD) 010 = 3DW, 有数据 0 1010
锁定完成 (CplLk) 000 = 3DW, 无数据 0 1011
带数据锁定完成 (CplDLk) 010 = 3DW, 有数据 0 1011
Fetch and Add原子操作请求 010 = 3DW, 有数据
011 = 4DW, 有数据
0 1100
无条件交换原子操作请求 010 = 3DW, 有数据
011 = 4DW, 有数据
0 1101
比较并交换原子操作请求 010 = 3DW, 有数据
011 = 4DW, 有数据
0 1110
本地TLP前缀 100 = TLP前缀 0L3L2L1L0
端到端TLP前缀 100 = TLP前缀 1E3E2E1E0

摘要/ECRC字段

TLP摘要位报告端到端CRC(ECRC)的存在。如果软件支持并启用此可选功能,设备会为它们发起的所有TLP计算并应用ECRC。注意,使用ECRC要求设备包含可选的高级错误报告寄存器,因为其功能和控制寄存器位于那里。

ECRC生成和检查。 ECRC覆盖在TLP跨拓扑转发时不会更改的所有字段。但是,有两个位可以在数据包穿越拓扑时合法更改:

  • 类型字段的位0 — 当配置事务跨桥转发并从类型1更改为类型0配置事务时(因为它已到达目标总线),会发生变化。这是通过更改类型字段的位0来实现的。
  • 错误/中毒(EP)位 — 如果与数据包关联的数据被视为已损坏,则此位可能在TLP穿越拓扑时发生变化。这是一个称为错误转发的可选功能。

谁检查ECRC? ECRC的预期目标是TLP的最终接收方。检查LCRC可验证给定链路上没有传输错误,但在将数据包转发到下一个链路之前,会在路由元件(交换机或根复合体)的出端口重新计算,这可能掩盖路由元件中的内部错误。为了防止这种情况,ECRC在请求者和完成者之间的传输过程中保持不变地携带。当目标设备检查ECRC时,有任何很高的概率检测到沿途的任何错误可能性。

规范对交换机在ECRC检查中的作用做了两个说明:

  • 支持ECRC检查的交换机会对目标位于交换机本身内部的TLP执行此检查。"在所有其他TLP上,交换机必须保留ECRC(原样转发)作为TLP的组成部分。"
  • "注意,交换机可以对通过交换机的TLP执行ECRC检查。交换机检测到的ECRC错误以与其他设备报告相同的方式报告,但不会更改TLP通过交换机的传输。"

5.4 使用字节使能

5.4.1 概述

与PCI一样,PCIe需要一种机制来将其DW对齐地址与有时需要非DW对齐传输大小或起始/结束地址的需求相协调。为此,PCI Express使用图5-3和表5-2中介绍的两个字节使能字段。第一个DW字节使能字段和最后一个DW字节使能字段允许请求者限定传输的第一个和最后一个双字中感兴趣的字节。

5.4.2 字节使能规则

  1. 字节使能位是高有效的。值为0表示完成者不应使用数据有效载荷中相应的字节。值为1表示应该使用。
  2. 如果有效数据全部在单个双字内,最后一个DW字节使能字段必须 = 0000b。
  3. 如果头部长度字段指示传输超过1DW,第一个DW字节使能必须至少有一个位使能。
  4. 如果长度字段指示传输3DW或更多,则第一个DW字节使能字段和最后一个DW字节使能字段必须设置连续位。在这些情况下,字节使能仅用于给出相对于DW对齐地址的有效起始和结束地址的字节偏移量。
  5. 如果传输是1DW,则允许第一个DW字节使能字段中的不连续字节使能位模式。
  6. 如果传输在一到两个DW之间,则允许第一个和第二个DW字节使能字段中的不连续字节使能位模式。
  7. 传输长度为1DW且没有字节使能设置的写请求是合法的,但对完成者没有影响。
  8. 如果1DW的读请求没有字节使能设置,完成者返回1DW的未定义数据有效载荷。这可用作刷新机制,利用事务排序规则强制所有先前发布的写操作在完成返回之前写入存储器。

5.4.3 字节使能示例

在这种情况下,字节使能用法的示例如图5-4所示。注意,传输长度必须从第一个有任何有效字节使能的DW延伸到最后一个有任何有效字节的DW。因为传输超过2DW,所以字节使能只能用于指定传输的起始地址位置(2d)和结束地址位置(34d)。

图5-4: 使用第一个DW和最后一个DW字节使能字段

![长度字段](../images/图5-4.长度字段.png)

示例: 从地址0x00000002开始,传输34字节到地址0x00000024(不包含)

存储器地址空间:
0x00000000  [00][01][02][03]  ← 第一个DW (部分有效,字节0无效)
0x00000004  [04][05][06][07]
...         ...
0x00000020  [20][21][22][23]  ← 最后一个DW (部分有效,所有字节有效)

第一个DW字节使能:  1110b (位3:0 = 1110b,字节0无效,字节1-3有效)
                  对应地址0x00000002-0x00000003
最后一个DW字节使能: 1111b (位7:4 = 1111b,所有字节有效)
                  对应地址0x00000020-0x00000023

实际传输字节: 从地址0x00000002到0x00000023(包含),共34字节
             (0x00000003-0x00000002+1) + (0x0000001F-0x00000004+1) + (0x00000023-0x00000020+1)
             = 2 + 28 + 4 = 34字节

5.5 事务描述符字段

当事务在请求者和完成者之间移动时,有必要唯一标识事务,因为在任何时刻可能有多个拆分事务从同一请求者排队。为了帮助解决这个问题,规范定义了几个重要的头部字段,它们形成唯一的事务描述符,如图5-5所示。

虽然事务描述符字段不在相邻的头部位置,但它们共同描述了关键事务属性,包括:

事务ID。 请求者ID(请求者的总线、设备和功能号)和TLP的标签字段的组合。

流量类别。 流量类别(TC)由请求者基于核心逻辑请求添加,并通过拓扑不变地传输到完成者。在每个链路上,TC被映射到其中一个虚拟通道。

事务属性。 基于ID的排序、宽松排序和不窥探位也与请求数据包一起传输到完成者。

5.5.1 带数据有效载荷TLP的附加规则

当TLP包含数据有效载荷时,适用以下规则:

  1. 长度字段仅指数据有效载荷。
  2. 有效载荷中的第一个数据字节(紧接在头部之后)始终与最低(起始)地址相关联。
  3. 长度字段始终表示传输的DW整数。部分DW使用第一个和最后一个字节使能字段进行限定。
  4. 规范指出,当完成者响应单个存储器请求返回多个事务时,每个中间事务必须在自然对齐的64或128字节地址边界结束(对于根复合体)。这由称为读完成边界(RCB)的配置位控制。所有其他设备遵循PCI-X协议,在自然对齐的128字节边界处拆分此类事务。这使得桥中的缓冲区管理更简单。
  5. 发送消息请求时,长度字段是保留的,除非消息是带数据的消息(MsgD)。
  6. TLP数据有效载荷不得超过设备控制寄存器中Max_Payload_Size字段的当前值。只有写事务有数据有效载荷,因此此限制不适用于读请求。
  7. 接收方需要在写操作期间检查Max_Payload_Size限制的违反情况,违反被视为格式错误的TLP。
  8. 接收方还必须检查长度字段值与TLP中实际传输的数据量之间的差异。这种类型的违反也被视为格式错误的TLP。
  9. 请求不得混合会导致存储器访问跨越4KB边界的起始地址和传输长度组合。虽然检查这一点是可选的,但如果看到则被视为格式错误的TLP。

5.6 特定TLP格式:请求和完成TLP

在本节中,描述了用于完成特定事务类型的3DW和4DW头部的格式。前面描述的许多通用字段适用,但重点放在特定事务类型处理不同的字段上。TLP头部格式的详细描述在以下章节中针对TLP类型:1)I/O请求,2)存储器请求,3)配置请求,4)完成,和5)消息请求。

5.6.1 I/O请求

虽然规范不鼓励使用I/O事务,但为传统设备和可能需要依赖驻留在系统I/O映射而非存储器映射中的兼容设备的软件提供了允许。虽然I/O事务在技术上可以访问32位I/O范围,但实际上许多系统(和CPU)将I/O访问限制在此范围的下16位(64KB)。图5-6描述了系统I/O映射和16位及32位地址边界。

不标识自己为传统设备的设备不允许在其配置基地址寄存器中请求I/O地址空间。

I/O请求头部格式

3 DW I/O请求头部如图5-7所示,每个字段在后续章节中描述。

图5-7: 3DW I/O请求头部格式

![数据负载](../images/图5-7.数据负载.jpg)

I/O请求TLP

成帧序列
(STP)  头部  数据  摘要  LCRC  成帧(End)

+0          +1          +2          +3
+----------------+----------------+----------------+----------------+
|0x0|0 0010 |0|000|R|00|R|0|0|0|0|00| 0 0000000001 | Byte 0
+----------------+----------------+----------------+----------------+
|                    Requester ID             | Tag |0000|1st DW BE| Byte 4
+----------------+----------------+----------------+----------------+
|            Address [31:2]           |   R   |
+-------------------------------------+-------+

字段值:
- Fmt = 000b (I/O读) 或 010b (I/O写)
- Type = 00010b
- TC = 000b (必须为0)
- Length = 0000000001b (1DW)
- Last DW BE = 0000b (I/O请求只能为1DW)

I/O请求头部字段

I/O请求头部中每个字段的位置和用途如表5-4所述。

表5-4: I/O请求头部字段

字段名称 头部字节/位 功能
Fmt[2:0] (格式) Byte 0 Bit 7:5 I/O请求的数据包格式:
000b = I/O读(3DW无数据)
010b = I/O写(3DW有数据)
Type[4:0] (类型) Byte 0 Bit 4:0 I/O请求的数据包类型为00010b
TC[2:0] (流量类别) Byte 1 Bit 6:4 I/O请求的流量类别始终为零,确保这些数据包永远不会干扰任何高优先级数据包。
Attr[2] (属性) Byte 1 Bit 2 基于ID的排序不适用于I/O请求,此位是保留的。
TH (TLP处理提示) Byte 1 Bit 0 TLP处理提示不适用于I/O请求,此位是保留的。
TD (TLP摘要) Byte 2 Bit 7 指示TLP末尾摘要字段(ECRC)的存在。
EP (数据中毒) Byte 2 Bit 6 指示数据有效载荷(如果存在)是否中毒。
Attr[1:0] (属性) Byte 2 Bit 5:4 宽松排序和不窥探位不适用于I/O请求,始终为零。

5.7 Linux内核实现参考

本节补充Linux内核中与TLP元素相关的实现说明。

5.7.1 PCIe设备控制寄存器

Linux内核在include/uapi/linux/pci_regs.h中定义了PCIe设备控制寄存器的相关位:

/* Device Control register (PCI_EXP_DEVCTL) */
#define PCI_EXP_DEVCTL_CERE     0x0001  /* Correctable Error Reporting En. */
#define PCI_EXP_DEVCTL_NFERE    0x0002  /* Non-Fatal Error Reporting Enable */
#define PCI_EXP_DEVCTL_FERE     0x0004  /* Fatal Error Reporting Enable */
#define PCI_EXP_DEVCTL_URRE     0x0008  /* Unsupported Request Reporting En. */
#define PCI_EXP_DEVCTL_RELAX_EN 0x0010  /* Enable relaxed ordering */
#define PCI_EXP_DEVCTL_PAYLOAD  0x00e0  /* Max_Payload_Size */
#define PCI_EXP_DEVCTL_EXT_TAG  0x0100  /* Extended Tag Field Enable */
#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */

5.7.2 宽松排序(Relaxed Ordering)实现

// drivers/pci/probe.c

/**
 * pcie_relaxed_ordering_enabled - Probe for PCIe relaxed ordering enable
 * @dev: PCI device to query
 *
 * Returns true if the device has enabled relaxed ordering attribute.
 */
bool pcie_relaxed_ordering_enabled(struct pci_dev *dev)
{
    u16 v;
    pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &v);
    return !!(v & PCI_EXP_DEVCTL_RELAX_EN);
}
EXPORT_SYMBOL(pcie_relaxed_ordering_enabled);

static void pci_configure_relaxed_ordering(struct pci_dev *dev)
{
    struct pci_dev *root;

    if (dev->is_virtfn)
        return;

    if (!pcie_relaxed_ordering_enabled(dev))
        return;

    root = pcie_find_root_port(dev);
    if (!root)
        return;

    /* 如果根端口不支持Relaxed Ordering,则禁用 */
    if (root->dev_flags & PCI_DEV_FLAGS_NO_RELAXED_ORDERING) {
        pcie_capability_clear_word(dev, PCI_EXP_DEVCTL,
                       PCI_EXP_DEVCTL_RELAX_EN);
        pci_info(dev, "Relaxed Ordering disabled...\n");
    }
}

5.7.3 最大载荷大小(Max Payload Size)实现

// drivers/pci/pci.c

/**
 * pcie_get_mps - Get PCIe Maximum Payload Size
 * @dev: PCI device to query
 */
int pcie_get_mps(struct pci_dev *dev)
{
    u16 ctl;
    pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
    /* Max Payload Size编码: 000b=128B, 001b=256B, 010b=512B, ... */
    return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
}

/**
 * pcie_set_mps - Set PCIe Maximum Payload Size
 * @dev: PCI device to configure
 * @mps: Desired Maximum Payload Size
 */
int pcie_set_mps(struct pci_dev *dev, int mps)
{
    u16 ctl, v;
    pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
    v = (ffs(mps) - 8) << 5;
    pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
                         PCI_EXP_DEVCTL_PAYLOAD, v);
    return 0;
}

5.7.4 Phytium PCIe控制器特定实现

// drivers/pci/controller/pcie-phytium-register.h

/* Phytium PCIe BAR配置 */
#define PHYTIUM_PCI_BAR(bar_num)    (0xe4 + bar_num * 4)
#define  BAR_IO_TYPE                (1 << 0)
#define  BAR_MEM_TYPE               (0 << 0)
#define  BAR_MEM_64BIT              (1 << 2)
#define  BAR_MEM_PREFETCHABLE       (1 << 3)

/* Phytium PCIe地址转换(ATR) */
#define PHYTIUM_PCI_WIN0_BASE       0x600
#define PHYTIUM_PCI_WIN0_SRC_ADDR0(table) \
    (PHYTIUM_PCI_WIN0_BASE + 0X20 * table + 0x0)
#define  ATR_IMPL                   0x1
#define  ATR_SIZE_MASK              0x3f
#define  ATR_SIZE_SHIFT             1

5.7.5 非合规设备处理(quirk)

// drivers/pci/quirks.c

/*
 * 某些设备在完成事务中不复制请求的TLP属性,
 * 可能导致Malformed TLP错误。
 * 解决方法是在上游根端口禁用RO和No Snoop属性。
 */
static void quirk_disable_root_port_attributes(struct pci_dev *pdev)
{
    struct pci_dev *root_port = pcie_find_root_port(pdev);
    
    pcie_capability_clear_and_set_word(root_port, PCI_EXP_DEVCTL,
                       PCI_EXP_DEVCTL_RELAX_EN |
                       PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
}

参考来源:

  • Linux内核: include/uapi/linux/pci_regs.h
  • Linux内核: drivers/pci/probe.c
  • Linux内核: drivers/pci/pci.c
  • Linux内核: drivers/pci/controller/pcie-phytium-register.h
  • Linux内核: drivers/pci/quirks.c

| AT[1:0] (地址


本章图片附录

以下是本章相关的原文图片:

图5-1.TLP格式.png

图5-1.TLP格式.png

图5-2.TLP头部.png

图5-2.TLP头部.png

图5-3.地址字段.png

图5-3.地址字段.png

图5-4.长度字段.png

图5-4.长度字段.png

图5-5.请求类型.jpg

图5-5.请求类型.jpg

图5-6.完成包.jpg

图5-6.完成包.jpg

图5-7.数据负载.jpg

图5-7.数据负载.jpg