PCIe——配置空间寄存器

每一个PCI设备都有一个256 byte的配置寄存器空间,它分为64 byte的头标区(如下图所示,固定不变)和192 byte 的设备关联区(标准扩展),标准扩展的寄存器组的第一个寄存器中的capabilities pointer字段保存的地址指向下一组标准扩展寄存器的首寄存器。
也就是说从0x100往后的配置空间是IP厂商自己设计,需要在每组扩展寄存器中的第一个寄存器里定义Next Capability offset,该字段保存的地址将指向下一处扩展寄存器组的首寄存器。

一、各类型设备的配置空间

RC设备、桥设备和EP的配置空间略有不同。同时,各家IP厂商的PCIe控制器实现不同,但配置空间寄存器含义均相同。

  • RC设备和桥设备
    在这里插入图片描述

  • EP设备
    在这里插入图片描述

二、通用配置空间

下图所示为所用PCIe设备均配有的寄存器。
在这里插入图片描述

  • Device ID和Vendor ID (只读)
    Vender ID代表PCI设备的生产厂商,Device ID代表这个厂商所生产的具体设备。
    Device ID和Vendor ID是区分不同设备的关键,OS和UEFI在很多时候就是通过匹配他们来找到不同的设备驱动(Class Code有时也起一定作用)。为了保证其唯一性,Vendor ID应当向PCI特别兴趣小组(PCI SIG)申请而得到。

  • BAR寄存器

PCIe配置空间中从地址0x10开始的6个寄存器(EP)或2个寄存器(RC),用于存储pcie设备在PCIe域的基地址、基址空间大小等属性。

这些PCI总线地址空间需要在初始化时就映射为存储器域的存储器地址空间,方便处理器访问。系统软件对PCI总线进行配置时,首先获得BAR寄存器的初始化信息,之后根据处理器系统的配置,将合理的基地址写入到响应的BAR寄存器中,这个过程在BIOS运行阶段和OS启动阶段完成。系统软件还可以使用该寄存器获得PCI设备使用的BAR空间的长度,其方法是向BAR寄存器写入0xFFFF_FFFF后在读取该寄存器。

每个PCI设备在BAR中描述自己需要占用多少地址空间,BIOS或者OS通过所有设备的这些信息构建一张完整的关系图,描述系统中资源的分配//情况,然后在合理的将地址空间配置给每个PCI设备。
BAR在bit0来表示该设备是映射到memory还是IO,bar的bit0是readonly的,也就是说,设备寄存器是映射到memory还是IO是由设备制造商决定的,其他人无法修改。
参考链接:BAR寄存器初始化

  • Revision ID和Class Code寄存器 (只读)
    Revision ID记载PCI设备的版本号,可以被认为是Device ID寄存器的扩展
    Class Code寄存器记载PCI设备的分类,用于系统软件识别当前PCI设备的分类。该寄存器由Base Class Code、Sub class code和interface三个字段组成。Base Class Code将PCI设备分类为显卡、网卡、PCI桥等设备;Sub class code对这些设备进一步细分;inteface定义为编程接口。

  • Header Type寄存器(只读)
    共8bit。系统软件使用该寄存器区分不同类型的PCI配置空间。

  1. 第7位为1表示当前PCI设备为多功能设备、为0表示单功能设备
  2. 第6-0位表示当前配置空间的类型,为0表示该设备使用PCI Agent设备的配置空间,普通PCI设备都使用该配置头; 为1表示使用PCI桥的配置空间,PCI桥使用这种配置头;为2表示使用cardbus桥片的配置空间。
  • cache line size
    记录host处理器使用的cache line长度

  • Capabilities pointer
    该寄存器存放capabilites寄存器组的基地址,该寄存器组存放域PCI设备相关的扩展配置信息。该寄存器对PCI设备可选,但PCIe总线规范要求其设备必须支持Capabilities结构,该寄存器组用于实现厂商自定义的PCIe设备功能。
    该寄存器存放Capabilities结构链表的头指针。在一个PCIe设备中,可能含有多个Capability结构,这些寄存器组成一个链表,其结构如图:

  • interript line寄存器
    记录当前PCI设备使用的中断向量号,在系统软件对该设备配置时写入。

  • Interrupt pin
    这个寄存器保存PCI设备使用的中断引脚。PCI 总线提供了四个中断引脚: INTA#、 INTB#、
    INTC#和INTD#。InterruptPin寄存器为1时表示使用INTA#引脚向中断控制器提交中断请
    求,为2表示使用INTB#, 为3表示使用INTC#, 为4表示使用INTD#。
    如果PCI设备只有一个子设备时,该设备只能使用INTA#;如果有多个子设备时,可以使用INTB ~ D#信号。如果PCI设备不使用这些中断引脚,向处理器提交中断请求时,该寄
    存器的值必须为0。值得注意的是,虽然在PCIe设备中并不含有INTA ~ D#信号,但是依然
    可以使用该寄存器,因为PCle设备可以使用INTx中断消息,模拟PCI设备的INTA ~ D#信号

