PCI总线进阶(二)

来源:互联网 发布:知乎 中国铁路 非洲 编辑:程序博客网 时间:2024/04/20 23:20

PCI总线进阶(二)

一、PCI配置空间简介

PCI具有三个相互独立的物理地址空间:总线物理地址空间、I/O设备地址空间和配置空间。其中配置空间是PCI总线所特有的一个物理地址空间,它存在的意义是:在系统加电自检的时候,BIOS检测PCI总线上挂载的PCI设备的时候,由于不同厂家、不同设备的驱动的差异性导致其具有不同的配置性,而系统需要对这些PCI设备进行统一配置,这就使得配置空间必须存在了,这样就可以实现设备参数的自动配置,达到即插即用的要求。

PCI总线规范定义的配置空间的总长度为256字节的长度,其中包含64字节的配置空间头,配置头的格式都是一样的,配置头的主要功能是用来识别设备、定义主机访问PCI卡的方式(I/O访问还是存储器访问)、配置中断信息。剩下的192个字节称为本地配置空间,主要定义PCI卡上局部总线的特性、本地空间基地址及范围等。


表1 配置空间头信息

       根据上表的配置空间头的信息,看一下比较重要的几个寄存器:

Device ID和Vendor ID是PCI设备的设备号和产商号,系统是通过这两个找到设备驱动的,地址空间是00h-03h。

Status:状态寄存器。记录设备状态。

Command:命令寄存器。负责设备控制。

Revision ID:版本ID号。

Class Code:类代码,用于标志PCI设备用途的。共三字节,分别是类代码、子类代码、编程接口。类代码不仅用于区分设备类型,还是编程接口的规范,这就是为什么会有通用驱动程序。地址空间处于09h-0Bh处,它是一段只读的代码。

BIST:内自建测试。

Header Type:头部类型。主要用于PCI scan的,减小桌面显示的延迟。地址空间是0Eh。这是一个8Byte 的寄存器,其中的第七位为0时,是single单功能设备;bit7为1时,是multi多功能设备。

Latency Timer:等待计时器。

CacheLine Size:缓存线大小。

IRQ Line:IRQ编号。使用APIC(高级可编程中断控制器),它支持管理24个中断,不同的中断号申请好之后进行相应的中断处理。
     IRQ Pin:中断引脚。PCI有4个中断引脚,该寄存器表明该设备连接的是哪个引脚。

Cardbus CIS Pointer:CIS卡总线指针。

Subsystem ID:子系统ID号。

Subsystem Vendor ID子系统版本号。

Base Address Register:基址寄存器。这是32位寄存器,如果从这个位置读出的值最低位是1,代表IO地址,否则代表内存地址。系统加电后,引导程序通过向基址寄存器写入全1,并重新读回,如果认为最低位为0,整个地址可以分成高位的全“1”部分,和低位的全“0”部分。引导程序可以判断出全“0”部分是PCI设备自己寻址空间,因而可以确定板卡需要资源的大小,并给它分配一块空闲的I/O或内存空间,并把分配地址的首地址写回到基址寄存器。驱动程序可以通过读这个基址寄存器取得基地址,再加上设备的偏移地址就可以访问这个设备的寄存器了。

                      

                                                                                   内存空间访问基地址寄存器                                                      I/O方式访问基址寄存器

                                                                

Expansion ROM Base Address:扩展基地址保留。寄存器格式如下:


Capabilities Pointer:能力指针。

Interrupt Line:中断线寄存器,它是一个8位寄存器,用来报告中断的连接情况,它是一个可读写的寄存器,凡是使用中断引脚的设备必须对它进行设置,设备驱动程序和操作系统可以利用这个信息来确定中断向量。该寄存器的值要受系统体系的支配。对于X86体系结构,该寄存器的值和标准8259配置中的IRQ编号(0~15)相对应。255表示没有连接到任何中断控制器,15~255之间的值为保留。

二、PCI配置空间访问的流程

PCI总线内部配置空间的访问方式有两种。

一种是I/O方式访问。通过I/O方式访问的配置空间可通过两个访问寄存器,CONFIG_ADDRESS寄存器(PCI配置空间地址)和CONFIG_DATA寄存器(PCI配置空间数据)。这两个寄存器在PC中分别对应着CF8h和CFCh端口,并且是32位端口,即读写要用的32为IN和OUT汇编指令。

每个PCI设备可应用三个信息进行定位,即BusNumber、Device Number和Function Number。另外,PCI配置空间一共是256个字节,被分割成64个4字节的寄存器,从0-63编号。

