PCI-Express-Technology(一)
https://github.com/ljgibbslf/Chinese-Translation-of-PCI-Express-Technology-/blob/main/1%20%E8%83%8C%E6%99%AF.md
在PCI层次结构中,总线上的每个设备(device)可以包含多达8个功能(function),这些功能都共享该设备的总线接口,功能编号为0到7(一个仅具有单功能的设备通常将被分配功能号0)。每个功能都能作为总线上事务(transaction)的目标方,而且它们中的大多数还可以作为事务的发起方。这样的发起方(称为总线主设备)有一对针脚(REQ#与GNT#,符号#表示他们为低有效信号),这一对信号用来处理共享PCI总线时的仲裁。如图 1‑2所示,请求信号(REQ#)有效时表示主设备需要使用总线,并将此信息告知总线仲裁器来与此时所有的其他请求一起进行评估仲裁,最终将得出下一时刻由哪一个设备来占用PCI总线。仲裁器通常位于桥(bridge)中,桥是一种在层次结构中位于总线之上的结构,它可以接收总线上所有事务发起方(总线主设备)所发出的仲裁请求。仲裁器将决定谁下一个占用总线,并将这个设备的Grant(GNT#)置为有效(asserts the Grant pin for that device)。根据协议,只要总线上的上一个事务结束且总线进入空闲状态时,对于任意设备,只要在此时看到自己的GNT#信号被置为有效,那么就可以认为它是下一个占用总线的主设备,它就可以开始发起它的事务。
图 1‑3展示了一次典型的PCI总线周期。PCI总线是同步总线,这意味着总线上的活动都发生在时钟边沿,因此将时钟信号放在图标的顶部,并将它的上升沿都用虚线标注出来,这是因为信号正是在这些虚线所表示的时刻被发出(driven out)或被采样(sampled)。下面对总线上发生了什么进行一个简明的描述。
\1. 在时钟沿1,FRAME#信号(此信号有效时表示正在进行总线访问)和IRDY#信号(Initiator ReaDY for data,发起方准备好数据)这两个信号都处于无效状态,这表示总线处于空闲状态。与此同时,GNT#有效,这意味着总线仲裁器已经选择了该设备作为下一个事务发起方(Initiator)。
\2. 在时钟沿2,发起方将会把FRAME#信号置为有效,这表示一个新的事务已经被它发起。与此同时,它将把这个事务的地址与命令驱动到总线上。总线上其他所有的设备都将会把这些信息锁存起来,并将地址译码,查看自己的地址是否与之相匹配。
\3. 在时钟沿3,发起方将IRDY#信号置为有效,这表示它已经准备好了进行数据传输。图中AD总线上的环形箭头符号表示这个三态总线(可以理解为inout型)正处于“转向周期”,这意味着这个信号的拥有者(三态总线上的信号此时是谁驱动的)发生了变化(这里出现“转向周期turn-around cycles”是因为这是一个读事务;发起者发出地址信息和接收数据的信号引脚是相同的)。打开目标方(Target)的缓冲buffer的时钟沿与关闭发起方缓冲buffer的时钟沿不能相同,因为我们想尽量避免两个 buffer 都在驱动同一个信号的可能性(即都往AD总线上驱动数据),哪怕只是很短的时间也不行。总线的争用将会损坏设备,因此,需要在前一个buffer关闭后的下一个时钟再打开一个新的 buffer 。在改变总线的驱动方向之前,每个共享信号都需要这样进行处理。
\4. 在时钟沿4,总线上的一个设备识别到了请求的地址与自己相匹配,于是便通过将DEVSEL#信号(device select)置为有效的方式对事务发出响应,声明参与到事务中。与此同时,它将把 TRDY# 信号(Target ReaDY)置为有效,这表示它(Target)正在发送读数据的第一部分,并将数据驱动到AD总线上(放到总线上的时间可以后延,总线允许目标方在FRAME#有效与TRDY#有效间间隔最多16个时钟周期)。由于此时 IRDY# 与 TRDY# 都处于有效状态了,数据将在此上升沿开始传输,至此完成了第一个数据阶段(first data phase)。Initiator 知道最终一共要传输多少字节的数据,但是 Target 并不知道。在事务的命令信息中并不包含字节数信息,因此每当完成一个数据阶段,Target 都必须查看 FRAME# 信号的状态,以此来得知 Initiator 是否对传输的数据量满意(即是否达到 Initiator 需要的数据量)。如果FRAME#依然有效,那么说明这并不是最后一个数据阶段,Target 仍然需要继续向 Initiator 传输数据,事务将按照相同的方式继续处理接下来传输的字节。
\5. 在时钟沿5,Target 还没有准备好继续发送下一组数据,因此它将 TRDY# 置为无效。我们将这种情况称为插入了一个“等待状态”,这使得事务被延迟了一个时钟周期。Initiator和Target都可以进行这样的操作,它们最多可以将下一次数据传输后延8个连续的时钟周期。
\6. 在时钟沿6,第二组数据被传输,并且由于传输后FRAME# 信号依然有效,Target就知道依然需要继续向Initiator传递数据。
\7. 在时钟沿7,Initiator强制总线进入了一个等待状态。在等待状态中,设备可以使当前事务暂停,并快速地将要发送的数据填充进buffer或是将接收到的缓存在buffer内的数据搬出,能进行这样的操作是因为Initiator和Target允许事务在暂停后直接恢复,而不需要进行中止和重启事务的操作。但是从另一方面来说,这样的操作将会十分低效,因为它不仅使当前事务停顿,同时也禁止其他设备访问和使用总线,尽管此时总线上没有数据在传输。
\8. 在时钟沿8,第三组数据被传输,并且此时FRAME#信号被置为无效,因此Target得知这就是最后一次数据传输。因此,在这一个时钟周期后,所有的控制信号线都关闭,总线再次进入了空闲状态。
在PCI总线上,许多信号都具有多种含义,这是为了减少引脚数量,以此来保持PCI设计中的低成本这一设计目标。数据和地址信号一起复用32bit总线,C/BE#(Command/Byte Enable)信号也共享他们的4个信号引脚。尽管减少引脚数量是一种可行的方法,并且它也是PCI使用“转向周期”这种会增加延时的方法的原因。但是这种复用引脚的做法阻止了对事务的流水线操作(在发送上一个周期的数据的同时发送下一个周期的地址)。握手信号诸如FRAME#、DEVSEL#、TRDY#、IRDY#以及STOP#被用来控制在事务(transaction)进行期间,各事件(event)何时进行。
图 1‑3简单的PCI总线传输
早期PCI事务
PCI同先前的总线模型一样,在数据传输上使用三种模型:Programmed I/O(PIO)、Peer-to-peer、以及DMA。这些模型的图解如图 1‑6所示,接下来的几个小节将对它们进行描述。
PIO在早期PC中被广泛使用,因为设计者不愿意增加设备中由于事务管理逻辑而带来的成本开销,以及额外增加的复杂度。在当时,处理器比任何其他设备工作的都要快,所以在这个模型中,处理器包揽了所有的工作。举例来说,当一个PCI设备向CPU发出中断,并表示它需要向memory中放入数据,则由CPU最终将数据从PCI设备中读出并放入一个内部寄存器中,然后再将这个内部寄存器的值复制进memory中。反之,如果数据要从memory移动到PCI设备中,那么软件会指示CPU将memory中的数据读出至内部寄存器中,然后再将内部寄存器的值写入到PCI设备中去。
这样的操作流程虽然可行,但是效率却比较低,其主要有两个原因。第一个原因,每一次数据传输都需要CPU开销两个总线周期。第二个原因,在数据传输过程中CPU都要忙于数据传输而不能做其他更有意义的任务。在早期,这是一种最快速的传输方式,而且单任务处理器也没有其他的任务要做。然而这种低效的方式显然不适用于更为先进的系统中,因此这种方式已不再是数据传输的常用方式,取而代之首选方法的是下一节将讲述的DMA方法。然而为了使软件与设备交互,Programmed IO仍然是一种必要的事务模型。
图 1‑6 PCI事务模型
在传输数据的方法上,一种更为高效的方式叫做 DMA(Direct Memory Access,直接内存访问)。在这个模型中有另一种设备称为DMA Engine,由它来代表处理器来负责掌握从 memory 向外设进行数据传输的各种细节操作,这就将这个繁琐的任务从CPU中卸载了下来。一旦CPU将起始地址(starting address)和数据字节总量(byte count)写入DMA Engine,DMA Engine将会自动处理总线协议与地址序列。这不涉及 PCI 外设的任何更改,并允许其自身保持低成本设计。后来,经过集成性改进,外设自身可以集成入这种DMA功能,这样它们就不再需要一个外部的DMA Engine。这些设备有能力处理自己发起的总线传输,我们将它们称为总线主设备(Bus Master device)。
图 1‑3 就是一个PCI总线上的总线主设备正在进行事务的过程。北桥可以对地址进行译码,以此来识别自己是否是这个事务的Target,比如北桥译码后发现地址与自己相匹配则认为自己是这个事务的Target。在总线周期中的数据传输阶段,数据在总线主设备与北桥之间传输,北桥即是数据传输的Target。北桥继而根据请求的事务内容,发起 DRAM 读写操作,与系统内存进行数据通信。在数据传输完成后,PCI外设可以产生一个中断来通知系统。DMA提升了数据传输效率,因为这种方式在搬移数据时不需要CPU的参与,那么对于CPU来说,只需要一个总线周期的开销,将起始地址和总数据量写入DMA Engine,即可完成高效的数据块搬移(move a block of data)。
如果一个设备能够作为总线主设备,那么它就提供了一个有趣的作用。一个PCI总线Master可以发起针对其他PCI设备的数据传输,而对于PCI总线自身来说这整个事务都是在本地进行的,并未引入任何其他系统资源。因为这个事务是在总线上的两个设备之间进行的,且这两个设备被认为是总线中两个对等的节点,因此这个事务被称作一个“点对点”的事务。显然,这种事务非常高效,因为系统中其余的部分仍然可以自由的完成其他的任务。然而,在实际场景中点对点事务很少被使用,这是因为Initiator和Target通常不会使用相同的数据格式,除非二者都是由一个供应商制造的。因此,数据一般必须要首先发送到memory,在那里由CPU在数据传输到Target之前,对其进行格式转换,而这就阻碍并破坏了点对点传输的设计目标。
PCI体系结构支持3种地址空间,如图 1‑10所示,包含:Memory(内存)、I/O、Configuration Address Space(配置地址空间)。x86处理器可以直接访问Memory和I/O空间。一个PCI设备映射到处理器内存地址空间,可以支持32或64位寻址。在I/O地址空间,PCI设备可以支持32位寻址,但是因为x86 CPU仅使用16位的I/O地址空间,所以许多平台都会将I/O空间限制在64KB(对应16bit)。
图 1‑10地址空间映射
PCI还引进了第三种地址空间,称为配置空间,CPU只能对它进行间接访问而非直接访问。PCI设备中的每个Function(功能)都包含专为配置空间所准备的内部寄存器,这些寄存器对于软件来说是可见的,并且软件可以通过一个标准化的方式来控制它们的地址与资源,这为PC提供了一个真正的即插即用(plug and play)的环境。每个PCI Function最多可以有256 Bytes的配置地址空间。考虑到PCI最多可支持单个设备含有8个Function、每路总线含有32个设备、单个系统包含256路总线,那么可以得到一个系统的配置空间总量为:
256B/Function *8Function/device * 32devices/bus *256buses/system =16MB,
即一个系统的配置空间总大小为16MB。
因为x86 CPU无法直接访问配置空间,所以它必须通过IO寄存器进行索引(然而在PCI Express中引入了一种新方法来访问配置空间,这种新方法是通过将配置空间映射入内存地址空间来完成的)。在传统模型中,如图 1‑10所示,使用了一种被称为配置地址端口(Configuration Address Port)的IO端口,它位于地址CF8h-CFBh;还使用了一种被称为配置数据端口(Configuration Data Port)的IO端口,它位于地址CFCh-CFFh。关于通过这种方法以及通过内存映射方法访问配置空间的细节将会在下一节进行解释。
由于IO地址空间的大小是有限的,传统模型的设计上对地址十分保守。在IO空间中常见的做法是令一个寄存器来指向一个内部位置,然后用另一个寄存器来进行数据的读取或写入。PCI的配置过程包含如下两步。
l 第一步:CPU产生一个IO写,写入的位置为北桥中IO地址为CF8h的地址端口(Address Port),这样就给出了需要被配置的寄存器的地址,即“用一个寄存器来指向一个内部位置”。如图 1‑11所示,这个地址主要由三部分组成,通过这三部分就可以定位一个PCI Function在拓扑结构中的位置,它们为:在256条总线中我们想访问哪一条总线、在该总线上的32个设备中访问哪一个、在该设备的8个Function中访问哪一个。除了这些以外,唯一还需要提供的信息是要确认访问这个Function的64dw(256Bytes)中的哪个dw。
l 第二步:CPU产生一个IO读或者IO写,操作的位置为北桥中的地址为CFCh的数据端口(Data Port),即“用另一个寄存器来进行数据的读取或写入”。在此基础上,北桥向PCI总线发起一个配置读事务(configuration read)或配置写事务(configuration write),事务要操作的地址即为步骤一中地址端口中所指定的地址。
图 1‑11配置地址寄存器
每个PCI Function可以包含最多256Bytes的配置空间。这个配置空间起始的64Bytes包含一个被称为Header(配置空间头)的结构,剩余的192Bytes用来支持一些其他可选功能。系统配置首先由Boot ROM固件来执行,在操作系统被加载完成后,它将重新对系统进行配置,重新进行资源分配。这也就是说系统配置的过程可能会被执行两次。
根据Header的类型,PCI Function被分为两个基本的类别。第一种Header称为Type 1 Header(类型1),它的结构如图 1‑12,它用于标识这个Function是一个Bridge,Bridge将会在拓扑结构上创建另一条总线。而Type 0 Header(类型0)就是用来指示这个Function不是一个Bridge(如图 1‑13)。关于Header类型的信息包含在dword3的字节2的同名字段中(Class Code字段),当软件在系统中发现某个Function时,第一件事就是要检查其Header的这个字段(软件发现系统中Function的过程称为枚举enumeration)。
关于配置寄存器空间以及枚举过程的更为详细的讲解将会稍后再进行。在这里我们只是希望你先熟悉一下各个部分是如何相互配合工作的。
图 1‑12 PCI配置Header Type 1(Bridge)
图 1‑13 PCI配置Header Type 0(非Bridge)
66MHz总线的吞吐量相较于33MHz已经翻倍,但是其存在一定的问题,图 1‑14显示了它的一个主要缺点:66MHz总线与33MHz总线使用相同的反射波开关模型,使得留给在传输线上信号传输的时间减半,这将会大大的降低总线的负载能力。最终造成的结果是,一条总线上只能有一个插入板卡。增加更多的设备意味着需要增加更多的PCI Bridge来产生更多的总线,这将会增加成本以及对PCB板材的要求。同时,64bit PCI总线相较于32bit增加了引脚数,这也会增加系统成本且降低可靠性。将上述结合起来,就可以很容易看出来为什么这些因素限制了64bit或者66MHz版本的PCI总线的广泛使用。
鉴于PCI总线上的实际负载以及信号渡越时间(signal flight times),PCI总线的时钟频率无法在66MHz上继续增加了。对于66MHz时钟来说,其时钟周期为15ns。分配给接收器的建立时间(Setup time)为3ns。因为PCI使用“非寄存输入(non-registered input)”信号模型,想要将其3ns的建立时间继续减小是不现实的。剩余的12ns的时序预算(timing budget)分配给了发送器的输出延迟以及信号传输时间。如果在66MHz的基础上进一步提高频率,那么总线上传输的信号将会因为无法及时到达接收端,无法在接收端被正确采样,从而导致传输失败。
在下一节将会介绍PCI-X总线,它将所有的输入信号都先用触发器来寄存一下,然后再去使用。这样做可以将信号的建立时间降低到1ns以下。在建立时间上的优化使得PCI-X总线可以跑到一个更高的频率,例如100MHz甚至133MHz。下一节中我们将会对PCI-X总线体系结构进行简明的介绍。
PCI-X对PCI的软件和硬件都能做到向后兼容,同时PCI-X还能提供更好的性能和更高的效率。它和PCI所使用的连接器也是相同的,因此PCI-X与PCI的设备可以插入相互的插槽。除此之外,它们二者还使用相同的配置模型,因此在PCI系统中能使用的设备、操作系统、以及应用,在PCI-X系统中仍然可以使用。
为了在不改变PCI信号传输模型的基础上达到更高的速率,PCI-X使用了几个技巧来改善总线时序。首先,他们实现了PLL(锁相环)时钟发生器,利用它在内部提供相移时钟。这使得输出信号在相位上前移,而输入再在相位上延后一点进行采样,从而改善总线上的时序。同时,PCI-X的输入信号都在Target设备的输入引脚被寄存(锁存),这样就使得建立时间更小。通过这些方法节省出来的时间可以有效增加信号在总线上传输的可用时间,并使得总线可以进一步提高时钟频率。
如图 1‑16所示,这是一个PCI-X总线上Memory突发读取(burst memory read)事务。
图 1‑16 PCI-X Memory突发读取的总线时序周期示例
需要注意的是,PCI-X并不允许在第一个数据阶段(first data phase)后插入等待态。之所以这样做是因为PCI-X中会在事务的属性阶段(Attribute Phase),将需要传输的数据总量告诉Target设备。因此与PCI不同,Target是知道自己需要传输多少数据的。此外,多数PCI-X总线时序周期是连续的,因为使用的是突发模式,并且数据通常以128Bytes作为一个数据块(block)来进行传输。这些特性使得总线利用率提高,同时也提高了设备buffer管理的效率。
在传统的PCI读事务中,总线Master向总线上某个设备发起读取。如前面的内容所述,若Target设备未准备好,无法完成事务,那么它既可以选择在获取数据的同时让总线保持等待态,也可以发起Retry来推迟事务。
PCI-X则不同,它使用拆分事务的方法来处理这些情况,如图 1‑17所示。
图 1‑17 PCI-X 拆分事务处理协议
为了方便描述追踪每个设备正在做什么,我们现在将例子中发起读取操作的一方称为Requester(请求方),将完成读取请求提供数据的一方称为Completer(完成方)。如果Completer无法马上对请求做出相应的服务,它将把这个事务的相关信息存储起来(地址、事务类型、总数据量、Requester ID),并发出拆分响应信号。这就告诉Requester可以将这个事务先放置在队列中,并结束当前的总线时序周期,释放总线使之回到空闲状态。这样,在Completer等待所请求的数据这段时间里,总线上可以执行其他的事务。Requester在这段时间里也可以自由的做其他事情,比如发起另一个请求,甚至是向此前的那个Completer发起请求都可以。一旦Completer收集到了所请求的数据,它将会申请总线占用仲裁,当获取到总线的使用权后,它将发起一个拆分完成(Split Completion)来返回此前Requester所请求的数据。Requester将会响应并参与拆分完成这个事务的总线时序周期,在这个过程中接收来自Completer的数据。对于系统来说,拆分完成事务其实与写事务非常相似。这种拆分事务模型(Split Transaction Model)的可行性在于,在请求发起时就通过属性阶段(Attribute Phase)指出了总共需要传输多少数据,并且也告诉了Completer是谁发起了这个请求(通过提供Requester自己的Bus:Device:Function号码),这使得Completer在发起拆分完成事务时能够找到正确的目标。
对于上述的整个数据传输过程来说,需要通过两个总线事务来完成,但是读请求和拆分完成这两个事务之间的这段时间里,总线是可以执行其他任务的。Requester不需要重复的轮询设备来检查数据是否已经准备好。Completer只需要简单的申请总线占用仲裁,然后在能使用总线时将被请求的数据返回给Requester即可。就总线利用率而言,这样的操作流程使得事务模型更加高效。
到目前为止,对PCI-X所做的这些协议增强改进使得PCI-X的传输效率提高至约85%,而使用标准PCI协议则为50%-60%。
【其实在操作DDR3的AXI总线也有类似的思想,CMD FIFO和outstanding,但是涉及ID太麻烦了】
PCI-X设备需要支持MSI中断(Message Signaled Interrupts),在传统中断体系结构中经常见到多个设备需要共享中断,开发这种功能是为了减少或者消除这种需要。想要产生一个MSI中断请求,设备需要发起一个memory写事务,其操作地址为一段预先定义好的地址区域,在这个地址区域内的一个写入操作就会被视为是一个中断,这个信息将会被传送给一个或多个CPU,写入的数据则是发起中断的设备的中断向量,这个中断向量在系统中是唯一的,仅指向这一个设备。对于CPU来说,拥有了中断号之后就可以迅速的跳转到对应设备的中断服务程序,这样就避免了还需要花费额外的开销来查找是那个设备发起的中断。此外,这样的中断方式就不需要额外的中断引脚了。
最后,我们来介绍一下PCI-X在每个事务开始时新加入的一个阶段,称之为属性阶段(如图 1‑16所示)。在段时间中,Requester会将所发起事务的一些信息传递给Completer,信息包括请求的总数据量大小、Requester是谁(Bus:Device:Function 号码),这些信息可以有效的让总线上的事务执行提升效率。除了这些以外,还新引入了2bit的信号来描述所发起的事务:“No Snoop(无窥探)”位与“Relaxed Ordering(宽松排序)”位。
l No Snoop(NS):一般来说,当一个事务要把数据移入或者移出Memory时,CPU内部的Cache需要检查被操作的Memory区域中是否存在已经被拷贝到Cache中的部分。若存在,那么Cache需要在事务访问Memory之前就把数据写回Memory,或是把Cache内的数据按失效处理。当然,这个窥探Memory内容有无出现在cache中的过程将会消耗一定的时间,并且会增加这个请求的延迟。在有些情况下,软件是知道某个请求的Memory位置永远不会出现在Cache中(这可能是因为这个位置被系统定义为不可缓存的),因此对于这些位置是不需要进行窥探的,应该跳过这一步骤。No Snoop位正是基于这种原因而被引入进来。
l Relaxed Ordering(RO):通常情况下,当事务通过Bridge的buffer时,事务间需要保持当初被放置到总线上时的顺序。这被称为强排序模型(Strongly ordered model),PCI与PCI-X一般都会遵循这种规则,除了一些个别情况。这是因为强排序模型可以解决有相互联系的事务之间的依赖问题,例如对同一个位置先进行写入再进行读取,按照强排序模型就可以使得读取操作读取到写入后的正确数据,而不是写入前的旧数据。然而实际上并不是所有的事务都存在依赖关系。如果有些事务不存在依赖关系,那么依然强制它们保持原来的顺序将会造成性能损失,这就是新添加这一属性比特位想解决的问题。如果Requester知道某个特定的事务是与此前其他事务不相关联的,那么就可以将这个事务的这一位置为有效,这样就能告诉Bridge可以在队列中向前调度该事务,提前执行,以此带来更好的性能。
并行设计的固有问题
需要注意的第一个问题是信号歪斜(signal skew)。当数据的多个bit在同一时刻被发出,他们所经历的延时会存在轻微的差别,这使得他们到达接收方的时刻也会存在轻微的差别。如果某些原因造成这种差别过大,那么就会出现图中所示的信号采样错误的问题。第二个问题是多个设备间的时钟歪斜问题。公共时钟到达各个设备的时间点并不是十分精确一致的,这也会大大的压缩了用于传输信号的时间预算长度。最后,第三个问题与一个信号从发送方传输到接收方所需要的时间有关,我们称这个时间为渡越时间(flight time)。时钟周期或是传输时间预算必须要大于信号渡越时间。为了确保满足这样的条件,PCB硬件板级设计需要让信号走线尽可能短,以此来让信号传输时延小于时钟周期。在许多电路板设计中,这种短信号走线的设计并不现实。
尽管存在这些自身局限,但是为了更进一步提升性能,还是有几项技术可以使用的。首先,可以对现有的协议进行简化而提高效率。第二,可以将总线模型更改为源同步时钟模型(source synchronous clocking model),在这种时钟模型中,总线信号和时钟(strobe选通脉冲)被驱动后将经历相同的传输延迟到达接收端(这里可以对比一下common clock与其的区别)。这种方法被PCI-X 2.0所采用。
PCI-X2.0更进一步的提高了PCI-X的带宽。如此前升级时一样,PCI-X 2.0依然保持了对PCI的软件与硬件的向后兼容性。为了达到更高的速率,总线使用了源同步传输模型,来支持DDR(双倍数据速率)或是QDR(四倍数据速率)。
“源同步”这个术语的意思是,设备在传输数据的同时,还提供了额外一个与数据信号基本传输路径相同的信号。如图 1‑19所示,在PCI-X 2.0中,那个“额外”的信号被称为“strobe(脉冲选通)”,接收方使用它来锁存发来的数据。发送方可以分配data与strobe信号之间的时序关系,只要data与strobe信号的传输路径长度相近且其他会影响传输延迟的条件也相近,那么当信号到达接收方时,这种信号间的时序关系也将不会发生变化。这种特点可以用于支持更高速率的传输,这是因为在源同步时钟模型中,接收方采样所使用的strobe(类似于原来的时钟)也是数据的来源方发送的,这样就避免了公共时钟到达各设备的延时不同而造成的时钟歪斜问题,因为如上所述此时的data与strobe之间的时序关系是基本相对不变的,这样就将时钟歪斜的这段时间单独剔除了出去,不再占用原来的传输时间预算。除此之外,因为存在了strobe信号来随路指示接收方何时锁存采样数据,所以信号渡越时间不再是个问题,即使走线较长也依然能由伴随data的strobe信号来让接收方正确的采样数据,而不必纠结在下一个“时钟上升沿”必须要进行采样,现在strobe何时指示则在何时采样。
图 1‑19源同步时钟模型
需要再次强调的是,这种非常高速的信号时序使得共享总线模型无法被使用,它只能被设计为点对点的传输形式。因此,想要增加更多的设备就需要更多的Bridge来产生更多的总线。为了达到这样的使用方法,可以设计一种设备,它拥有3个PCI-X 2.0接口,其内部还有一个Bridge结构以此来让他们三者进行相互通信。不过,这样的设备将会具有很高的引脚数量,而且成本也会更高,这使得PCI-X 2.0被困在了高端市场而无法大面积普及。
由于协议制定者认识到了PCI-X 2.0的昂贵解决方案仅能吸引更多的高端开发者而不是一般开发者,因此PCI-X 2.0提供了更符合高端设计的技术标准,例如支持ECC的生成与校验。PCI-X的ECC比PCI中使用的奇偶校验要更为复杂,鲁棒性(robust)也要高得多,它可以动态的自动纠正单bit错误,以及对多bit错误进行可靠的检测。这种改进后的错误处理方法增加了成本,但是对于高端平台来说,它所能增加的可靠性才是更为看中的,因此即使增加了成本这也是一个合理的选择。
尽管PCI-X 2.0将带宽、效率以及可靠性都提升了,但是并行总线的终究还是走到了它的终点,需要一种新的模型来满足人们对于更高带宽且更低成本的不断的需求。最终被选中的新型总线模型使用的是串行接口,从物理层面来看它与PCI是完全不同的总线,但是它在软件层面保留了对PCI的向后兼容性。
这个新模型,就是我们所知道的PCI Express(PCIe)。