• 正文
  • 相关推荐
申请入驻 产业图谱

PCIe初始化流程详解:链路训练 → 枚举 → 资源分配

07/15 10:43
7655
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

本主要介绍PCIe初始化的基本流程。在PCIe复位释放后,PCIe硬件会开始链路训练和初始化流程,链路训练完成后会进行PCIe枚举扫描,最后进行BAR空间等资源分配,然后就可以正常工作了。

1. 链路训练

PCIe冷复位、暖复位、或者热复位释放后,LTSSM(链路训练状态机)会开始链路训练,正常会按照Detect → Polling → Configuration → L0的顺序跳转。

首次进入L0状态时链路会进入PCIe gen1模式,如果双方都支持更高的速率,会立即进行gen2/3/4等速率的训练,当再次进入L0状态时链路训练完成。

LTSSM共有11个顶层状态:Detect、Polling、Configuration、Recovery、L0、L0s、L1、L2、Hot Reset、Loopback和Disable。状态跳转图如下:

在链路训练期间,主要完成的工作有以下:

完成位锁定(Bit Lock)。接收端的时钟和数据恢复逻辑(CDR,Clock and Data Recovery)通过使用数据比特流作为时钟的参考信号,来重建发送端的时钟。一旦从数据流中恢复了时钟,接收端就完成了位锁定,可以正确采样输入数据。

完成Symbol锁定或者Block锁定(Gen3)。

如有必要,校正通道极性翻转。

得知通道支持的链路数据速率。

协商链路宽度

为各通道(lane)指定编号

检测是否需要lane reversal,并校正

补偿各个通道之间的时序偏斜(Deskew Lane-to-Lane timing differences)。

PCIe链路训练的详细介绍参考:PCIe链路初始化和训练介绍

2. 枚举

扫描链路训练完成后,会进行PCIe枚举扫描。配置软件(例如UEFI)需要扫描PCIe网络结构,来发现整个PCIe拓扑。

配置软件一般是读取Function的Vendor ID寄存器来搜索这个Function是否存在。PCI-SIG给每个厂商分配了唯一的16-bit的Vendor ID。

枚举的一个关键部分就是确定Function是bridge还是EP。Header Type寄存器(位于配置空间Header的偏移地址0Eh)的低7-bit标识Function的类型,一共定义了三个值:

0 = 不是Bridge(即PCIe EP)

1 = PCI-to-PCI Bridge(缩写为P2P)

2 = CardBus Bridge(legacy接口)

下图是一个经过枚举后的系统示例,枚举过程具体如下。

软件将Host/PCI Bridge的Secondary Bus Number更新为0,并将Subordinate Bus Number更新为255。设置为最大值是因为Sub Num可以保持不变,直到Host/PCI Bridge下方的所有总线都识别出来,再更新成准确值。此时Host/PCI Bridge下方的总线范围是Bus 0到255。

从Device 0(bridge A)开始,软件读取Bus 0上的32个可能存在的Device,读取它们Function 0中的Vendor ID。如果Bus 0,Device 0,Function 0返回了有效的Vendor ID,那么认为这个设备存在且至少含有一个Function。若Bus 0,Device 0,Function 0没有返回有效的Vendor ID,则继续探测Bus 0,Device 1,Function 0。

在本例中,值为1的Header Type字段表示这是一个PCI-to-PCI Bridge。Header Type寄存器中的multi-function bit为0,表示Function 0是这个Bridge唯一的Function。协议并没有阻止Bridge这样的设备去实现多个Function,相反地,当有多个Function时,每个Function可以作为虚拟PCI-to-PCI bridge,甚至是非bridge的function。

现在软件发现了一个bridge,并进行一系列配置写操作来设置该Bridge的总线号寄存器:Primary Bus Number Register = 0;Secondary Bus Number Register = 1;Subordinate Bus Number Register = 255。即这个Bridge下方直接相连的总线号为1,它下方从属的最大总线号是255。

软件必须进行深度优先的搜索。在继续发现Bus 0上的其他Device/Function之前,它必须先搜索Bus 1。

软件读取Bus 1,Device 0,Function 0的Vendor ID,即目标设备是Bridge C。这次读取将返回一个有效的Vendor ID,表示Bus 1上存在Device 0,Function 0。

Header寄存器中的Header Type字段的值为1,表示它是PCI-to-PCI Bridge。multi-function bit = 0,表示Bridge C是一个单Function设备。

软件执行一系列的配置写操作来设置Bridge C的总线号寄存器:Primary Bus Number = 1;Secondary Bus Number = 2;Subordinate Bus Number = 255。

软件继续进行深度优先的搜索,读取Bus 2,Device 0,Function 0的Vendor ID。Bus 2上的Device 0,Function 0是Bridge D。

读请求返回有效的Vendor ID,表示Bus 2,Device 0,Function 0存在。

Header寄存器中的Header Type字段的值为1,表示它是PCI-to-PCI Bridge,且multi-function bit=0,表示Bridge D是一个单Function设备。

软件执行一系列的配置写操作来设置Bridge D的总线号寄存器:Primary Bus Number = 2;Secondary Bus Number = 3;Subordinate Bus Number = 255。

软件继续深度优先的搜索,读取Bus 3,Device 0,Function 0的Vendor ID。Bus 3上的Device 0,Function 0是Bridge D。

读请求返回了有效的Vendor ID,表示Bus 3,Device 0,Function 0存在。

Header寄存器中的Header Type字段的值为0,表示它是EP。由于它是EP而不是Bridge,因此它有一个Type 0 Header,并且下面没有PCI兼容总线了。这个EP的multi-function bit = 1,表示它是multi-function设备。

