发布时间: 2012 年 11 月 13 日

数据链路层数据包(Data Link Layer Packets)

 除了用头(2 字节)包装了 TLP 并在末尾添加 CRC(实际上是 LCRC,4 字节)之外,
数据链路层(Data Link layer)还运行自己的数据包以保持可靠传输。这些特殊数据包是数据链路层数据包 (DLLP)。我们将很快列出它们:

  • Ack DLLP 用于确认成功接收到 TLP (Ack DLLP for acknowledging successfully received TLPs)
  • Nack DLLP 用于指示到达的 TLP 已损坏,并且重传到期。请注意,还有一个超时机制,以防没有任何看起来像 TLP 的东西到达。
  • Flow Control DLLPs:InitFC1、InitFC2 和 UpdateFC,用于声明credit,如下所述。
  • 电源管理 DLLP。

流量控制(Flow control)

如前所述,数据链路层(data link layer)具有流量控制 (FC) 机制,可确保仅当link partner有足够的缓冲空间来接受 TLP 时才传输 TLP。

我故意使用术语(term )“link partner”而不是“destination”。例如,当外围设备(peripheral )通过交换机连接到 Root Complex 时,它会针对交换机而非最终目的地运行其流量控制机制。换句话说,一旦 TLP 从外设传输过来,它仍然受制于交换机和 Root Complex 之间的流量控制机制。如果路上有更多的交换机,每条腿都有自己的流量控制。

该机制(mechanism)并不是最简单的,它在规范中的描述会让您起鸡皮疙瘩。所以我会尽量把它说得相当清楚。

流量控制机制(flow control mechanism)对 6(六个!)不同的缓冲区消费者运行独立的统计:

  1. 已发布请求 TLP 的头(Posted Requests TLP’s headers)
  2. 发布的请求 TLP 的数据(Posted Requests TLP’s data)
  3. Non-Posted Requests TLP 的头(Non-Posted Requests TLP’s headers)
  4. 非发布请求 TLP 的数据(Non-Posted Requests TLP’s data)
  5. 完成 TLP 的头(Completion TLP’s headers)
  6. 完成TLP的数据(Completion TLP’s data)

这些是六种credit类型。

  计费在流量控制单元中完成,对应于 4 个 DW 流量(16 字节),总是四舍五入到最接近的整数。由于头的长度始终为 3 或 4 个 DW,
因此传输的每个 TLP 都会消耗相应标头信用中的一个单元。
传输数据时,消耗单元数为TLP中数据DW的个数除以4,向上取整。所以我们可以想象每个 16 字节的接收器的数据桶,
我们不允许在其上混合来自不同 TLP 的数据。
每个桶是一个流量控制单元。

  现在让我们想象一下,发射器上有一个看门人,它计算自链接建立以来消耗的流量控制单元总数分别针对每种credit类型。
这是要跟踪的六个数字。这个看门人还有关于这些信用类型中每一种允许达到的最大数量的信息。
如果用于传输的某个 TLP 会使这些计数单元中的任何一个超过其限制,则不允许通过。
另一个 TLP 可能会被传输(根据重新排序规则),或者看门人只是等待限制上升。

这就是流量控制的工作方式。当链接建立时,双方交换他们的初始限制。
当每个接收器处理传入的数据包时,它会更新其链接伙伴的限制,以便它可以使用释放的缓冲区空间。
定期发送 UpdateFC FLLP 数据包以宣布新的信用额度。

好吧,我忽略了一个小细节:由于我们计算的是自链接开始以来的单元总数,因此总是存在溢出的可能性。
PCIe 标准为每个credit类型计数器及其限制分配一定数量的位(8 位用于头credit,12 位用于数据credit),知道它们很快就会溢出。
通过使用简单的模运算在每个计数器及其限制之间进行比较,可以解决此溢出问题。
所以给定一些限制不要把 limit 设置的比 counter 高太多,流量控制机制实现了上面描述的 doorkeeper。

允许总线实体宣布对六种credit类型中的任何一种或全部的无限credit限额,这意味着该特定credit类型的流量控制被禁用。
事实上,端点(与交换机和根联合体相反)
必须为完成头和数据通告无限credit。
换句话说,端点不能拒绝接受基于流量控制的完成 TLP。
因此,非过帐交易的请求者必须负责通过在发出请求时验证其是否具有足够的缓冲区空间来接受完成。
这也适用于不允许对等交易的根复合体。

虚拟通道(Virtual channels)

本指南的第一部分,我将示例 TLP 中的 TC 字段标记为绿色,表示这些字段几乎总是零。TC 代表流量类别,
是用于创建虚拟通道的标识符。
这些虚拟通道只是具有独立流量控制信用和计数器的独立数据缓冲区集。
因此,通过选择非零的 TC(并相应地设置总线实体),可以使 TLP 受制于独立的流量控制系统,
从而防止属于一个通道的 TLP 阻塞属于另一个通道的 TLP 的流量。

