关于上一章
上一章节讨论了主要的三种类型的数据包:TLP 事务层包(Transaction Layer Packets)、DLLP 数据链路层包(Data Link Layer Packets)、Ordered Sets 命令集。并且在上一章中主要讲述 TLP 的用法、格式和不同种类的定义,并将详细的讲解 TLP 的相关字段含义。关于 DLLP 的内容将会在 Chapter 9 “DLLP Element”中进行单独讲解。
关于本章
本章节将讨论流量控制协议(Flow Control)的目的以及细节操作。流量控制是用来确保在接收者无法接收 TLP 时,发送方不会再继续发送 TLP。这避免了接收 Buffer 溢出,也消除了原本 PCI 工作方式中的一些低效行为,比如断开(disconnect)、重试(retry)和等待态(wait-state)。
关于下一章
下一章节将会讨论用于支持 QoS(Quality of Service)的机制,并描述对网络结构中传输的不同数据包的传输时间和带宽进行控制的意义。这些机制包括,特定应用的软件将会给每个数据包都分配优先级,以及每个设备内构建可选的硬件来启用事务优先级管理。
6.1 流量控制概念(Flow Control Concept)
每条 PCIe 链路两端的端口都必须实现流量控制机制。在一个数据包允许被发出之前,流量控制机制必须确认接收端口拥有足够的可用 Buffer 空间来接收数据包。而在像 PCI 这种并行总线中,尽管不知道目标方是否准备好了处理数据,事务也会尝试去发送执行,如果因为没有足够的可用 Buffer 空间,请求被拒绝了,那么事务就会一直重试(Retry)直至完成。这就是 PCI 的“Delayed Transaction Model 延迟的事务模型”,然而这种方式的效率很低。
流量控制机制可以改善当多个虚拟通道(Virtual Channel,VC)被占用时的传输效率。每个虚拟通道自己的事务是与其他 VC 的事务流量相独立的,因为流量控制 Buffer 是 VC 各自单独维护的。因此,若一个 VC 的流控 Buffer 满了,它也不会阻塞对其他 VC Buffer 的访问。PCIe 支持最多 8 条虚拟通道。
流量控制机制使用一种基于信用(Credit-based)的机制,使得发送端口可以知道接收端口有多少可用的 Buffer 空间。作为它们初始化的一部分,每个接收者都要将自己的 Buffer 大小报告给链路对端的发送者,并在运行过程中使用 DLLP 来定期地更新这个“信用值”。技术上来说,DLLP 显然是一种开销,因为它并不携带任何数据荷载,但是 DLLP 开销很小(始终为 8 个符号大小),这样可以使其对性能的影响最小。
流量控制逻辑实际上是两个层级的共同职责:事务层包含了计数器用于对可用 Buffer 空间进行计数,但是数据链路层也要负责发送和接收这些包含了可用 Buffer 空间信息的 DLLP。如图 6‑1 展示了这种共同职责。在流量控制的的工作过程中:
- 设备报告可用的 Buffer 空间:接收者的每个端口都要报告自己的流量控制 Buffer 的大小,单位为 credit(信用)。一个 Buffer 中 credit 的数量会从自身接收侧事务层发送至发送侧数据链路层(如图 6‑1)。在合适的时间,数据链路层将会产生一个流量控制 DLLP 来将这个 credit 信息转发至每个流量控制 Buffer 的链路对端的接收者的。
- 接收者寄存 Credit:接收者收到流量控制 DLLP,并将 credit 值传输至自身发送侧事务层。这就完成了 credit 从链路的一端到对端的传输。这些操作是双向进行的,直到所有的流量控制信息都完成了交换。
- 发送者检查 Credit:在发送者可以发送一个 TLP 之前,它需要检查流量控制计数器(Flow Control Counter),以此来知道对方是否有足够的 credit。如果 credit 数量足够,那么就将 TLP 转发至数据链路层,但是如果 credit 不足,那么 TLP 的发送操作就会被阻塞直至获知的更多的足够的流量控制 credit。
图 6‑1 流量控制逻辑所处的位置
6.2 流量控制 Buffer 和 Credit(Flow Control Buffer and Credits)
一个端口所支持的每个 VC(Virtual Channel)资源都会实现流量控制 Buffer。回想一下,链路两端的端口可能支持的 VC 数量是不同的,因此由软件进行配置和启用的 VC 的最大数量就是两个端口间的最大公共 VC 数量。
6.2.1 VC 流量控制 Buffer 的组成(VC Flow Control Buffer Organization)
接收者的每个 VC 流控 buffer 是按照通过 VC 的每种事务类别来进行管理的。这些类别为:
- Posted 事务——MWr(Memory Write)和 Message
- Non-Posted 事务——MRd(Memory Read)、配置读/写、IO 读/写
- 完成包——读和写完成包
除此之外,每个既有 Header 也有数据荷载的事务类别,都会被分成 Header 和数据两部分。这将产出 6 种不同的 Buffer,每个 Buffer 都实现自己的流控。(见图 6‑2)。
图 6‑2 流控 Buffer 组成
对于有些事务,例如读请求,它只由 Header 组成,而像写请求就既有 Header 也有数据荷载。发送者必须确认 Header 的 Buffer 空间和数据荷载的 Buffer 空间都满足了事务传输的需求,才能发送事务。注意,当事务被转发至软件或者是在 Switch 中被转发至出口端口时,必须在 VC 流控 Buffer 中保持事务顺序。因此,接收者也必须跟踪 Buffer 内的 Header 和数据荷载的顺序。
6.2.2 流控 Credit(Flow Control Credits)
接收者需要报告 Buffer 空间的大小,使用的单位是 Credit,或者叫流量控制 Credit。Header 和数据荷载 Buffer 的一个流控 Credit(FCC,Flow Control Credit)的大小值分别为:
- Header Credit:Header 大小的最大值+digest
— 完成包为 4DW
— 请求包为 5DW
- 数据 Credit:4DW(16byte 对齐)
流量控制 DLLP 会传达这个信息,而 DLLP 自身的传输是不需要流控 Credit 的。这是因为 DLLP 产生和终结的地方都是在数据链路层,并不需要使用到事务层 Buffer。
6.3 初始流量控制通告(Initial Flow Control Advertisement)
在流控的初始化过程中,PCIe 设备们会通过对 Buffer 空间的流控 Credit 进行“advertising 通告”的方式,来相互传达各自的 Buffer 大小。PCIe 也专门定义了一些 Buffer 需要的流控 Credit 初始值。一个通告过自己的初始 Buffer 空间的接受者,可以有效的保证它的 Buffer 空间永远不会溢出。
6.3.1 流控通告的最大值和最小值(Minimum and Maximum Flow Control Advertisement)
协议规范定义了各种不同的流控 Buffer 类型所报告的 Credit 数量的最小值,表 6‑1 列出了这些 Buffer 类型对应的 Credit 最小值。然而,设备一般通告的 Credit 数量会远大于最小值,表 6‑2 列出了协议所允许的 Credit 数量通告的最大值。
表 6‑1 流控通告最小值
表 6‑2 流控通告最大值
6.3.2 无限 Credit(Infinite Credits)
可以注意到,如果流控 Credit 值为 00h,那么其意思就是拥有无限多的 Credit。在流控初始化之后,不会再进行任何的通告(advertisement)。发起事务的设备必须要保留足够的 buffer 空间,这些 buffer 空间用来存放执行拆分事务(split transaction)的过程中返回的数据以及状态信息。这些事务组合包括:
- Non-Posted 读请求和返回的完成包数据
- Non-Posted 读请求和返回的完成包状态
- Non-Posted 写请求和返回的完成包状态
6.3.3 无限 Credit 通告的特殊用法
协议规范指出,当一个设备只实现了 VC0 时,需要有一些特别的考虑。例如,仅有的两种 Posted 写请求:IO Write 与 Configuration Write,二者仅允许使用 VC0。因此,Non-Posted 数据 buffer 并不会被 VC1-VC7 所使用,那么就可以为 VC1-VC7 宣告一个无限 Credit 值。然而 Non-Posted Header 仍然需要进行操作,所以 Header Credit 的值需要继续更新,而并非保持无限。
6.4 流量控制初始化(Flow Control Initialization)
6.4.1 整体说明(General)
在发送任何事务之前,都需要进行流控初始化(flow control initialization)。事实上,在流控初始化成功完成之前,TLP 是不能在链路上进行发送的。流控初始化会发生于系统中的每一个链路,其过程主要为链路两端设备之间的一次握手。这一过程会在物理层的链路训练(link training)完成后就开始进行。链路层观察 LinkUp 信号是否有效,获知物理层何时处于 Ready 状态,如图 6‑3 所示。
图 6‑3 物理层报告自己已经 Ready
一旦流量控制初始化开始,那么这个过程从根本上来说在各个虚拟通道(VC)上都是一样的,当一个 VC 被启用时,硬件就负责控制它的流量控制初始化过程。VC0 默认总是启用,因此它的初始化过程是自动的。这使得配置事务可以遍历整个拓扑结构,并进行枚举过程。其他的 VC 只有在配置软件对链路两端都进行了设置并且启用它们之后才会进行初始化。
6.4.2 流控初始化流程(The FC Initialization Sequence)
流控初始化过程涉及到链路层的 DLCMSM(Data Link Control and Management State Machine,数据链路控制与管理状态机)。如图 6‑4 所示,复位后状态机将处于 DL_Inactive 状态。在 DL_Inactive 状态中,会发出 DL_Down 信号,这个信号会作用于链路层和事务层。与此同时,也会在此状态中等待物理层的 LinkUp 信号,此信号是用于表示 LTSSM(Link Training and Status State Machine,链路训练和状态状态机)已经完成了工作,也就是物理层已经处于 Ready 状态。当物理层 Ready 后,将会使得 DLCMSM 跳转至 DL_Init 状态,它包含两级子状态用于处理流控初始化:FC_INIT1 和 FC_INIT2。
图 6‑4 DLCMSM 数据链路控制与管理状态机
6.4.3 FC_Init1 详细说明(FC_Init1 Details)
在 FC_Init1 状态中,设备会连续的发送一系列的 3 种 InitFC1 流控 DLLP 来通告它的接收 Buffer 大小(见图 6‑5)。根据协议规范,数据包必须按照这样的顺序来进行发送:Posted,Non-Posted,Completion 完成包,如图 6‑6 所示。协议规范中强烈建议频繁的重复这些 DLLP 的发送,这会使接收设备能够更容易的发现它们,特别是在当前没有 TLP 或者 DLLP 需要发送的情况下。
每个设备都需要从对端设备接收到这一系列 DLLP,这样它才可以将对端设备的 Buffer 空间大小记录下来。一旦设备已经发送出了自身的 Buffer 空间大小的值,并且接收到了足够次数的完整的 DLLP 序列,所谓足够次数是指,设备足以相信接收到的对端 Buffer 大小的值是正确的,那么此时设备就已经可以跳出 FC_INIT1 状态了。要进行跳出的操作,设备需要将接收到的值记录在传输计数器(transmit counter)中,置起一个内部的 flag 信号(FL1),然后就可以跳转到 FC_INIT2 状态来进行初始化操作中的第二步。
图 6‑5 INIT1 流控 DLLP 格式及内容
图 6‑6 设备在 DL_Init 状态下发送 InitFC1 DLLP
6.4.4 FC_Init2 详细说明(FC_Init2 Details)
在 FC_Init2 这个状态中,设备需要连续的发送 InitFC2 DLLP。发送 DLLP 的顺序和 FC_Init1 中一样,并且其中也包含了相同的有关 Credit 的信息,但是这些 DLLP 还有一个作用就是发送方用来确认流控初始化(FC initialization)已经成功。由于设备在 FC_Init1 状态中就已经寄存了对端的可用 Buffer 值,因此它不在需要任何关于 Credit 数量的信息,它在等待 InitFC2 DLLP 时会忽略传来的所有 InitFC1 DLLP。虽然对于链路另一端设备来说此时并没有完成初始化,但是设备可以在此时发送 TLP。初始化的完成与否是通过 DL_Up 信号来对事务层进行指示的。(见图 6‑7)
你可能会有疑问,初始化过程为什么还需要这个第二阶段呢?一个简单的回答是:链路两端的设备在完成流控初始化(FC Initialization)时需要的时间可能是不一样的,而这样两步的方法可以确保在较快的设备完成初始化后,较慢的那一端依然能够继续接收到需要的流控信息(FC Information)。
一旦设备接收到了任意 Buffer 种类的 FC_INIT2 数据包,那么它就会置起一个内部的 flag(FL2)。(这个操作不需要等待接收到所有种类的 FC_INIT2 数据包)注意,FL2 也可以在接收到一个 UpdateFC 包或者 TLP 之后被置起的。当链路两端的设备都完成了这些步骤并且都发送出了 InitFC2 DLLP 之后,DLCMSM 将会跳转至 DL_Active 状态,链路层初始化完成。
图 6‑7 FC 值已经寄存-接下来发送 InitFC2 并报告 DL\_Up
6.4.5 FC_INIT1 和 FC_INIT2 的传输速率(Rate of FC_INIT1 and FC_INIT2 Transmission)
在协议规范中定义了发送 FC_INIT DLLP 之间的延时,如下:
- VC0:基于硬件进行流控的 VC0 要求 FC_INIT1 包和 FC_INIT2 包要 “以最大速率连续传输"。也就是说重发计时器(resend timer)设置的重发间隔值为 0。
- VC1-VC7:当软件启动了其他 VC(VC0 之外)的流控初始化时,FC_INIT DLLP 序列需要在“当没有其他 TLP 或者 DLLP 需要传输时”被重复发送。
- 需要注意,两组 DLLP 序列发送的起始时刻最多只能间隔 17us。
6.4.6 流控初始化协议违例
设备对流控初始化协议违例的检测是可选的。检测到的错误可以作为数据链路层协议错误进行报告。
原文: Mindshare
译者: Michael ZZY
校对: LJGibbs
文章来源:https://zhuanlan.zhihu.com/p/509861647
《PCI Express Technology 3.0》翻译系列
- 《PCI Express Technology 3.0 》Chapter 5 第3 节
- 《PCI Express Technology 3.0》Chapter 5
- PCI Express Technology 3.0:Chapter 1 Background/背景
- PCI Express Technology 3.0:PCIe体系结构概述 2.1 节
- PCI Express Technology 3.0:PCIe体系结构概述 2.2-2.3
- PCI Express Technology 3.0:PCIe体系结构概述 2.2-2.3 节(完)
- PCI Express Technology 3.0:PCIe配置概述 3.1-3.7 节
- PCI Express Technology 3.0:PCIe配置概述 3.8-3.14 节(完)
- PCI Express Technology 3.0:地址空间与事务路由4.1-4.2节
- PCI Express Technology 3.0:地址空间与事务路由 4.3-4.4节
更多IC设计技术干货请关注FPGA的逻辑技术专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。