每次要访问PCI配置空间时,先设置CONFIG_ADDRESS寄存器,这时CONFIG_DATA存储器的内容就对应着该PCI配置空间中的相应的寄存器。我们来看一看CONFIG_ADDRESS寄存器每一个bit位的情况: 
     31 位:Enabled位,寄存器使能操作。
     23:16 位:总线编号。
      15:11 位:设备编号。
      10: 8 位:功能编号。
      7: 2 位:配置空间寄存器编号。
     1: 0 位:恒为“00”。这是因为CF8h、CFCh端口是32位端口

另一种访问配置空间的方式是通过寄存器进行访问。总线内部为配置空间准备了16M寻址空间,找到寻址空间的起始地址,根据公式PCIE_BASE_ADDRESS + (bus<<20) + (dev<<15) +(fun<<12) + offset计算出总线地址,在该地址处读取数据。

PCI总线访问配置空间总是向下一级一级进行访问的。所有PCI主桥和PCI-PCI桥都必须能够产生类型0和类型1这两类配置命令,当发生CF8h寄存器32位写操作时,根据Bus Number,若为0,主桥则产生类型0配置命令,否则产生类型1配置命令。假设产生类型0配置命令,主桥将其(产生的32位命令数据)放在其局部总线上,在其局部总线上寻找选择目标设备,找到目标设备将其IDSEL#有效,所有的PCI设备都要响应类型0配置命令,在其IDSEL#有效情况下,使其DEVSEL#有效,告诉桥设备自己就是目标设备,再根据寄存器索引来访问具体的配置寄存器;若其局部总线上没有目标设备,则产生类型1配置命令并将其(产生的32位命令数)放在其局部总线上,所有的PCI设备(除PCI-PCI桥)都要忽略类型1配置命令,只有桥设备响应类型1命令,桥设备译码总线号判断配置事物的目标总线是否在其后,若目标总线不在其后,忽略该配置事物,否则,它将声明目标总线,若总线号不是该桥所挂接的总线,则原封不变的向下传递,直到遇到设备并将类型1配置命令转换成类型0配置命令放在其挂接的总线上。

三、对配置空间中设备控制和设备状态的管理

1、对于配置空间Command寄存器的读写

对于配置空间的command寄存器的读写,它为一个设备发出和响应PCI总线命令提供粗略的控制,负责设备控制。以下便是命令寄存器格式:


图1 命令寄存器格式


  这边是一个16字节的寄存器,其中高六位保留,使用低十位进行编辑,它的每一位是采用高电平控制响应的,这几位为“0”时忽略,在值为“1”的时候进行相应的响应,其中默认的缺省值为“0”。

2、对于配置空间Status寄存器的读写

对于配置空间中的Status寄存器的读写,它主要用来为PCI总线相关事件记录状态信息的。以下便是命令寄存器格式:

图2 状态寄存器的格式

       这也是一个16位的寄存器,其中低三位(0-2)进行保留。

InterruptStatus是中断状态位,这个bit位是一个只读位,只有在命令寄存器中的中断禁止为0和这个中断状态位为1的时候将这个设备/功能的INT#信号响应。这个bit位置为“1”,对其并没有什么影响。

CapabiltiesList是功能列表,这个只读选项位表示的是是否实现34H处新功能链表的内容,指针存放在这个34H处。“0”表示没有新功能链表可用,“1”表示有新功能链表的使用。

66 MHz Capable是66 MHz的频率选择。只读寄存器,“0”表示读取33MHz的频率,“1”表示读取66MHz的。

Reserved表示第七bit位进行保留。

FastBack-to-Back Capable表示快速回传能力。只读bit位,若设置为“1”表示可以进行快速回传,否则设置为“0”。

Master DataParity Error主机数据就校验。这个bit位是总线控制的,以下三个条件都满足时设置成立:1在读、写中发生了PERR错误,2在该错误产生的时候这个bit位设置了总线控制管理,3命令寄存器设置了奇偶校验位的响应。

DEVSEL Timing是设备响应时间,分为快速、中速和慢速三种情况,这些位是只读的。除了在进行配置读写的时候,任何总线命令都要设置DEVSEL的最低响应时间。

Signaled Target Abort:目标信号截止。该位必须由设备设置,无论设备终止目标间传输。设备无需注意这个bit位。

Received Target Abort接受目标终止。这个bit位必须由主设备进行设置,所有主机设备都必须注意这个bit位。

