硬盘是大家都很熟悉的设备,一路走来,从 HDDSSD,从 SATA 到 NVMe,作为 NVMe SSD 的前端接口,PCIe 再次进入我们的视野。作为 x86 体系关键的一环,PCIe 标准历经 PCI,PCI-X 和 PCIe,走过近 30 年时光。其中 Host 发现与查找设备的方式却一脉沿袭,今天我们先来聊一聊 PCIe 设备在一个系统中是如何发现与访问的。

 

首先我们来看一下在 x86 系统中,PCIe 是什么样的一个体系架构。下图是一个 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 设备拓扑结构。

 


系统上电以后,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 在最左边),可以看到这是一个非易失性存储控制器,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 设备物理功能,对于 STAR1000 系列芯片而言,物理功能是 NVMe,memory 中存放的是 NMVe 的控制与状态信息,对于 NMVe 的控制以及工作状态的获取,都需要通过 memory 访问来实现。

 

下面以 NVMe 命令下发为例简单描述 PCIe 设备的 memory 访问。NVMe 命令下发的基本操作是 1)Host 写 doorbell 寄存器,此时使用 PCIe memory 写请求。如下图所示,host 发出一个 memory write(MWr)请求,该请求经过 switch 到达要访问的 NVMe SSD 设备。

 

 

这个请求会被端点设备接收并执行 2)NVMe 读取命令操作。如下图所示,此时 NVMe SSD 作为请求者,发出一个 memory read(MRd)请求,该请求经过 Switch 到达 Host,Host 作为完成者会返回一个完成事物包(CplD),将访问结果返回给 NVMe SSD。

 

 

这样,一个 NVMe 的命令下发过程就完成了。同样,NVMe 的其他操作比如各种队列操作,命令与完成,数据传输都是通过 PCIe memory 访问的方式进行的,此处不再详述。

 

通过上面的描述,相信能够帮助大家了解 PCIe 的设备枚举和 memory 空间访问。以后会继续与大家探讨 PCIe 的其他内容,比如 PCIe 的协议分层,链路建立,功耗管理等等。目前 PCIe 协议还正在不断的快速演进中,2017 年发布的 PCIe Gen4 标准,每条 Serdes 支持的速率已经达到 16Gbps,Gen5 也在加速制定中,其速率会再翻一倍达到 32Gbps。Starblaze 会紧跟技术的发展趋势,提供速率更高,性能更好更稳定的 NVMe SSD 系列产品。