本系列文章仅介绍在FPGA设计中我们一般需要了解的PCI知识点,完整的PCI协议远比本系列文章介绍的复杂,所以本系列适合新手快速学习,让不了解PCI的初学者对PCI有个概念,进而学习PCIe,如果我们需要在项目中深入了解PCI时(一般不会。。。),那就查阅标准文档吧。虽然现在PCI应用场合比较少,但是这是学习PCIe的基础,作为接口方向的FPGA工程师,PCI也是必须要学习的。在文章的开头,先给出官方PCI和PCIe的英文标准文档的下载链接。本系列文章参考自马鸣锦老师编写的《PCI、PCI-X和PCI Express的原理及体系结构》和一位技术大牛的博客http://blog.chinaaet.com/justlxy/p/5100053251。
PCI总线的全称是Peripheral Component Interconnect,直译过来就是外部的组件互相连接,也就是外设互联标准的意思。PCI总线是一种共享总线,共享总线就是真的有条总线,总线上的设备分时共享这条总线,这点和PCIe或AXI等总线系统不一样,所以PCI是一种古老的总线,并不是高速总线。分时共享也注定了PCI需要总裁器(Arbiter),如下图所示。
上图是老式计算机的一个大概结构,在这个结构中,北桥(也叫作HOST桥)连接CPU、内存、显卡和PCI总线,PCI总线连接北桥和南桥(所谓的桥,就是一个在多种总线之间的实现协议转换和事务转发的设备)。PCI总线的仲裁器是在北桥中的,但是这并不意味着PCI总线上的所有的读写操作的主设备只能是北桥,实际上,所有在PCI总线上的设备都可以是主设备。当然,对于一个挂载在PCI总线上的设备来说,它既可以是主设备也可以是从设备,也可以即是主设备又是从设备(比如PCI桥这种特殊设备)。北桥中的仲裁器则通过很多组的REQ#(request) 和GNT# (grant)来分别与各个设备连接,用以决定哪一个设备获得PCI总线的控制权。从上图还可以看到,PCI总线是典型的外围总线,连接的都是比较低速的设备,显卡和内存这种高速设备不会连接在PCI上(现代计算机的显卡几乎都是连在PCIe上的)。另外,PCI总线可以挂载PCI桥(可以不止一个),PCI桥下面可以多出一条独立的PCI总线,称之为下级总线,同北桥一样,PCI桥里也会有一个对于下级总线的仲裁器。显然,下级总线同样可以挂载PCI桥,这样子如果一级一级级联下去,整个PCI就会形成一个树形结构(一个系统最多256条PCI总线)。PCI桥这种特殊的设备是连接上下级总线的特殊存在,它可能既是上级总线的从设备又是下级总线的主设备。因此,在PCI这个树形结构中,我们一般讨论主设备和从设备都是针对同一条总线在某一个时间段才有意义,如果直接说某某是主设备,某某是从设备,严格意义上来说是有问题的,对于某设备跨总线访问另一个设备这种情况,本质上是通过中间的PCI桥中转,PCI桥在中转的过程中对于不同级别的总线分别承担了主设备或从设备的角色。
PCI总线是地址/数据复用总线,即地址和数据占用同一组信号线,这组信号线的位宽有32位或64位,由于PCI采用了一种叫做Reflected-Wave Signaling的技术(反射信号增强技术,这种技术可以降低功耗,有兴趣的读者可以参考http://blog.chinaaet.com/justlxy/p/5100053079),总线时钟一般只有33MHZ,最高只支持133MHZ,且这个技术导致每条PCI总线的负载数远低于理论值(32个),并且总线时钟频率越高挂载的负载数越少。所以它在33MHZ总线时钟和32位数据宽度下,一条PCI总线的点对点传输峰值带宽仅为133MB/s,只能挂载10-12个设备(如果是插槽型的设备,还要对半减少,因为插槽本身就是一个设备),如果想要多挂载设备,只能通过PCI桥来添加一条下级总线。哪怕后续发展的133MHZ总线时钟的PCI-X(改进型PCI),在64位数据宽度下,点对点峰值带宽虽然有4262MB/s,但一条总线只能挂载一个设备且信号完整性很受外部影响。现在无论是计算机还是手机都是大量数据高速高稳定传输,因此,PCI这种并行总线就逐渐被淘汰了。
下图表示一个挂在PCI总线上的设备拥有的信号引脚,无论主设备、从设备还是桥设备都是这些信号(带#号的表示低电平有效,如果一个设备永远不当主设备,则可以没有仲裁信号),图中左边的是要求任何一个PCI设备都应具备的信号,右边的是可选信号,根据具体PCI设备的功能决定。对PCI总线上的设备的各种操作,都是以总线事务为单位进行的,这是因为PCI总线上的数据传输是基于猝发传输的机制,这点和我前段时间写过的AXI总线类似(一次猝发传输包括传输一个地址之后再传输一个或多个数据),总线事务类型包括存储器读、存储器写、IO读、IO写、配置读、配置写、中断响应等,在后文中会有具体介绍。
AD[31:0]——地址和数据复用引脚,一个PCI总线事务用它先传输地址,再传输数据。在一般情况下,FRAME#为0之后的第一个时钟传输的是地址相,FRAME#为0的其他时钟节拍传输的是数据项,在双地址周期总线事务的情况下,FRAME#为0之后的第一和第二个时钟传输的是地址相。双地址周期用来传输一个64位地址。
C/BE[3:0]#——总线命令和字节使能复用引脚,在地址相期间,该信号表示总线事务类型编码,也就是总线命令,总线事务类型参见表1,表中红色部分是最常用的。在数据项期间,这个信号是AD[31:0]的字节使能,C/BE[0]#为0代表AD[7:0]有效,以此类推。
PAR——上面两组信号的偶校验位(上面两组信号加上这位之后,一共37位,保证这37位数据中1的个数为偶数),在地址相期间校验地址和命令,在数据项期间校验数据和字节使能。
这三组信号的驱动规则是一样的:无论读写事务,主设备都驱动地址相,对于读事务,从设备驱动数据项,对于写事务,主设备驱动数据项。
C/BE[3:0]# | 总线事务类型 |
0000 | Interrupt Acknowledge(中断响应) |
0001 | Special Cycle(特殊周期) |
0010 | IO Read(IO读) |
0011 | IO Write (IO写) |
0100 | 保留 |
0101 | 保留 |
0110 | Memory Read(存储器读) |
0111 | Memory Write(存储器写) |
1000 | 保留 |
1001 | 保留 |
1010 | Configuration Read(配置读) |
1011 | Configuration Write(配置写) |
1100 | Memory Read Multiple(存储器多行读) |
1101 | Dual Address Cycle(双地址周期) |
1110 | Memory Read Line(存储器行读) |
1111 | Memory Write and Invalidata(存储器写和无效使能) |
FRAME#——为0表示一个总线事务正在进行中,该信号由当前取得总线控制权的主设备驱动。
IRDY#和TRDY#——主从握手信号,在总线时钟上升沿,这两个信号必须同时为0才表示此时数据项有效。IRDY#由主设备驱动,TRDY#由从设备驱动。
STOP#——从设备驱动,请求主设备停止当前事务。
DEVSEL#——从设备驱动,表示主设备发出的地址相被某一设备识别了,该设备认领了这次事务之后成为从设备。
IDSEL——这个信号是配置PCI设备时使用的,桥设备(HOST桥或PCI桥)对PCI设备进行配置访问时,由桥设备中的配置译码电路产生的片选信号。
LOCK#——主设备利用这个信号把从设备锁定住。从设备不允许其他主设备访问它,除非这个信号无效。
REQ#——主设备输出给PCI总线仲裁器请求使用总线。
GNT#——仲裁器对主设备请求的响应。
PERR#——该信号由接收数据的设备驱动,一般用来报告奇偶校验错误。
SERR#——该信号可以由任何一个设备驱动,一般也用来报告奇偶校验错误,也可以用来报告其他致命错误,这个信号同样也作为一个NMI中断(不可屏蔽中断)。
CLK和RST#——总线时钟和系统复位信号。
INTA/B/C/D#——整个PCI系统只有这4条中断信号(不算SERR#中断的话),如果每个设备都只需报一个中断,那么也只能共用INTA#,只有当一个设备要报告多个中断时,才会使用到INTB/C/D#。多个设备的相同名字的中断信号通常都是“线或”之后连到系统的中断控制器上。所以PCI系统中只要有一个设备报告了中断,则CPU就要去查询到底是谁报告的中断。
需要注意的就是下面几点:
1、扩展时C/BE[7:4]#只作为高4位字节的字节使能。
2、REQ64#是主设备驱动的,请求从设备进行64位数据传输,ACK64#是从设备驱动的,表示允许主从之间进行64位数据传输。
3、PAR64是AD[63:32]和C/BE[7:4]#的偶校验位。
4、64位地址的传输可以用双地址周期的方式,也可以用一次传输64位地址的方式。
测试时使用,一般不用管。
这里只给出读事务和写事务(无论是存储器读写、配置读写还是IO读写)的波形图,作为初学者看懂这两个图就够了,其他的事务波形图请参考本文所附的PCI3.0标准文档的第3.3节。
这两个波形图对着上面的信号定义还是比较容易看懂的,需要注意的是IRDY#和TRDY#之中只有一个不为0,则认为是插入了等待周期。
本篇就到此结束,下一篇我会讨论PCI的配置空间以及它和系统地址空间之间的关系,这是初学者容易搞混淆的地方,然后讨论PCI设备的基地址寄存器的配置过程,最后讲讲PCI总线系统的三种数据通信模式。