三、RC设备特有配置寄存器

如下图所示是RC设备的配置空间:

在这里插入图片描述

  • Subsystem ID和Subsystem Vender ID寄存器
    与Device iD和Vender ID类似,是这二者的补充
  • Expansion ROM base address
    有些PCI设备在CPU未运行OS前,就需要完成基本的初始化设置,如显卡、键盘、硬盘等。为此,PCI设备需要提供一段ROM程序,而处理器在boot阶段运行这段程序从而初始化这些设备,该寄存器即记载ROM程序的基地址。

PCIe的各种特性如Max Payload、Complete Timeout(CTO)等等都通过这个链表链接在一起,Capabilities ID由PCIe spec规定。链表的好处是如果你不关心这个Capabilities(或不知道怎么处理),直接跳过,处理关心的即可,兼容性比较好。另外扩展性也强,新加的功能不会固定放在某个位置,淘汰的功能删掉即好。

三、扩展配置空间

### CPU 访问 PCIe 设备私有寄存器的方法 对于PCIe设备而言,CPU访问这些设备上的私有寄存器通常依赖于内存映射I/O (MMIO, Memory Mapped I/O),即所谓的Memory-Mapped Accesses[^1]。具体来说,在Linux操作系统环境下,这种访问方式主要通过`ioremap()`函数来实现物理地址到虚拟地址的映射。 当需要操作特定PCIe设备内部的配置空间或BAR(Base Address Register)指向的资源时,先利用`pci_resource_start()`获取该设备某个BAR对应的起始物理地址,再调用`ioremap()`将其映射至进程可直接寻址的空间内: ```c #include <linux/io.h> #include <linux/pci.h> // 假设已获得 pci_dev 结构体指针 dev 和要访问的 BAR 编号 bar_index unsigned long phys_addr = pci_resource_start(dev, bar_index); void __iomem *virt_addr; if (!phys_addr || !pci_resource_len(dev, bar_index)) { printk(KERN_ERR "Invalid BAR\n"); } else { virt_addr = ioremap(phys_addr, pci_resource_len(dev, bar_index)); } ``` 一旦完成了上述映射工作,则可以通过诸如`readl()`, `writel()`这样的辅助宏来进行实际的数据交换动作,从而达到读取或设置目标寄存器的目的: ```c u32 reg_value; reg_value = readl(virt_addr + offset); // 读取指定偏移处的寄存器值 writel(new_value, virt_addr + offset); // 向指定偏移位置写入新数值 ``` 值得注意的是,这里提到的操作均需在具备适当权限的情况下执行,比如处于内核模式下的驱动程序环境中。此外,完成所有必要的交互之后应当记得释放之前建立起来的映射关系以节省系统资源——这可通过调用`iounmap()`来达成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KGback

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值