PCIe总线的事物层和链路层
在讨论AI scale up网络中,总是绕不开两种底层方案,一种是基于以太的方式,复用以太serdes和转发芯片,类似ethx;另一种是类似基于PCIe的方式,类似ualink(ualink当前版本也计划采用以太)。为此又复习整理了一下PCIe传输的一些流程,其中核心在事物层和链路层。其中重点关注PCIe的路由(寻址),乱序和重传处理方式。
事务层
事务层是PCIe总线层次结构的{BANNED}最佳高层,该层次将接收 PCIe 设备核心层的数据请求,并将其转换为 PCIe 总线事务, PCIe 总线使用的这些总线事务在 TLP 头中定义。
由上图可知TLP头{BANNED}最佳大长度是12B(3DW)或16B(4DW),当存储器读写支持64位地址模式时TLP头将采用4DW。此外payload的长度可变,{BANNED}最佳小为0,{BANNED}最佳大为1024DW(4096B)。
Posted和Non-Posted
首先,PCIe总线事物分为Posted和Non-Posted两种,如果一个事物请求发送出去需要等待应答的,我们称之为Non-Posted,否则,就称为Posted。在PCIe的事物层中,我们主要讨论也是常见的操作有:存储器的读写,I/O的读写,以及配置的读写。而其中只有存储器写TLP使用Posted方式进行传送,其他总线事物则使用Non-Posted方式,换句话说,除了写存储器请求,其他操作都是需要应答的。
PCIe总线规定所有Non-Posted存储器请求使用Split总线方式进行数据传递。当PCIe设备进行存储器读、I/O读写或者配置读写请求时,首先向目标设备发送数据读写请求TLP,当目标设备收到这些读写请求TLP后,将数据和完成信息通过完成报文(Cpl 或者 CplD)发送给源设备。其中Cpl是指TLP层的完成报文,CplD是指带数据的完成报文。
保序方式
TLP层的保序方式和TLP头中的Attr字段有关。Attr 字段由3位组成, 其中第2位表示该TLP是否支持PCIe总线的ID?based Ordering;第1位表示是否支持 Relaxed Ordering; 而第 0 位表示该 TLP在经过 RC 到达存储器时,是否需要进行Cache共享一致性处理。一个TLP可以同时支持ID?based Ordering和Relaxed Ordering两种位序。
当使用标准的强序模型时,在数据的整个传送路径中,PCIe设备在处理相同类型的TLP时,如PCIe设备发送两个存储器写TLP时,后面的存储器写TLP必须等待前一个存储器写TLP完成后才能被处理,即便当前报文在传送过程中被阻塞,后一个报文也必须等待。
如果使用Relaxed Ordering模型,后一个存储器写TLP可以穿越前一个存储器写TLP,提前执行,从而提高了PCIe总线的利用率。有时一个PCIe设备发出的TLP,其目的地址并不相同,可能先进入发送队列的TLP,在某种情况下无法发送,但这并不影响后续TLP的发送,因为这两个TLP的目的地址并不相同,发送条件也并不相同。
值得注意的是,在使用PCI总线强序模型时,不同种类的TLP间也可以乱序通过同一条PCIe链路,比如存储器写TLP可以超越存储器读请求TLP 提前进行。而PCIe 总线支持Relaxed Ordering模型之后,在TLP的传递过程中出现乱序种类更多,但是这些乱序仍然是有条件限制的。在PCIe总线规范中为了避免死锁,还规定了不同报文的传送数据规则,即Ordering Rules。这里不再展开。
PCIe V2.1总线规范引入了一种新的"序"模型,即IDO (ID-Based Ordering)模型,IDO模型与数据传送的数据流相关,是PCIe V2. 1 规范引入的序模型。该模型引入了"数据流"的概念,即相同的数据源发出的TLP属于相同的"数据流",而不同数据源发出的TLP属于不同的"数据流"。PCIe链路可以根据"数据流"对TLP进行区分。IDO模型允许分属不同"数据流"的TLP之间没有序的要求,即可以“自由乱序”。 IDO模型的引入有利于Switch对发向不同Egress端口的报文进行优化处理。
至于Attr字段的第0位是"No Snoop Attribute"位。当该位为0时表示当前TLP所传送的数据在通过FSB时,需要与Cache保持一致,这种一致性由FSB通过总线监听自动完成而不需要软件干预;如果为1,表示FSB并不会将TLP中的数据与Cache进行一致,在这种情况下,进行数据传送时,必须使用软件保证Cache的一致性。
地址翻译
TLP头中的AT字段与PCIe总线的地址转换相关。在一些PCIe设备中设置了ATC(Address Transla-tion Cache)部件,这个部件的主要功能是进行地址转换。只有在支持IOMMU技术的处理器系统中,PCIe设备才能使用该字段。
AT字段可以用作存储器域与PCI总线域之间的地址转换,但是设置这个字段的主要目的是为了方便多个虚拟主机共享同一个PCIe设备。对这个字段有兴趣的读者可以参考Ad-dress Translation Sevices规范,这个规范是PCI的IO Virtualization规范的重要组成部分。
TLP路由
TLP 的路由是指 TLP 通过 Switch 或者 PCIe 桥片时采用哪条路径, {BANNED}最佳终到达 EP 或者 RC 的方法。 PCIe 总线一共定义了三种路由方法, 分别是基于地址 (Address) 的路由, 基于ID的路由和隐式路由(Implicit)方式。
l 基于地址的路由
存储器和I/O读写请求TLP使用基于地址的路由方式,这种方式使用TLP中的Address 字段进行路由选径,{BANNED}最佳终到达目的地。
l 基于 ID 的路由
而配置读写报文、“Vendor_Defined Messages”报文、Cpl和CplD报文使用基于ID(即BDF号)的路由方式,这种方式使用PCI总线号( Bus Number)进行路由选径。在Switch或者多端口RC的虚拟PCI?to?PCI桥配置空间中,包含如何使用PCI总线号进行路由选径的信息。(使用 Bus Number、 Device Number和Function Number进行路由寻址。)
l 隐式路由
隐式路由方式主要用于 Message 报文的传递。在PCIe总线中定义了一系列消息报文,包括“ INTx Interrupt Signaling”,“ Power Management Messages”和“ Error Signal Messages”等报文。在这些报文中,除了“Vendor_Defined Messages”报文,其他所有消息报文都使用 隐式路由方式,隐式路由方式是指从下游端口到上游端口进行数据传递的使用路由方式,或者用于RC向EP发出广播报文。
存储器读写请求 TLP
下面我们看一下{BANNED}最佳长见的存储器读写请求TLP的头格式。
前面说过,对于Non-Posted数据请求,目标设备需要使用完成报文做为回应。那么源端如何知道这个回应报文对应哪个请求呢?这里就要提上面重要的Requester ID字段了。
Requester ID字段包含"生成这个TLP报文"的PCIe 设备的总线号(Bus Number)、设备号(Device Number)和功能号(Function Number),其格式如图。存储器,I/O读请求TLP中含有Requester ID和Tag字段。在PCIe总线中Requester ID 和Tag字段合称为Transaction ID。
对于存储器写请求TLP,Requester ID字段并不是必须的,因为目标设备收到存储器写请求TLP后,不需要完成报文作为应答(Posted方式),因此RequesterID字段对于存储器写请求TLP并没有实际意义。对于Non-Posted数据请求,目标设备需要使用完成报文做为回应。在这个完成报文中,需要使用源设备的Requester ID字段。因此在Non-Posted数据请求TLP 中,如存储器读请求、I/O和配置读写请求TLP,必须使用Requester ID字段。
在PCIe总线中,所有Non-Posted数据请求都需要完成报文作为应答,才能结束一次完整的数据传递。一个源设备在发送Non-Posted数据请求之后,如果并没有收到目标设备回送的完成报文,TLP报文的发送端需要保存这个Non-Posted数据请求,此时该设备使用的TransactionID(Tag字段)不能被再次使用,直到一次数据传送结束,即数据发送端收齐与该TLP对应的所有完成报文。
在同一个时间段内,PCIe设备发出的每一个Non-Posted 数据请求TLP,其Transaction ID 必须是唯一的。即在同一时间段内,在当前PCI总线域中不能存在两个或者两个以上的存储器读请求 TLP,其Transaction ID完全相同。
源设备发送 Non-Posted数据请求后,在没有获得全部完成报文之前,不能释放这个Transaction ID占用的资源。在同一个PCIe设备发送的TLP中,其Requester ID字段是相同的,因此PCIe的设计者管理的资源是Tag字段,需要合理的管理Tag资源,以保证数据传送的正确性。
因此Tag字段的长度决定了发送端能够暂存多少个同类型的TLP,如果Tag字段长度为5,发送端能够暂存32个报文;如果PCle设备使能了Extended Tag位,Tag字段可以由8位组成,此时发送端能够暂存256个报文。通过Tag字段的长度,可以发现每个PCIe 设备{BANNED}最佳多可以暂存256个同类型的Non-Posted 报文。但是在多数情况下,一个PCle设备可能只包含 1 个Function。因此PCle设备还可以使用Function 号扩展Tag字段,从而扩展"暂存TLP报文"的数目。PCle 设备在 PCI Express Capability 结构的 Device Control寄存器中,设置了一个Phantom Functions Enable位。当一个PCle设备仅支持一个Function 时,Phantom Functions Enable位可以被设置为1,此时PCle设备可以使用Requester ID的Function Number 字段对 Tag字段进一步扩展,此时一个PCle设备{BANNED}最佳多可以支持2048个同类型的数据请求。
由以上分析可以发现,一个PCIe设备{BANNED}最佳多可以支持2048个存储器读数据请求,基本上可以满足绝大多数需要。但是在某些特殊应用场合,PCle设备即使可以暂存2048个存储器读请求,也并不足够。
与PCI总线相比,PCIe总线的数据传送延时较长,而为了弥补这个传送延时,PCle设备通常使用流水线技术。此时PCIe设备必须能够连续发送多个存储器读请求报文,随后RC 也将连续回送多个存储器读完成报文,在PCIe设备的实现中,需要保证能够源源不断地从RC接收这些报文,以充分利用报文接收流水线。
数据链路层
PCIe 总线的数据链路层处于事务层和物理层之间, 主要功能是保证来自事务层的 TLP 在 PCIe 链路中的正确传递, 为此数据链路层定义了一系列数据链路层报文,即DLLP。
与事务层不同,数据链路层主要处理端到端的数据传送。在事务层中,源设备与目标设备间的传送距离较长,设备之间可能经过若干级Switch;而在数据链路层中,源设备与目标设备在一条PCle链路的两端(逐条的)。
数据链路层的组成结构
数据链路层使用ACK/NAK协议发送和接收TLP,由发送部件和接收部件组成。其中发送部件由 Replay Buffer、ACK/NAK DLLP接收逻辑和TLP发送逻辑组成;而接收部件由"Error Check"逻辑、ACK/NAK发送逻辑和TLP接收逻辑组成。数据链路层的拓扑结构如下图所示。在该图中含有两个PCIe设备,分别为Device A 和 Device B,使用的PCIe 链路为Device A 的发送链路,同时也为 Device B的接收链路。
数据链路层使用ACK/NAK协议保证TLP的正确传送,ACK/NAK协议是一种滑动窗口协议。来自事物层的TLP首先暂存在Replay Buffer中,然后发到目标设备。源设备的数据链路层根据来自目标设备的ACK/NAK DLLP报文决定是重发这些TLP,还是清除保存在Replay Buffer中的TLP。因此,Replay Buffer的大小决定了事物层可以暂存在数据链路层的报文数。
DLLP 的格式
DLLP与TLP的概念并不相同,DLLP产生于数据链路层,终止于数据链路层, 这些报文不会出现在事务层中,而且对系统软件透明。设置 DLLP 的目的是为了保证 TLP 的正确传 送和管理 PCIe 链路。
值得注意的是,DLLP并不是指TLP加上Sequence前缀和LCRC后缀组成的数据报文(这是TLP在数据链路层的封装方式,但不是DLLP),而是具有单独的格式,如下图所示。
其中比较常见的就是ACK/NAK。
ACK/NAK 协议
ACK / NAK 协议是一种滑动窗口协议。发送端在发送 TLP 时, 首先将这个 TLP 放入发送窗口中(这个窗口即 Replay Buffer),并对这些 TLP 从 0~n进行编号。当发送端收到接收端对第 n个报文的确认后,表示第n、n-1、n-2等在窗口中的报文都已经被正确收到,然后统一滑动这个窗口。 PCIe 总线使用这种方法可以提高窗口的利用率。与此对应, 接收端也维护了一个窗口, 该窗口记录数据报文的发送序列号范围。
l 发送端收到ACK DLLP报文
发送端使用计数器 NEXT_TRANSMIT_SEQ 的当前值设置 TLP 的 Sequence 号, 该计数器 的初始值为 0。 PCIe 设备每发送完毕一个 TLP, 这个计数器将加 1, 直到该计数器的值为 4095(NEXT_TRANSMIT_SEQ 的{BANNED}最佳大值)。与此对应, 接收端也设置了一个 12 位的计数器 NEXT_RCV_SEQ。 这个计数器记录接收 端即将接收的 TLP 的 Sequence 号。
(1)发送端向接收端发送TLP3~7,其中 TLP3 是{BANNED}中国第一个报文,TLP7是{BANNED}最佳后一个报文。此时发送端的 NEXT_TRANSMIT_SEQ 计数器为 8, 表示即将填入到 Replay Buffer 中的报文序 列号为 8。
(2)接收端按序收到 TLP3~5,而TLP6和7仍在传送过程中。接收端的 NEXT_RCV_ SEQ计数器为6,表示即将接收的报文序列号为 6。
(3)接收端通过报文检查决定接收TLP3~5,然后发送 ACK DLLP,此时这个 ACK DLLP 的 AckNak_Seq_Num 字段为5。为了提高总线的利用率,接收端不会为每一个接收到的TLP都做出应答(不会逐包ACK,而是类似TCP聚合ACK)。 在这个例子中,AckNak _Seq _Num 字段为5表示TLP3~5都已经被接收。
(4)发送端收到 AckNak_Seq_Num 字段为 5 的ACK DLLP后,得知TLP3~5 都被成功接收。此时发送端将 TLP3 ~ 5 从 Replay Buffer 中清除。
(5)接收端陆续收到 TLP6~7后, 接收端的 NEXT_ RCV_ SEQ 计数器为 8,表示即将 接收的报文序列号为8。然后接收端向发送端发送 ACK DLLP, 这个DLLP的 AckNak_Seq_ Num字段为7,即为 NEXT_RCV_SEQ-1。
(6)发送端收到AckNak_Seq_Num字段为7的ACK DLLP 后,得知TLP6~7都被成功接收。此时发送端将TLP6 ~ 7从Replay Buffer 中清除。
l 发送端收到NAK DLLP报文
(1)发送端向接收端发送TLP3~7其中TLP3是{BANNED}中国第一个报文,而TLP7是{BANNED}最佳后一个报文。
(2)接收端按序收到TLP3~5,而TLP6和7仍在传送过程中。
(3)接收端通过报文检查决定接收TLP3~4,此时 NEXT_RCV_SEQ为5,表示即将接收TLP5。
(4)TLP5没有通过完整性验证,此时接收端将向对端发送NAK DLLP,这个 DLLP 的 AckNak_Seq_Num 字段为4,即为 NEXT_RCV_SEQ - 1。AckNak_Seq_Num 字段为4表示接收端{BANNED}最佳后一个接收正确的TLP,其 Sequence 号为4。
(5)发送端收到AckNak_Seq_Num字段为 4 的 NAK DLLP 后,得知 TLP3 ~ 4 已被成功接收。 此时发送端首先停止从事务层接收新的 TLP,之后将TLP3 ~ 4 从 Replay Buffer 中清除。
(6)发送端重新发送在 Replay Buffer 中从 TLP5 开始的报文。 在这个例子中, 发送端将重新发送TLP5~7(go-back-n方式)。
当出现NAK的时候,表示链路肯定出了问题,所以不能无限次的发送同一个东西。为此在发送端中设置了一个 2 位计数器 REPLAY_NUM,这个计数器的初始值为 0,当数据链路层处于 Inactive 状态时,该计数器保持为 0。 REPLAY_NUM 计数器按照以下几个原则进行更新。
l 接收端发送 ACK DLLP
(1)发送端发送TLP3~7 给接收端,其中TLP3是{BANNED}中国第一个报文,而 TLP7是{BANNED}最佳后一个报文。
(2)接收端按序收到TLP3~5,而TLP6和7仍在传送过程中。此时 NEXT_RCV_SEQ的值被更新为6,表示下一个即将接收的 TLP,其Sequence号为6。
(3)接收端通过报文检查决定接收TLP3~5,然后发送ACK DLLP,这个DLLP 的Ack?Nak_Seq_Num字段为5。为了提高总线的利用率,接收端不会对每一个接收到的 TLP 都做出应答。在这个例子中,AckNak_Seq_Num字段为5表示 TLP3~ 5都已经被接收。
(4)接收端将接收到的TLP3~5传递给事务层。
(5)接收端陆续收到TLP6~7后,继续执行步骤3~4。
l 接收端发送 NAK DLLP
下面将使用一个实例进一步说明接收端如何发送 NAK DLLP,该实例如上图所示,其描述如下所示。
(1)发送端向接收端发送TLP3~7,其中TLP3是{BANNED}中国第一个报文,而TLP7是{BANNED}最佳后一个 报文。
(2)接收端按序收到TLP3~5,并将这些报文放入Receive Buffer,当然也可以在这些报 文通过完整性检查后,再决定是否将这些TLP放入Receive Buffer中,而 TLP6和7仍在传送过程中。
(3)接收端通过报文检查决定接收TLP3~4,此时NEXT_RCV_SEQ为5,表示即将接收TLP5。此时接收端将TLP3~4传递给事务层。
(4)而TLP5没有通过完整性验证,此时接收端将发送NAK DLLP,这个DLLP 的Ack? Nak_Seq_Num字段为4,即 NEXT_RCV_SEQ - 1。AckNak_Seq_Num字段为 4 表示接收端{BANNED}最佳后一个正确接收的TLP,其 Sequence号为4。此时接收端将设置 NAK_SCHEDULED 位为 1,而 NEXT_RCV_SEQ 保持不变,即为 5。
(5)接收端将丢弃TLP5。 当TLP6~7到达时,接收端仍然丢弃这些报文, 即便这些报 文通过了完整性检查,因为这些报文的 Sequence 号大于 NEXT_RCV_SEQ。 接收端不会为TLP6~7发送NAK DLLP,因为此时 NAK_SCHEDULED 位有效。
(6)发送端收到NAK DLLP,其序号为4,此时发送端首先将TLP3~4从 Replay Buffer中清除,因为TLP3~4已经被接收端正确接收,然后重新发送 TLP5~7。
(7)接收端如果正确接收到TLP5时,发现其Sequence号与NEXT_RCV_SEQ相等, 将清除 NAK_SCHEDULED 位。
(8)接收端陆续接收到TLP6~7,并根据CRC的检查结果决定发送ACK DLLP 或者 NAK DLLP。
在某些情况下, 接收端发送的 NAK DLLP 可能并没有被发送端正确接收, 因此接收端在 很长一段时间内都不会得到 “发送端重试的” TLP。 此时接收端将会择时重发 NAK DLLP, 为此接收端设置了一个 AckNak_LATENCY_TIMER 计数器, 当该计数器溢出时, 接收端将重 发 NAK DLLP。