Received Master Abort接受主机终止信号。该bit位有主机设置,所有主设备必须注意这个bit位。

Signaled System Error:标记系统错误。当设置了SERR位之后该bit位必须被设置。设备不会被设置该信号,不用考虑该bit位。

DetectedParity Error奇偶校验错误检测。该bit位必须通过设备设置,当他检测到一个奇偶校验错误的时候,甚至在奇偶校验错误被忽略的时候(它是由命令寄存器的第六bit位进行控制的)。

3、对于配置空间地址寄存器的读写

将要访问配置空间寄存器的总线号、设备号、功能号和寄存器号以一个双字的格式写到配置地址端口 (CF8H-CFBH),接着执行配置数据端口(CFCH)的读和写,向配置数据口写数据即向配置空间写数据,从配置数据口读数据即从配置空间读数据。配置地址端口(CF8H)的格式定义如下:

  31           30           24 23       16 15       11 10        8 7                  2       1       0

使能 

保留

总线号

设备号

功能号

寄存器号

 

 

 

        寄存器号:选择配置空间中的一个双字(32位)

        功能号:  选择多功能设备中的某一个功能,有八种功能,0--7

        设备号:  在一条给定的总线上选择32个设备中的一个。0--31

        总线号:  从系统中的256条总线中选择一条,0—255

                使能:         寄存器使能

尽管理论上可以有256条总线,但实际上PC机上PCI插槽的总线号都是1,有些工控机的总线号是2或者3,所以我们只需要查找0--4号总线就足够了。PCI规范规定,功能0是必须实现的,所以,如果功能0的头标类型字段的位7为0,表明这是一个单功能设备,则没有必要再去查其他功能,否则要查询所有其他功能。

注:寄存器号(offset)占的位置是后面8位空间,但是在32Byte的寄存器中,最后两位的必须是00。这边的计算公式0xCF8的地址为0x80000000+bus << 16+dev << 11+fun << 8+ offset若用户需要读8000b830h处的数据直接写,而用户需要读取8000b831h处的数据,我们在这边out的时候依然是8000b830h,只是在传递给用户的时候需要使用两个16bit的寄存器代表后面那两个Byte位,指明用户要读的是最后一个Byte位的低8位。

假设用户读取Bus0、Dev 17H、Fun 0、Offset 30H处地址的command:

(1)   使用I/O方式读取:

OUT       0CF8H         8000B830H

IN           0CFCH

(2)   使用Memory方式读取:

MOV      DX        8000B830H

IN           DX

 

4、对于配置空间BIST寄存器的读写

BIST是内自建测试。不支持BIST的返回值为0,支持BIST的设备在BIST运行的时候并不能阻止PCI总线的正常运行。

 

图3 BIST寄存器的格式

 

Completion Code:完成码。0值表示该设备通过了BIST,若不是0则表示未通过内自建测试。

Start BIST:开始测试。该bit位写“1”被调用,当BIST完成时该位被重置,当测试2秒之后仍未完成时,软件显示测试失败。

BIST Capable:支持BIST。返回值为“1”表示主持BIST,“0”则表示不支持。

四、PCI设备被识别的过程

当机器刚启动以后,内核在起初始化阶段会依次采用上面提到的三种访问配置空间的方法来查询主板上PCI总线上的PCI设备的配置空间。内核首要查找的是PCI总线编号为0的那个PCI根总线上的Host/PCI桥或者是VGA现实控制器的PCI设备,或者是厂商ID为INTEL和Compaq的PCI设备,只要上述的设备之一被检查到,那么内核就结束了PCI总线的探测工作,确定了通过哪种方式来访问配置空间。随后,系统会进行PCI总线枚举过程,也就是说它会依次查询各个PCI总线上的所有设备(从根总线开始查找其下面的每一个设备,其它的总线对于上级总线来说也类似于一个PCI设备,一次可以递归深入查找),枚举的过程其实就是往CONFIG_ADDRESS端口发送各种不同BusNumber以及Device Number的命令字。经过这个枚举过程,系统就得到了当前主板上的所有PCI设备的配置信息了。

