备注

只注重架构以及软件的实现, 其余内部的具体信号还有其他的引脚不会记录

PCI的结构

image-20220914095228114

如图所示, PCI总线的相关模块包括:

  • Host主桥
  • PCI总线
  • PCI桥
  • PCI设备

整个PCI架构以一个树状结构呈现, Host主桥用于连接CPU与PCI设备, PCI桥用于拓展PCI设备(树中的非叶子节点), PCI总线代表树中的枝, PCI设备则代表树中的所有节点(下面的定义中, PCI桥也是PCI设备的一部分,只是他是用于拓展其他PCI设备用的).

Host主桥

Host主桥的主要功能有:

  • 隔离处理器系统的存储器域与PCI总线域, 一个Host主桥对应一个PCI总线域
  • 管理PCI总线域
  • 完成处理器与PCI设备之间的数据交换

PCI总线

PCI总线由HOST主桥以及PCI桥进行管理, 用来连接各种设备

PCI设备

PCI总线中一共有三种设备:

  • PCI主设备, 可以通过总线仲裁获得PCI总线的使用权, 主动发出请求
  • PCI从设备, 只能被动接受来自HOST主桥或者其他PCI设备的读写请求
  • 桥设备, 用于管理下游的PCI总线, 即为PCI桥

PCI总线规范中将PCI主从设备统称为PCI Agent设备, 比如常见的网卡, 显卡, 声卡.

PCI设备配置

PCI设备配置空间

PCI设备头部的64个字节为固定的, 所有PCI设备必须支持, 具体定义如下图:

PCI设备配置空间

  1. Device ID和 Vendor ID: Vendor ID代表PCI设备的生产厂商, Device ID代表厂商生产的具体设备
  2. Status: 大多数为只读, 保存PCI设备状态
  3. Command: PCI设备的命令寄存器, 初始化设为0
  4. Class Code和 Revision ID: 只读寄存器, 其中后者记录的是PCI设备的版本号, 可以认为是Device ID的拓展, Class Code貌似在其他情况下也是用于区分唯一设备的
  5. Header Type: 只读, 由8位组成:
    • 第7位代表PCI设备为多功能设备, 0为单功能设备
    • 第6~0为标识当前配置空间的类型, 0代表为PCI Agent设备的配置空间, 1代表PCI桥的配置空间(两者不太一样), 2代表Cardbus桥片的配置空间
  6. Latency Timer: 用于控制PCI设备占用PCI总线的时间, 当PCI设备获得总线使用权后, 这个寄存器会递减, 归零后取消使用权
  7. Cache Line Size: 记录处理器使用的Cache行长. 在 PCI 总线中和 Cache 相关的总线事务,如 存储器写并无效和 Cache 多行读等总线事务需要使用这个寄存器, 由软件写入, 硬件逻辑使用
  8. Subsystem ID 和 Subsystem Vendor ID: 与Device ID和Vendor ID类似, 记录PCI设备的生产厂商和设备名称
  9. Expansion ROM base address: 用于存储在运行操作系统前, 进行基本初始化的程序代码基地址(存储在ROM中)
  10. Capabilities Pointer: PCI可选, PCIe必须支持, 用于存储Capabilities寄存器组的基地址, PCI设备使用Capabilities 寄存器组存放一些与PCI设备相关的扩展配置信息
  11. Interrupt Line(右下角, 被logo挡住了): 软件对PCI设备配置时写入, 记录当前PCI设备使用的中断向量号, 设备驱动程序可以通过该寄存器注册服务到操作系统中
  12. Interrupt Pin: 保存PCI设备使用的中断引脚, 1-4 分别使用 INTA#, INTB#, INTC#, INTD#引脚, 不使用则设为0
  13. Base Address Register(BAR) 0~5: 保存PCI设备使用的地址空间基地址, 保存的是设备在PCI总线域中的地址, PCI设备复位后, 每个PCI设备在BAR中描述自己需要占用多少地址空间, 这段空间是IO空间还是存储器空间, 软件之后可以通过向VAR寄存器写入全1, 读取该寄存器, 清空特殊编码值, 取反并加1得到需要占用的地址空间大小.
  14. Capabilities Pointer: 如果status中启用了capabilities, 代表这个设备使用了64~256字节之间的拓展配置空间, pointer指向的是下一个capability的偏移(在这个地址空间内)

