深入解析PCIe地址空间与寄存器机制:从地址映射到TLP生成的完整流程

发布于:2024-12-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

往期内容

本文章相关专栏往期内容,PCI/PCIe子系统专栏:

  1. 嵌入式系统的内存访问和总线通信机制解析、PCI/PCIe引入
  2. 深入解析非桥PCI设备的访问和配置方法
  3. PCI桥设备的访问方法、软件角度讲解PCIe设备的硬件结构
  4. 深入解析PCIe设备事务层与配置过程
  5. PCIe的三种路由方式
  6. PCI驱动与AXI总线框架解析(RK3399)

Uart子系统专栏:

  1. 专栏地址:Uart子系统
  2. Linux内核早期打印机制与RS485通信技术
    – 末片,有专栏内容观看顺序

interrupt子系统专栏:

  1. 专栏地址:interrupt子系统
  2. Linux 链式与层级中断控制器讲解:原理与驱动开发
    – 末片,有专栏内容观看顺序

pinctrl和gpio子系统专栏:

  1. 专栏地址:pinctrl和gpio子系统

  2. 编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用

    – 末片,有专栏内容观看顺序

input子系统专栏:

  1. 专栏地址:input子系统
  2. input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
    – 末片,有专栏内容观看顺序

I2C子系统专栏:

  1. 专栏地址:IIC子系统
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有专栏内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有专栏内容观看顺序

img

1.地址空间和寄存器介绍

1.1 地址空间

从PCIe的角度来看,内部总线可寻址资源分为两个地址空间。也就是说,PCIe有两个地址空间。第一个专用于内部寄存器,第二个专用于远程设备。cpu发出的地址信息范围符合这两个地址空间的范围

  1. 内部寄存器
  • Client Register Set:地址范围 0xFD000000~0xFD7FFFFF,比如选择PCIe协议的版本(Gen1/Gen2)、电源控制等
  • Core Register Set :地址范围 0xFD800000~0xFDFFFFFF,所谓核心寄存器就是用来进行设置地址映射的寄存器等
  • img
  1. 远程设备
  • Region 0:0xF8000000~0xF9FFFFFF , 32MB,用于访问外接的PCIe设备的配置空间
  • Region 1:0xFA000000~0xFA0FFFFF,1MB,用于地址转换
  • Region 2:0xFA100000~0xFA1FFFFF,1MB,用于地址转换
  • ……
  • Region 32:0xFBF00000~0xFBFFFFFF,1MB,用于地址转换
  • 其中Region 0大小为32MB,Region1~31大小分别为1MB。
  • img

远程设备的映射,总大小为64
MB,基址为0xF8000000,共分为33个区域,以下方程式描述了如何选择0到32的区域。区域0由A[RW]ADDR[25]==0解码,区域大小为32M字节。当A[RW]ADDR[25]==1时,区域1至32(配置的区域数量)由区域x=A[RW]ADDR[24:20]+1解码:

  • Region 00xF8000000~0xF9FFFFFF,大小为32 MB。

  • Region 1 到 32:每个大小为1 MB,共32个区域。

  • 区域0通过 A[RW]ADDR[25] == 0 来选择,表示当 A[RW]ADDR 的第25位为0时,选择 Region 0,其大小为 32 MB。

  • A[RW]ADDR[25] == 1 时,选择 Region 1 到 32,这些区域通过以下公式选择:

    • 区域 x = A[RW]ADDR[24:20] + 1

注意:A[RW]ADDR[25] 是地址信号的第 25 位, A[RW] 是区分读地址的信号还是写地址的信号