从 TC 到虚拟通道的映射由每个总线实体的软件完成。
无论如何,到目前为止我看到的真实 PCIe 元件仅支持一个虚拟通道 VC0,因此仅使用 TC0,这是规范要求的最低要求。
因此,除非某些特殊应用程序需要这样做,否则所有 TLP 中的 TC 将保持为零,并且可以忽略整个问题。

数据包重新排序

在分组网络中想到的问题之一是 TLP 到达的顺序可能与发送顺序不同的程度。
例如,Internet 协议(IP,如 TCP/IP 中的 IP)允许在传输过程中进行任何数据包重组。
PCIe 规范允许一定程度的 TLP 重新排序,实际上在某些情况下重新排序是强制性的,以避免死锁。

幸运的是,在这个问题中也考虑了遗留 PCI 兼容性问题,除非在 TLP 中设置了“宽松排序”位,而这种情况很少见。
这是 Attr 字段中的一位,在
本指南第 I 部分的TLP 示例中标记为绿色。
因此,总而言之,我们可以相信一切都会正常进行,就好像我们正在与一辆不错的旧公共汽车交谈一样。
我们这些写入几个寄存器,然后通过写入另一个寄存器来触发事件的人可以继续这样做。
为了安全起见,我关闭了 BAR 的预取位,尽管没有任何迹象表明它与写入有任何关系。

该规范详细定义了重新排序规则,但要弄清底线并不容易。所以我会提到这些规则的一些结果。这里所说的一切都是假设在所有交易中都清除了宽松的排序位。我也完全忽略了 I/O 空间(为什么要使用它?):

  • 已发布的写入和 MSI 按发送顺序到达。现在,所有内存写入都已发布,MSI 实际上是(已发布)内存写入。所以我们肯定知道内存写入是按顺序执行的,如果我们在填充缓冲区(写入...)后发出 MSI,它将在实际写入缓冲区后到达。
  • 读取请求永远不会在写入请求或 MSI 之前到达。事实上,执行读取请求是等待写入完成的安全方法。
  • 写入请求很可能先于读取请求在它们之前发送。这种机制可以防止某些特殊情况下的死锁。不要在等待读取完成进来的时候写入某个内存区域。
  • 特定请求(即具有相同标签和请求者 ID)的读取完成按它们发送的顺序到达(因此它们按地址上升的顺序到达)。不同请求的读取完成可能会重新排序(但谁在乎)。

除此之外,任何东西都可以改变顺序或到达,包括可以在它们之间重新排序和读取完成的读取请求。

为了消除对中断消息在其之前的写操作之前到达的任何偏执,规范中的第 2.2.7 节详细说明了这一点:

用于 MSI/MSI-X 事务的请求格式与上面定义的内存写入请求格式相同,并且 MSI/MSI-X 请求在排序、流量控制和数据完整性方面与内存写入没有区别。

零长度读取请求

正如刚才提到的,写入总线实体后从中读取是等待写入操作真正完成的安全方法。但是,如果我们对数据不感兴趣,为什么还要阅读任何东西呢?所以他们编造了一个零长度的请求,什么也没读。所有四个字节使能都被分配为零,这意味着没有任何内容被读取。至于完成,规范中的第 2.2.5 节说:

如果 1 DW 的读取请求指定不启用任何字节读取(第一个 DW BE[3:0] 字段 = 0000b),则相应的完成必须指定 1 DW 的长度,并包括 1 DW 的数据有效载荷

这样我们就完成了一个垃圾数据的DW。这很公平。

有效载荷大小和边界

每个承载数据的 TLP 必须将有效负载数据 DW 的数量限制为 Max_Payload_Size,这是配置期间分配的数字(通常为 128 字节)。此数字仅适用于有效载荷,而不适用于长度字段本身:内存读取请求的长度不受 Max_Payload_Size(根据规范 2.2.2)的限制,但受 Max_Read_Request_Size(根据规范 2.2.7)的限制

因此,一个内存读取请求可能会请求比一个 TLP 允许的更多的数据,因此多个 TLP 完成是不可避免的。

无论 Max_Payload_Size 限制如何,(内存)读取请求的完成可能会分成几个完成 TLP。根据规范 2.3.11,剪切必须位于按 RCB 字节(请求完成边界,128 字节,对于根联合体可能为 64)对齐的地址中。如果请求没有跨越这样的对齐边界,则只允许一个单一的完成 TLP。单个读取请求的多个内存读取完成必须以增加的地址顺序返回数据(这将由交换网络保留)。

最后一点,引用规范 2.2.7:请求不得指定导致内存空间访问跨越 4 KB 边界的地址/长度组合。

http://xillybus.com/tutorials/pci-express-tlp-pcie-primer-tutorial-guide-2