软件读取Bus 3,Device 0上的可能的8个Function的Vendor ID,确认除了Function 0之外只存在Function 1。Function 1也是EP(Type 0 Header),因此这个设备下面没有其他的总线了。

软件继续在Bus 3上扫描,寻找Device 1~31上有效的function,但没有找到其他的function。

软件找完Bridge D下面的所有Function后,会更新Bridge D的信息,即将从属总线号更新为真实值3。然后软件回到上一个总线层级(Bus 2),并继续在这个总线上扫描,寻找有效的function。在本例中,Bus 2上的Device 1,Function 0是Bridge E。

读请求返回有效的Vendor ID,表示Bus 2,Device 1,Function 0存在。

Header寄存器的Header Type字段的值为1,表示它是PCI-to-PCI Bridge,且bit 7的值为0,表示Bridge E是一个单function设备。

软件进行一系列的配置写操作来设置Bridge E的总线号寄存器:Primary Bus Number = 2;Secondary Bus Number = 4;Subordinate Bus Number = 255。

软件继续深度优先的搜索,读取Bus 4,Device 0,Function 0的Vendor ID。

读请求返回有效的Vendor ID,表示Bus 4,Device 0,Function 0存在。

Header寄存器的Header Type字段的值为0,表示它是EP设备,且bit 7值为0,表示它是单function设备。

软件继续在Bus 4上扫描,寻找Device 1~31上有效的function,但没有找到其他function。

软件已经到达这个搜索分支的底部,因此它会更新当前总线上相连的Bridge(即Bridge E),将其从属总线号更新为真实值4。然后软件回到上一个总线层级(Bus 2),继续读取该总线上的下一个设备(Device 2)的Vendor ID。由于Bus 2上并没有Device 2~31,因此软件没有发现Bus 2上的其他设备。

软件更新Bus 2上面相连的Bridge(即Bridge C),将其从属总线号更新为真实值4,并回到上一个总线层级(Bus 1),继续读取该总线上的下一个设备(Device 1)的Vendor ID。本例中,Bus 1上没有实现Device 1~31,因此软件没有发现Bus 1上有其他设备。

软件更新Bus 1上面相连的Bridge(即Bridge A),将其从属总线号更新为真实值4,并回到上一个总线层级(Bus 0),继续读取该总线上的下一个设备(Device 1)的Vendor ID。本例中,Bus 0上的Device 1,Function 0是Bridge B。

与前面类似,枚举软件发现Bridge B,并进行一系列的配置写操作来设置Bridge B的总线号寄存器:Primary Bus Number = 0;Secondary Bus Number = 5;Subordinate Bus Number = 255。

软件在Bus 5上发现Bridge F,并进行一系列的配置写操作来设置Bridge F的总线号寄存器:Primary Bus Number Register = 5;Secondary Bus Number = 6;Subordinate Bus Number = 255。

接着软件在Bus 6上发现Bridge G,并进行一系列的配置写操作来设置Bridge G的总线号寄存器:Primary Bus Number Register = 6;Secondary Bus Number = 7;Subordinate Bus Number = 255。

当软件继续在Bus 7上搜索,发现一个单Function的EP设备,Bus 7,Device 0,Function 0,因此将Bridge G的从属总线号更新为7。

软件向上回到Bus 6继续搜索,然后发现Bridge H,并进行一系列的配置写操作来设置Bridge H的总线号寄存器:Primary Bus Number Register = 6;Secondary Bus Number = 8;Subordinate Bus Number = 255。

然后软件在Bus 8上发现Bridge J,并执行一系列的配置写操作来设置Bridge J的总线号寄存器:Primary Bus Number Register = 8;Secondary Bus Number = 9;Subordinate Bus Number = 255。

Bus 9上的所有设备以及Function都已经被搜索完成,它们都不是Bridge,所以将Bridge H和Bridge J的从属总线号更新为9。

软件向上回到Bus 6继续搜索,然后发现了Bridge I,并执行一系列的配置写操作来设置Bridge I的总线号寄存器:Primary Bus Number Register = 6;Secondary Bus Number = 10;Subordinate Bus Number = 255。

当软件继续在Bus 10上搜索,发现一个单Function的EP设备,Bus 10,Device 0,Function 0。

由于枚举软件已经到达这个搜索分支的底部,这个底部也是整个PCIe拓扑树状结构的底部,因此Bridge B、F、I的从属总线号会被更新为10,而Host/PCI Bridge也会将从属总线寄存器更新为这个值。

每个Bridge中最终的主总线号、次级总线号和从属总线号可以参考图3‑9。

枚举详细介绍:PCIe枚举过程

3. 资源分配

枚举完成后,会配置BAR寄存器来分配memory等资源。BAR寄存器如下:

软件把每个BAR都通过配置写操作将可写入的bit写为全1(被固定的低位bit不受影响)。如下图,除了bit[11:0]以外,所有的bit都被写为1。写全1是为了确定最低位的可写入的bit是哪一位,这个bit的位置指示了请求的地址空间大小。在本例中,最低位可写入的bit为bit 12,因此这个BAR需要请求2^12(4KB)的地址空间。如果最低可写入bit为bit 20,那么就要请求2^20(1MB)的地址空间。

然后软件从BAR0开始,依次读取每个BAR的数值,从而确定各个BAR要请求的地址空间的大小和类型。

然后,软件通过将起始地址写入BAR0来为BAR0分配一个地址范围。在本例中,这个起始地址为F900_0000h。

至此,对BAR0的配置就完成了。一旦软件使能了命令寄存器(Command register,偏移地址04h)中的Memory Space Enable bit,那么这个设备就会接受地址在F900_0000h-F900_0FFFh(4KB大小)范围的memory请求。

PCIe地址空间详细介绍:PCIe四种地址空间介绍

相关推荐