存储技术原理分析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第3章

PCI子系统

3.1 概述

PCI是外围设备互连(Peripheral Component Interconnect)的简称,它是一种通用总线接口标准。PCI总线是一种常见的主机I/O总线,用于连接高速的外部设备。从1992年创立规范到如今,PCI逐渐取代之前的技术,成为事实上计算机的标准总线。PCI有许多优点,比如即插即用(Plug & Play)、中断共享等。

PCI总线体系结构是一种层次式的体系结构。图3-1是一个较为典型的PCI总线体系结构图。从图3-1中可以看到,PCI总线体系结构中包含以下主要的设备。

图3-1 PCI体系结构

• PCI设备:遵循PCI规范,工作在PCI局部总线环境下的设备。典型的PCI设备是封装在集成线路板(IC Package)中,或者集成到PCI扩展卡上的完整的外围元件适配器。通常见到的有网络、显示或者SCSI适配器。PCI局部总线规范指出,每个PCI设备可以包含最多8个PCI功能,每个功能是一个逻辑设备。

• PCI桥设备:由于电子负载限制,每条PCI总线上可以挂载的设备数目是有限的,因此使用了一种特殊的设备,即PCI-PCI桥设备。PCI-PCI桥为两条独立的PCI总线提供连接。PCI-PCI桥设备也是一种广义上的PCI设备。在本文中,PCI-PCI桥设备简称PCI桥。

• 主桥设备:PCI桥设备和PCI设备最终通过Host-PCI桥设备连接到CPU,使整个系统看起来像一颗倒置的树状结构,树的顶端是CPU和内存。由Host-PCI桥设备引出的PCI总线也称为PCI根总线。在本文中,Host-PCI桥设备简称为主桥。

PCI桥所处的PCI总线称为主总线(Primary Bus),它所连接的PCI总线称为次总线(Secondary Bus)。主总线称为次总线的父总线,而次总线称为主总线的子总线。从CPU的角度来看,主总线是靠近CPU一侧的总线,而次总线是远离CPU一侧的总线。主总线位于上游(Upstream),次总线位于下游(Downstream)。

此外,在PCI总线体系结构上,还有一些其他的桥接设备,例如PCI-ISA桥设备,用于连接一些低速外围设备,由于不在研究范围之内,这里不予以考虑。

要使得PCI设备能正常工作,CPU必须和PCI设备进行通信,也就是说它们需要有共享的地址空间。同CPU一样,PCI设备也有自己的内存空间和I/O空间,这些空间被映射到CPU的内存空间和I/O空间。在映射之后,PCI设备上的物理资源“变成”了CPU的本地资源。

和某些其他总线使用手动跳线的方式进行地址空间映射相比,PCI设备的地址空间映射在操作系统启动过程中自动完成。要实现自动映射,必须解决下面几个问题。

• 操作系统如何确定PCI设备有多少地址空间需要映射?

• 操作系统如何通知PCI设备它在CPU地址空间的窗口?

• 桥设备如何知道要访问的地址空间对应于它下游的PCI设备?

PCI规范为PCI设备预先定义了基地址寄存器(Basic Address Register)。操作系统往基地址寄存器中写入全1,然后再回读,就可以获得PCI设备需要映射到地址空间的大小。操作系统使用特定的机制确保地址映射不能冲突,之后再将映射后的地址写入PCI设备的基地址,PCI设备因此得知系统为它分配了哪一段地址空间。

同时,PCI规范为PCI桥设备定义了基地址寄存器和限制寄存器,通过向这些寄存器中写入特定的值,安置了一个涵盖了所有下游PCI设备地址空间的窗口范围。PCI桥设备只将这个窗口范围内的PCI I/O和PCI内存读/写请求向下传递,所有其他PCI I/O和内存地址都会被忽略。这种过滤机制防止了地址不必要的在系统中传播。

这样,CPU在指令执行的时候给出一个地址,这个地址首先送到主桥,主桥判断出这个地址是落在内存地址空间或是PCI地址空间上,如果落在PCI空间地址中,则主桥通过PCI总线仲裁申请,把地址送到PCI总线,总线上的PCI设备和PCI桥设备会根据自己的地址空间或者窗口范围来进行如下的比较。

PCI桥设备如果在主总线上接收到地址访问:

• 如果要寻址的地址不在自己的地址窗口内,则忽略之;

• 如果要寻址的地址落在自己的地址窗口内,则从主总线传递到次总线上。

PCI桥设备如果在次总线上接收到地址访问:

• 如果要寻址的地址落在自己的地址窗口内,则忽略之;

• 如果要寻址的地址不在自己的地址窗口内,则从次总线传递到主总线上。

对于PCI设备:

• 如果要寻址的地址不在自己的地址空间内,则忽略之;

• 如果要寻址的地址落在自己的地址空间内,则作为PCI从设备相应,完成数据传输。

由此可以看到,要让PCI设备工作,操作系统必须先“配置”好PCI设备(以及PCI桥设备)的一些寄存器,上面我们看到了PCI设备的基地址寄存器,以及PCI桥设备的基地址和限制寄存器,后面我们还会看到一些其他寄存器,它们一起构成了PCI设备的配置空间。

