不同于并行总线,PCIe 这样的串行总线不使用总线上的控制信号来表示某时刻链路上正在发生什么。相反地,PCIe 链路上的发送方发出的比特流必须要有一个预期的大小,还要有一个可供接收方辨认的格式,这样接收方才能理解比特流的内容。此外,PCIe 在传输数据包时并不使用任何直接握手机制(immediate handshake)。
除了逻辑空闲符号(Logical Idle symbol)和 Ordered Set 的物理层包外,在活跃的 PCIe 链路上传输的信息的基本组块被称为 Packet(包),包是由符号组成的。链路上交换的两类主要的数据包为高层的 TLP(Transaction Layer Packet,事务层包),和低层的用于链路维护的包称为 DLLP(Data Link Layer Packet,数据链路层包)。这些包和它们的传输流如图 5‑1 所示。物理层的 Ordered Set 也是一种包,但是它并不像 TLP 和 DLLP 一样会被封装上包起始符号和包结束符号(也就是前面章节所讲的组帧符号),并且 Ordered Set 也并没有像 TLP 和 DLLP 一样的字节条带化过程,相反地,Ordered Set 会在链路的每个通道(lane)上都复制一份,而不是像字节条带化一样把信息按字节分配到各个通道上。
图 5‑1 TLP 和 DLLP 包
5.1.2 使用基于数据包协议的动机
使用基于包的协议(Packet-Based Protocol)有三个明显的优点,特别是对于数据完整性(data integrity)来说:
5.1.2.1 精心定义的数据包格式
像 PCI 这种早期的总线,它们允许总线上不确定数据量大小的传输,这使得只要传输没有结束就无法识别出数据荷载(payload)的边界。此外,任何一个设备都可以在此次传输完成前终止这个传输,这使得发送方很难去计算并传输一个覆盖了整个数据荷载的校验和或者 CRC。相反地,PCI 使用了一种简单的奇偶校验的方案,并在每个数据阶段(data phase)进行奇偶校验。(这里如果没理解请参阅 Chapter 1 内的 PCI 相关内容)
相比之下,PCIe 包具有一个已知的大小和格式。位于包的开头的部分为 Header,它用来指示这个包的类型,并包含了必需字段(required field)和可选字段(optional field)。除了地址字段以外,Header 中的其他字段长度都是固定的,地址字段可以为 32bit 也可以为 64bit。一旦一次传输开始,接收方不能暂停或者提前终止它。这种结构化的格式使得我们可以在 TLP 中加入一些信息来更有利于进行可靠的传输,例如加入组帧符号(framing symbol)、CRC、以及一个包的序列号(Sequence Number)。
5.1.2.2 使用组帧符号定义包的边界
在 PCIe Gen1 和 Gen2 操作模式中使用的是 8b/10b 编码,因此在这 Gen1 和 Gen2 中每个 TLP 和 DLLP 在发送前都会使用起始符号(Start)和结束符号(End)这两种控制符号来进行组帧,这样就可以给接收方清晰地定义出包的边界。这是在 PCI 和 PCI-X 上的重大改进,在 PCI 和 PCI-X 中使用一个单独的 FRAME# 信号来指示一个事务的开始和结束,如果 FRAME# 出现了毛刺,或者其他的控制信号出现了毛刺,那么都有可能造成目标设备对总线行为的误解。一个 PCIe 接收方必须在最后的链路活动开始或结束之前,就正确完成一个完整 10bit 符号的译码,这样接收方能更容易识别不期望出现或者无法识别的符号,并且将其作为一个错误来处理。
对于 PCIe Gen3 使用的 128b/130b 编码来说,控制字符不再被使用,并且也没有组帧符号了。对于更多的关于 Gen3 编码与早期版本的差异的内容,请参阅 Chapter 12“Physical Layer-Logical(Gen3)”。
5.1.2.3 使用 CRC 保障整个数据包的正确传输
在 PCI 体系结构中,会在地址阶段(address phase)和数据阶段(data phase)中使用奇偶校验边带(side-band)信号,但是在 PCIe 中则不同。PCIe 中使用带内(in-band)的 CRC 值来验证整个数据包是否进行了无错误的传输。同时 TLP 还会被发送方的数据链路层添加上一个序列号(Sequence Number),这使得当这个序列号的数据包传输出错时可以很简单的定位到它,并进行自动的重传。发送方会在自己的 Retry Buffer 内保存每个 TLP 的一个副本,直到接收方确认了这个 TLP 成功无错传输后才会将副本清除。这种 TLP 的确认机制被称为 Ack/Nak 协议(更多内容请参阅 Chapter 10 “Ack/Nak Protocol” ),它用来形成基础的链路级 TLP 错误检测和纠正机制。这种 Ack/Nak 协议提供的错误恢复机制使得我们可以在问题发生的地方或者链路上及时的解决问题,但是这也要求有一个本地的硬件解决方案来支持这种协议。
5.2 TLP 细节(TLP Details)
在 PCIe 中,高层次事务起源于发送方的 Device Core,终止于接收方的 Device Core。事务层会处理这些请求,其中,发送端的事务层组装 TLP,接收端的事务层解析 TLP。在这个过程中,每个设备的数据链路层和物理层也会参与包的组装。
5.2.1 TLP 的组包和拆包
如图 5‑2 所示的是链路的发送端组装 TLP 和接收端拆解 TLP 的一般流程。现在让我们讲一讲从一个包的生成,到它被传送到接收端的 Device Core 的各个步骤。下面列出了 TLP 组包和拆包的关键阶段,列出的编号与图 5‑2 中的编号相对应。
图 5‑2 PCIe TLP 的组包与拆包
发送方
- 设备 A 的 Device Core 向它的 PCIe 接口发送一个请求(具体 Device Core 是如何给 PCIe 接口发送请求的,这并不是 PCIe 协议或者本书的讨论范畴)。这个请求中包括:
-
目标地址或者 ID(也就是路由信息)
-
源端信息,例如 Requester ID 和 Tag
-
事务类型/数据包类型(需要执行的命令,例如一个内存读取 MRd)
-
数据荷载大小 payload size 和数据荷载内容(如果 TLP 需要带数据)
-
流量类型(Traffic Class,用于分配数据包的优先级)
-
请求的自身属性(No Snoop 无窥探、Relaxed Ordering 宽松排序,等等)
-
基于这个请求,事务层将会组建 TLP Header,并在其后附上数据荷载(如果有),以及如果启用并支持可选项的话也可以再附上 ECRC(End-to-End CRC)。随后 TLP 就会被放入一个虚拟通道 Buffer。这个虚拟通道会根据事务排序规则来管理 TLP 的顺序,并在向下转发 TLP 到数据链路层之前,确认接收方有足够的 Buffer 来接收一个 TLP。
-
当 TLP 到达数据链路层,它会被分配一个序列号(Sequence Number),并基于 TLP 的内容和序列号来计算出一个 LCRC(Link CRC)来附加在原 TLP 后。然后会将经过这些处理过程之后的 TLP 保存一个副本,这个副本会保存在数据链路层的重传 Buffer(Replay Buffer,也可称为 Retry Buffer)中,这是为了应对传输出错的情况。与此同时,这个 TLP 也会被向下转发至物理层。
-
物理层将会进行一系列的操作来准备对这个数据包进行串行传输,包括字节条带化(Byte Striping)、扰码(Scrambling)、编码(Encoding)以及并串转换(Serializing)。对于 Gen1 和 Gen2 的设备,当进行 8b/10b 编码时,会将 STP 和 END 这两个控制字符分别加在 TLP 的首端和尾端。最后,这个数据包通过链路进行传输。在 Gen3 操作模式中,STP 令牌(STP token)会被添加在 TLP 的首端,但是并不会在尾端加上 END,而是在 STP 令牌中包含 TLP 大小的信息来判断 TLP 的尾部位置。
接收方
-
在接收方(本例中是 Device B),为了发送包所做的一切准备现在都必须撤销。物理层将对比特流进行串并转换(deserialize)、对串并转换后的符号进行解码,然后再进行字节反条带化(un-stripes)。控制字符将被移除,因为它们仅在物理层有意义,然后这个数据包就会被向上转发至数据链路层。
-
数据链路层将会计算 CRC(具体一点是 LCRC)并与 TLP 中的 CRC 进行比较。如果 CRC 比较结果相同,那么就再检查序列号(Sequence Number)。如果都没有出现错误,那就把 CRC 和序列号都从 TLP 剥除,并随后将 TLP 向上转发给接收方事务层,与此同时要通过返回给发送方一个 Ack DLLP 来通知发送方这个 TLP 被成功接收。相反地,如果前面的过程中检查出了错误,那么就要返回给发送方一个 Nak DLLP,这样发送方将会使用它的重传 Buffer 来对 TLP 进行重传。
-
在事务层,TLP 被进行解码,并将 TLP 内的信息传递给 Device Core 来进行相应的操作。如果当前接收设备就是数据包的最终目的地,那么它可以检查 ECRC 错误,并在发现任何 ECRC 错误时报告给 Device Core。
5.2.2 TLP 结构
一个事务层包 TLP 中每个字段域的基本用法在表 5‑1 中进行了定义。
表 5‑1 TLP Header 的 Type 字段定义了事务不同种类
下面对表 5‑1 中的内容进行复述。
-
Header
-
协议层次:事务层
-
该组件用法:大小为 3 或 4DW(12 或 16Bytes)。Header 的格式会随类型而变化,但是 Header 也定义了一些参数,包括:
-
事务类型(Transaction Type)
-
目标地址、ID 等
-
传输数据量大小(如果有数据)、字节使能(Byte Enable)
-
属性(Attribute)
-
流量类型(Traffic Class)
-
-
Data
-
协议层次:事务层
-
该组件用法:可选的 1-1024DW 大小的数据荷载,具体的大小由字节使能或者字节对齐的开始和结束地址来进行描述。需要注意指定的长度不能为 0,但是一个 0 长度的读取可以通过指定长度为 1DW 然后将字节使能全部置为 0 来进行近似处理(在某些情况下会使用)。来自 Completer 的结果数据虽然是未定义的种类,但是 Requester 并不使用它,因此就和指定 0 长度的目的等效了。
-
Digest/ECRC
-
协议层次:事务层
-
该组件用法:可选的功能。当需要使用时,ECRC 的大小永远为 1DW。
5.2.3 通用 TLP Header 格式(Generic TLP Header Format)
5.2.3.1 概括(General)
如图 5‑3 中,展示了一个 4DW 的通用 TLP Header 的格式和内容。在本节内,会对几乎所有事务的 TLP Header 中的公共字段进行总结,并会在稍后讨论与特定事务类型相关 Header 格式差异。
图 5‑3 通用 TLP Header 的各字段域
5.2.3.2 通用 Header 各字段摘要
表 5‑2 中对通用 TLP Header 中的每个字段的大小和用途进行了总结。需要注意的是,在图 5‑3 中被标注为“R”的字段是保留字段(reserve),应该被置为 0。
Header 中的字段名 | Header 中的位置 | 字段作用 |
---|---|---|
Fmt[2:0] 格式 (Format) | Byte 0 Bit 7:5 | 这些 bit 的编码信息是关于 Header 的大小,以及这个 TLP 中是否存在数据荷载部分: 000b:3DW Header,无数据荷载 001b:4DW Header,无数据荷载 010b:3DW Header,有数据荷载 011b:4DW Header,有数据荷载 100b:1DW,属于 Prefix TLP 不难发现,除了 Prefix TLP 外,只要 Fmt[0]=0 那么就是 3DW Header,反之则为 4DW Header。若 Fmt[1]=0 那么就无数据荷载,反之则有数据荷载。 对于低于 4GB 的地址,必须使用 3DW Header。协议规定,如果使用 4DW Header 但是地址小于 4GB,也就是说将 64bit 地址的高 32bit 置为 0,那么这种情况下接收方的行为是未进行定义的(undefined)。 |
Type[4:0] 类型 | Byte 0 Bit 4:0 | 这些 bit 编码的信息是 TLP 的不同事务类型。Type 字段用来跟 Fmt[1:0]字段一起指定了事务类型、Header 大小、以及是否存在数据荷载。更多详细信息请参阅“Generic Header Field Details”一节。 |
TC[2:0] 流量类型 (Traffic Class) | Byte 1 Bit 6:4 | 这些 bit 表示将应用于这个 TLP 和与之完成相关(如果需要完成包)的流量类型: 000b:Traffic Class 0(默认) 001b:Traffic Class 1 …… 111b:Traffic Class 7 TC 0 是默认类型,而 TC 1-7 是用来提供差异化的服务。更多信息请参阅“Traffic Class”一节。 |
Attr[2] 属性 (Attribute) | Byte 1 Bit 2 | 这个第三位的 Attribute 位(它共有 3 位)用于表示这个 TLP 是否使用基于 ID 的排序(ID-based Ordering)。更多内容请参阅“ID Based Ordering”一节。 |
TH TLP 处理提示 (TLP Processing Hints) | Byte 1 Bit 0 | 它用于表示何时 TLP 中会包含 TLP 提示(TLP Hints),以便让系统了解如何更好的处理这个 TLP。更多内容请参阅“TPH(TLP Processing Hints)”。 |
TD TLP 摘要 (TLP Digest) | Byte 2 Bit 7 | 如果 TD=1,那么这个 TLP 中将包括可选的 4Byte TLP Digest 字段,也就是 ECRC 值。 它有一些规则: 所有的接收者都必须要通过这个 bit 来检查是否存在 Digest 字段。 如果一个 TLP 的 TD=1,但是它又没有 Digest,那么它将被当做畸形 TLP(Malformed TLP)处理。 如果接收设备支持 ECRC 校验,且此 TLP 内 TD=1,那么这个接收设备必须进行校验。 如果一个作为 TLP 最终目的地的设备并不支持 ECRC 校验(因为这是可选功能),那么它必须要忽略 Digest 字段。 更多内容请参阅“CRC”和“ECRC Generating and Checking”这两节。 |
EP 受污染的数据 (Poisoned Data) | Byte 2 Bit 6 | 如果 EP=1,那么就认为所有伴随此数据的数据都是无效的,尽管相关事务依然允许正常的完成。关于 Poisoned 数据包的更多内容,请参阅“Data Poisoning”一节。 |
Attr[1:0] 属性 (Attributes) | Byte 2 Bit 5:4 | Bit 5 = Relax Ordering(宽松排序):当它被置为 1 时,这个 TLP 会启用 PCI-X 的宽松排序。如果它为 0,则是用 PCI 的严格的 PCI 排序(strict PCI ordering)。 Bit 4 = No Snoop(无窥探):当置为 1 时,Requester 的意思是这个 TLP 不会存在 host cache 一致性的问题(host cache coherency issues),因此系统硬件可以通过跳过普通处理器对这个请求的 cache 窥探,以此来节省时间。而当这一位被置为 0 时,需要进行 PCI-type 的 cache 窥探保护。 |
AT[1:0] 地址类型 (Address Type) | Byte 2 Bit 3:2 | 对于 Memory 和 Atomic 请求来说,这个字段用于支持虚拟化系统(virtualized system)的地址转换。这个地址转换协议由一个单独的规范进行描述,被称为 Address Translation Services,可以看到该字段的编码为: 00 = 默认/未转换(Default/Untranslated) 01 = 转换请求(Translation Request) 10 = 已转换(Translated) 11 = 保留 reserve |
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 = 1023DW 00 0000 0000 = 1024DW |
Last DW BE[3:0] 末尾 DW 的字节使能 (Last DW Byte Enable) | Byte 7 Bit 7:4 | 这个字段中的 4 个高有效的 bit,与数据荷载中最后一个 DW 中的 4 个 Byte 一一对应。 Bit 7 = 1:末尾 DW 的 Byte 3 是有效的;否则为无效的。 Bit 6 = 1:末尾 DW 的 Byte 2 是有效的;否则为无效的。 Bit 5 = 1:末尾 DW 的 Byte 1 是有效的;否则为无效的。 Bit 4 = 1:末尾 DW 的 Byte 0 是有效的;否则为无效的。 |
1st DW BE[3:0] 第一个 DW 的字节使能 (First DW Byte Enable) | Byte 7 Bit 3:0 | 这个字段中的 4 个高有效的 bit,与数据荷载中第一个 DW 中的 4 个 Byte 一一对应。 Bit 3 = 1:末尾 DW 的 Byte 3 是有效的;否则为无效的。 Bit 2 = 1:末尾 DW 的 Byte 2 是有效的;否则为无效的。 Bit 1 = 1:末尾 DW 的 Byte 1 是有效的;否则为无效的。 Bit 0 = 1:末尾 DW 的 Byte 0 是有效的;否则为无效的。 |
标签:TLP,Header,PCIE,PCIe,数据流,DW,Byte,Bit From: https://www.cnblogs.com/VerweileDoch/p/18181895