电子产业一站式赋能平台

PCB联盟网

搜索
查看: 40|回复: 0
收起左侧

PCIe枚举过程

[复制链接]

335

主题

335

帖子

3655

积分

四级会员

Rank: 4

积分
3655
发表于 2024-12-11 11:32:00 | 显示全部楼层 |阅读模式
在系统复位或上电之后,配置软件(例如UEFI)需要扫描PCIe网络结构,来发现整个拓扑,并得知这个网络结构是如何填充的。在这之前,如图3?10,软件唯一知道的就是拓扑中有一个Host/PCI Bridge以及这个Bridge的次级总线是Bus 0。需要注意,一个Bridge上方相连的总线称为Primary Bus,而Bridge下方相连的总线称为次级总线(Secondary Bus)。扫描PCIe结构来发现整体拓扑的过程称为枚举过程。

y1mnaci4l1s6404140311.png

y1mnaci4l1s6404140311.png

1. 搜索Function是否存在
处理器上运行的配置软件发现Function的方式一般是读取这个Function的Vendor ID寄存器。PCI-SIG给每个厂商分配了唯一的16-bit的Vendor ID,每个厂商会将自己设计的Function中的Vendor ID寄存器的值固定为自己的Vendor ID。通过读取整个系统中的Bus,Device,Function这三者所有组合中的Vendor ID寄存器,枚举软件可以搜索遍整个拓扑,并得知哪些设备存在。这个过程比较简单,但可能会出现两个问题:目标设备可能不存在,或者它虽然存在但是没有准备好响应事务请求。下面介绍如何处理这两种情况。
设备不存在:
枚举过程中,目标设备不存在的情况可能会发生多次。在PCI总线中,配置读请求有可能在总线上超时(timeout),那么会产生一个主设备放弃(Master Abort)的错误情况。由于没有设备来驱动总线,所有的信号都是拉高的,那么数据位在总线上就是全为1的,这个全为1的值将成为总线上被看见的数据值。这使得值为FFFFh的Vendor ID成为一个保留值。如果软件读取到的Vendor ID是FFFFh,那么说明设备不存在。这并不是一种错误情况,因此在枚举过程中,Master Abort不会被当做是错误来报告。
对于PCIe总线,对一个不存在的设备的配置读请求将使得目标设备上方连接的Bridge返回一个不携带数据的完成包,完成包的状态字段被置为UR(Unsupported Request)。为了向后兼容传统的枚举模型,若RC在枚举过程中收到这样的完成包,它会给处理器返回数据FFFFh。注意,枚举软件是依赖于接收到一个返回值为全1(FFFFh)的配置读请求来判定目标设备不存在,而系统在读取这样一个不存在的设备Function的Vendor ID时,其实是返回了一个状态为UR的完成包。
当设备不存在时,也需要避免意外发送错误报告。尽管这种超时或者UR在系统正常运行时是错误情况,但在枚举过程中不能认为是错误,它是可以预料到的结果。为了避免这种混乱,设备一般在这过程中不启用错误信号,直到枚举完成才启用。对于PCIe来说,记录这种事件(目标设备不存在)仍然是有用的,这也是为什么PCIe Capability寄存器块中存在第4个“错误”状态位,称为Unsupported Request Status。有了这个状态位,目标设备不存在的情况可以被记录下来,不会被当做是一个错误。在枚举时,错误处理软件的能力比较有限,可能使问题无法解决。因为通常是在OS或者其他错误处理软件可用之前就已经执行了枚举软件,所以这种情况下枚举软件可能运行失败。为了避免这种风险,枚举时通常不应该报告错误。
设备未准备好:
另一个问题就是目标设备虽然存在,但可能未准备好响应配置访问。对于配置操作,需要考虑发起配置的时间点,因为设备准备好被访问是需要时间的。如果数据速率小于等于5.0GT/s,软件必须在复位后等待100ms,再发起配置请求。如果数据速率高于5.0GT/s(Gen3速率),软件必须在链路训练完成100ms之后再尝试发起配置操作。之所以更高的速率需要更多的延时,是因为Gen3的链路训练中的均衡过程(Equalization Process)可能需要较长时间。
PCIe协议规定初始化时间为1.0s(+50%/-0%)。Function可以利用这段时间来填充自己的配置寄存器,例如加载一个外挂的串行EEPROM的内容做为寄存器初始值。加载EEPROM内容需要花费一点时间,加载完成前Function都没有准备好响应配置请求。
在PCI中,若在Function准备好之前收到了配置访问,那么有三个选择:忽略这个请求、Retry这个请求、接收这个请求但是延期响应直到Function完全准备好。最后一种选择可能会给热插拔(Hot-plug)系统带来问题,因为延期响应的时长可能会达到1s,在这1s中总线都是停止的,直到这个请求执行完,总线才重新工作。
PCIe也有相同的问题,但有一点不同。首先,PCIe Function在临时无法响应配置访问时必须要给出一个完成包,这个完成包也要指定状态,即Configuration Request Retry Status(CRS,配置请求重试状态)。这个状态只有在响应配置请求时才是合法的,如果其他请求收到了这个状态的完成包,则可能会认为数据包格式错误(Malformed Packet error)。这种状态的响应也只在复位后的1秒内有效,因为1秒之后Function被认为可以正常响应配置请求,如果1秒之后无法正常响应配置请求,那么就认为它已经损坏了。
除了系统复位后的那一段时间之外,RC处理配置读请求的CRS完成包的方式是跟具体实现有关的。在系统复位后的那一小段时间里,RC有两种选择,选择哪一种取决于Root Control寄存器中的CRS Software Visibility bit(如图3?11)。