PCI设备通过这些配置信息(看上面的那个表)告诉系统自己的ID(用来区分不同的设备以及帮助驱动程序绑定特定的设备),同时还可以把PCI设备上的一些寄存器、内存和IO资源通过BAR(Base Address Register)告诉系统,从而系统可以根据这些信息给设备上的寄存器和内存和IO端口资源分配总线地址,在必要的时候再将这些资源映射到系统内存和端口上(这一步是通过驱动程序实现的,将PCI设备的物理资源(即总线地址)建立虚拟映射),这样访问PCI设备,就变成了访问系统内存和IO端口操作了(通过命令iowrite32/ioread32操作)。对于设备上大内存的访问,也有通过DMA实现的,它可以将PCI总线这边的(也就是主机下的)内存里的数据,搬移到PCI总线上的设备的内存中,当然前提是这个设备支持这种DMA(做法是把主机内存的虚拟地址转换成总线地址,然后再填写设备的目标和源地址,以及传输的数据大小,启动DMA就可以了)。

设备被识别以后,驱动程序也建立好了相应的内存和端口映射了,那么就是要按照不同的PCI设备的手册来读写设备的寄存器、内存以及IO端口,完成设备相应的功能。PCI驱动程序一般只是封装了几组底层的访问特定寄存器、内存和IO端口的操作,提供给用户一组实在的功能,如读取PCI设备的视频信息等。

五、PCI设备的枚举过程

PCI设备是使用拓扑结构的,在PCI主线上可以挂载设备的同时,在PCI总线上可以扩展PCI桥,设备通过挂载在PCI桥上可以达到与挂载在总线上同样的功能,区别只是设备的硬件地址的区别。

PCI 总线扫描的原理是从总线0 扫描到总线 255,对于每条总线,系统都会扫描所有(其中总共有256条总线,32个设备号,8种功能号,64个寄存器),将总线上的设备进行一层层的编号,找到总线上的设备,读取设备中的Device ID和Vendor ID,如果这两个寄存器的值是个无效值(0xFFFF),则说明当前位置上没有设备,接着扫描下一个位置。如果是有效值(非0xFFFF),当前位置是个有效的PCI 设备/桥。进而再读取该设备的Header Type 寄存器,如果该寄存器为 1,则表示当前设备是PCI 桥,否则是 PCI 设备。

六、HOST访问PCI设备的两种方式

1、Posted方式

Posted总线事务指PCI主设备向PCI目标设备进行数据传递时,当数据到达PCI桥后,即由PCI桥接管来自上游总线的总线事务,并将其转发到下游总线。采用这种数据传送方式,在数据还没有到达最终的目的地之前,PCI总线就可以结束当前总线事务,从而在一定程度上解决了PCI总线的拥塞。Posted数据请求在通过PCI总线之后,将逐级释放总线资源,因此PCI总线的利用率较高。只有在采用Memory写请求的时候就是使用的这种总线事务。

2、Non-Posted方式

Non-Posted总线事务是指PCI主设备向PCI目标设备进行数据传递时,数据必须到达最终目的地之后,才能结束当前总线事务的一种数据传递方式。采用Non-Posted传送方式时,PCI总线在没有结束当前总线事务时必须等待。这种等待将严重阻塞当前PCI总线上的其他数据传送,因此PCI总线使用Delayed总线事务处理Non-Posted数据请求,使用Delayed总线事务可以相对缓解PCI总线的拥塞。其中存储器读请求、I/O读写请求、配置读写请求就是使用的这种总线事务。

七、PCI总线配置的两种方式

PCI总线定义了两类配置请求,一个是Type 00h配置请求,另一个是Type 01h配置请求。PCI总线使用这些配置请求访问PCI总线树上的设备配置空间,包括PCI桥和PCI Agent设备的配置空间。

其中HOST主桥或者PCI桥使用Type 00h配置请求,访问与HOST主桥或者PCI桥直接相连的PCIAgent设备或者PCI桥;而HOST主桥或者PCI桥使用Type 01h配置请求,需要至少穿越一个PCI桥,访问没有与其直接相连的PCI Agent设备或者PCI桥。

当x86处理器对CONFIG_DATA寄存器进行读写操作时,HOST主桥将决定向PCI总线发送Type 00h配置请求还是Type01h配置请求。在PCI总线事务的地址周期中,这两种配置请求总线事务的不同反映在PCI总线的AD[31:0]信号线上。

处理器首先将目标PCI设备的ID号保存在CONFIG_ADDRESS寄存器中,之后HOST主桥根据该寄存器的Bus Number字段,决定是产生Type00h配置请求,还是Type 01h配置请求。当BusNumber字段为0时,将产生Type00h配置请求,因为与HOST主桥直接相连的总线号为0;大于0时,将产生Type 01h配置请求。

1、Type 01h配置请求

