PCI-E有 x1/x4/x8/x16等多种插槽模式,那么这些有什么区别呢?
1、PCI-E x16插槽全长89mm,有164根针脚,靠主板外侧端有一卡口,将16x分为前后两组,较短的插槽有22根针脚,主要用于供电,较长的插槽142根,主要用于数据传输,具有16通道所带来的高带宽。
PCI-E x16插槽,主要用于显卡以及RAID阵列卡等,这个插槽拥有优良的兼容性,可以向下兼容x1/x4/x8级别的设备。可以说PCI-E x16插槽是PCI-E的万能插槽。
由于PCI-E x16插槽常用于显卡,与cpu处理器直接相通,在物理位置上直接靠近cpu,这样显卡与处理器之间的数据交换就可以减少延迟,让系统的性能可以得到充分的发挥。
2、PCI-E x8插槽全长56mm,有98根针脚,与PCI-E x16比较,主要是数据针脚减少至76根,短的供电针脚仍然是22针脚。
为了兼容性,PCI-E x8插槽通常加工成PCI-E x16插槽的形式,但数据针脚只有一半是有效的,也就是说实际带宽只有真正的PCI-E x16插槽的一半。可以观察主板布线,x8的后半段没有线路连接,甚至针脚也没有焊接。
实际上除了旗舰级的主板,能提供多条真正的PCI-E x16插槽外,主流级主板,只会提供一条真正的PCI-E x16插槽,就是最靠近cpu的那条。而第二条和第三条PCI-E x16插槽,则多数是PCI-E x8甚至是x4级别的。
3、PCI-E x4插槽的长度为39mm,同样是在PCI-E x16插槽的基础上,以减少数据针脚的方式实现,主要用于PCI-E SSD固态硬盘,或者是通过PCI-E转接卡安装的M.2 SSD固态硬盘。
PCI-E x4插槽通常由主板芯片扩展而来,不过随着cpu内部PCI-E通道数的增多,现在有部分高端主板可以开始提供直连cpu的PCI-E x4插槽,用于安装PCI-E SSD固态硬盘。
主板上为什么找不到PCI-E x4插槽?其实它是以M.2接口的形式出现的。
不过与PCI-E x8插槽一样,PCI-E x4插槽为了兼容性,现在多数也是做成PCI-E x16插槽的形式,或是扩展为M.2接口,用于安装M.2 SSD、M.2无线网卡或者其它M.2接口设备,其余扩展卡则留给PCI-E x1插槽负责。
4、PCI-E x1插槽的长度是最短的,仅有25mm,相比PCI-E x16插槽,其数据针脚是大幅度减少至14个。PCI-E x1插槽的带宽通常由主板芯片提供,主要用途是独立网卡、独立声卡、USB 3.0/3.1扩展卡等都会用到PCI-E x1插槽,甚至可以通过转接线给PCI-E x1插槽装上显卡,用来挖矿或者实现多屏输出。
X1是用来替代原来的PCI设备的。
注意:为了长、短插槽互相兼容(物理长度兼容、版本的兼容包括1.0、2.0、3.0互相兼容),短插槽自然可以插入长插槽中,但长的插到短的插槽中就不容易,一般X1还是保持原来的长度,而x4、x8、x16在实际的主板中,都统一长度到x16。只是速度不同而已。多余的脚是悬空的,没有用处。这样外形上就统一兼容了,仔细看一下针脚是否焊接了线路,一般主板上都标明了是PCI-E x多少。
二、PCIE版本及规范
截止到当前,PCIE已经发展到6.0版本,以下是PCIE不同版本对比:
三、PCIE硬件设计注意事项
PCIe2.0 设计中请注意:
1、Slot 设计时,外围电路及电源需要满足 Spec 要求;
2、PCIe2.0接口的TXP/N差分信号上串接的100nF交流耦合电容,AC耦合电容建议使用0201封装,更低的 ESR 和 ESL,也可减少线路上的阻抗变化;
PCIe3.0 设计中请注意:
1、Slot 设计时,外围电路及电源需要满足 Spec 要求;
2、PCIe3.0 接口的 TX0P/N,TX1P/N 差分信号上串接的 220nF 交流耦合电容,AC 耦合电容建议使用 0201 封装,更低的 ESR 和 ESL,也可减少线路上的阻抗变化。
四、PCIE的拓扑结构
4.1 Root Complex(RC)
Root Complex就是一个实现CPU与PCIE上组件通信的媒介,CPU要读取某个组件信息,告知RC,其它的均有RC代劳了。可以说是PCIE拓扑结构的中枢机构,负责初始化和管理 PCIe 总线上的所有设备。通过它,计算机系统中的各种 PCIe 设备能够进行高速、高效的数据传输。其实协议对RC还有很多的规定且随着处理器不同而不同,本是一个模糊的概念,对于X86处理器RC除了管理PCIE设备还有其它事情要干,但在这里还是应该把它理解为PCIE总线的主控制器。
RC支持的主要功能有:
4.1.1、配置空间管理:RC管理 PCIe 总线上所有设备的配置空间。每个 PCIe 设备都有一些配置寄存器,这些寄存器存储设备的特定信息,如设备ID、厂商ID。
4.1.2、地址映射:RC负责分配 PCIe 总线上各个设备的地址。这包括内存映射和I/O映射,以确保不同设备之间的数据传输能够正确进行。
4.1.3、中断管理:PCIe设备可能会生成中断信号,RC负责管理这些中断,确保它们正确地传递到相应的处理器核心。
4.1.4、通信与数据传输:RC与其他 PCIe 设备之间通过 PCIe 链路进行通信。PCIe 链路分为一到多个通道,每个通道包含一个上行和一个下行通道,用于双向数据传输。
4.2 Endpoints
Endpoints称之为端点或者说终点,RC的命令最终要去的地方就是这里。Endpoint是PCIe总线上的终端设备,负责提供各种服务和数据传输功能。它要实现对RC的请求进行响应,并接收RC的配置。例如计算机里面的声卡、网卡,还有应用到PCIE的其他功能设备,均是端点。
4.3 Switch
如下图所示是一个Switch组件,作用好比路由器,是扩展PCIE总线的的,能够允许更多的Endpoints设备链接到系统。它的内部可以视为多个虚拟的PCI到PCI的桥,实现扩展功能。该组件会根据事务类型将数据送到不同的通道。该组件对数据包没有处理的功能只有转发功能。
Switch具有多个端口,每个端口连接到PCIe总线的一个通道,或者连接到另一个Switch的端口。通道是数据传输的通路,每个通道包含一个上行通道和多个下行通道,用于双向数据传输。上行通道与Root Complex或上层的Switch连接,以接收和发送数据。 S下行通道与多个Endpoint设备连接,为这些设备提供PCIe总线访问。
五、PCIE协议的分层结构
对于复杂的协议,分层是必须要做的事情。对PCIE或者说PCI协议,也是进行了层级划分,如下图所示,划分为事务层、数据链路层和物理层三层。这些层中的每一层分为两个部分,一部分是发送数据内容,一部分是接收信息。
1、事务层
事务层是PCIE协议的对外接口层,用户对数据进行组帧和解析是在本层进行,本层产生的数据包称之为事务层数据包,即(Transaction Layer Packets,TLP)。此外事务层还具有基于信用积分的流控功能,支持不同事务类型的不同形式的数据传输。
2、数据链路层
数据链路层充当事务层和物理层之间的中间阶段,主要职责包括链路管理和数据完整性,包括错误检测和纠正。
数据链层在传输链路上接收事务层的TLP数据包添加序列号和校验码交给物理层,而且链路层非常尽责,会对事务层交付的数据包进行缓存,如果检测到传输错误会进行重发,直到接收正确或确定链路通信失败。
链路层还具有链路管理功能,并有相应的数据包,称之为“数据链路层数据包(DLLP)”该数据包是实现两个组件间的数据交换,并没有路由功能,换言之,如果一包数据要跨过大山大河到达接收端,中间经过无数站点,DLLP数据包只在两站之间进行数据交换,主要实现的功能有流量控制、电源管理、应答机制和虚拟通道。
3、物理层
物理层分为两个部分,一部分是逻辑子层,一部分是电气子层。逻辑子层模块负责与数据链路层的数据交换,会对接收链路层事务进行再次封装,对接收电气子层事务进行解析,并会进行8B/10B编码或者128B/130B编码,进行传递之间的转换和极性反转等工作,电气子层则更多的负责时钟数据恢复、均衡等电气操作。物理层数据包称之为PLP,Physical Layer Packet。
六、PCIE协议的事务类型
PCIE协议总共规定了Memory、I/O、配置和消息四种事务类型,其中前三种是从PCI继承过来的,消息是PCIE所扩展的。
6.1. 内存事务
其实内存事务并不一定是读写内存,而是代表一种大数据量的数据交换方式,PCIE支持内存事务类型的读写以及原子操作(AtomicOp)。
6.2. I/O事务
I/O事务是被PCIE嫌弃的一类事务,会逐渐被弃用并被MMIO替代。MMIO(Memory-Mapped I/O,内存映射I/O)是一种访问设备寄存器和配置空间的机制。在PCIe架构中,MMIO是一种I/O访问方法,它允许软件通过内存地址范围来读取或写入设备寄存器的值而不是通过传统的I/O端口方式。
6.3. 配置
每个 PCIe 设备都有一组配置寄存器,包含设备的重要信息、功能设置以及与设备通信所需的控制寄存器等。在PCIE设备应用前必须经过配置阶段。
6.4. 消息
消息是PCIE新增的一种事务类型,用来取代PCI的一些边带信号,使得所有的信号传递都通过报文来实现。PCIE支持的消息事务有中断、电源管理、错误消息等共8项。
PCIE的传输机制
正如前文所述,数据交换是基于请求与完成(响应)的机制分为Non-Posted和Posted两种模式。这种分类也是满足不同的需求,例如内存模式进行大量的数据交换,如果每发送一包就要求响应,则会大大降低传输效率,但对于一些事务,为了保证可靠性是必须响应的故有两种模式。对于Non-Posted模式并不是说数据包发送过去就不管不顾了,接收端会在链路层向发送端发送响应信号。
七、PCIE的路由方案
路由是指路由器从一个接口上收到数据包,根据数据包的目的地址进行定向转发到另一个接口的过程。PCIE的路由方案有三种,分别是ID路由、地址路由和模糊路由。
1、ID路由
ID路由即BDF路由方案,即采用Bus Number、Device Number和Function Number来确定目标设备的位置。这是一种兼容PCI的路由方案,主要用于设备的配置、带数据和不带数据的返回数据包。
对于EP来说,收到数据包会检查BDF是否与自己一致,如果一致就解析、使用和动作,如果不一致就忽略掉。如果要发送数据包,按格式填充即可。
对于Switch不仅要检查数据包是否给自己的,还需要判断是否需要路由。判断依据就是根据配置空间里的如下字段:
Primary Bus Number :当前的上游总线号
Secondary Bus Number :当前桥的总线号
Subordinate Bus Number:存放当前PCIE子树中编号最大的PCIE总线号,也就是一个分支里面最深最靠下的总线号
如果是配置数据,有Type0和type1两种数据包
配置读写请求只能是RC发出,有Type00和Type01两种TLP包
Bus0总线:PCIE总线的Type00类型配置请求TLP不能够穿越桥片。在RC发出Type00配置请求访问PCI Bus0总线上设备时,所有在PCI Bus0上的设备都要监听,但因为只有Switch,所以只有switch的上游端口会消耗1个TLP包并返回完成信号(此时switch作为一个PCI设备,所以可以接收访问其配置空间的请求)。
跨switch:RC可以通过Type 01配置请求包访问P-P2桥片、P-P3桥片、EP1和EP2,这里分为两种情况,一种是访问P-P2或P-P3,另一种是访问EP1和EP2
1)访问P-P2桥片、P-P3桥片的情况
如果这个TLP包的类型为Type 01,Bus Number为1,那他访问的就是PCI BUS1总线上的设备,P-P1会将这个Type01类型数据包转换为Type00数据包给到P-P2、P-P3让它们各自判断属于谁的,此时这俩桥也是作为一个PCI设备。
2)访问EP1、EP2
如果这个TLP包的类型为Type 01,Bus Number为位于P-P1桥的Secondary Bus Number和Suboridinate Bus Number之间,P-P1桥将该TLP包之间透传到PCI BUS1虚拟桥上,此时P-P2、P-P3都检查这个数据包是谁的,然后将其转换为Type 00给到对应的EP。例如TLP包的Bus Number是2,就给到EP1。
如果是下游设备给上游设备的TLP,ID路由数据包可以是RC发起给下游设备的,但对于消息则可以是下游给上游的。直连上游设备也会检查是不是给自己的,如果发现地址不在自己包含范围内,那么就继续往上推,直至给到RC。
2、地址路由
地址路由包括对IO和Memory两种事务类型的路由,在帧头中包含了目的设备的地址信息,处理器会为每个设备分配一段地址信息,这也是数据包传输过程中的标志符。
对于EP来讲,就是检查这个地址是否与其某个BAR所指示的地址范围相匹配。匹配接收,不匹配忽略。
对于switch来说,也会检查这个TLP包是不是给自己的,如果不是给自己的,就根据配置空间里面的Limit和Base来判断是不是给我下游的,如果是就往下传,如果不是就忽略。
RC发起给到EP1,TLP1通过BUS0总线给到Switch的上游端口P-P1,判断是否属于自己或下游的,根据配置空间Limit和base的检查,发现是属于自己下游的,则继续向下传递,经由下游端口P-P2、P-P3检查是否属于自己的,属于P-P2且不是给自己的,就往下传,EP1判断给自己的,就消化了。
EP2发起上传给到RC,目标地址是RC,肯定不包括在P-P3里面,按照上述逻辑就该丢弃了,但PCIE对于向上传递的数据规定不属于自己空间范围的就往上传。
3、模糊路由
模糊路由(Implicit Routing,又译为隐式路由)只能用于Message的路由。用来实现电源管理、错误信号、热插拔、虚拟通道等功能。
到这里一些基础的介绍就完成了,接下来我们开始学习事务层数据包的格式
八、PCIE事务层的数据包
我们在使用PCIE IP核进行开发时,事务层数据包需要我们自己进行组包和解包。下来我们就来简单学习一下事务层数据包都有哪些格式。
8.1 事务类型的介绍
“Address Spaces” 指的是不同的寻址空间,代表着不同内存区域或者设备寄存器区域,对应着不同的事务类型。PCIE使用不同的地址空间来访问不同类型资源,这些地址空间可以是如下表所示
1.内存地址空间
内存地址空间(Memory Address Space)也就是内存事务类型,用于访问主机系统中的内存区域。设备可以通过PCIe直接访问主机内存,实现数据的读取和写入。这种地址空间允许设备通过 DMA(直接内存访问)等技术直接在内存中进行数据传输。
内存地址空间支持读的请求与完成、写请求和原子操作的请求与完成这三大类事务类型,支持32bit地址和64bit地址两种地址空间。
2.配置地址空间
配置地址空间(Configuration Address Space)也就是配置事务,用于访问设备的配置寄存器。每个 PCIe 设备都有一组配置寄存器,包含设备的重要信息、功能设置以及与设备通信所需的控制寄存器等。通过配置地址空间,系统可以对设备进行初始化、配置和管理。这也是使用PCIE设备前的必要步骤。
这种配置空间的访问方式为系统和设备之间的通信提供了标准化的接口,有助于确保设备之间的互操作性和系统的稳定性。事务类型包括两种类型:
1)Configuration Read Transaction(读事务): 主机系统通过 PCIe 总线向目标设备的配置空间读取信息。这通常涉及读取设备的配置寄存器,以获取设备的状态、特性和其他信息。
2)Configuration Write Transaction(写事务): 主机系统通过 PCIe 总线向目标设备的配置空间写入信息。这用于配置设备的各种参数,使其适应系统需求。
3.IO地址空间
IO地址空间(IO Address Space)用于访问设备的 IO 空间,允许设备使用类似于传统 IO 端口的方式进行通信和数据传输。这部分主要是兼容PCI的。支持读的请求与完成和写的请求与完成,支持的地址格式是32bit。
4.Message 地址空间
Message 地址空间(Message Address Space)提供了一种灵活的数据传输机制,使得设备之间可以通过异步的方式进行通信,从而更好地支持一些事件驱动的场景,例如中断传递、错误通知、电源管理等。这种异步通信机制有助于提高系统的效率和响应速度。
8.2 数据包格式概述
事务层由请求和完成两种类型组成,是基于包的方式进行端到端的数据交换。一个事务层数据包或者是TLP由一个或多个可选的TLP前缀、一个TLP帧头、一段payload、一个可选的TLP摘要组成。PCIE协议有个DW的概念,就是Double Words,1个DW表示4字节,也就是32bit。
8.2.1 事务层数据包的帧头(header)
如下图是PCIE协议事务层Header部分描述,PCIE支持64bit 和32bit两种地址格式,即短地址和长地址。我们发现短地址的header占用3个32bit,长地址的header占用4个32bit,这也就是PCIE所谓的3DW和4DW。第二行内容是根据不同事务类型来定的,第一行则是通用的,所有的PCIE事务层共有的一种格式。
1.FMT和type
即Format of TLP,这是一个3bit数,用来确定TLP的格式。注意是格式也就是3DW和4DW,不是类型,如下表所示。Fmt与下面的type配合就可以表示PCIE支持的所有类型了。Type是一个5bit数据,是用来表示传输类型的,与fmt结合,就确定了数据包的属性。
2.TC
位宽3bit,是Traffic Class的缩写。表示事务的优先级,默认是0,数越大优先级越高。但对它的理解并不是我们通常所说的的优先级,是和虚拟通道有关系的。PCIE总线支持虚拟通道,也就是VC(Virtual Channel)技术,优先级不同的数据报文可以使用不同的虚拟通路,每一路虚通路可以独立设置缓冲,从而可以使得优先级高的优先传输。默认是0,因为大多EP是没有该功能的。总结来说:1、TC机制是为了实现流量(数据包)的差异化服务而引入的,不同的数据包可以具有不同的优先级,通过TC对这些优先级标记,以便在整个传输过程中灵活管理。2、虚拟通道是一种机制会在后文阐述,QoS的实现方法就是根据TC将数据包划分到不同的通道里面。
3.Attr
Attr是Attributes的缩写,属性的意思。
我们主要就关注attr[2]和attr[1]即可,简单介绍一下:
ID-Based Ordering
说白了就是保序。这是PCIE总线的一种数据传输和控制机制,主要用于优化和控制数据包在PCIE总线上的传输效率。PCIE有种路由方式叫ID路由,采用Bus Number、Device Number和Function Number(BDF)来确定目标设备的位置。每个PCIE设备都有唯一的ID,通常由设备的物理位置和逻辑配置决定的。ID-based Ordering 机制确保具有相同源 ID 和目的 ID 的数据包按照它们被发出的顺序到达。这意味着,如果两个数据包来自同一个发送者并且目的地也相同,那么它们将会按照发送顺序进行传输和接收。
通过保持特定通道上数据包的顺序,ID-based Ordering 可以减少需要的缓存和重新排序操作,从而提高数据传输的效率。在出现错误或数据丢失的情况下,ID-based Ordering 也有助于快速定位问题,因为数据包的顺序性提供了额外的上下文信息。
Relaxed Ordering
这是PCI-X总线的一种特点的总线事务的处理特性,用于处理并发的数据交换,目的是提高总线的性能。Relaxed Ordering我们可以翻译为松散排序,这种松散排序允许传输的数据在特定情况下不必严格按照发起顺序来处理,也就是说允许一定程度上的乱序,这样可以减少由于严格排序所引起的性能瓶颈。
NO Snoop
首先得了解下Snoop的意思,通常是对系统内存内容的监视或检查,主要用于缓存一致性中的。那么何为缓存一致性呢?在多处理器系统中,每个处理器可能都有自己的缓存。为了保持所有处理器之间的数据一致性,系统需要确保一个处理器的缓存更新能够被其他处理器感知到。这就是通过 “Snoop” 操作来实现的。但它也可能引起性能损失,因为它需要额外的内存访问和处理器间通信。在 PCIe 中,“No Snoop” 属性可以被设置在特定的数据传输上。当这个属性被激活时,它指示系统不进行 “Snoop” 操作,即不监视或检查这些数据传输对应的内存内容。通常用于不需要缓存一致性维护的数据传输。
对于ID-Based Ordering和Relaxed Ordering这俩属性,协议做出了如下指示。1、Attr[1](Relaxed Ordering)对于配置请求、I/O请求、消息请求和作为消息中断的内存请求是不适用的。2、Attr[2](ID-based Ordering)对于配置请求和I/O请求需置零。3、Attr[0](NO Snoop)如上文解释同样对配置请求、I/O请求、消息请求和作为消息中断的内存请求不适用。
4.TH
这是一个标志信号,位宽1bit,表示当前 TLP 中含有 TPH(TLP Processing Hint)信息。TPH 是 PCIe V2.1 总线规范引入的一个重要功能。用来表示通知接收端即将访问数据的特性,以便接收端合理地预读和管理数据,其实也是针对CPU的缓存架构提出的一种提升性能的技巧,例如数据很快被使用,就别放到低速缓冲区例如硬盘,而是放到高速缓冲区上。但TH只是一个开关,或者是粗略的控制,具体还得有其他信号一块搭配。
5.TD和EP位
TD表示TLP中的TLP Digest是否有效,为1表示有效,为0表示无效。而EP位表示当前TLP中的数据是否有效,为1表示无效,为0表示有效。
6.AT
AT是Address Type的缩写,AT字段与 PCIe 总线的地址转换相关。看《PCIE导读》上说只有在支持 IOMMU技术的处理器系统中,PCIe 设备才能使用该字段。此外AT字段可以用作存储器域与PCI域之间的地址转换,也可以用于方便多个虚拟机共享同一个PCIE设备。这个现在也搞不明白,还是先知道是地址转换相关就这样吧。
7.Length
Length 字段用来描述 TLP 的有效负载(Data Payload)大小。 PCIe 总线规范规定一个 TLP的 Data Payload 的大小在0B~ 4096B之间。Length对于读请求和写请求有着不一样的含义。对于读请求,TLP包中不包含Payload字段,Length表示从目标设备读取的数据长度。对于写请求,则表示当前数据包中payload的实际长度。
8.Last DW BE和First DW BE
Length的最小单位是DW,取值范围是0到1023,如果是0,表示数据长度为1024个DW。读写类型由通用帧里面的“Fmt”和“type”决定,要读多长和写多长由length决定。协议里面给出了另外一种场景,就是“Zero-Length”,也就是读请求我可以请求0DW,而这样做的目的是为了实现读刷新等操作,实现方式是将Length置1,且“Last DW BE”和“First DW BE”均为0。
9.DW EB字段
PCIE总线以字节为单位就那些数据的传递存储,但Length的最小单位是32bit。所以需要用“Last DW BE”和“First (1st)DW BE”这两个字段进行字节使能,使得在一个TLP中,有效数以字节为单位。其中“Last DW BE”表示数据的最后一个双字中的有效位,“First DW BE”表示字段中第一个双字中的有效位。
10.Requester ID和Tag字段
“Requester ID”的15bit包括生成报文的总线号(8bit ,Bus Number)、设备号(5bit,Device Number)和功能号(3bit,Function),但对于存储器写请求的TLP,“Requester ID”并不是必须的,因为目标设备并不需要对接收数据进行应答。但对于存储器的读请求、I/O和配置读写请求则必须该字段。
存储器、I/O读请求TLP中含有Requester ID和Tag字段,合称为“Transaction ID”,格式如下所示,主要是用于请求TLP的目标设备用于返回确定完成数据包的返回地址。Tag用来标识Requester ID相同的同类不同类型的数据包。对于PCIE总线,所有的Non-Posted数据请求都需要完成报文作为应答,才能结束一次完整的数据传递。一个源设备发出请求若没有收到完成数据包就会保存“Transaction ID”不能再被使用,这里主要的区别就在于Tag字段,Tag决定了发送端能够暂存多少个同类型的TLP。
11.R为保留位
以上就是存储器读写请求的TLP包格式了,当然也有IO事务,IO事务请求包与存储器请求包类似,只是多了一些要求。
1、只能是32位地址模式和基于地址的路由方式,
2、只能使用NON-Posted方式进行传递,这也是为了兼容PCI,在以后的协议几乎要取消I/O 读写请求了
3、TC必须为0
4、Attr的低两位必须为0
5、AT必须为0
6、Length必须为1
7、Last DW必须为0。
我们接下来来看完成包的格式
8.3 完成包格式
完成包类似响应或者说应答信息,所有的读操作、Non-Posted写、原子操作都需要完成包。
完成包如下图所示,完成报文主要用于两类操作,一类是用于数据传递,例如一个设备发来了读请求(MDR)、I/O读请求(IORd)、配置读请求(CFGRdx)以及原子操作,当我们接收到这类数据包时需要返回对应内容,返回的数据内容必然携带数据信息。
另一类是I/O写请求(IOWr)、配置写请求,它们是Non-Posted模式,需要对其响应(应答),当收到这样一包数据后就会发送一包不含数据信息的数据,也就是完成报文。无论何种类型,完成事务都采用ID路由的方式,即BDF路由方案,下面我们就解析下完成事务的帧格内容。
第一行为通用帧头
Requester ID(Byte 8)与内存事务的请求ID一致,该字段与Tag组成“Transaction ID”用来实现数据包的路由。当收到了上文所说的读请求(MDR)、I/O读请求(IORd)、配置读请求(CFGRdx)以及原子操作,需将接收数据包中的“Transaction ID”填充到此进行返回。
Completer ID(Byte4)首先明确一件事情,对于ID路由模式,依靠的是“Transaction ID”,也就是Requester ID和Tag组合,所以“Completer ID”并不具有路由功能。它的存在是当发送的报文是用来实现应答响应功能时所要添加的内容。
Compl Status是一个3bit数,该字段保存完成报文的状态
BCM和Byte Count
BCM(Byte Count Modified)是用于PCI-X设备的,PCIE不适用。PCI-X设备进行内存读请求时,目标设备不一定一次就能将所有数据传递给源设备。此时目标设备在进行第一次数据传递时,需要设置Byte Count字段和BCM位。
Lower Address字段,对于 Memory Read Completions,Lower Address 表示返回数据的起始字节地址。对于 AtomicOp Completions,Lower Address 字段保留,没有特定的含义。对于其他类型的 Completions,Lower Address 字段被设置为全零。接收方可以选择检查这个规则是否被违反。
配置数据包
对于PCIE,配置是由RC发起的,采用ID路由方式与目标设备进行数据交换实现配置功能,只支持3DW帧头,如下为配置事务的帧格式。
第一行(Byte0~Byte3)
这部分是通用帧头,与前文所述一致,但对于配置事务,有些字段是固定的。Fmt=000是配置读,=010是配置写,此外还需遵守以下规则:
1、TC[2:0]必须为0
2、TH保留
3、Attr[2]保留
4、Attr[1:0]必须为0,
5、AT [1:0]必须为0,
6、Length[9:0]必须为1,因为读取或写入标准配置空间的寄存器时,请求通常限制为最多4字节(32位),如果扩展的话用Ext.Reg. Number和Register Numbers实现
6、last DW BE[3:0] 必须为0。
7、First DW 字段根据配置读写请求的大小设置。
第二行的请求ID和tag与内存事务的请求ID一致,该字段与Tag组成“Transaction ID”用来实现数据包的路由。
“BDF”指的是目标设备地址。Ext.Reg. Number 和Register Numbers是存放寄存器号的。
Register Number这个字段指定了标准配置空间中的寄存器地址。标准配置空间大小为256字节,包含设备的基本信息,如供应商ID、设备ID、中断设置等。寄存器编号通常用于访问这256字节内的标准配置寄存器。例如,寄存器编号0x10指向设备的第一个基址寄存器(BAR)。
Ext.Reg.Number(扩展寄存器编号)在PCIe 中,还有一个扩展配置空间,它提供了比标准配置空间更多的寄存器和功能。这个扩展配置空间可以达到4096字节。扩展寄存器编号用于访问这个更大的配置空间中的寄存器。扩展配置空间通常用于较新的或更高级的PCIe设备,它们需要比标准配置空间更多的配置选项。
消息包格式
如下图是消息事务的帧头,除了第一行公共字段外,还有Requseter ID、Tag作为ID路由的依据,还有Message Code用来指定包含在请求中的特定消息。消息事务类型有Msg和MsgD两种,Vendor-Defined Messages只能使用Msg,Slot Power Limit Messages只能使用MsgD,其它的消息类型两种都支持。
Attr2字段不能做保留设置,但除非特别说明,Attr[1:0]需设置为0。
HT不适用于消息类型
AT必须设置为0,接收端也别去检查。一般8到15byte保留处理
消息请求包不需要完成包
消息包和Memory Write Requests的order顺序规则一致。
Non-Posted事务类型:
Non-Posted模式即请求端(requester transmits)发送完一包数据到目标设备(completer),目标设备要返回一包数据到请求设备,告知其接收情况。Posted模式则不需要目标设备返回TLP数据包,但在链路层有相关的应答机制用于维护链路传输。
Non-Posted模式有可分为两类,第一类是读事务,即MRd、MRdLk、IORd、CfgRd0/1,第二类是写事务,即IOWr、CfgWr01。post事务类型包括MWr、Msg和MsgD。
九、PCIE的TYPE00配置空间
9.1 Type00的Header介绍
PCIE或者PCI的配置空间有一部分是一致的,就是Type header,64字节,16DW,分为Type0和Type1两种。Type01用于PCI桥,Type00用于PCI设备,由于二者的功能不同,所以根据实际需求做了区分。剩余的192byte是根据设备的实际需求可自定义的。总共是256byte(64DW),是否还记得CFG_ADDR的Register_Number的位宽是多少?6bit可寻址范围是0到63,也就对应这256byte配置空间,每次可访问其中的32bit。等到了PCIE,有了MMIO机制,每个功能的配置空间扩展到了4KB,但Header部分都还是一样的。
Type0 header
Device ID 和Vendor ID
这两个值由PCISIG(Peripheral Component Interconnect Special Interest Group)分配,只读。Vendor ID代表PCIE设备的生产厂商,Device ID代表这个厂商所生成的具体设备。
Command是PCIE设备的命令寄存器,在初始化时其值为0,此时只能接受配置请求事务,需要对该寄存器配置才能访问该设备的存储器或者I/O请求。如下图是比较形象的展示了每个bit位的作用,其中标*的表示不适用于PCIE,必须置为0。
SERR# Enable如果置位会发送ERR_FATAL和ERR_NONFATAL错误消息给到RC
Parity Error Response表示是否存在奇偶校验错误,而这个bit位如果置1,则使能奇偶校验的检测,如果置为0,则不使能奇偶校验的检测。
Status
状态寄存器,一共16bit
Revision ID
修订板(扩展)ID寄存器,就是版本号。它的值由功能制造商指定,用于确定该功能的修订版本号。
Class Code
这是类代码寄存器,记载PCI设备的分类,可供系统转接进行不同设备的识别,该寄存器由三个字段组成,分别为Base Class Code、Sub Class Code和Interface。其中Base Class Code将设备分为显卡、网卡等设备,Sub Class Code则是对这些设备进一步细分,Interface定义接口。
Cache Line Size
记录HOST处理器使用的Cache行长度,PCIE也用到了,可以判断与Cache行的对应关系。
Latency Timer:对PCIE来讲置零
Header Type
只读寄存器,由8bit组成,bit7=1表示当前PCI设备是多功能设备,为0表示为单功能设备,bit6~0表示当前配置空间类型。
BIST
BIST字段都提供了设备自检的信息,支持完整性检查,等后续如果写道了看看具体怎么用的。
Base Address
这个是个比较关键的内容我们一般简称bar寄存器,用来保存在内存空间的基地址的,一个设备可以最多申请6个基地址空间。也就是说设备可以在内存中申请一片内存区域,这个bar寄存器就保存了这片内存区域的基地址和大小。
Cardbus CIS Pointer
这个是用来记录使用的Cache行长度的,对于PCIE是没有意义的置0。
Subsystem ID和Subsystem Vendor ID
记录设备的生产厂商和设备名称。是扩展用的,如果上面的ID无法区分可用这部分的。
Expansion ROM Base Address
对于PCI设备,在处理器还没有运行操作系统之前,就需要完成基本的初始化设置,比如显卡、键盘和硬盘等设备,为了实现这个“预先执行”功能,PCI设备需要提供一段ROM程序,处理器在初始化过程中将运行这段程序,初始化些PCI设备,这个即记录了这段ROM程序的基地址。
Capability Pointer
这个对PCIE挺有用,Capability Pointer存放Capabilities寄存器组的基地址,可以访问扩展的配置信息。
Interrupt Line
是系统软件进行配置时写入的,记录当前设备使用的中断向量号
Interrupt Pin
中断引脚寄存器是一个只读寄存器,用于标识功能使用的旧式中断消息。有效值为01h,02h,03h和04h,分别映射到INTA,INTB,INTC和INTD的旧式中断消息。值00h表示功能未使用旧式中断消息。
9.2 Type01的Header
type01是用来配置PCI桥的,
Primary 、Secondary、Subordinate Bus Number
这仨也就是用于HOST主机进行数据路由的依据,而且在操作系统上电初始化阶段,主机会对PCI总线树进行遍历,以了解其拓扑结构,此时会根据遍历结果将这仨置为1,如果没有对应设备则置0。其中
1、Primary Bus Number :当前PCI桥的上游总线号
2、Secondary Bus Number :当前桥的总线号
3、Subordinate Bus Number:存放当前PCI子树中,编号最大的PCI总线号,也就是一个分支里面最深最靠下的总线号,但不一定是整个拓扑里面最大的。
Memory Limit和Base
PCIE的内存事务采用地址路由,这两个寄存器存放所有这些PCI桥所在分支的设备使用的存储器地址空间集合地址和大小,用于地址路由。
十、 PCIE的BAR
一方面这还是和CPU、操作系统的机制有关,它们不允许PCI设备自己在内存中划拉片地方供自己使用,而是必须由系统统一分配。另一方面PCI设备需要一定内存空间来存储自身需要的一些数据,例如功能相关的配置数据,而且不同设备不同功能的情况下需要的空间大小还不一致,所以就有了bar寄存器了,用于对不同设备进行量身定制内存空间。
PCI设备有6个bar寄存器,可以启用全部或其中几个,一旦Bar寄存器启用后,这个bar寄存器就与内存建立了关系,主机就可以通过BARs中所写入的地址范围进行访问。任何时候,当设备发现一个请求事务的地址是映射到自己的一个BAR时,它就会接收这个请求事务,因为它自己就是这个请求的目标设备。
bar寄存器如何使用?
根据上图可知,type0和type1拥有的bar数量是不一样的,为什么不一样放在后面说,bar在使用上可分为三种,分别是
1、32bit内存地址空间请求
2、64bit内存地址空间请求
3、IO地址空间请求
但在配置机制上是一致的,均是分为三个步骤,
第一步:主机发起配置请求向目标PCIE设备的目标bar寄存器,对这个bar寄存器进行写1操作。第二步:主机发起通过读配置请求读取目标PCIE设备的目标bar寄存器的值,由于有写bit为主机是无法改写的,在上电就强制为固定值了,所以主机回读的值部分不是1。第三步:主机根据读取的值判断bar请求内存空间的大小并为其分配,并将起始地址写入目标bar寄存器,这就完成了两个功能,一是确定基地址,二是确定了寻址范围。
拿32bit的bar空间申请举例
bar寄存器的存在的目的是告诉使用我需要多大的空间,在诉说之前自身肯定得先知道并做一定设置,这种设置呢就是将某些位写0,且不可更改。
例如下图的(1)所描述的“Uninitialized bar”,bit0bit3是固定内容,bit11bit4的预设0就是表示申请内存大小的,12bit写0表示申请内存空间位4KB,因为2^12=4096嘛,如果bit13也是0,那就表示想要申请8KB的内存空间。
那么为啥要这样搞呢?主机又是怎么知道的呢?主机首先会对bar寄存器各个bit位都写1(0XFFFFFFFF)然后再回读。对于我们这个例子回读内容就是0XFFFFF000,通过bit3~0知道了申请内存空间的属性,又因为bit12是1,所以知道了申请内存大小位4KB。
随后主机再把内存基地址写入bar寄存器,例如下图的0xF9000000,如此主机就知道了这个设备的对应功能的bar0申请4KB内存。
最后说,为什么要这样做,在我看来,这样做即实现了我们的目的,也节省了需要的配置字。我们需要告知主机需要多大的空间和属性,可以单独占一个字段,让主机读就行了。但再发TLP包时,主机寻址也需要知道目的地址,这样还需要一个字段。现在将两字段合一的,而且目的还实现了。
十一、PCIE访问机制
PCIe板卡访问PC内存时,板卡向 PC 发送 TLP 包,例如 MWr 包,地址信息就是PC 的物理地址;如果是 MRd 包,那 PC 收到后回复一个完成包,板卡从完成包分析出数据即得到 MRd 读取地址的数据。这是PCIe板卡访问PC.
PC访问PCIe板卡,简单的解释,PC 启动时,BIOS 探测所有的外设。对 PCIe设备来说,BIOS 检测到板卡有多少个 BAR 空间,每个空间有多大,然后对应为这些 BAR 空间分配地址。对 PC 设备来说,它能“看”到 PCIe 板卡的空间只有 BAR 空间,也就只能访问这些 BAR 空间。也就是说,板卡可以发送合法的 PCIe TLP 包,并得到 PC 端的相应;但是 PC 端访问板卡被局限在 BAR 空间。
PCIE首先进行链路训练,上电复位后,链路训练状态机进入L0状态时链路训练完成后进入gen1模式,如果双方支持更高的速率,则立即进行gen2/3、4速率的训练,当链路训练状态机再次进入L0状态,链路训练完成。
链路训练完成开始进行枚举扫描,枚举扫描主要目的是CPU需要知道系统中有哪些PCIE 设备,并且为每个设备分配总线号。PCIE的配置读TLP报文中包含响应设备的BFD,因此PCIE设备需要知道自己的bus_number是多少。
枚举扫描过程中,CPU会通过配置读TLP读取PCIE配置空间的的verdor id和头标类型寄存器以及其他配置寄存器,此时PCIE返回的读返回TLP报文中complete_id(bus_number、function_number、device_number)为全0,因此此时PCIE不知道自己的bus_number。枚举过程中,CPU完成对PCIE的配置读后,会发起配置写TLP,此时PCIE接受到CPU发来的配第一次置写TLP,会从TLP中解析出bus_number字段存下来。随后的配置读TLP返回中,就会使用bus_number、和function_number和device_number拼接成complete_id。
枚举扫描完成后,会配置基地址寄存器,给PCIE分配地址空间。通过对BAR0/1、BAR2/3等基地址寄存器先进行写全32’hffff_ffff,得到信息确认PCIE想申请32bit地址还是64bit地址以及想获得的地址空间,从而分配基地址。