往期内容
本文章相关专栏往期内容,PCI/PCIe子系统专栏:
- 嵌入式系统的内存访问和总线通信机制解析、PCI/PCIe引入
- 深入解析非桥PCI设备的访问和配置方法
- PCI桥设备的访问方法、软件角度讲解PCIe设备的硬件结构
- 深入解析PCIe设备事务层与配置过程
- PCIe的三种路由方式
- PCI驱动与AXI总线框架解析(RK3399)
Uart子系统专栏:
- 专栏地址:Uart子系统
- Linux内核早期打印机制与RS485通信技术
– 末片,有专栏内容观看顺序interrupt子系统专栏:
- 专栏地址:interrupt子系统
- Linux 链式与层级中断控制器讲解:原理与驱动开发
– 末片,有专栏内容观看顺序pinctrl和gpio子系统专栏:
专栏地址:pinctrl和gpio子系统
编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用
– 末片,有专栏内容观看顺序
input子系统专栏:
- 专栏地址:input子系统
- input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
– 末片,有专栏内容观看顺序I2C子系统专栏:
- 专栏地址:IIC子系统
- 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
– 末篇,有专栏内容观看顺序总线和设备树专栏:
- 专栏地址:总线和设备树
- 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
– 末篇,有专栏内容观看顺序
目录
1.地址空间和寄存器介绍
1.1 地址空间
从PCIe的角度来看,内部总线可寻址资源分为两个地址空间。也就是说,PCIe有两个地址空间。第一个专用于内部寄存器,第二个专用于远程设备。cpu发出的地址信息范围符合这两个地址空间的范围
- 内部寄存器
- Client Register Set:地址范围 0xFD000000~0xFD7FFFFF,比如选择PCIe协议的版本(Gen1/Gen2)、电源控制等
- Core Register Set :地址范围 0xFD800000~0xFDFFFFFF,所谓核心寄存器就是用来进行设置地址映射的寄存器等
- 远程设备
- 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。
远程设备的映射,总大小为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 0:
0xF8000000~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个寄存器:
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的其他信息
注 – 配置空间和寄存器
这提到的两个地址怎么不一样?
Region 0 的地址范围为
0xF8000000 ~ 0xF9FFFFFF
,表示这是 32MB 的内存区域,用于访问外部 PCIe 设备的配置空间。这是地址空间,即主机通过 PCIe 总线访问外部设备时使用的实际物理地址。而在寄存器表中提到的 Region 0 Outbound Config Registers,如
ob_addr0
、ob_addr1
等,则是配置这些设备地址空间的控制寄存器。这个寄存器范围从0x000
开始,表示这是主机内部的配置寄存器的偏移地址。它们在PCIe控制器内部,并用于设置 PCIe 控制器如何管理和访问这些设备的地址空间。区别与联系:
- Region 0 (
0xF8000000 ~ 0xF9FFFFFF
) 是外部设备的实际内存空间,主机可以通过这个范围访问连接的 PCIe 设备。- Outbound Config Registers(例如
ob_addr0
,ob_addr1
,ob_desc0
等)是用于配置和映射这个设备地址空间的控制寄存器。通过这些寄存器,可以指定设备如何响应主机的内存访问请求,或者主机如何通过这些配置寄存器访问PCIe设备。配置流程:
配置阶段:
- 主机首先通过控制器的配置寄存器(如
ob_addr0
、ob_desc0
等)来设置如何映射外部设备的地址空间。这些寄存器通常位于PCIe控制器内部。- 通过这些寄存器,主机可以指定外部设备的基地址、大小、访问方式等信息。也就是说,主机通过这些寄存器告诉PCIe控制器:当我访问某个特定的地址区间时,我希望它指向某个PCIe设备。
访问阶段:
- 一旦配置完成,主机就可以通过特定的地址空间(例如
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_addr0
、ob_desc0
等)来解析这个请求,决定如何处理。
ob_addr0
和ob_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_addr0
、ob_desc0
等)解析和生成TLP。- TLP发送到PCIe外设,执行相应的操作。
- 最终,PCIe设备的响应(如数据读取)通过TLP返回给CPU。
在这个过程中,寄存器起到桥接CPU的地址请求和PCIe外设地址空间的作用。它们负责地址映射和生成正确的TLP,从而实现主机和PCIe设备的通信。
CPU去设置PCIe控制器的Region中的信息,PCIe控制器中的寄存器(肯定是需要先去设置好reg才行)去解析Region中的信息然后发出TPL
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一般用于读写配置空间,它对应的寄存器如下:
1.2.2 用于内存和IO
CPU访问Region 1~32的地址时,将会导致PCIe控制器发出读写内存、IO空间的TLP。
而其寄存器如下:
2.访问示例
2.1 配置空间读写
设置寄存器,才能去解析转化CPU地址信息
要读写设备的配置空间,首先要定位:Bus/Dev/Function/Reg:
怎么发出这些"Bus/Dev/Function/Register"信息?如下图所示:
当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]
使用过程步骤如下:
- 配置Region 0对应的ob_desc寄存器用于读写配置空间
通过设置寄存器ob_desc0来配置要发送的TLP的Header中的Fmt和Type字段,选择配置读/写还是IO读/写等。
- 配置Region 0对应的地址转换寄存器ob_addr
比如我们可以设置bit[5:0]为27,意味着cpu_addr[27:0]这28条地址线都会传入TLP。
- 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地址信息
- 配置Region 1对应的ob_desc0寄存器用于内存读写
通过设置寄存器ob_desc0来配置要发送的TLP的Header中的Fmt和Type字段,选择配置读/写还是IO读/写等。
- 配置Region 1对应的地址转换寄存器ob_addr0/1
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?????