-
PCI(Peripheral Component Interconnect,外设组件互连):符合 PCI 总线标准的设备就被称为 PCI 设备,PCI 总线架构中可以包含多个 PCI 设备。
-
PCIe(Peripheral Component Interconnect Express,快速外设组件互连):PCI Express,简称 PCIe,是电脑总线 PCI 的一种,它沿用了现有的 PCI 编程概念及通讯标准,但建基于更快的串行通信系统。是 Intel 提出的新一代的总线接口,PCI Express 采用了目前业内流行的点对点串行连接,比起 PCI 以及更早期的计算机总线的共享并行架构每个 PCIe 设备都有自己的专用连接,不需要向整个总线请求带宽,而且可以把数据传输率提高到一个很高的频率,达到 PCI 所不能提供的高带宽。
如上图,PCI 插槽都是等长的,防呆口位置靠上,大部分都是纯白色。PCIe 插槽大大小小,最小的 x1,最大的 x16,防呆口靠下。
PCI 与 PCI-E 总线PCI(Peripheral Component Interconnect,外设部件互连标准):是一种同步且独立于 CPU 的 32 位或 64 位并行局部总线,工作频率为 33MHz,具有即插即用(Plug and Play,P&P)功能。PCI 总线由 ISA 总线发展而来并取代了 ISA,是目前微机中使用最为广泛的接口,几乎所有的主板产品上都带有 PCI 插槽。PCI 总线连接的高速 I/O 接口的设备,称为 PCI 设备,主要为显卡、网卡、声卡、SCSI 卡。
从结构上看,PCI 总线是在系统总线之间插入的一级总线,具体由一个桥接电路(e.g. 南桥)实现了对这一层级的管理,这个桥接电路作为 PCI 总线控制器,含有集中式的总线仲裁器,还实现了上下层级之间的接口来协调数据的传送。
PCI-E(PCI-Express):是一种通用的总线规格,最终的设计目的是为了取代现有电脑系统内部的总线传输接口,这不只包括显示接口,还囊括了 CPU、PCI、HDD、Network 等多种应用接口,继而解决系统内部数据传输的瓶颈问题。
PCI 总线属于共享并行互联结构,系统的各种设备共用一个带宽,,这极大影响了系统的整体性能,而且并行通信的串扰问题也严重制约了日后速度的进一步提升。而 PCI-E 总线则采用了串行互联的方式,以点对点的形式进行数据传输,也就是说每个设备都有自己的专用连接,可以独享带宽,而不必向共享总线请求带宽。一个标准的 PCI-E 连接可以包含多个信道(Lane),当需要增加数据传输带宽时,可以通过增加信道的数量来达到目的。单个信道的 PCI-E 可以提供单向 250MB/s 的带宽,PCI-E * 16 信道则可以提供 4GB/s 的带宽。PCI-E 相对于 PCI 大大的提高了传输速率,而且也为更高的频率提升创造了条件。
PCI-E 支持对 PCI 和 PCI-X 的软件兼容,但主板上得到接口插槽却不兼容,因为 PCI-E 是串行接口,针数会更少,插槽会更短,PCI-E 插槽的长度跟信道的数目有关。
作为 x86 体系重要的一环,PCIe 标准历经了 PCI,PCI-X 和 PCIe,走过了近 30 年时光。下图是一个 PCIe 的拓扑结构示意,PCIe 协议支持 256 个 Bus,每条 Bus 最多支持 32 个 Device,每个 Device 最多支持 8 个 Function,所以由 BDF(Bus,Device,Function)构成了每个 PCIe 设备节点的身份证号。
PCIe 体系架构一般由 root complex,switch,endpoint 等类型的 PCIe 设备组成。在 root complex 和 switch 中通常会有一些 embeded endpoint(这种设备不对外暴露 PCIe 接口)。这么多的设备,CPU 启动后要怎么去找到并认出它们呢?
Host 对 PCIe 设备扫描是采用了深度优先算法,本质是一种遍历算法,对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。我们一般称这个过程为 PCIe 设备枚举。枚举过程中 Host 通过配置读事物包来获取下游设备的信息,通过配置写事物包对下游设备进行设置。
- 第一步,PCI Host 主桥扫描 Bus 0 上的设备(在一个处理器系统中,一般将 root complex 中与 Host Bridge 相连接的 PCI 总线命名为 PCI Bus 0),系统首先会忽略 Bus 0 上的 embedded EP 等不会挂接 PCI 桥的设备,主桥发现 Bridge 1 后,将 Bridge1 下面的 PCI Bus 定为 Bus 1,系统将初始化 Bridge 1 的配置空间,并将该桥的 Primary Bus Number 和 Secondary Bus Number 寄存器分别设置成 0 和 1,以表明 Bridge1 的上游总线是 0,下游总线是 1,由于还无法确定 Bridge1 下挂载设备的具体情况,系统先暂时将 Subordinate Bus Number 设为 0xFF。
-
第二步,系统开始扫描 Bus 1,将会发现 Bridge 3,并发现这是一个 switch 设备。系统将 Bridge 3 下面的 PCI Bus 定为 Bus 2,并将该桥的 Primary Bus Number 和 Secondary Bus Number 寄存器分别设置成 1 和 2,和上一步一样暂时把 Bridge 3 的 Subordinate Bus Number 设为 0xFF。
-
第三步,系统继续扫描 Bus 2,将会发现 Bridge 4。继续扫描,系统会发现 Bridge 下面挂载的 NVMe SSD 设备,系统将 Bridge 4 下面的 PCI Bus 定为 Bus 3,并将该桥的 Primary Bus Number 和 Secondary Bus Number 寄存器分别设置成 2 和 3,因为 Bus3 下面挂的是端点设备(叶子节点),下面不会再有下游总线了,因此 Bridge 4 的 Subordinate Bus Number 的值可以确定为 3。
-
第四步,完成 Bus 3 的扫描后,系统返回到 Bus 2 继续扫描,会发现 Bridge 5。继续扫描,系统会发现下面挂载的 NIC 设备,系统将 Bridge 5 下面的 PCI Bus 设置为 Bus 4,并将该桥的 Primary Bus Number 和 Secondary Bus Number 寄存器分别设置成 2 和 4,因为 NIC 同样是端点设备,Bridge 5 的 Subordinate Bus Number 的值可以确定为 4。
-
第五步,除了 Bridge 4 和 Bridge 5 以外,Bus2 下面没有其他设备了,因此返回到 Bridge 3,Bus 4 是找到的挂载在这个 Bridge 下的最后一个 Bus 号,因此将 Bridge 3 的 Subordinate Bus Number 设置为 4。Bridge 3 的下游设备都已经扫描完毕,继续向上返回到 Bridge 1,同样将 Bridge 1 的 Subordinate Bus Number 设置为 4。
-
第六步,系统返回到 Bus0 继续扫描,会发现 Bridge 2,系统将 Bridge 2 下面的 PCI Bus 定为 Bus 5。并将 Bridge 2 的 Primary Bus Number 和 Secondary Bus Number 寄存器分别设置成 0 和 5, Graphics card 也是端点设备,因此 Bridge 2 的 Subordinate Bus Number 的值可以确定为 5。
至此,挂在 PCIe 总线上的所有设备都被扫描到,枚举过程结束,Host 通过这一过程获得了一个完整的 PCIe 设备拓扑结构。
PCIe 设备的信息查询系统上电以后,Host 会自动完成上述的设备枚举过程。除一些专有系统外,普通系统只会在开机阶段进行进行设备的扫描,启动成功后(枚举过程结束),即使插入一个 PCIe 设备,系统也不会再去识别它。
在 Linux 操作系统中,我们可以通过 lspci –v -t 命令来查询系统上电阶段扫描到的 PCIe 设备,执行结果会以一个树的形式列出系统中所有的 PCIe 设备。如下图所示:
其中黄色方框中的 PCIe 设备是 Bejing Starblaze Technology Co., LTD. 推出的 STAR1000 系列 NVMe SSD 主控芯片,图中显示的 9d32 是 Starblaze 在 PCI-SIG 组织的注册码,1000 是设备系列号。
STAR1000 设备的 BDF 也可以从上图中找出,其中 bus 是 0x3C,device 是 0x00,function 是 0x0,BDF 表示为 3C:00.0,与之对应的上游端口是 00:1d.0。
我们可以通过 lspci –xxx –s 3C:00.0 命令来列出该设备的 PCIe 详细信息。这些内容存储在 PCIe 配置空间,它们描述的是 PCIe 本身的特性。如下图所示:
可以看到这是一个非易失性存储控制器,0x00 起始地址是 PCIe 的 Vendor ID 和 Device ID。Class code 0x010802 表示这是一个 NVMe 存储设备。0x40 是第一组 Capability 的指针,如果你需要查看 PCIe 的特性,就需要从这个位置开始去查询,在每组特征的头字段都会给出下一组特性的起始地址。从 0x40 地址开始依次是 Power management、MSI 中断、链路控制与状态、MSI-X 中断等特性组。这儿特别列出了链路特征中的一个 0x43 字段,表示 STAR1000 设备是一个 x4lane 的链接,支持 PCIe Gen3 速率(8Gbps)。
当然也可以使用 lspci –vvv –s 3C:00.0 命令来查看设备特性,初学者看到下面的列表也就一目了然了。
Host 在枚举设备的同时也会对设备进行配置,每个 PCIe 设备都会指定一段 CPU memory 访问空间,从上面的图中我们可以看到这个设备支持两段访问空间,一段的大小是 1M byte,另一段的大小是 256K byte,系统会分别指定它们的基地址。基地址配置完成以后,Host 就可以通过地址来对 PCIe memory 空间进行访问了。
PCIe Memory 空间关联的是 PCIe 设备的物理功能(PF),对于 STAR1000 系列芯片而言,物理功能是 NVMe,memory 中存放的是 NMVe 的控制与状态信息,对于 NMVe 的控制以及工作状态的获取,都需要通过 memory 访问来实现。
下面以 NVMe 命令下发为例简单描述 PCIe 设备的 memory 访问。NVMe 命令下发的基本操作是:
- Host 写 doorbell 寄存器,此时使用 PCIe memory 写请求。如下图所示,Host 发出一个 memory write(MWr) 请求,该请求经过 switch 到达要访问的 NVMe SSD 设备。
- NVMe 读取命令操作,这个请求会被端点设备接收并执行。如下图所示,此时 NVMe SSD 作为请求者,发出一个 memory read(MRd) 请求,该请求经过 switch 到达 Host,Host 作为完成者会返回一个完成事物包(CplD),将访问结果返回给 NVMe SSD。
这样,一个 NVMe 的命令下发过程就完成了。同样,NVMe 的其他操作比如各种队列操作,命令与完成,数据传输都是通过 PCIe memory 访问的方式进行的,此处不再详述。
参考文章https://mp.weixin.qq.com/s/FlRc2q8r0fUOzxJFWulGfw