在PCI总线中,只有PCI桥能够接收Type01h配置请求。Type 01h配置请求不能直接发向最终的PCIAgent设备,而只能由PCI桥将其转换为Type01h继续发向其他PCI桥,或者转换为Type00h配置请求发向PCI Agent设备。PCI桥还可以将Type 01h配置请求转换为SpecialCycle总线事务(HOST主桥也可以实现该功能)。

在地址周期中,HOST主桥使用配置读写总线事务,将CONFIG_ADDRESS寄存器的内容拷贝到PCI总线的AD[31:0]信号线中。CONFIG_ADDRESS寄存器与Type 01h配置请求的对应关系如图4所示。

图4 PCI总线Type 01H配置

从图中可以发现,CONFIG_ADDRESS寄存器的内容基本上是原封不动的拷贝到PCI总线的AD[31:0]信号线上。其中CONFIG_ADDRESS的Enable位不被拷贝,而AD总线的第0位为必须为1,表示当前配置请求是Type 01h。

当PCI总线接收到Type 01配置请求时,将寻找合适的PCI桥接收这个配置信息。如果这个配置请求是直接发向PCI桥下的PCI设备时,PCI桥将接收这个Type 01配置请求,并将其转换为Type00h配置请求;否则PCI桥将当前Type01h配置请求原封不动的传递给下一级PCI总线。

2、Type 00h配置请求

如果HOST主桥或者PCI桥发起的是Type 00h配置请求,CONFIG_ADDRESS寄存器与AD[31:0]的转换如图5所示。


图5 PCI总线Type 00H配置

此时处理器对CONFIG_DATA寄存器进行读写时,处理器将CONFIG_ADDRESS寄存器中的Function Number和RegisterNumber字段拷贝到PCI的AD总线的第10~2位;将AD总线的第1~0位赋值为00。PCI总线在配置请求总线事务的地址周期根据AD[1:0]判断当前配置请求是Type 00h还是Type 01h,如果AD[1:0]等于00表示是Type 00h配置请求,如果AD[1:0]等于01表示是Type 01h配置请求。

而AD[31:11]与CONFIG_ADDRESS的Device Number字段有关,在Type00h配置请求的地址周期中,AD[31:11]位有且只有一位为1,其中AD[31:11]的每一位选通一个PCI设备的配置空间。PCI设备配置空间的片选信号是IDSEL,因此AD[31:11]将与PCI设备的IDSEL信号对应相连。这边表示的是一根引脚线对应一个设备,这根线相当于译码一样,第几位为“1”就表示对应的第几个设备。

当以下两种请求之一满足时,HOST主桥或者PCI桥将生成Type00h配置头,并将其发送到指定的PCI总线上。

(1)       CONFIG_ADDRESS寄存器的Bus Number字段为0时,处理器访问CONFIG_DATA寄存器时,HOST主桥将直接向PCI总线0发出Type 00h配置请求。因为与HOST主桥直接相连的PCI总线号为0,此时表示HOST主桥需要访问与其直接相连的PCI设备。

(2)      当PCI桥收到Type 01h配置头时,将检查Type01配置头的Bus Number字段,如果这个BusNumber与PCI桥的SecondaryBus Number相同,则将这个Type 01配置头转换为Type00h配置头,并发送到该PCI桥的Secondary总线上。

以下是我使用汇编语言写的一个读取配置空间资料的小程序,这边使用了较多的寄存器,造成了大量的资源浪费,同时也增加了程序的时间复杂度,仅作参考,并不需要去认真理解。

Name:read_cfg_spac

Function:read pci config space with io port

Use Register:ebx eax al ax dx

Return:eax

read_cfg_spac proc near

 

push       ebx        eax         al    ax   dx    //寄存器入栈操作

MOV     ebx        0x80000000        //使能位为1时,其他都为0时的地址

MOV     eax         PCI_BUS_NUM

SAL      eax         10H           //左移16位

MOV     al           PCI_DEV_NUM

SAL      al           0bH                            //左移11位

MOV    ax          PCI_FUN_NUM

SAL      ax          08H                            //左移8位

MOV     dx          PCI_REG_NUM

OR        eax         al

OR        eax         ax

OR        eax         dx                       

OR        eax         ebx                      //得到地址偏移量

XOR     dx          dx                        //将寄存器dx清零

MOV     dx          CF8H                  //对应PCI_CFG_ADDR_PORT

OUT      dx          eax

MOV     dx          CFCH                 //对应PCI_CFG_DATA_PORT

IN          dx

Pop        eax                                     //出栈

Ret                                                  //返回值




以上纯属个人见解,若有错误,感谢大神斧正。


0 0
原创粉丝点击