PCI设备的配置空间也称为配置寄存器组,其中前面64个字节(16个32位寄存器)的用途和格式是标准的,被称为配置寄存器组的“头部”(Configuration Header)。PCI规范定义了三种类型的PCI配置空间头部。我们这里只关注用于标准PCI设备的“类型0”(Type 0)和用于PCI -PCI桥的“类型1”(Type 1)。类型00h的配置空间头的图示如图3-2所示。

图3-2 类型00h的配置空间头

类型0的配置空间为PCI标准设备定义,前面16个字节是各种类型的配置空间所共有的,即为配置空间的公共域。

厂商ID(Vendor ID):这个域表示了设备的制造商。厂商标识符由PCI特别兴趣小组(PCI-SIG)统一分配,以确保唯一性。0xFFFF是一个无效值。

设备ID(Device ID):这个域表示了特定的设备。这个标识符由厂商自行分配。

厂商ID和设备ID寄存器标识PCI设备,通常被称为PCI ID。

修正号ID(Revision ID):这个域指定了设备的修正号。这个标识符由厂商选定。0也有效。这个域应该被看作设备ID的扩充。

状态寄存器(Status Register)被用来报告支持哪些特性,以及是否出现了某种错误。例如,状态寄存器的位4表明PCI设备偏移34h处为指向设备能力链表的指针,操作系统从它开始查找PCI设备支持的能力。

命令寄存器(Command Register)包含了各种特性的位掩码,这些特性可以被独立允许或禁止。例如,在配置好PCI设备后,启用PCI设备时,其中的工作之一就是将命令寄存器的位0和/或位1置位,分别允许PCI设备对I/O空间和/或内存空间的访问做出响应。又如,命令寄存器的位2控制设备作为PCI总线上的主控设备(Bus Master)的能力。值0禁止设备生成PCI访问,值1允许设备作为总线主控设备。PCI设备要支持DMA,就必须设置这一位。

头类型(Header Type):这个域的位7表示这个设备是否包含多个功能。如果为1,表示这个设备是多功能设备;否则为单功能设备。这个域的位6~位0表示这个设备的类型,因而确定了配置空间头部10h字节偏移开始的布局。如果为00h,则这个设备是一般的PCI设备,如果为01h,则为PCI-PCI桥,如果为02h,则为CardBus桥,其他值保留未使用。

类型0的配置空间头部从偏移10h开始是六个32位基地址寄存器。设备可以使用任何一个或多个基地址寄存器。64位的地址将占用两个连续的基地址寄存器。系统软件,包括BIOS和操作系统,查找这些寄存器,看这个PCI设备是否有地址空间映射需求。如果设备需要将资源同时映射到CPU的内存空间和I/O空间,必须使用两个基地址寄存器。通常来说,PCI设备都会实现至少一个基地址寄存器,将它的资源映射到CPU的内存空间,如图3-3所示。

基地址寄存器的位0用来确定是否寄存器被映射到内存或者I/O空间。它是只读的,映射到内存空间的基地址寄存器必须在位0返回0;映射到I/O空间的基地址寄存器必须在位0返回1,如图3-4所示。

映射到I/O空间的基地址寄存器总是32位宽,第0位保持为1,位1被预留,在被读取时必须返回0,而剩下的位用来映射设备到I/O空间。

图3-3 映射到内存空间的基地址寄存器

图3-4 映射到I/O空间的基地址寄存器

映射到内存空间的基地址寄存器可以是32位或64位(以支持映射到64位地址空间)宽,第0位保持为0。对于内存基地址寄存器,位2和位1表示映射到类型。如果数据可预取,则位3设置为1;否则位3设置为0。

子系统厂商ID(Subsystem Vendor ID)和子系统设备ID(Subsystem Device ID)进一步标识这个设备。厂商ID是指芯片制造商,而子系统厂商ID则是PCI卡的制造商。子系统设备ID由子系统厂商分配。

中断引脚(Interrupt Pin):中断引脚寄存器报告设备(确切地说,是设备功能)使用哪个中断引脚。值1对应INTA#,值2对应INTB#,值3对应INTC#,值4对应INTD#。如果不使用中断引脚,则该域应该清零。这个寄存器是只读的。

中断线(Interrupt Line):中断线寄存器给出了ISA IRQ编号。这个寄存器可读/写,使用了中断引脚的设备(设备功能)都必须实现。在PCI设备扫描时,系统软件将路由信息写到这个寄存器,这个值被系统软件和设备驱动使用,PCI设备本身并不使用它。

类型01h的配置空间头的图示如图3-5所示。

图3-5 类型01h的配置空间头

类型1的配置空间为PCI桥设备定义,其开头16个字节的用途和格式与类型0相同,包含着有关头部的类型、设备的种类、特性以及制造商等信息。类型1的配置空间其他主要域如下。

主总线编号(Primary Bus Number)寄存器用来记录桥的主接口所连接的PCI总线的编号。系统软件负责填入该寄存器的值。桥使用主总线编号寄存器解码次接口上类型1配置事务,被转换为主接口上的事务。PCI设备必须实现这个寄存器为读/写,在系统复位后,这个寄存器的默认值应该为0。