这些Region存在于PCIe控制器内部,并且是由控制器通过寄存器进行管理和配置的。

  • Region 的作用:

    • 每个 Region 代表了PCIe控制器中用于访问外部PCIe设备的一部分地址空间。
    • CPU发出的地址如果在某个 Region 内,那么这个地址会被映射到相应的PCIe外设上。
  • Region 是在 PCIe 控制器内部管理的:

    • 这些Region并不直接对应外设,而是PCIe控制器内部用来定义访问哪些外设,或者配置空间的一个机制。通过这些Region,PCIe控制器可以管理和调度外部PCIe设备的访问。
    • PCIe控制器通过寄存器(如ob_addr0, ob_desc0等)来描述和管理这些Region。寄存器会包含如何将主机的地址转换为PCIe TLP,以及如何访问外设。
  • 外部设备的配置空间:

    • 例如,Region 0 可能用于外部PCIe设备的配置空间,允许主机通过该Region访问PCIe设备的配置寄存器。
    • Region 1~32 可能用于外设的内存访问(例如设备的BAR空间)。

CPU访问Region 0的地址时,将会导致PCIe控制器发出读写配置空间的TLP。

CPU访问Region 1~32的地址时,将会导致PCIe控制器发出读写内存、IO空间的TLP。

1.2 寄存器介绍

CPU访问一个地址,导致PCIe控制器发出TLP。TLP里含有PCIe地址、其他信息。

这些寄存器必定涉及这2部分:

  • 地址转换:把CPU地址转换为PCIe地址
  • 提供TLP的其他信息

Region0、Region1~32,每个Region都有类似的寄存器。

一个Region,可以用于读写配置空间,可以用于读写内存空间、可以用于读写IO空间,还可以用于读写消息。

这由Region对应的寄存器决定。

每个Region都有一样寄存器,以Region 0为例,有6个寄存器:

img

CPU访问某个Region时,它是想干嘛?

  • 读写配置空间、发出对应TLP?
  • 读写内存空间、发出对应TLP?
  • 读写IO空间、发出对应TLP?
  • 读写消息、发出对应TLP?

到底是发出哪种TLP,由Region对应的ob _desc0寄存器决定:

ob_desc0[3:0] 作用
1010 发出的TLP用于访问Type 0的配置空间
1011 发出的TLP用于访问Type 1的配置空间
0010 发出的TLP用于读写内存空间
0110 发出的TLP用于读写IO空间
1100 发出的TLP是"Normal Message"
1101 发出的TLP是"Vendor-Defined Message"

CPU访问某个Region时,最终都是要发出TLP,TLP的内容怎么确定?

  • 地址信息:ob_addr0/1把CPU地址转换为PCIe地址,提供TLP里面的地址信息
  • 其他信息:ob_desc0/1/2/3提供TLP的其他信息

注 – 配置空间和寄存器

img

img

这提到的两个地址怎么不一样?

Region 0 的地址范围为 0xF8000000 ~ 0xF9FFFFFF,表示这是 32MB 的内存区域,用于访问外部 PCIe 设备的配置空间。这是地址空间,即主机通过 PCIe 总线访问外部设备时使用的实际物理地址。

而在寄存器表中提到的 Region 0 Outbound Config Registers,如
ob_addr0ob_addr1 等,则是配置这些设备地址空间的控制寄存器。这个寄存器范围从 0x000
开始,表示这是主机内部的配置寄存器的偏移地址。它们在PCIe控制器内部,并用于设置 PCIe 控制器如何管理和访问这些设备的地址空间。

区别与联系:

  • Region 0 (0xF8000000 ~ 0xF9FFFFFF) 是外部设备的实际内存空间,主机可以通过这个范围访问连接的 PCIe 设备。
  • Outbound Config Registers(例如 ob_addr0, ob_addr1, ob_desc0 等)是用于配置和映射这个设备地址空间的控制寄存器。通过这些寄存器,可以指定设备如何响应主机的内存访问请求,或者主机如何通过这些配置寄存器访问PCIe设备。

配置流程:

  1. 配置阶段

    • 主机首先通过控制器的配置寄存器(如 ob_addr0ob_desc0 等)来设置如何映射外部设备的地址空间。这些寄存器通常位于PCIe控制器内部。
    • 通过这些寄存器,主机可以指定外部设备的基地址、大小、访问方式等信息。也就是说,主机通过这些寄存器告诉PCIe控制器:当我访问某个特定的地址区间时,我希望它指向某个PCIe设备。
  2. 访问阶段

    • 一旦配置完成,主机就可以通过特定的地址空间(例如 0xF8000000 ~ 0xF9FFFFFF 这样的外部设备地址空间)访问外部PCIe设备。
    • 当主机试图访问这些地址空间时,PCIe控制器会根据配置寄存器中的设置,将这些访问请求转换并转发给实际的外部PCIe设备。

