上一章节讨论了流量控制协议(Flow Control)的目的以及细节操作。流量控制是用来确保在接收者无法接收TLP时,发送方不会再继续发送TLP。这避免了接收Buffer溢出,也消除了原本PCI工作方式中的一些低效行为,比如断开(disconnect)、重试(retry)和等待态(wait-state)。
本章节会讨论用于支持QoS(Quality of Service)的机制,并描述对网络结构中传输的不同数据包的传输时间和带宽进行控制的意义。这些机制包括,特定应用的软件将会给每个数据包都分配优先级,以及每个设备内构建可选的硬件来启用事务优先级管理。
下一章节将会讨论PCIe拓扑结构中的事务对排序的要求。这些规则是继承自PCI的,它们许多都受到“生产者/消费者(Producer/Consumer)”程序设计模型的推动,因此在下一章内会描述这个模型的机制原理。原始的规则中还考虑了必须要避免的那些死锁情况。
如今的许多计算机系统并不含有用于管理外设流量的机制,但是有一些应用是需要这种机制的。举一个例子,流媒体视频通过一个通用数据总线(General-Purpose data bus)进行传输,这要求数据要在正确的时间进行传输。在嵌入式引导控制系统中,及时的传输视频数据对于系统操作来说也是极为关键的。由于预见到了这些需求,原始的PCIe协议规范中就包含了QoS(服务质量)机制,它可以使得某些流量更优先被传输或处理。更广义的说法是,这是一种差异化的服务(Differentiated Service),因为数据包会根据被分配到的优先级不同而受到不同的处理对待,并且这种机制允许有一个范围较广的服务偏好(也就是允许的优先级比较多)。在这个范围的高的一端,QoS机制可以为这个优先级的应用提供可预测的(Predictable)以及可保证的(Guaranteed)的性能。这种级别的服务支持被称为“Isochronous(同步的)”服务(可以类比同步以太网、TTE,这里的Isochronous就是这个同步的含义),这个术语来源于两个希腊单词“isos”(相等)和“chronos”(时间),合起来就表示某个事情以相同的时间间隔发生。要使得这样的机制在PCIe中能正常工作,这要求硬件元素和软件元素的共同作用。
支持高级别的服务给系统性能提出了一些要求。例如,传输速率需要足够的高,这样才能满足应用在一个时间范围内传输足够多的数据的需求,同时这也使得应用能够适应与其他流量之间的竞争。另外,传输时延也需要足够低,这样才能确认数据包及时传输到达,避免出现延迟问题。最后,必须要对错误处理进行管理,这样它才不会影响到需要及时传输的数据包的传输。要实现上述这些目标,需要有一些特殊的硬件元素,其中一种就是一组称为Virtual Channel Capability Block(虚拟通道能力块)的配置寄存器组,如图 7‑1所示。
图 7‑1虚拟通道能力寄存器
我们所需要的第一件事情是要用一种方式来区分各种流量,也就是需要用一些东西来区分哪些数据包具有高优先级。这种需求是通过为数据包标明流量类型(TC)来完成的,流量类型(TC)中定义了8个优先级,它通过TLP Header中的3bit TC字段来表示(TC 0-7,数字越大优先级越高)。图 7‑2所示的32位memory请求的TLP Header中标识出了TC字段的位置。在初始化过程中,设备驱动程序会与同步管理软件(Isochronous Management software)沟通服务的级别,同步管理软件会返回相应的TC值来使用各种流量类型的数据包。然后驱动程序就会给数据包分配正确的TC优先级。TC值默认为0,这样就可以让不需要高优先级服务的数据包不会干扰到那些需要高优先级服务的数据包。
图 7‑2 TLP Header中的流量类型(TC)字段
不识别PCIe的配置软件(指传统PCI的配置软件)是无法识别出这些新的寄存器的,它就会为所有的事务都分配默认的TC0/VC0。另外,也有一些数据包一直被要求要使用TC0/VC0,比如配置(Configuration)、I/O以及Message事务。如果这些数据包被认为是维护包流量,那么就有必要将它们限制在VC0中,并且要让它们原理高优先级数据包的传输路径,避免影响到高优先级包。
VC(虚拟通道)实际上是硬件Buffer,它用来作为输出数据包的队列。每个端口都必须要包含默认的VC0,并且最多可以有8个VC(VC0-VC7)。每个通道都表示可供输出数据包使用的不同传输路径。使用多通道路径的想法类似于收费公路,如果司机购买了某种无线电标签,那么收费站就允许他们在高优先级车道行驶,而对于那些没有购买无线电标签的司机来说,虽然他们仍然可以在这条收费公路行驶,但是他们必须在每个收费站都停下来,且每次通过都要付钱,这使得这些司机通过收费站需要更长的时间。如果只有一条路径,那么要经过的每个人所花费的时间将受到最慢的那个司机的影响和延误,而如果有多条路径的话就意味着有高优先级的司机不会被延误(可以使用高优先级通道)。
分配给每个数据包的TC值在整个传输过程中是不会改变的,而且必须在每个服务点都有TC对VC的映射,也就是某种TC的数据包要使用哪条路径传输到目的地。VC映射是链路自己指定的,从一个链路到另一个链路后VC映射是可以改变的。配置软件会在初始化时建立这种映射关系,它是通过VC资源控制寄存器(VC Resource Control Register,图 7‑1)中的TC/VC Map字段来实现的。这个8bit的字段使得多个TC值可以被映射到某一个VC,因为它的每个bit都代表了一个对应的TC值(bit 0 = TC0,bit 1 = TC1,etc.)。将一个VC的8bit TC/VC Map字段中的某一位置为1,就表示将相应的TC分配给了这个VC。图 7‑3展示了一个TC-VC的映射例子,这个例子中TC0和TC1被映射到了VC0,而TC2、TC3、TC4则是映射到了VC3。
软件在分配VC ID和映射TC的方面有很大的灵活性,但是在进行TC/VC映射时也要遵循一些规则:
n 对于相同的一条链路的两端的两个端口来说,它们的TC/VC映射必须是相同的。
n TC0自动映射到VC0。
n 其他的TC(除了TC0)可以映射到任何一个VC。
n 一个TC只能映射到一个VC,而可以有多个TC映射到同一个VC。
所使用的虚拟通道的数量取决于链路两端设备所能共享的最大虚拟通道资源容量。软件会为每个VC分配ID,并且会将TC映射至VC。
图 7‑3 TC到VC的映射举例
软件会检查连接在公共链路上的设备所能支持的VC的数量,并且一般会选择链路两端设备所能支持的最大数量作为VC的实际使用数量。考虑如图 7‑4所示的拓扑示例,在这里,Switch上的每个端口(port)都支持全部的8个VC,而设备A仅支持默认的VC0,设备B支持4个VC,设备C支持8个VC。需要注意,虽然Switch的端口A能够支持8个VC,但是由于设备A仅能支持VC0,因此端口A的其余7个VC是无法被使用的。相似地,Switch的端口B实际也仅能使用4个VC。
图 7‑4一个设备支持多个虚拟通道
配置软件要确定每个端口所能支持的最大VC数量,它是通过读取虚拟通道能力寄存器组(Virtual Channel Capability register,图 7‑1)中的Extended VC Count字段来完成的,这个字段如图 7‑5所示。
图 7‑5扩展VC支持字段
配置软件需要给每个VC都分配一个ID号,除了VC0(因为它是固定存在的)。如图 7‑3所示,VC能力寄存器空间中,每个VC都有自己12Byte的配置寄存器组。第一组寄存器总是由VC0使用。扩展VC计数(Extended VC Count)字段定义了当前端口实现的额外VC的数量(除VC0之外),每个实现的VC都有一组配置寄存器。注意,字段的值“n”表示的是额外实现的VC的数量,例如扩展VC计数字段为3,那么就说明除了VC0以外还有3个VC,以及它们每个都有一组自己的配置寄存器。
软件通过使用VC ID字段为每个额外的VC分配一个编号(见图 7‑3)。这些ID号并不一定要是连续的,但是必须只能出现一次而不能重复。
当一个设备有多个VC,且每个VC都有待发送的数据包,那么就需要VC仲裁机制来决定数据包传输的顺序。软件可以从硬件所实现的几种选择方案中选择任意的一种。设计的目标是要实现所需的服务策略,并且确保所有的事务都在向前推进,以防止发生意外超时。另外,VC仲裁也会受到流量控制(flow control)和事务排序(Transaction Ordering)的相关需求的影响。这些主题内容将在其他章节中讨论,但是要注意它们也会影响仲裁,因为:
n 每个受支持的VC都有它自己的Buffer和流控处理
n 映射到同一个VC的多个事务一般会按照严格的顺序进行传输(尽管也有例外,比如一个数据包的“宽松排序Relaxed Ordering”这一属性标志位被置为1时。)
n 事务排序(Transaction Ordering)仅应用在一个VC内,因此对于分配给不同的VC的几个数据包之间是不存在排序关系的。
图 7‑6中的例子中含有两个VC(VC0和VC1),VC1:VC0的传输优先级是3:1的比率,也就是说每传输3个VC1数据包才会发送1个VC0数据包。Device Core向TC/VC映射逻辑发送请求(请求包中包含TC值)。基于已经规划好的映射,数据包被放入相应的VC Buffer中等待传输。最终,VC仲裁器决定这个由VC优先发送数据包。这个例子展示了一个单向的流程,但是同样的逻辑也适用于同时进行相反方向的传输。
VC能力寄存器提供了三种基本的VC仲裁方式:
\1. 严格优先级仲裁(Strict Priority Arbitration**)**:在所有含有数据包的VC中,拥有最大编号的VC永远赢得仲裁。
\2. 分组仲裁(Group Arbitration**)**:VC们被硬件分成1个低优先级组和1个高优先级组。低优先级组使用的仲裁方法是由软件从可选方法中来选取的,而高优先级组则是使用严格优先级仲裁(Strict Priority Arbitration)。
\3. 硬件固定仲裁(Hardware Fixed arbitration**)**:仲裁方案构建在硬件中。
图 7‑6 VC仲裁示例
默认的优先级方案是基于VC ID的固有优先级(VC0=最低优先级,VC7=最高优先级)。这种机制是自动的,并不需要任何额外配置。图 7‑7展示了一个严格优先级仲裁的例子,它包含了所有的8个VC,这其中使用VC ID号来管理事务的发送顺序。使用严格优先级仲裁的VC的最大编号不能大于扩展VC计数(Extended VC Count)字段(如图 7‑5)。此外,如果设计者选择让所有的VC都使用严格优先级仲裁,那么图 7‑8中的Port VC Capability Register 1(端口VC能力寄存器1)的Low Priority Extended VC Count字段需要固定为0。
图 7‑7严格优先级仲裁
严格优先级方案要求高编号的VC永远要优先于低编号的VC。举例来说,如果所有的8个VC都使用严格优先级方案来管理,那么VC0就只能在其他所有VC都不需要发送数据包时才能发送数据包。这样的方式可以使得最高优先级的数据包得到很高的带宽以及最低的延时。但是,严格优先级方案可能会使得低优先级的通道分配不到带宽(一直有高优先级通道占用所有可用带宽),所以必须要小心地确保不会发生这种情况。协议规范中要求要对高优先级流量进行管理,以免出现低优先级通道的带宽饥饿,并给出了两种对高优先级流量进行管理的方法:
n 发起事务的端口可以限制高优先级数据包的注入速率,从而就可以为低优先级事务提供更多带宽。
n Switch可以在出口端口(egress port)调节多个流量。这种方法可能会限制某些应用和设备的吞吐量,通常这些应用设备是一些试图超过可用带宽限制的高带宽应用和设备。
设备的设计者可能也需要限制使用严格优先级的VC的数量,可以通过将VC们分为1个低优先级组合1个高优先级组来实现这一目标,在下一小节我们将会讨论这种分组方法。
图 7‑8展示了Port VC Capability Register 1(端口VC能力寄存器1 )的Low Priority Extended VC Count字段。这个只读(read-only)字段指定了该设备低优先级仲裁组的VC ID上限。例如,如果这个字段的值是4,那么就说明VC0-VC4都属于低优先级组,而剩下的VC5-VC7则是高优先级组。注意,若Low Priority Extended VC Count的值为7,则说明没有VC使用严格优先级。
图 7‑8低优先级扩展VC计数
如图 7‑10所展示的那样,高优先级组的VC会继续使用严格优先级仲裁,但是低优先级仲裁组则会使用设备支持的其他的一种仲裁方法。如图 7‑9所示,Port VC Capability Register 2(端口VC能力寄存器2)用来表示设备可以支持低优先级组使用哪些替代仲裁方法,而VC Control Register(VC控制寄存器)会允许具体选择了哪种仲裁方法。低优先级仲裁方案包括:
n 基于硬件的固定仲裁(Hardware Based Fixed Arbitration)
n 加权轮询仲裁WRR(Weighted Round Robin Arbitration)
图 7‑9 VC仲裁能力字段
图 7‑10 VC仲裁优先级(分组仲裁)
这个方案定义了一种基于纯硬件的仲裁方法,并且不需要额外的软件设置。这种纯硬件的仲裁方法可以是硬件设计者实现的任何一种方法,而且这种方法还可以基于预期的设备负载或带宽来进行设计。举一个简单的例子,一个普通的轮询调度中,每个VC都会得到相同的次数来循环轮流发送数据包。
在这种方法中,某些VC可以被给予比其他VC更大的权重(更高的优先级),这些高权重VC会比其他VC得到更多的entries(条目),也就是更多的发送数据包的机会。协议规范定义了三种WRR选项,每种选项都具有不同数量的entries(称为phase“阶段”)。表项的大小通过往Port VC Control Register(端口VC控制寄存器,图 7‑9)中的VC Arbitration Select(VC仲裁选择)字段写入相应的值来进行选择。表项中的每个条目都代表一个软件使用低优先级VC的阶段。VC仲裁器会以连续的方式反复的扫描表项的所有条目,然后会通过表项条目内指定的VC来发送数据包。一旦一个数据包被发出,那么仲裁器就会马上去扫描下一个条目(entry),也就是进入下一个阶段(phase)。图 7‑11展示了一个拥有64个条目的WRR仲裁表。
图 7‑11 WRR VC仲裁表
VC Arbitration Table(VAT,VC仲裁表)位于配置空间中,它的具体的位置的表示方法是一个偏移量,其基址为VC能力结构(VC Capability Structure)的地址,如图 7‑12所示。
如图 7‑13所示,VAT(VC Arbitration Table)中的每个条目都是一个4bit字段,它用来指示这个阶段中需要发送数据的VC的ID号。整个VAT表的长度(有多少条目)是通过图 7‑9中的仲裁选项来进行选择的。
图 7‑12 VC仲裁表偏移字段以及加载VC仲裁表字段
这个表由配置软件加载,以实现虚拟通道所需要的优先级顺序。当表发生了任何变化时,硬件会设置VC Arbitration Table Statue bit(VC仲裁表状态位),这为软件提供了一个途径来验证表项虽然发生的改变但是还没有真正作用在硬件上。一旦表被加载完成,软件就会设置端口VC控制寄存器(图 7‑9)中的Load VC Arbitration Table bit(加载VC仲裁表位)。这会使得硬件对新的表项进行加载,将新的值应用在VC仲裁器中。当表项被加载完成并应用后,硬件就会清除VC Arbitration Table Statue bit(VC仲裁表状态位),这也就通知了软件关于表项加载的操作已经完成。这种表项加载的方式的动机可能是为了在系统运行中也能更改表内容而不中断系统的运行。但是问题是配置写操作一次仅能更新1DW的表内容,它是一种相对慢速的事务,这也就意味着需要一段比较长的时间才能完成整个表内容的更新,而在这段时间里这个表仅仅进行了部分更新。换句话说,这有可能会导致设备在这段时间里继续运行时出现意料之外的行为。为了避免这种情况,这种机制允许软件完成对整个表的更改之后,在统一的一次将它们全部应用在硬件仲裁器中。
图 7‑13加载VC仲裁表条目
Switch端口和Root端口经常接收到一些需要转发至其他端口的数据包。这些数据包虽然由多个端口进行接收,但是它们可能会都需要转发至同一个出口端口的同一个VC,那么这个时候就需要仲裁机制来决定这些不同端口输入的数据包如何依次的放入同一个目标VC。类似于VC仲裁,端口仲裁也有多种可选方案供配置软件选择。TC、VC和仲裁方法的多种组合可以支持一系列的服务级别,这些服务级别分为两个大类:
\1. 异步类型(Asynchronous**)**:数据包会得到“尽力而为Best Effort”的服务,并且可能本身也没有收到任何的偏好需要考虑。许多设备和应用,例如大容量存储设备,对带宽或者延时并没有很严格的要求,并不需要特殊的定时机制。从另一方面说,通过为不同的数据包建立流量类型(Traffic Class)这种层次结构,那些需求更多的应用所产生的数据包可以仍然划分优先级并且保持其优先级地位。在服务的级别需要保证之前,即使是差异化服务也会被认为是异步的。当然,异步服务将总是可用的,而不需要任何特别的软件或者硬件选项。
\2. 同步等时类型(Isochronous**)**:当服务需要保证延时和带宽时,我们将它移动到同步等时类型(Isochronous)这一大类中。当两个设备之间通常需要同步连接(Synchronous connection)时,这将会非常有用。例如,当耳机直接插入驱动器时,它与从音乐CD中获取数据的CD-ROM之间使用的是同步连接。然而,当音频数据必须要通过一个像PCIe一样的通用总线之后才能到达外部扬声器,那么这个时候的连接就不是同步的了,因为其他的流量也有可能要使用相同的数据流路径。为了达到相同的效果,同步等时服务必须保证在不阻止其他流量使用链路的同时,能够及时正确的传输这些时间敏感(time-sensitive)的音频数据信息。毫无疑问,这就必须要有专门的软件和硬件设计来支持它。
关于端口仲裁的概念如图 7‑14所示。需要注意,端口仲裁在系统中的多个位置都会出现:
n Switch的出口端口(egress)
n 支持Peer-to-Peer事务时的RC端口
n 某些RC出口端口,它们通向的目的地是主存
端口仲裁通常需要对Switch或者Root的出口端口的每个虚拟通道进行软件配置。在下面的例子中,Root端口2支持来自Root端口1和3的Peer-to-Peer传输,因此这里需要端口仲裁。但是需要注意的是,对于Root端口间支持Peer-to-Peer的传输是可选的,因此并不是每个Root出口端口都需要端口仲裁。
与系统内存相连接的路径是一条有趣的路径。这里可能会有来自多个入口端口(ingress)的数据包在同一时间都想要访问这一个出口端口,因此它需要支持端口仲裁。然而,它所使用的并不是一个PCIe端口,因此它并不需要通过配置我们接下来要讨论的PCIe寄存器来对端口仲裁进行支持。相反地,RC需要提供供应商指定的(vendor-specific)的一组寄存器来提供这方面的功能,它被称为Root Complex Register Block(RCRB,RC寄存器块)
因为出口端口的每个VC的端口仲裁都是独立管理的,所以每个支持可编程的端口仲裁的VC都需要有一个单独的仲裁表,如图 7‑15所示。只有Switch和RC端口才支持端口仲裁表,EP内是不允许使用端口仲裁表的。
图 7‑14端口仲裁概念
图 7‑15每个VC的端口仲裁表
尽管在协议规范中并没有说明,但是实际上针对不同的数据包流之间的仲裁需要在出口端口中使用额外的Buffer来积攒来自多个端口的流量,如图 7‑16所示。这个例子中有两个入口端口(端口1和2),它们的事务都被路由至同一个出口端口(端口3)。Switch所进行的操作包括:
\1. 到达入口端口的数据包直接根据TC/VC映射被放入相应的流控Buffer(VC)。
\2. 数据包从流控Buffer中被转发至路由逻辑,路有逻辑将决定数据包被路由至哪个出口端口,并完成路由操作。
\3. 被路由至出口端口(端口3)的数据包通过TC/VC映射,以此来确定他们应该被放入出口端口的哪一个VC。
\4. 每个入口端口都存在一组Buffer与之相关联,允许在端口仲裁完成前都可以追踪入口端口号。
\5. 端口仲裁逻辑决定每组入口Buffer发送事务的顺序。
图 7‑16端口仲裁缓冲
实际上端口仲裁的机制的定义与VC仲裁所使用的模型比较相似。配置软件通过读取图 7‑17所示的寄存器来确定端口的能力,并为每个VC选择端口仲裁方案。
图 7‑17软件选择端口仲裁方案
这种仲裁机制不需要软件设置。一旦它被选择使用,那么就仅由硬件来进行管理。实际的仲裁方案由硬件的设计者选择,可能会基于设备的预期需求来进行选择。它也许只是简单的保证了仲裁的公平性,或者是优化了设计的某些方面,但是它无法支持差异化或同步服务。
就像VC仲裁中的加权轮询仲裁机制类似,软件可以设置端口仲裁表,以此来让一些端口在仲裁中有更多的被选中的机会。这种方法为来自不同端口的流量分配了不同的仲裁权重。
在扫描仲裁表时,其中的每个阶段(Phase)都会指定下一个被接收的数据包的入口端口号。一旦当前阶段的数据包被发送,仲裁逻辑就会立即跳转至下一个阶段。如果并没有数据包需要发送给被选中的端口,仲裁器就将会立即推进到下一个阶段去。对于仲裁表中的这些条目并没有与它们相关的时间值。
对于WRR端口仲裁,有四种仲裁表长度可以使用,仲裁表长度确定了它包含的阶段数量。若仲裁表中的条目数量越多,那么也就可以使用更复杂更特别的仲裁选择比率。而从另一方面来说,仲裁表条目数量越少则会使用更少的存储空间以及更低的成本。
要对同步类型的服务进行支持,这种机制是必需的。像它名字所述的那样,基于时间的加权轮询(TBWRR)为每个仲裁阶段都加入了时间元素。就像在WRR中,端口仲裁器从仲裁表中获得当前阶段所指示的端口号,然后根据这个端口号去从相应的入口端口VC Buffer中取出数据包用于传输。而对于现在的TBWRR来说,基于时间的仲裁器并不会立即推进到下一个阶段,而是要等到当前的虚拟时隙(timeslot)过去之后才推进到下一个阶段。这确保了从入口端口接收事务的时间间隔是确定的。如果被选中的端口并没有需要发送的事务,那么也会等待至下一个时隙,而在这中间的这段等待时间不会有任何信息被发送。注意,时隙的并不控制整个传输所持续的时间,而是控制两次传输之间的间隔。这里的事务传输的最大持续时间是完成轮询并且返回到初始时隙中所花费的时间。时隙的长度也许在未来会发生变化,但是目前它的值为100ns。
基于时间的WRR仲裁所支持的最大长度的仲裁表含有128个阶段,但是一个具体的VC可用的仲裁表条目实际数量可能会低于这个数值。这个值是由硬件初始化,并通过每个支持TBWRR的VC的Maximum Time Slots字段来进行表示,这个字段如图 7‑18所示。
图 7‑18最大时隙寄存器
端口仲裁表的实际大小和格式,是一个和阶段数量和入口端口数量有关的函数,这里的入口端口可以是Switch端口、RCRB(Root Complex Register Block)或者是支持Peer-to-Peer传输的RC端口。端口仲裁表所支持的最大入口端口数量为256个。实际的仲裁表中每个条目的bit数量,其设计考虑和管理都依赖于可以将事务传输至此出口端口的入口端口数量。每个仲裁表条目的大小表示在端口VC能力寄存器1(Port VC Capability Register 1)中的Port Arbitration Table Entry Size(端口仲裁表条目大小)字段,这个字段的长度为2bit。其中各个数值含义为:
n 00b:1bit(也就是在两个端口之间进行选择)
n 01b:2bits(4个端口)
n 10b:4bits(16个端口)
n 11b:8bits(256个端口)
配置软件通过将各端口号填入仲裁表来完成仲裁表的加载,以实现支持的每个VC所需的端口优先级功能。如图 7‑19所示,仲裁表的格式取决于每个条目的实际大小以及该设计中所支持的阶段的数量。
图 7‑19端口仲裁表格式
现在让我们考虑一个3端口Switch的例子,以此来对端口仲裁和VC仲裁进行举例讲解。我们在示例中假设,由入口端口0和入口端口1接收的数据包会向上行移动,而端口2则是面向上行的出口端口(也就是面向RC)。下面的关于这个示例的讨论可以参照图 7‑20。
图 7‑20 Switch中的仲裁示例
\1. 到达入口端口0的数据包根据TC/VC映射关系,被端口0放入相应的接收VC中。如图中展示的一样,流量类型为TC0或是TC1的TLP会被放入端口0的VC0 Buffer;流量类型为TC3或是TC5的TLP会被放入端口0的VC1 Buffer。在这个端口的链路上,其他的TC都是不允许出现的,如果出现了一个数据包而它的TC无法正确的映射到任何一个存在的VC,那么就会被视作发生了一个错误。
\2. 到达入口端口1的数据包也会根据TC/VC映射关系被放入相应的VC中,但是端口1的TC/VC映射关系和端口0的不同。在端口1中,流量类型为TC0的TLP会被放入端口1的VC0 Buffer;流量类型为TC2-4(TC2、TC3、TC4)的TLP会被放入端口1的VC3 Buffer。链路上不允许出现其他TC的数据包。
\3. 对于端口0和端口1这两个端口来说,它们接收的数据包的目的出口端口是由每个数据包的路由信息来决定的。例如,Memory或是IO请求TLP会使用地址路由方式。
\4. 所有目的出口端口为端口2的数据包,都会被呈交给端口2的TC/VC映射逻辑。如图中所示的那样,流量类型为TC0-2(TC0、TC1、TC2)的TLP会被放入端口2中被标识有TLP各自入口端口号的VC0 Buffer;流量类型为TC3-7(TC3、TC4、TC5、TC6、TC7)的TLP会被放入端口2中被标识有TLP各自入口端口号的VC1 Buffer。
\5. 端口仲裁是端口2中的每个VC独立进行的,各个VC会将自己的一组带有各入口端口号标识的Buffer中的数据包进行排队,以此来决定哪个入口端口传来的TLP将要成为下一个真正被放入真实VC的数据包。
\6. 最后,由VC仲裁来确定在各真实的VC Buffer实际在链路上传输事务的顺序是怎样的。
\7. 注意,VC仲裁仅会在有足够的流控Credit时,才回去选择数据包用于传输。
有一组寄存器称为Multi-Function Virtual Channel(MFVC)Capability(多功能虚拟通道能力寄存器组),这组寄存器是为了在具有多种Function的EP设备中实现QoS的这种特定情况而定义的。
在协议规范中描述了这种仲裁的两种情况。第一种情况,如图 7‑21所示,EP中虽然有两个Function,但是只有Function 0中含有VC能力寄存器(VC Capability Registers),并且所有的Function的任务分配都是相同的。对于这种情况,Function之间的仲裁方式将按照一些厂商指定(Vendor-Specific)的方法去进行。这是最简单的方法,但是这无法在内部包含一个标准结构用于定义不同Function的请求的优先级,因此它无法支持QoS。
图 7‑21简单的多Function仲裁
如果希望支持QoS,那么就需要在VC0中实现一个MFVC(Multi-Function Virtual Channel)寄存器组,并且每个Function都要有自己独立的一组VC能力寄存器。为了保持软件的向后兼容性,协议规范中声明,所有不适用MFVC的设备的VC Capability ID(VC能力ID)都必须为0002h,而所有内部实现了MFVC的设备的VC Capability ID都必须为0009h。
图 7‑22展示了MFVC寄存器组,以及一个示例框图,示例中的EP内含有两个Function,这个EP的端口支持2个VC。每个Function都有自己的事务层以及VC能力寄存器组,但是并没有实现更低的层级。相反地,它们都连接到了二者共享的端口的事务层,这个共享端口具有所有的层级。这种共享硬件接口的方式可以降低成本和开销,并且各Function事务层种添加的MFVC寄存器组使得Function可以处理同步等时流量(Isochronous Traffic)。
在图 7‑22中可以看到,MFVC寄存器仅存在于Function 0中,并定义了此接口使用的VC以及仲裁方法。MFVC寄存器组看起来与VC能力寄存器组十分相似,它可以支持VC仲裁与Function仲裁。由于来自多个不同Function的数据包有可能在同时访问同一个VC,因此就需要Function仲裁来决定不同Function数据包之间的优先级。这种方式在现在看来应该是非常熟悉的,因为它与此前的端口仲裁其实是相同的概念,甚至连仲裁方法选项都是一样的,包括TBWRR。VC仲裁选项也与单Function VC寄存器中的仲裁选项相同。
图 7‑22多Function仲裁中的QoS支持
前面的内容中提到,并不是每个设备或者应用都需要支持同步等时服务,但是也有一些是必须要支持同步服务的。由于PCIe从设计之初就想要支持同步等时服务,因此让我们来考虑一下要支持它的话会有什么样的要求。
考虑图 7‑23中展示的例子,例子中的同步连接(synchronous connection)使我们所期望的,但是这是无法实际实现的。相反地,我们通过通过同步等时机制来模拟一个同步连接。在这个例子中,等时性(Isochrony)定义了在服务间隔内需要被传输的用来完成服务需求的数据量。下面对操作流程进行了描述:
\1. 同步源(摄像机和PCIe接口)在第一个等时服务间隔(Service Interval,SI 1)中在Buffer A积累数据。
\2. 摄像机在下一个服务间隔(SI 2)中通过通用总线发送完Buffer A中所有被缓存积累的数据,同时也将下一块新数据积累在Buffer B中。
明显地,系统必须要能保证Buffer A中的全部内容要在这个服务间隔内发送,不管链路上是否还有其他流量在传输。要做到这一点,可以给这些时间敏感(time-sensitive)数据包分配高优先级,并且按照需要规划仲裁方案使得这些数据包即使在有其他流量竞争的情况下也能被优先处理。还需要注意,只要所有的数据在时间窗口内发送出去即可,而至于它们具体什么时间到达在这里并不关心,它们可能会在服务间隔中很分散的发送,也有可能集中在服务间隔的某一时刻紧密的发送。只要它们都在服务间隔中被发送出去,那么就依然可以做到需要的保证。
\3. 在SI 2中,录音机接收到数据,并将这些输入数据缓存起来,随后这些数据可以在SI 3被传递到存储介质来完成录制。摄像机也会在SI 3期间将Buffer B中的数据卸到链路上,同时也在SI 3内在Buffer A中积累新的数据,以此循环进行。
图 7‑23同步事务的示例应用
PCIe使用TBWRR端口仲裁机制中的时隙(time slot)来定义同步等时时长。目前,每个时隙是100ns,它代表TBWRR仲裁表的128个条目中的1个。一旦设置完成,仲裁器就会每12.8us循环重复一次这个仲裁表,这也代表全部的服务间隔。
要想让一个同步等时路径像预期的一样工作,这需要有一定的考虑。首先,数据包必须在可以测的时间点、以固定的间隔被发送。第二,同步等时数据需要被传输的最大数据量必须是要提前可以获知的,并且数据包不允许超过这个最大值上限。第三,链路必须要有足够的带宽来支持一个给定时隙内所要传输的数据量。
考虑接下来这个例子。一个单通道链路工作在2.5Gbps,每4ns发送一个符号(10bit符号)。也就是说每个100ns的时隙可以发送25个符号,但是这足够吗?在很多情况下这是不够的,因为1个TLP需要28byte组成 Header、序列号(Sequence Number)、LCRC等等的常规开销。这意味着100ns根本不足以完成这些常规开销的发送,更不用说发送数据荷载了。如果我们需要发送128byte的数据,那么需要的带宽就将是128+常规开销=156bytes。解决这个问题的一个选项是将链路宽度增加至8通道,这使得一次可以传输的数据量变为原来的8倍。这种改变会使链路在100ns内可以传输200bytes,那么也就可以在1个时隙中发送所有的同步等时数据。另一种解决方法是依然使用单通道链路,但是可以给予端口更多的时隙用于发送数据,比如将服务间隔由1个时隙增加到8个时隙,那么就也可以在1个服务间隔中传输200byte数据了。具体的选择哪种解决方法取决于成本和性能的权衡,但是系统的设计者必须知道同步等时路径对时间规划和带宽的要求,这样才能正确的建立同步等时路径。
像前面的例子中所描述的,为了要使一个设计进行正确的操作,时间规划已经成了必不可少的一部分,它是由到目前为止我们所讨论的多种元素的组合来完成执行的。首先,软件中必须选择高优先级TC,在硬件中配置VC,并定义TC到VC的映射关系,以此来让正确的数据包放入高优先级VC中。然后,期望的的时间规划其实是一个仲裁方案规划问题,通过这个规划来适配特定时间内所需要的带宽。例如,VC仲裁可能会使用严格优先级仲裁,因为这是唯一可以确保高优先级数据包不会被其他数据包阻塞延迟的仲裁方式。而对于端口仲裁则必须是TBWRR,这样才能执行确定好的时间规划。
要支持同步等时服务,还需要系统中各个软件元素之间的协调合作。在一个PC系统中,设备驱动会向OS(操作系统)报告同步等时服务的需求和能力(Capabilities),OS会根据这些信息对整个系统的需求进行评估,并为系统分配合适的资源。嵌入式系统会有所不同,因为所有的这些各部分的信息在一开始就是已知的,所以这里的软件可以更简单一些。在接下来的讨论中,我们将会描述PC系统中的情况,因为嵌入式系统的情况只是PC情况中更简单的一个子集。
一个设备驱动必须能够将时间规划的需求报告给用于监督同步等时操作的软件,并在真正尝试发送同步等时服务数据包之前,先要获得这个软件的许可。需要注意的重要的一点是,驱动级软件不应该自己直接改变硬件配置或者是仲裁策略,即使它有这个能力也不行,因为这将会引起混乱。如果多个驱动程序都各自独立的尝试更改硬件配置或仲裁策略,那么最后进行更改操作的驱动程序将会把之前的更改都覆盖掉。为了避免这种情况,一种被称为同步等时代理(Isochronous Broker)的OS级程序将会接收来自系统设备的时间规划请求,并以一种协调的方式来分配系统资源,以此来适配所有的这些请求。
这种程序用来管理端到端的同步等时数据包流。它从设备驱动接收到同步等时时间规划请求,用一种能适配目标路径上的各个请求的方式,来协调分配系统资源。在协议规范中,这被称为“在一对Requester/Completer(请求者/完成者)和PCIe结构之间,建立了一个同步等时契约(Isochronous Contract)”。要做到这些,需要确定期望的路径上确实可以支持同步等时流量,然后就编程写入合适的仲裁方案,以确保它在指定的时间规划要求下工作。
到目前为止,我们应该已经相当清楚需要做些什么来支持系统中的同步等时流量,但是让我们来看最后一个例子,在这个例子里将会把之前的各个内容部分全都结合起来。如果我们继续对此前的视频捕捉示例进行展开详述,以此来展示一个更加复杂的系统,如图 7‑24所示的系统,当摄像机可以把捕捉到的视频数据传送至系统存储时,我们就可以基于它来讨论这个系统中所必需的每个部分。这对于同步等时服务来说是一个比较困难的系统环境,因为系统中存在许多需要竞争路径上的带宽的设备,但是这也使得这个例子非常适合用来讲解这些过程中需要考虑的方方面面。
让我们从系统的最底部开始,对于录像EP设备本身,它的PCIe接口中需要有什么?在硬件中,如果我们想要区别不同的数据包,那么就需要1个以上的VC。为了简化例子,我们假设这个设备是一个单Function设备。设备驱动程序需要向OS级的同步等时代理程序报告设备的能力(Capabilities)和同步等时时间规划需求,同步等时代理程序将会根据这些信息对系统进行评估,并向设备驱动报告是否可能建立同步等时契约以及软件应该使用哪些TC。
驱动程序则将会将对VC的编号和数量进行编程写入,并将相应的TC映射至各VC。它也很有可能会将高优先级通道的VC仲裁编程为严格优先级仲裁(Strict Priority)。这里需要注意的一点是,仲裁必须要是“公平的”,意思是那些低优先级通道不能出现访问机会匮乏(Starve)。这意味着那些高优先级VC不能一直占用仲裁让自己的流量处于待发送状态,而是要随时间分批的传输数据包。
在结束对EP的讨论之前,还需要注意关于链路操作的另一个问题,那就是流量控制。设备在同步等时路径上的接收Buffer必须要足够大,只要数据包是按照同步等时契约(Isochronous Contract)均匀的输入,那么这个Buffer就要大到在不需要任何反压的情况下能够处理预期的输入数据包流。此外,流控更新DLLP(Flow Control Update)的返回速度必须足够快,以此来避免传输停滞。
图 7‑24同步等时系统示例
在讨论了EP之后,接下来让我们考虑一下在EP与RC之间的Switch中需要提供什么。Switch通常是没有自己的设备驱动程序的,所以只能由OS级软件(如同步等时代理Isochronous Broker)来读取它们的配置信息,以此来确定这些Switch能支持怎样的服务。首先,在同步等时路径上的所有端口都必须要支持至少2个VC,且链路两端的TC/VC映射关系也必须是相匹配的。要记住,一旦数据包到达了Switch端口的事务层,那么数据包里只有TC信息而没有VC信息,TC具体对应哪个VC是每个端口自己指定的而不会将这个信息携带在TLP中。Switch 1的下行端口与它下方连接的EP的各自TC/VC映射关系必须要相匹配,但是至于其他的Switch端口可以是不同的以此来匹配它们各自链路上的EP。
仲裁方法的选择是很直接的。为了简单起见,在我们的例子中的同步等时路径上承载的流量是单向的。对于在存储器读取操作中,其实是有可能出现双向的同步等时流量的,但是在我们的例子中选取的是类似视频流的情况。
同步等时出口端口使用的VC仲裁通常是严格优先级方案,原因与EP相同。端口仲裁则将会使用TBWRR方案,这意味着软件必须了解适当的访问比率,并将端口仲裁表(Port Arbitration Table)编程写入,以此来实现这种端口的访问比率。如果路径中有多个Switch,那么要做到这一系列事情可能并不是听起来的那么简单,因为即使他们使用的都是相同的TBWRR仲裁方案,也无法很清晰的知道它们各自的服务间隔(service interval,SI)是如何协同工作的。如果几个Switch的SI之间并未对齐,那么就意味着等时的时间规划更加难以实现,这将取决于链路的繁忙程度。但是协议规范中并没有考虑SI之间的协调合作,因此这里就将再次涉及到一个非标准方法。显然,如果在一个同步等时路径上并不存在多个Switch而只有1个,那么这个问题就将会被大大简化。
图 7‑25展示了我们的例子中数据包在两个EP间传送的时间点。粗线条的大箭头是来自视频设备的数据包,它具有已知确定的大小,并按照周期性的、确定性间隔的进行发送。细线条的小箭头则是代表来自SCSI驱动器的数据包,它具有较低的优先级且发送时间点是不可预知的。在EP中,数据包只需要简单的含有TC即可,但是Switch还需要确认执行了合适的时间规划方案。这是通过使用TBWRR来完成的,它将会指定在一个给定的时间点上,哪个端口可以进行访问,以及访问时间有多长。由于知道同步等时数据包的大小以及传送频率,这使得软件可以正确的安排时间规划,但是什么样的时间规划才是我们需要的呢?
图 7‑25同步等时数据包的注入
首先,让我们通过一个简单的示例来回顾一下需要涉及到的参数。回忆一下,PCIe中基于参考时钟周期定义了时隙(time slot),而时隙的长度是由端口能力寄存器1(Port Capability Register 1)中的参考时钟字段(Reference Clock)所给定的。目前,这个字段的值只能是100ns,并且TBWRR仲裁表只能是128个条目的大小。服务间隔(Service Interval,SI)的长度就是它们的乘积,也就是12.8us。一个给定设备的带宽可以用下面给出的公式来表达,其中Y表示1个时隙中将要发送的数据量(协议规范中声明,必须要将配置过程中编程写入的Max Payload Size值用于这个带宽的计算中,也就是先假设1个时隙就能传输1个数据包),M表示的是时隙的数量,T表示整个SI长度。举例来说,如果我们选择Payload为128Byte,又已知SI为12.8us,那么对于分配的每个时隙来说,BW=10MB/s。
现在让我们考虑一个更贴近实际的例子。假设链路工作在Gen2的速率,视频设备需要保证有100MB/s的带宽,且它将会发送数据荷载为512Byte的数据包,我们先假设1个时隙就能发送1个数据包,代入公式中得到M=2.5,也就是在这种情况下要在1个SI中发送2.5个512Byte数据包。
但是实际上1个时隙中可以传输多少数据呢?这个问题的答案当然与链路速率以及链路宽度有关。在5.0Gb/s的速率下,每发送1个10bit符号就需要2ns,因此在每个同道中100ns则可以传输50个符号。如果数据包的大小是512Byte再加上28Byte左右的Header,那么使用x1链路传输大小为550个符号的数据包则需要11个时隙。在有需要的时候,可以让同一个端口得到连续的多个时隙,这是一种解决办法。由于将要发送的数据包的大小是确定一致的,我们不能真的就将M定为2.5个512Byte,而是应该将其定为3。对于我们的公式来说,在1个SI中发送3个512Byte的带宽实际上是120MB/s。这虽然高于我们的需求带宽,但是确实解决了我们的问题。由于刚才计算过传输550个符号需要11个时隙,那么传输3个就需要11×3=33个时隙,在1个SI中还留有95个时隙用作它用。在1个SI中的3个时隙组(每组11个)中,每一组内的11个时隙必须是连续相邻的,但是组与组之间可以是分开的。
另外一个解决办法就是增加链路宽度。尽管这样的解决办法会增加硬件开销,但是如果使用11通道的链路则可以真正的在1个时隙内完成需要的数据的发送但是在CEM协议规范中(PCIe Card Electromechanical Spec ,PCIe卡Spec),并没有x11的选项,但是可以使用其中的x12选项来应用在我们的例子中。使用更宽的链路意味着软件只需要为每个数据包分配1个时隙,因此只需要在1个SI中分配3个时隙,即可满足本例子中设备的同步等时流量带宽需求。与x1的情况不同,现在我们不需要让分配的时隙连续相邻。相反地,它们可以以某种更优的方式分散在SI中。
对于同步等时流量来说,必须要设计好TBWRR仲裁表来保证有足够的及时的带宽,并且这部分带宽不会被其他流量所影响。如图 7‑25中所示,SCSI控制器在SI 1中发送了1个数据包,在SI 3中发送了另一个数据包。如果时间规划就按照这样,允许EP每个SI发送1个数据包,那么这样的方式是可行的。
现在我们假设SCSI控制器尝试发送更多的数据包,其数量已经超过了SI 1对它的许可数量,如图 7‑26所示。这是协议规范中提到的两个带宽分配问题中的第一个,它被称为“过载(oversubscription)”。这将会影响到同步等时流量,但是这个问题可以通过合理的规划TBWRR仲裁表就能比较容易的避免,因为在某个特定的时间点下,只有被仲裁选中的端口可以发送数据包。如果这个端口在发送完仲裁许可的数据包后依然有很多数据包在队列中等待发送,那么它也需要等到下一次被仲裁选中才能继续发送后续的部分数据包,对于这个例子中也就是SCSI控制器想在SI 1中多发送的那一个数据包实际上可能会放到SI 2中去发送。最终,这种“过载”会导致发送处的流控反压。
图 7‑26超额使用带宽
第二个问题被称为“拥塞(congestion)”,当在一个给定的时间窗口内存在了太多同步等时服务请求时,就有可能会发生这种问题,如图 7‑27所示。这个问题和第一个有点相似,但是它是无法那么简单就解决的。与之前的情况不同,这里不能将高优先级数据包推迟到下一个时隙发送,因此系统必须要有办法来处理全部的这些请求。要纠正拥塞的问题,软件需要改变发送数据包时间点的分布,以便它们都能够得到可用硬件带宽的支持。
图 7‑27带宽拥塞
管理数据包发送的延时也是等时性很重要的一部分,这个延时包括网络结构传播延时与Completer延时。网络结构传播延时取决于系统中各种组件之间的链路的全部特性,特别是链路宽度和操作频率。一种将这个网络结构传播延时值最小化的简单方法,就是对PCIe拓扑结构的同步等时路径上的复杂度进行约束。Completer延时取决于目的EP内部特性,例如存储速率以及内部仲裁方式等等。
RC的仲裁和时间规划需求和Switch是一样的。它会从多个下行端口接收数据包,并通过符合前面所描述的等时性规则的方法将它们转发至目的地。然而,这些事情的完成方法大都是Vendor特定的,因为协议规范中并没有定义同步等时服务的RC工作方式或者是它应该如何被编程。
在影响RC的时间规划和延时的各种因素里,我们还有一个没有进行讨论,那就是窥探过程(process of snooping)。一般情况下,对系统内存的访问会发生在处理器认为可缓存(cacheable)的位置,这意味着这个位置的内存可以在它的本地Cache中保存一个自己的临时副本。如果外部设备尝试访问这个内存区域,那么芯片组必须先检查处理器Cache,然后才能允许此次内存访问,因为Cache中缓存的副本可能已经被更改过,与原内存区域中的数据已经不相同了。如果Cache中缓存的副本确实被更改过,那么在设备访问这个内存区域之前,先要让Cache中的数据写回内存(Write Back)。虽然对内存Cache一致性的确认工作是有必要的,但是问题就是这个窥探过程是需要花费时间的。它需要花费的时间通常是在一个范围内有限的,但是是不可预测的,因为它取决于CPU在当时具体正在执行什么工作。结合上时间规划的时间确定性需求,窥探时间的不确定性很有可能会破坏同步等时数据流。
对于外部设备来说,一种避免窥探的方法就是仅访问不可缓存(Uncacheable)的内存区域。另一种方法是由软件设置高优先级数据包Header中的“No Snoop(无窥探)”属性位。这种方式将会强制芯片组跳过窥探步骤,不管内存是否是uncacheable都直接去访问内存,这需要软件用一些方法来保证这样做不会引发问题。为了将这种方式作为同步等时路径的一个需求来执行,对于高优先级VC来说,硬件可以对RC端口中的另一个bit进行初始化配置,它叫做“拒绝窥探事务(Reject Snoop Transaction)”,如图 7‑17中所示的VC资源能力寄存器。这样做的目的是,让这个VC只允许传输No Snoop的事务。任何输入的没有设置No Snoop属性位的数据包豆浆杯丢弃,以此来确保时间规划的时间确定性不会被窥探过程花费的不确定时间所破坏。
电源管理是一个简单的监测过程,但是当PCIe中的一段路径是时间敏感路径的时候,那么这个路径上设备的电源管理机制PM(Power Management)就需要仔细的处理。配置软件可以读取到每种PM条件下造成的延时,并从中选择一种PM条件使得它的延时可以满足时间规划要求。最简单的办法就是在同步等时路径上禁用PM。设备可以进入设备状态D0然后就停留在这个状态,同时禁用硬件控制的链路电源管理机制(更多关于PM的内容请参阅Chapter 16“Power Management”)。
最后,还剩下最后一个问题:当链路上出现错误时应该怎么办。ACK/NAK协议提供了一个自动地、基于硬件的重试机制(Retry)来纠正出现传输问题的数据包。这个原本还不错的特性会给等时性带来一个问题,因为它需要花费时间来完成,并且它用来解决几个传输错误花费的时间也会有较大差异,这取决于具体检测到了什么样的错误。
要解决这个问题,我们需要知道系统能容忍多少不确定的时间,使得系统仍能提供同步等时数据传输。如果延时时间预算太紧张,那么显然就没有时间可以用来重试传输失败的数据包,也就需要禁用ACK/NAK协议。有趣的是,PCIe的协议规范制定者显然没有考虑到这一点,因为并没有设计用来禁用ACK/NAK协议的配置bit,也没有设计配置bit用来决定如何处理那些本来会被重传而现在不会被重传的数据包。因此,禁用ACK/NAK协议需要使用一些非标准机制,例如Vendor-specific寄存器。
如果没有足够的时间用于重传,那么目的方可能就会简单的将损坏的数据包丢弃。另一个选择是就直接使用损坏、错误的所有数据包。对于一些使用同步等时服务支持的应用来说,使用损坏数据包的这种操作并没有听起来这么不可接受。例如,视频流中的错误可能会导致显示器偶尔出现故障闪烁,但是这个风险被认为是可以接受的。
不同于上面的情况,如果在服务间隔(Service Interval)中,有足够的时间用于重传,那么就可以通过添加一个计时器的方式来给重试延时设置一个上限,这个计时器会一直记录时间直到当前SI结束,可以使用它来决定是否可以进行重试操作。当然,错误情况不应该很频繁的出现,所以这种方式对于偶尔出现的传输错误来说是足够的,并且同时也可以保持同步等时的时间规划。