以下图片分别为Memory和IO下的BAR寄存器结构:

Memory

IO

status各字段含义:

img

PCI桥配置空间

img

与PCI设备配置空间差不多, 对于BAR01, 这两组的含义与上面的一致, 只是这两组寄存器为可选寄存器, 大多数PCI桥不存在私有寄存器, 操作系统也不需要为PCI桥提供专门的驱动程序, 如果PCI桥不存在私有空间, 则BAR01被设为0, 其他特有寄存器如下:

  • Subordinate Bus Number、Secondary Bus Number和Primary Bus Number: 第一个代表桥下面管理的编号最大的PCI总线, 第二个代表最小的PCI总线号, 最后一个代表PCI桥上游的总线号, 一个PCI桥能管理的PCI总线号范围即为Secondary Bus Number~Subordinate Bus Number, 他们三者初始化时均为0, 可提供过这个来判断是否被配置过
  • I/O Limit和I/O Base: 用于存放PCI子树中所有设备使用的IO地址空间集合的基地址(后者)和大小(前者)
  • Memory Limit和Memory Base: 用于存放PCI子树中所有设备使用的存储空间集合的基地址(后者)和大小(前者)
  • Prefetchable Memory Limit和Prefetchable Memory Base: 如果管理的PCI设备支持预读, 则需要从PCI桥的可预读空间中获取地址空间. 这两个寄存器分别存放PCI设备使用的可预读存储器空间的基地址和大小.

PCI总线树初始化

PCI总线树中, 有多少个PCI桥(包括HOST主桥), 就有多少条总线. 软件遍历时需要对PCI总线进行编号, 初始化PCI桥的Primary、Secondary和Subordinate Bus Number寄存器, 下图是DFS后, 总线号分配情况以及桥上总线相关寄存器的初始化情况:

img

配置PCI设备

x86定义了两个IO端口寄存器, 分别为CONFIG_ADDRESS和CONFIG_DATA寄存器, 地址分别为0xCF8和0xCFC, 利用这两个IO端口即可访问PCI设备的配置空间, 其中CONFIG_ADDRESS寄存器存放PCI设备的ID号, CONFIG_DATA寄存器存放配置的读写数据. CONFIG_ADDRESS寄存器结构如下:

img

  • Enable bit: 该位为1时, 对CONFIG_DATA寄存器进行读写会引发PCI总线的配置周期
  • Bus Number: PCI设备的总线号
  • Device Number: PCI设备的设备号
  • Function Number: PCI设备的功能号
  • Register Number: PCI设备的寄存器号

当CONFIG_ADDRESS中的Enable为1时, 对CONFIG_DATA寄存器进行的IO读写访问会由Host主桥转化为PCI配置读写总线事务, 然后发送到PCI总线上, 之后会根据CONFIG_ADDRESS中的总线号, 设备号, 功能号, 寄存器号完成对特定设备的指定寄存器读写

通过总线号, 设备号, 功能号能找到一个特定的PCI设备, 相当于一个特定PCI设备的身份证号, 寄存器号则代表配置空间寄存器偏移量, 比如0就代表不偏移, 4就代表偏移4bytes

PCIe 扩展了配置空间的大小, 最大到4K, 具体如下:

img

  • 0~3Fh位基本配置空间大小, 就是上面说的64字节
  • PCI Express Capability Structure ,PCI可选支持,PCIe支持
  • PCI Express Extended Capability Structure,PCI不支持,PCIe支持

为了访问>256字节的空间, 支持通过MMIO的方式访问配置空间, 但为了兼容性, 保留了IO访问方式, 最后附上PCIe设备的空间:

img

PCI设备读写

配置好PCI设备的BAR寄存器后, 即可通过BAR空间地址对PCI设备进行读写操作, 具体如下:

img

参考文档或链接

  1. PCI Express体系结构导读
  2. 深入PCI与PCIe之二:软件篇
  3. PCI入门