举例说明:

  • 配置阶段:主机通过 ob_addr0 等寄存器,将控制器配置空间的0xF8000000 (Region0)这一段内存映射到某个外部 PCIe 设备的寄存器或内存空间。例如,0xF8000000 可以映射到一个 PCIe
    网络卡的配置空间。
  • 访问阶段:当主机读取或写入 0xF8000000 这一地址时,实际上是在和这个 PCIe 网络卡进行通信。PCIe控制器负责将这个请求转发给外部设备,并将设备的响应返回给主机。

疑问: CPU发出地址信息,落在PCIe控制器对应的地址空间上,地址空间对应的(比如Region0)PCIe控制器中的寄存器根据region0中的信息去解析,然后寄存器就发出TLP到对应的PCIe外设,去设置/访问PCIe外设的配置空间(地址空间) ???

1. CPU发出地址请求:

CPU发出一个内存读写请求(可能是访问PCIe外设的配置空间或内存空间),该请求的地址落入了PCIe控制器的地址空间。例如,落在了提到的Region
0
(用于PCIe设备的配置空间),设置了里面Region0空间里面的信息。

2. 地址请求到达PCIe控制器:

该地址请求进入了PCIe控制器。在PCIe控制器中,每个Region(如Region 0、Region
1等)通过对应的寄存器(例如ob_addr0ob_desc0等)来解析这个请求,决定如何处理。

  • ob_addr0ob_addr1 寄存器定义了CPU AXI地址如何映射到PCIe设备的地址空间。
  • desc0等描述符寄存器包含了如何生成PCIe TLP的具体信息,尤其是TLP头部的信息。

3. 寄存器生成TLP:

PCIe控制器通过这些寄存器,生成TLP(Transaction Layer
Packet)
。TLP是PCIe协议中用于通信的基本单位,负责在主机和PCIe设备之间传递读写请求、配置请求等。

  • 例如,当CPU需要访问某个PCIe外设的配置空间时,PCIe控制器根据Region 0的寄存器配置,生成一个配置写配置读的TLP。

4. TLP发送到PCIe外设:

生成的TLP包通过PCIe链路发送到目标PCIe设备。TLP包会包含地址、操作类型(读/写)、数据等信息。

5. PCIe外设接收并执行:

PCIe设备接收到TLP包后,会根据TLP中的地址和操作类型,执行相应的操作。例如:

  • 如果是写操作,PCIe设备会更新其配置空间或内存。
  • 如果是读操作,PCIe设备会返回读取的数据。

6. PCIe设备的响应:

如果是读请求,PCIe设备会返回相应的读响应TLP。PCIe控制器接收到这个响应后,会将结果转换为主机能够理解的形式,并返回给CPU。

大概:

  • CPU发出的请求首先到达PCIe控制器。
  • PCIe控制器通过配置寄存器(如ob_addr0ob_desc0等)解析和生成TLP。
  • TLP发送到PCIe外设,执行相应的操作。
  • 最终,PCIe设备的响应(如数据读取)通过TLP返回给CPU。

在这个过程中,寄存器起到桥接CPU的地址请求和PCIe外设地址空间的作用。它们负责地址映射和生成正确的TLP,从而实现主机和PCIe设备的通信。

CPU去设置PCIe控制器的Region中的信息,PCIe控制器中的寄存器(肯定是需要先去设置好reg才行)去解析Region中的信息然后发出TPL

img

PCIe设备内部的配置空间和寄存器的关系???