gmgekae5qov6404140411.png

gmgekae5qov6404140411.png


  • 如果这个bit被置为1,而且请求是配置读请求,要读取Vendor ID寄存器的两个byte(枚举通过这样的操作来搜索Function是否存在),那么在收到CRS完成包时RC需要给Host返回虚假的0001h作为读取的寄存器值,并将这个请求的其他字节都置为全1。这个Vendor ID并没有被任何真实的设备使用,软件会把0001h的Vendor ID理解为访问这个设备可能需要很长的延迟。这个信息非常有用,因为软件可以选择去执行其他任务,更充分的利用这段等待设备响应的时间,稍后再返回来查询这个设备。要进行这样的操作,软件必须确保在复位后对Function的第一次访问就是读取2 byte的Vendor ID的配置读访问。
  • 对于配置写访问或者其他的配置读访问,RC必须自动地以一个新请求的形式对之前的配置请求进行重新下达。
    2. 确认Function是EP还是Bridge
    枚举的一个关键部分就是确定Function是bridge还是EP。如图3?12,Header Type寄存器(位于配置空间Header的偏移地址0Eh)的低7-bit标识Function的类型,一共定义了三个值:
  • 0 = 不是Bridge(即PCIe EP)
  • 1 = PCI-to-PCI Bridge(缩写为P2P)
  • 2 = CardBus Bridge(legacy接口)

    wu0unjzdaqq6404140511.png

    wu0unjzdaqq6404140511.png


    3. 单RC枚举示例(Single Root Enumeration Example)
    如图3?13是一个经过枚举后的系统示例。在系统启动时,配置软件将进行如下枚举过程。

    crnolyxw33g6404140611.png

    crnolyxw33g6404140611.png


  • 软件将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也会将从属总线寄存器更新为这个值。
    [/ol]
    每个Bridge中最终的主总线号、次级总线号和从属总线号可以参考图3?9。

    eosnguxylpz6404140712.png

    eosnguxylpz6404140712.png



    4. 多RC枚举示例(Multi-Root Enumeration Example)
    整体介绍
    考虑图3?14所示的多RC系统。在这个系统中,每个RC:
  • 实现了各自的配置地址端口和配置数据端口,并且各个RC的这些端口的IO地址相同,比如RC 0和RC 1的配置地址端口的IO地址相同。(对于x86系统而言)
  • 实现了增强型配置机制。
  • 包含一个Host/PCI Bridge。
  • 实现了各自的次级总线号寄存器和从属总线号寄存器。

    下图中,每个RC都是一个chipset成员,其中一个RC被设计为primary RC,它的Bridge下方相连的总线为Bus 0。而另一个RC的Bridge下方相连的总线暂时是Bus 255,它被称为Secondary RC。

    0mvns1sld5y6404140812.png

    0mvns1sld5y6404140812.png


    多RC枚举过程
    在对图3?14中左侧的树状结构进行枚举的过程中,Secondary RC的Host/PCI Bridge将会忽略所有的配置访问,因为左侧的目标总线号中最大的也没有超过9。注意,虽然Bus 8被检测到了并且编了号,但是这条总线上没有连接设备。一旦左侧树状结构的枚举完成,枚举软件会进行如下步骤对Secondary RC进行枚举:
  • 本例中,枚举软件会把Secondary RC中Host/PCI Bridge的次级总线号和从属总线号都更改为64。(64和128这两个值在多RC系统中经常用来作为起始总线号,但这只是软件习惯,PCI或PCIe协议中并没有对这种配置方式有规定。如果在本例中给Secondary RC的起始总线号设置为10,也不会有问题。)
  • 枚举软件开始在Bus 64上搜索,发现了与RC下方端口相连的Bridge。
  • 然后软件进行一系列的配置写操作来设置Bridge的总线号寄存器:Primary Bus Number Register = 64;Secondary Bus Number = 65;Subordinate Bus Number = 255。
  • 软件在Bus 65上搜索,发现了Device 0,且它仅有一个Function 0。进一步搜索发现,Bus 65上没有其他Device了,因此搜索过程返回到上一级总线(bus 64)。
  • 软件在Bus 64上搜索,然而没有发现其他设备,因此将Host/PCI Bridge的从属总线号更新为65。
  • 这样就完成了枚举过程。
    [/ol]
    参考:
    Mindshare PCI Express Technology 3.0
    PCI Express Base Specification Revision 6.0
    [/ol]end

    一口Linux

    关注,回复【1024】海量Linux资料赠送
    精彩文章合集
    文章推荐
    ?【专辑】ARM?【专辑】粉丝问答?【专辑】所有原创?【专辑】linux入门?【专辑】计算机网络?【专辑】Linux驱动?【干货】嵌入式驱动工程师学习路线?【干货】Linux嵌入式所有知识点-思维导图
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表