次总线编号(Secondary Bus Number)寄存器用于记录桥的次接口所连接的PCI总线的编号。系统软件负责填入该寄存器的值。桥使用次总线寄存器确定是否响应主接口上的类型1配置事务,以及将配置事务转换为次接口上的类型0配置事务。桥还使用次总线编号寄存器以及附属总线编号寄存器确定是否将类型1的配置事务向上游转发。PCI设备必须实现这个寄存器为读/写,在系统复位后,这个寄存器的默认值应该为0。

附属总线编号(Subordinate Bus Number)寄存器用于记录这个桥段下游PCI总线的最大编号。换句话说:对于每个PCI桥,位于它下游的PCI总线的编号都必须在它的次总线编号(含)和附属总线编号(含)之间。系统软件负责填入该寄存器的值。桥使用附属寄存器以及次总线编号寄存器确定是否响应主接口上的类型1配置事务,以及将配置事务传递到次接口。桥还使用次总线编号寄存器和附属总线编号寄存器确定是否将类型1的配置事务向上游转发。PCI设备必须实现这个寄存器为读/写,在系统复位后,这个寄存器的默认值应该为0。

操作系统启动过程中,必须完成所有PCI设备的配置。未成功配置的PCI设备不能正常工作。PCI设备配置需要解决下面两个问题。

• PCI设备及其配置空间是如何寻址的呢?

• 操作系统如何访问PCI设备的配置空间?

前面看到,PCI体系为层次式结构。PCI设备寻址用到了三个元素:总线编号、插槽编号和功能编号。总线编号唯一标识了PCI设备所在的PCI总线,插槽编号为PCI设备在该总线上的编号。一个物理PCI设备可能包含多个功能,每个功能对应一个逻辑PCI设备,有一个功能编号。这里的PCI设备寻址是指逻辑PCI设备。找到了PCI设备,要寻址其配置空间,只需要给出寄存器在配置空间的偏移,即寄存器编号。

由于Intel x86上没有配置空间,我们不能像上面的内存空间和I/O空间那样采用“映射”的方法。实际上,操作系统对配置空间访问借助了CPU的I/O空间。对于i386结构的处理器,PCI总线的设计者在I/O地址空间保留了两个32位的寄存器,第一个是“配置地址寄存器”0xCF8(CONFIG_ ADDRESS),第二个是“配置数据寄存器”0xCFC(CONFIG_DATA)。要访问某个设备的配置空间中某个寄存器时,CPU先往配置地址寄存器写入目标地址,然后通过配置数据寄存器读/写数据。

写入配置地址寄存器中的目标地址布局如图3-6所示,包括总线号、设备号、插槽号、功能号,以及配置寄存器地址。

图3-6 配置地址寄存器的布局

• 总线号8位:每个系统最多256条总线。

• 插槽号5位:每条总线最多32个设备。

• 功能号3位:每个设备最多8个功能。

• 寄存器地址6位:每个功能有64个32位(256字节)的寄存器。

• 字节编号2位:每个(32位的)寄存器有4个字节。

配置地址空间的大小为每个功能256字节×每个插槽8个功能×每条总线32个插槽×每个系统256条总线。

CPU对配置地址寄存器和配置数据寄存器的访问将被接收到的主桥转换为配置事务发到PCI总线上。主桥首先检查配置事务中的总线编号,如果为0,则这个配置事务是针对主桥自身或者PCI总线0上的PCI设备的,如图3-7所示。主桥将配置事务转换为类型0的配置事务并在总线0内广播。

图3-7 类型0的PCI配置事务

类型0的配置事务根据“设备选择(Device Select)”域选择目标PCI设备。在收到类型0的配置事务后:

• 如果“设备选择”选中的是本设备,则响应之;

• 如果“设备选择”选中的不是本设备,则忽略之。

如果主桥接收到的配置事务中的总线编号大于0,则主桥将配置事务转换为类型1的配置事务,如图3-8所示。

图3-8 类型1的PCI配置事务

当PCI桥看到一个类型1的配置事务的时候:

• 如果要寻址的总线编号不在桥的次总线编号和总线的附属编号之间,忽略之;

• 如果要寻址的总线编号和桥的次总线编号符合,就把它转变成为类型0的配置事务;

• 如果要寻址的总线编号大于次要总线编号并且小于或等于附属总线编号,就不加改变地传递到次总线接口上。

从上可以看到,次总线编号和附属总线编号一起,构成了一个过滤窗口,防止配置事务不必要地在系统中传播。换句话说,次总线编号和附属总线编号寄存器对于配置空间的访问,与基地址和限制寄存器对于I/O空间和内存空间的访问起着相同的作用。

PCI子系统核心代码包括体系架构相关部分以及通用部分,分别位于arch/x86/pci/以及drivers/pci两个目录下。PCI子系统的主要功能是:

1.枚举PCI总线和PCI设备,在内存建立可供设备驱动使用的核心结构;

2.配置PCI设备和桥设备,包括总线号、基址寄存器以及中断线等。

3.在sysfs文件系统中构造PCI子系统的目录树。