配置空间是PCIe设备中访问寄存器的一种机制和入口。它定义了设备的控制和状态寄存器的位置和访问方式。**可以把配置空间看作是设备寄存器访问的"目录",而寄存器是具体的操作和状态信息。**寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。

  • 配置空间中的寄存器:在PCIe配置空间中,有专门的控制和状态寄存器,这些寄存器的内容定义了设备的行为。例如:

    • Command Register:用于启用或禁用I/O空间、内存空间、总线主设备等功能。
    • Status Register:反映设备的状态,例如中断、总线错误等。
  • 基址寄存器(BAR,Base Address Registers):配置空间中的基址寄存器(BARs)定义了PCIe设备的寄存器(或内存)映射。它指定了设备的内存空间或I/O空间在系统地址空间中的位置。这些寄存器address告诉操作系统设备的寄存器或数据缓冲区在哪个地址范围,从而可以进行内存映射I/O(MMIO)或端口映射I/O操作。

    • 内存映射寄存器:通过配置空间中的BAR,设备的寄存器可以映射到系统的内存地址空间,主机可以通过对这些内存地址进行读写来操作设备的寄存器。

1.2.1 用于配置空间

CPU访问Region 0的地址时,将会导致PCIe控制器发出读写配置空间的TLP。

Region0一般用于读写配置空间,它对应的寄存器如下:

img

1.2.2 用于内存和IO

CPU访问Region 1~32的地址时,将会导致PCIe控制器发出读写内存、IO空间的TLP。

而其寄存器如下:

img

2.访问示例

2.1 配置空间读写

设置寄存器,才能去解析转化CPU地址信息

要读写设备的配置空间,首先要定位:Bus/Dev/Function/Reg:

img

怎么发出这些"Bus/Dev/Function/Register"信息?如下图所示:

img

img

当Region 0的寄存器ob_desc0[3:0]被配置为读写配置空间时, CPU发出Region 0的地址,地址里面隐含有这些信息:

  • Bus:cpu_addr[27:20]
  • Dev:cpu_addr[19:15]
  • Fun:cpu_addr[14:12]
  • Reg:cpu_addr[11:0]

使用过程步骤如下:

  1. 配置Region 0对应的ob_desc寄存器用于读写配置空间

通过设置寄存器ob_desc0来配置要发送的TLP的Header中的Fmt和Type字段,选择配置读/写还是IO读/写等。

img

  1. 配置Region 0对应的地址转换寄存器ob_addr

比如我们可以设置bit[5:0]为27,意味着cpu_addr[27:0]这28条地址线都会传入TLP。

img

  1. CPU读写Region 0的地址

Region 0的地址范围是:0xF8000000~0xF9FFFFFF。

CPU想访问这个设备:Bus=bus,Dev=dev,Fun=fun,Reg=reg,那么CPU读写这个地址即可:

0xF8000000 + (bus<<20) | (dev<<15) | (fun<<12) | (reg)

(bus<<20) | (dev<<15) | (fun<<12) | (reg)就可以组成cpu发出的addr_cpu

2.2 MEM/IO读写示例

设置寄存器,才能去解析转化CPU地址信息

img

  1. 配置Region 1对应的ob_desc0寄存器用于内存读写

通过设置寄存器ob_desc0来配置要发送的TLP的Header中的Fmt和Type字段,选择配置读/写还是IO读/写等。

img

  1. 配置Region 1对应的地址转换寄存器ob_addr0/1

img

addr0、addr1寄存器里保存的是PCIe地址,也就是CPU发出这个Region的CPU地址后,将会转换为某个PCI地址。

怎么转换?由addr0、addr1决定。

Region 1的CPU地址范围是:0xFA000000~0xFA0FFFFF,是1M空间。

我们一般会让PCI地址等于CPU地址,所以这样设置:

  • addr0:

    • [5:0]等于19,表示CPU_ADDR[19:0]共20位地址传入TLP
    • [31:8]等于0xFA0000
  • addr1:设置为0

如上设置后,CPU读写地址时0xFA0???,就会转换为PCI地址:0xFA0???,转换过程如下:

pci_addr = cpu_addr[19:0] | (addr0[31:20] << 20) | (addr1<<32)
         = 0x????? + (0xFA0 << 20) | (0 << 32)
         = 0xFA0?????