引言
1991 年下半年,Intel 公司首先提出了PCI 的概念,并联合IBM、Compaq、AST、HP、DEC 等100 多家公司成立了PCI 集团,其英文全称为:Peripheral Component Interconnect Special Interest Group(外围部件互连组),简称PCISIG。PCI总线一经推出以来,以其独有的特性受到众多厂商的青睐,已经成为计算机扩展总线的主流。目前,国内的许多技术人员已经具备开发PCI总线接口设备的能力。但是PCI总线的编程技术,也就是对PCI总线设备的操作技术,一直是一件让技术人员感到头疼的事情。PCI总线编程的技术是对相应板卡配置空间的理解和访问。一般软件编程人员基于对硬件设备原理的生疏,很难理解并操作配置空间,希望硬件开发人员直接告诉他们怎样操作;而PCI总线硬件开发人员虽深刻地理解了其意义,在没有太多编程经验地前提下,也难于轻易地操作PCI板卡。结果大多是硬件技术人员花费大量时间和精力去学习DDK、WINDRVER等驱动程序开发软件。浪费了时间不说,还大大加长了开发软件时间。
本文是作者在开发PCI总线接口设备时,经过对PCI总线协议的深入研究,从协议本身的角度出发,找到一种方面而快捷的PCI配置空间操作方法,只使用简单的I/O命令即可找到特定的PCI总线设备并对其所有的配置空间进行读写操作。一旦读得其配置空间的内容,即可中得到担任系统对该PCI总线设备的资源分配。希望给有需要的人带来帮助,为大量开发软件的同行带来便利。
1 PCI总线简介
PCI是Peripheral Component Interconnect(外设部件互连标准)的缩写,它是目前个人电脑中使用为广泛的接口,几乎所有的主板产品上都带有这种插槽。PCI插槽也是主板带有多数量的插槽类型,在目前流行的台式机主板上,ATX结构的主板一般带有5~6个PCI插槽,而小一点的MATX主板也都带有2~3个PCI插槽,可见其应用的广泛性。
PCI总线是一种不依附于某个具体处理器的局部总线。从结构上看,PCI是在CPU和原来的系统总线之间插入的总线,具体由一个桥接电路实现对这一层的管理,并实现上下之间的接口以协调数据的传送。管理器提供了信号缓冲,使之能支持10种外设,并能在高时钟频率下保持高性能。PCI总线也支持总线主控技术,允许智能设备在需要时取得总线控制权,以加速数据传送。
PCI 总线的主要性能有:
(1) 支持10 台外设
(2) 总线时钟频率33.3MHz/66MHz
(3) 数据传输速率133MB/s
(4) 时钟同步方式
(5) 与CPU 及时钟频率无关
(6) 总线宽度 32 位(5V)/64 位(3.3V)
(7) 能自动识别外设
PCI (Peripheral Component Interconnect)总线是一种高性能局部总线,是为了满足外设间以及外设与主机间高速数据传输而提出来的。在数字图形、图像和语音处理,以及高速实时数据采集与处理等对数据传输率要求较高的应用中,采用PCI总线来进行数据传输,可以解决原有的标准总线数据传输率低带来的瓶颈问题。
PCI总线是一种同步的独立予处理器的32位或64位局部总线,工作频率为33MHz,峰值速度在32位时为132MB/s,64馒时必264MB/s,总线规范由PCISIG发布。ISA总线相比,PCI总线和有如下显着的特点:
①高速性 PCI局部总线以33MHz的时钟频率操作,采用32位数据总线,数据传输速率可高达132MB/s,远超过以往各种总线。
②即插即用性 考虑PCI总线的高速性,单凭其即插即用性,就比ISA总线优越了许多。
③可靠性 PCI独立于处理器的结构,形成一种独特的中间缓冲器设计方式,将中央处理器子系统与外围设备分开。
④复杂性 PCI总线强大的功能大大增加了硬件设计和软件开发的实现难度。
⑤自动配置 PCI总线规范规定PCI插卡可以自动配置。
⑥共享中断 PCI总线是采用低电平有效方式,多个中断可以共享一条中断线,而ISA总线是边沿触发方式。
⑦扩展性好 如果需要把许多设备连接到PCI总线上,而总线驱动能力不足时,可以采用多级PCI总线,这些总线上均可以并发工作,每个总线上均可挂接若干设备。
⑧多路复用 在PCI总线中为了优化设计采用了地址线和数据线共用一组物理线路。
⑨严格规范 PCI总线对协议、时序、电气性能、机械性能等指标都有严格的规定,保证了PCI的可靠性和兼容性。
2 PCI总线配置空间及配置机制
为避免各PCI设备在资源的占用上发生冲突,PCI总线采用即插即用协议。即在系统建立时由操作系统按照各设备的要求统一分配资源,资源分配的信息由系统写入各PCI设备的配置空间寄存器,并在操作系统内部备份。各PCI设备有其独自的配置空间,设计者通过对积压设备(或插槽)的ISDEL引脚的驱动区分不同设备的配置空间。配置空间的前64个字节称为配置空间的预定自区,它对每个设备都具有相同的定义且必须被支持;共后的空间称为设备关联区,由设备制造商根据需要定义。与编程有关的配置空间信息主要有:
(1)设备号(Device ID)及销售商号(Vendor ID),配置空间偏移量为00h,用于对各PCI设备的区分和查找。为了保证其性,Vendor ID应当向PCI特别兴趣小组(PCI SIG)申请而得到。
(2)PCI基地址(PCI Base Address),配置空间偏移量为10~24h,设备通过设定可读写的高位数值来向操作系统指示所需资源空间的大小。比如,某设备需要64K字节的内存空间,可以将配置空间的某基地址寄存器的高16位设成可读写的,而将低16位置为0(只可读)。操作系统在建立时,先向所有位写1,实际上只有高16位被接收而被置成了1,低16位仍为0.这样操作系统读取该寄存器时,返回值为FFFF0000h,据此操作系统可以断定其需要的空间大小是64K字节,然后分配一段空闲的内存空间并向该寄存器的高16位填写其地址。
其它可能与编程有关的配置空间的定义及地址请参阅参考文献[1]。
由于PC-AT兼容系统CPU只有内存和I/O两种空间,没有专用的配置空间,PCI协议规定利用特定的I/O空间操作驱动PCI桥路转换成配置空间的操作。目前存在两种转换机制,即配置机制1#和配置机制2#。配置机制2#在新的设计中将不再被采用,新的设计应使用配置机制1#来产生配置空间的物理操作。这种机制使用了两个特定的32位I/O空间,即CF8h和CFCh。这两个空间对应于PCI桥路的两个寄存器,当桥路看到CPU在局部总线对这两个I/O空间进行双字操作时,就将该I/O操作转变为PCI总线的配置操作。寄存器CF8h用于产生配置空间的地址(CONFIG-ADDRESS),寄存器CFCh用于保存配置空间的读写数据(CONFIG-DATA)。
配置空间地址寄存器的格式如图1。
CF8H(局部总线):
当CPU发出对I/O空间CFCh的操作时,PCI桥路将检查配置空间地址寄存器CF8h的31位。如果为1,就在PCI总线上产生一个相应的配置空间读或写操作,其地址由PCI桥路根据配置空间地址寄存器的内容作如图2所示的转换。
CFCh (局部总线):
设备号被PCI桥路译码产生PCI总线地址的高位地址,它们被设计者用作IDSEL信号来区分相应的PCI设备。6位寄存器号用于寻址该PCI设备配置空间62个双字的配置寄存器(256字节)。功能号用于区分多功能设备的某特定功能的配置空间,对常用的单功能设备为000。某中PCI插槽的总线号随系统(主板)的不同稍有区别,大多数PC机为1,工控机可能为2或3。为了找到某设备,应在系统的各个总线号上查找,直到定位。如果在0~5号总线上不能发现该设备,即可认为该设备不存在。
理解了上述PCI协议里的配置机制后,就可以直接对CF8h和CFCh两个双字的I/O空间进行操作,查找某个PCI设备并访问其配置空间,从而得到操作系统对该PCI设备的资源分配。
3 用I/O命令访问PCI总线配置空间
要访问PCI总线设备的配置空间,必须先查找该设备。查找的基本根据是各PCI设备的配置空间里都存有特定的设备号(Device ID)及销售商号(Vendor ID),它们占用配置空间的00h地址。而查找的目的是获得该设备的总线号和设备号。查找的基本过程如下:用I/O命令写配置空间的地址寄存器CF8h,使其位为1,总线号及设备为0,功能号及寄存器号为0,即往I/O端口CF8h80000000h;然后用I/O命令读取配置空间的数据寄存器CFCh。如果该寄存器值与该PCI设备的Device ID及Vendor ID不相符,则依次递增设备号/总线号,重复上述操作直到找到该设备为止。如果查完所有的设备号/总线号(1~5)仍不能找到该设备,则应当考虑硬件上的问题。对于多功能设备,只要设备配置寄存器相应的功能号值,其余步骤与单功能设备一样。
如查找设备号为9054h,销售商号为10b5的单功能PCI设备,用VC++6.0编写的程序如下:
char bus;char device;
unsigned int ioa0,iod;
int scan( )
{
bus=0;device=0;
for(char i=0;i<5;i++) {
for(char j=0;j<32;j++) {
bus=i; device=j;
ioa0=0x80000000+bus*0x10000
+(device*8)*0x100;
_outpd(0xcf8,ioa0);
iod=_inpd(0xcfc);
if (iod0= =0x905410b5) return 0;
}
}
retrn -1
}
调用子程序scan( ),如果返回值为-1,则没有找到该PCI设备。如果返回值为0,则找到了该PCI设备。该设备的总线号和设备号分别在全局变量bus和device中,利用这两个变量即可轻易对该设备的配置空间进行访问,从而得到分配的资源信息。假设该PCI设备占用了4个资源空间,分别对应于配置空间10h~1ch,其中前两个为I/O空间,后两个为内存空间,若定义其基地址分别为ioaddr1,ioaddr2,memaddr1,memaddr2,相应的程序如下:
unsigned short ioaddr1,ioaddr2;
unsigned int memaddr1,memaddr2;
unsigned int iobase,ioa;
void getbaseaddr(char bus,char device);
{
iobase=0x80000000+bus*0x10000+(device*8)*0x100;
ioa=iobase+0x10;/*寻址基地址寄存器0*/
_outpd(0xcf8,ioa);
ioaddr1=(unsigned short)_inpd(0xcfc)&0xfffc;
/*屏蔽低两位和高16位*/
ioa=iobase+0x14; /*寻址基地址寄存器1*/
_outpd(0xcf8,ioa);
ioaddr2=(unsigned short)_inpd(0xcfc)&0xfffc;
ioa=iobase+0x18;/*寻址基地寄存器2*/
_outpd(0xcf8,ioa);
memaddr1=_inpd(0xcfc) & 0xfffffff0;
/*屏蔽低4位*/
ioa=iobase+0x1c; /*寻址基地址寄存器3*/
_outpd(0xcf8,ioa);
memaddr2=_inpd(0xcfc) & 0xfffffff0;
}
对于I/O基地址,两位D0、D1固定为01,对地址本身无效,应当被屏蔽。对PC-AT兼容机,I/O有效地址为16位,因此高位也应被屏蔽。对于内存地址,位D0固定为0,而D1~D3用于指示该地址的一些物理特性[1],因此其低4位地址应当被屏蔽。需要指出的是该内存地址是系统的物理地址,在WINDOWS运行于保护模式时,需要经过转换得到相应的线性地址才能对该内存空间进行直接读写。介绍该转换方法的相关文章较为常见,此处不再赘述。
上述程序给出了读取配置空间里的基地址的方法。另有相当多PCI设备通过配置空间的设备关联区来设置该设备的工作状态,可轻易地用I/O命令进行相应的设置,无须编写繁杂的驱动程序。在开发PCI视频图像采集卡的过程中,该方法得到了实际应用。相信可以给大家带来不少方便。
[1]. PCI datasheet https://www.dzsc.com/datasheet/PCI+_1201469.html.
免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。