本文章致力于对PCI相关知识做一个普及。
另外推荐一个PCIe讲解的不错的系列博客:http://blog.chinaaet.com/justlxy/p/5100061871
1. x86处理器系统初步认识
1.1 x86主板布局
PCI是Peripheral Component Interconnect(外设部件互连标准)的缩写,本文介绍的是x86处理器系统下PCI相关知识。首先来看一张x86主板图:
如上图所示,靠近CPU的是北桥芯片(North Bridge),北桥芯片中集成了内存控制器、PCI控制器等,主要负责和高速设备通信,如内存、显卡等。在北桥芯片附近的是南桥芯片(South Bridge),主要负责和一些低速外设之间的通信。从图中还可以看出,内存插槽和PCI插槽都是靠近北桥芯片的,目的就是减少这些插槽到北桥芯片之间的布线长度,从而降低北桥芯片和这些设备的通信延迟。
1.2 芯片组系统框架
如上图所示,intel 82915P是一款典型的北桥芯片,intel ICH6x是南桥芯片,这两款芯片加在一起被称为i915P芯片组。从中看出,某个芯片组其实就是以北桥芯片的型号来命名的。从图中可以了解到以下信息:
- CPU和北桥之间通过前端总线FSB通信
- 北桥负责和显卡、内存设备通信
- 南桥负责和低速外设通信,如USB设备、硬盘等
- 南桥不能直接和CPU通信,必须经过DMI和北桥通信,然后统一由北桥和CPU通信
上图的这种的CPU、北桥、南桥架构是10几年前的经典架构,目前北桥的大部分功能已经被整合进处理器中,如内存控制器和PCIe控制器。在intel某些型号的处理器中,南桥也被整合进处理器中,从而主板上只剩下处理器一颗芯片,更类似于ARM处理器系统中的架构。
2. x86的几个地址空间
2.1 CPU地址空间
CPU地址空间是指CPU所能寻址的空间大小,比如对于32位CPU来说,其所能寻址的空间大小为0~4G。这是由CPU自身的地址总线数目决定的。这段空间也被称作CPU物理地址空间。
2.2 内存地址空间
内存地址空间就是指内存控制器所能寻址的空间大小。在x86处理器系统中,内存地址空间是CPU地址空间的一部分。但是在32位x86系统中,并不是所有的内存地址空间都能被系统使用,如下图:
如图所示,左侧0~4G为CPU所能寻址的地址空间,红框内的空间是能被CPU识别的内存地址空间。右侧为内存控制器所能寻址的空间大小为4GB,但是只有一部分空间能被CPU所识别。这就是为什么4GB内存在32位x86系统只能被识别为3G多的原因,因为剩下的CPU地址空间被其他设备占用了,比如PCI设备、Local APIC或者IO APIC等,这些CPU地址空间对应的是这些设备的寄存器区域。
2.3 设备的内存映射空间
注意,这里的设备内存映射空间是指硬件机制上实现的设备寄存器访问方式映射,而不是指MMU中虚拟地址到物理地址的映射。在上图中,我们可以得知,CPU通过CPU地址空间中的内存地址空间能访问内存设备,大概的访问流程如下:
CPU发送相关信号给北桥,告知其要访问内存,然后北桥通过内存控制器来访问内存设备。在汇编指令级别,这是通过MOV指令实现的,比如MOV eax,[mem addr],就能触发北桥通过内存控制器访问内存。这是CPU访问内存设备的情况,那么CPU如果要访问别的设备呢,比如要访问PCI设备,这是如何实现的?
从第2小节的图中可以看出,在CPU地址空间中有一段空间叫做PCI Memory Range,这段空间也叫做PCI设备寄存器映射区。CPU通过访问这段空间,从而达到访问PCI设备的目的。由于这种方式同样是通过MOV指令实现的,故这种方式被称为内存映射访问,意思就是CPU能通过像访问内存那样的方式来访问这些设备。注意,CPU访问CPU地址空间上的设备都是通过MOV指令来实现的。CPU访问PCI设备的大概流程如下:
CPU发送相关信号给北桥,告知其要访问PCI设备,然后北桥通过PCI控制器来访问PCI设备中的寄存器。
2.4 端口地址空间
端口地址访问是x86处理器系统中另外一种访问设备的方式,不同于CPU地址空间,端口地址空间只有64KB大小,为0~65535,这是因为访问端口时必须将端口地址放入dx寄存器,dx寄存器是16位的,故端口地址空间只有64KB大小。如下图:
端口其实就是设备中的寄存器,只不过访问这些寄存器并不是通过MOV指令来访问,而是通过IN/OUT指令来访问。比如在北桥中有一个端口,其地址为0xCF8,访问这个端口流程如下:
CPU发送相关信号给北桥,告知其要访问0xCF8端口,然后北桥发现这个端口是属于自己的,就直接访问。
2.5 x86处理器系统中设备驱动编写注意
在PPC和ARM处理器系统中,外设的寄存器都是被映射到CPU地址空间上的,所以访问这些外设寄存器通过readl、writel等系统接口就可以,因为readl、writel这些系统接口最终都会被翻译为MOV指令。但是在x86处理器系统中,还存在着通过端口地址空间访问设备的情况,如果一个设备的寄存器是通过端口地址来访问的,那么就需要用in8、out8等这些系统接口,因为这些系统接口最终会被翻译为IN/OUT指令。所以在为x86处理器系统下的设备编写驱动时,一定要搞清楚设备的寄存器是通过CPU地址空间来访问的还是通过端口地址来访问的。比如显卡设备,有的寄存器是通过CPU地址空间来访问的,有的寄存器是通过端口地址来访问的,这时候访问这些不同方式的寄存器就需要使用不同的系统接口。
3. PCI的基础知识
3.1 PCI总线组成结构
在x86处理器系统中,每一个PCI设备在系统中的ID都是唯一的,PCI设备的ID由Bus Number、Device Number、Function Number、Register Number组成。
- Bus Number:标明PCI设备是在哪条PCI总线上,有几个PCI桥设备就有几条PCI总线
- Device Number:标明在同一PCI总线下的哪个PCI设备
- Function Number:一个PCI设备最多可以有8个逻辑功能,但是大多数PCI设备只有一个功能
- Register Number:标明PCI设备配置空间寄存器号
3.2 HOST主桥
HOST主桥是一个很特殊的桥片,其主要功能是隔离处理器系统的CPU地址空间和PCI地址空间。PCI设备具有独立的地址空间,即PCI总线地址空间,该空间与CPU地址空间通过HOST主桥隔离。处理器需要通过HOST主桥才能访问PCI设备,而PCI设备需要通过HOST主桥才能访问CPU地址空间。在x86处理器系统中,HOST主桥在北桥中。
3.3 PCI地址空间
PCI地址空间就是能被HOST主桥识别的PCI地址范围,如果HOST主桥能识别32位PCI地址,那么PCI地址空间就为0~4G。注意,虽然这个PCI地址空间大小和CPU地址空间一样,但是PCI地址空间和CPU地址空间是两种东西!
下图所示的是PCI设备的引脚图,如图所示,PCI设备的数据引脚和地址引脚是复用的,有32位和64位两种。
3.4 PCI总线事务
总线的基本任务是实现数据传送,PCI总线使用地址译码的方式进行数据传递,而使用ID译码方式进行配置信息的传递。其中地址译码方式使用PCI设备的地址信号,而ID译码方式使用PCI设备的ID号,包括Bus Number、Device Number、Function Number、Register Number。ID译码方式只在对PCI设备的配置空间读写的时候才会用到,也就是说,对PCI设备的配置空间读写并不会用到PCI设备的地址线信号。
4. PCI设备的配置空间
4.1 基本介绍
其实PCI设备的配置空间就是配置PCI设备的那些寄存器集合。PCI通常将PCI配置信息存放在E2PROM中,PCI设备上电初始化时,将E2PROM中的信息读到PCI设备的配置空间中作为初始值。这个过程由硬件逻辑完成,绝大多数PCI设备使用这种方式初始化其配置空间。系统加电时,BIOS检测PCI总线,确定所有连接在PCI总线上的设备以及它们的配置要求,并进行系统配置。所以,所有的PCI设备必须实现配置空间,从而能够实现参数的自动配置。
PCI总线规范定义的配置空间总长度为256个字节,配置信息按一定的顺序和大小依次存放。前64个字节的配置空间称为配置头,对于所有的设备都一样,配置头的主要功能是用来识别设备、定义主机访问PCI卡的方式(I/O访问或者存储器访问,还有中断信息)。其余的192个字节称为本地配置空间,主要定义卡上局部总线的特性、本地空间基地址及范围等。
一般我们需要关心配置空间中配置头的信息。
4.2 配置空间中配置头信息
配置头寄存器信息如下:
- Device ID和Vendor ID寄存器,这两个寄存器的值由PCISIG分配,只读。其中Vendor ID代表PCI设备的生产厂商,而Device ID代表这个厂商所生产的具体设备。如intel公司的基于82571EB芯片的系列网卡,其Vendor ID为0x8086,而Device ID为0x105E。
- Revision ID和Class Code寄存器,这两个寄存器只读。其中Revision ID寄存器记载PCI设备的版本号,该寄存器可以被认为是Device ID寄存器的扩展。而Class Code寄存器记载PCI设备的分类。
- Header Type寄存器,改寄存器只读,第7位表示当前设备是多功能还是单功能设备。第0~6位表示当前配置空间的类型,系统软件使用该寄存器区分不同类型的PCI配置空间。如普通PCI设备、PCI桥、CardBUS设备。
- Subsystem ID和Subsystem Vendor ID寄存器,这两个寄存器和Device ID及Vendor ID类似,也是记录PCI设备的生产厂商和设备名称。
- Expansion ROM base address寄存器,有些PCI设备在处理器还没有运行操作系统之前,就需要完成基本的初始化设置,比如显卡、键盘和硬盘等设备。为了实现这个“预先执行”功能,PCI设备需要提供一段ROM程序,而处理器在初始化过程中将运行这段ROM程序,初始化这些PCI设备。Expansion ROM base address记载这段ROM程序的基地址。
- Interrupt Line寄存器,这个寄存器是系统软件对PCI设备进行配置时写入的,该寄存器记录当前PCI设备使用的中断向量号,设备驱动可以通过这个寄存器,判断当前PCI设备使用处理器系统中哪个中断向量号,并将驱动程序的中断服务例程注册到操作系统中。不过目前在绝大多数处理器系统中,并没有使用该寄存器存放PCI设备使用的中断向量号。
- Interrupt Pin寄存器,这个寄存器保存PCI设备使用的中断引脚。为1时表示使用INTA#引脚作为中断引脚,2时表示使用INTB#引脚作为中断引脚,依次类推。
- Base Address Register0~5寄存器,该组寄存器简称为BAR寄存器,BAR寄存器保存PCI设备使用的地址空间的基地址,注意,该基地址是指PCI设备的PCI总线地址,并不是在CPU地址空间中的地址。其中每一个设备最多可以有6个基地址空间,但多数设备不会使用这么多组地址空间。
在PCI设备复位之后,BAR寄存器存放PCI设备需要使用的基址空间大小,着段空间可以是IO空间,也可以是存储器空间。系统软件对PCI总线进行配置时,首先获得BAR寄存器中的初始化信息,之后根据处理器系统的配置,将合理的基地址写入相应的BAR寄存器中。系统软件还可以使用该寄存器,获得PCI设备使用的BAR空间的长度,其方法是向BAR寄存器写入0xFFFF-FFFF,之后再读取该寄存器。
处理器访问PCI设备的BAR空间时,需要使用BAR寄存器提供的基地址。值得注意的是,处理器使用的是CPU地址空间的地址,而BAR寄存器存放的是PCI总线地址。因此处理器系统需要将PCI总线地址转换为CPU地址空间的地址。在许多处理器系统中,如Alpha和PowerPC处理器系统,PCI总线地址与CPU地址空间地址并不相等。但是在x86系统中,这两个地址的值是一样的,也就是它们之间的关系是一一映射的。但是,即使这两个地址的值是一样的,也需要明白这两个地址是不同地址空间中的地址。同时这个CPU地址空间的地址表示的是物理地址,需要使用ioremap这类接口映射为虚拟地址后,才能被驱动或内核使用。
5. PCI设备的几个空间资源概念
5.1 配置空间
如第(四)小节所述,每个PCI设备都有而且是必须要有一个配置空间,操作系统通过设备的配置空间获取PCI设备的基本信息,如设备的厂商ID和设备ID,以及通过BAR寄存器组获得BAR空间信息。那么CPU是如何访问一个PCI设备的配置空间的呢?
在x86处理器的北桥中,有两个端口寄存器,分别为CONFIG_ADDRESS和CONFIG_DATA寄存器,其地址为0xCF8和0xCFC。x86处理器使用这两个IO端口来访问PCI设备的配置空间。PCI总线规范也以这两个寄存器为例,说明处理器如何访问PCI设备的配置空间。其中CONFIG_ADDRESS寄存器存放PCI设备的ID号,而CONFIG_DATA寄存器存放进行配置读写的数据。
CONFIG_ADDRESS寄存器的结构如下图所示:
- Enable位,第31位。该位为1时,对CONFIG_DATA寄存器进行读写时将引发PCI总线的配置周期。
- Bus Number,第23~16位,为PCI设备的总线号。
- Device Number,第15~11位,为PCI设备的设备号。
- Function Number,第10~8位,为PCI设备的功能号。通过此位也能知道为什么一个PCI设备最多有8个逻辑功能了,因为Function Number位只有3位,所以最多有8个功能
- Register Number,第7~2位,为PCI设备配置空间的寄存器号。
当x86处理器对CONFIG_DATA寄存器进行IO读写访问(也就是通过IN/OUT指令访问此端口地址),且CONFIG_ADDR寄存器的Enable位为1时,HOST主桥将这个IO读写访问转换为PCI配置读写总线事务,然后发送到PCI总线上,PCI总线根据保存在CONFIG_ADDR寄存器中的ID号,将PCI配置读写请求发送到指定PCI设备的指定配置空间寄存器中。如下图所示:
那么系统是怎么知道PCI设备的总线号等这些号的呢,在x86系统下,系统上电时,BIOS会根据某种算法遍历本处理器系统中的所有的PCI设备,从而就得知了每个PCI设备的总线号等。而SylixOS启动时会直接使用BIOS遍历的结果,目前并不会再次对设备进行遍历。
5.2 PCI设备的存储器地址空间(又叫做Memory空间资源)
PCI设备的存储器地址空间是指PCI地址空间映射在CPU地址空间中的一段地址范围,这段地址范围可能是一个PCI设备的相关控制寄存器集合(注意,这里的寄存器和配置空间中的寄存器是两种不同的寄存器,比如显卡中进行2D和3D渲染的寄存器),或者是PCI设备的存储空间,如显存。在x86处理器系统中,PCI设备的存储器地址空间是被一一映射到CPU地址空间的,这样CPU就能像访问内存那样使用MOV指令访问PCI设备的控制寄存器和存储空间了,如下图所示:
下面这张图从地址空间分布角度反映了这种情况:
5.3 PCI设备的IO地址空间(IO资源)
PCI设备的IO地址空间是指PCI设备中映射到x86处理器端口地址空间中的寄存器集合。访问这些寄存器需要使用IN/OUT指令。如下图所示:
这些IO寄存器控制的是PCI设备上的一些外设,如显卡上的风扇,如下图:
2021年3月8日 10:38 1F
偶哟哟
2021年3月17日 08:35 2F
这个文章有点长,在地铁上看费劲
2022年1月11日 17:55 3F
开始写PCIe驱动啦~~~
2023年5月1日 12:58 4F
牛逼 0123456789
2023年5月1日 12:58 5F
牛的很
2023年6月1日 09:59 6F
这写的也太好了
2023年6月28日 21:39 7F
妈的。。。太强了。