PCI总线的基础知识快速学习(二)(适合新手)
一、系统地址空间、PCI的地址空间和PCI的配置空间之间的映射关系
从上一篇我们知道PCI有32位或64位的地址/数据复用线,所以对于一个32位地址线的PCI主设备,如果它发起存储器访问,它可以寻址到的最大地址是B=
B*
*
*
=1KB*1024*1024*4=4GB,对于64位,它可以寻址到的最大地址是16EB(1EB=
B),这两个数据也就是PCI总线上的所有设备内部拥有的最大存储空间(如果发起IO访问,也是这么大)。一般情况下32位地址就足够使用,所以下面的讨论全部是针对32位PCI总线进行的。
学过计算机组成原理的朋友应该知道,老式的x86CPU的计算机内部存在着两个独立的物理地址空间,一个叫做存储器空间,一个叫做IO空间,这里我把它们统称为系统地址空间。从上一篇PCI的文章中,我们知道PCI总线上的事务有存储器读和存储器写,也有IO读和IO写,也就是说对于一个PCI设备来说,它既可以访问PCI的存储器空间,也可以访问PCI的IO空间,这里我把PCI的这两个空间统称为PCI的地址空间(这两个空间都最大支持4GB)。实际上,系统的地址空间和PCI的地址空间是统一编址的,也就是说,对于一个老式的x86CPU的计算机,系统的存储器空间和PCI的存储器空间可以看作是一个空间,IO空间也同理。对于前端总线是32位的计算机系统,系统的存储器空间大小也是4GB,因为它和PCI的存储器空间统一编址,所以PCI的存储器空间其实并没有4GB。对于IO空间,理论上PCI的IO空间可以支持4GB,但是由于x86CPU只支持16位的IO地址线,即64KB的IO地址空间,所以PCI的IO空间也只在这64KB内。
除了上述两个独立的空间,还有一个独立的空间叫做PCI的配置空间,是用来配置PCI总线上的设备用的,PCI总线上的设备都有格式和布局统一的配置寄存器(后面会详解是哪种格式)。每一个PCI设备中的每个功能都有一个PCI配置空间,一个PCI配置空间最大为256B,整个PCI的总配置空间的理论最大值为256B*8功能/设备*32设备/总线*256条总线=16MB,然而实际上,一个PCI系统远没有用到这么大的配置空间。PCI的早期标准规定了PCI的配置空间是x86CPU不能直接访问的,访问必须通过IO空间的两个32位寄存器来进行间接访问,CONFIG_ADDRESS和CONFIG_DATA,它们的地址在系统IO空间的CF8h~CFBh和CFCh~CFFh,也叫作地址端口和数据端口。间接访问的意思就是CPU将配置地址写进CONFIG_ADDRESS,将配置数据写进CONFIG_DATA(或从这个寄存器读出),这个操作就被HOST桥转换成了PCI总线上的配置事务,各个PCI设备在认领了各自的配置事务之后就自然知道CPU访问的是它们的配置空间。
系统地址空间、PCI的地址空间和PCI的配置空间的关系如下图(原图地址http://blog.chinaaet.com/justlxy/p/5100053219)所示。图中左边的Memory Map是系统的存储器空间,中间的IO Map是系统的IO空间,右边的PCI Configuration Space是PCI的总配置空间。图中的紫色部分和PCI有关,左边的紫色部分代表PCI的存储器空间,中间的紫色部分代表PCI的IO空间和那两个间接访问PCI配置空间的寄存器。
上面的三个空间对于初学者来说,确实有点让人搞混淆,事实上,后面我们会知道,对于PCIe设备,使用增强型的配置访问机制,PCIe的配置空间和系统地址空间(包括存储器空间和IO空间)都是可以统一编址的,整个系统就可以看作只有一个空间。
二、PCI的配置空间
一个256字节的PCI配置空间分为两部分,64字节的头标区和192字节的设备关联区。头标区是每个PCI配置空间都具有的区域,就是前文提到的每个PCI设备都具有的格式统一的配置寄存器,设备关联区内的寄存器的设置则取决于每个PCI设备的具体功能。所以这里只介绍头标区。头标区主要分为00h和01h两种类型(还有插槽02h这种类型,但是设计中几乎遇不到,所以本文不讨论),分别用于一般的PCI设备和PCI桥设备,两种类型的头标区的前24个字节(00h~14h)的定义相同,如下两图所示,其中图3原图地址在http://blog.chinaaet.com/justlxy/p/5100053220。
头标区按功能分类,主要有以下几类寄存器,其中1~4是两种头标区共有的,5~6是01类型头标区才有的,没有介绍到的寄存器暂时可以不管,我会在PCIe系列文章中再介绍。
1、设备识别
操作系统首先通过Header Type(头标类型)寄存器来识别该PCI设备是普通PCI设备还是PCI桥或插槽。该寄存器的[6:0]是头标类型,只有3种类型,为0表示是普通PCI设备,为1表示是PCI桥,为2表示是插槽;最高位为0表示本设备是单功能设备,为1表示是多功能设备,如果是多功能设备,则表明这个设备有多个头标区。一般我们在设计中接触到的都是单功能设备,所以一个设备只有一个配置空间,也就只有一个头标区,我在后文的叙述中都默认这个设定。
在这之后,操作系统就会去进一步地识别这是个什么具体设备,有4个寄存器是和这个识别过程相关的,它们是:Device ID(设备ID)、Vendor ID(厂商ID)、Revision ID(修改版本ID)、Class Code(分类代码),如果是普通PCI设备还要参考额外的两个寄存器Subsystem ID(子系统ID)、Subsystem Vendor ID(子系统厂商ID)。操作系统就是根据这6个寄存器的值来加载相应的驱动程序的。
以上这7个寄存器都是只读的。
2、基地址寄存器(BAR)
在普通PCI设备的配置头标区中,基地址寄存器(Base Address Register)有6个,通常我们把它们叫做BAR0~BAR5,在桥设备的配置头标区中,BAR有2个。每一个基地址寄存器对应着一块系统地址空间,表示该PCI设备内部的寄存器映射到系统地址空间时(可以映射到系统存储器空间,也可以映射到IO空间),操作系统为该设备分配的统一编排的地址,基地址寄存器可读可写,它的内容同时也表达了该设备需要申请多大的系统地址空间。以下以BAR0为例来说明操作系统中的配置软件配置BAR的这个过程。
配置软件先写0xFFFFFFFF到BAR0,然后再读出该寄存器的值,此时读出的值的最后几位为0(最后几位被PCI设备硬拉到了0),比如读出的值如果是0xFFFFC000时,表明该寄存器的最后14位被硬拉成0,不接受写入的任何值,写入的值必须是高18位的值。这表示BAR0申请的系统地址空间为2^14B,即16MB,然后配置软件分配16MB的系统地址空间给该PCIE设备,并写入一个初始地址(这里假设是0x40000000)到BAR0,经过这个过程之后,该PCI设备内部的寄存器映射到了系统地址空间的0x40000000~0x40003FFF处,CPU直接访问0x40000000~0x40003FFF就是访问该设备内部的寄存器。
3、中断寄存器
操作系统通过查询Interrupt Pin(中断引脚)寄存器,来确认该PCI设备使用的是哪个中断引脚,1表示INTA#,2表示INTB#,3表示INTC#,4表示INTD#。Interrupt Lin(中断线)寄存器指明了该PCI设备的中断引脚连到了系统中断控制器的哪个输入上。
4、状态寄存器和命令寄存器
Status(状态)寄存器和Command(命令)寄存器都是十分重要的寄存器,但是本篇文章中不介绍(因为PCIe的这两个寄存器和PCI的区别较大,我们只需掌握PCIe的就行了),我将在PCIe系列文章中再详解。
下面两种类型的寄存器都是PCI桥设备用来判断是否将总线事务传递到桥的另一边时使用的。
5、总线号寄存器
这个寄存器位于01类型头标区的18h地址的低24位,可读可写。为了说明这个寄存器需要简单介绍下PCI总线的编号规则:对于一个多层总线的PCI系统来说,Host桥下面的那条PCI总线我们给它编号BUS0,然后从上到下,从左到右进行编号BUS1~BUSn。PCI系统是一个树形结构,我们在给总线编号时上下关系优先于左右关系,也就是先选定最左边的一个分支,将这个分支的所有PCI总线按1~m编号完之后再在左边第二个分支从m+1进行编号(此规则先大概了解下,我在介绍PCIe时会再次说明这个规则)。
对于PCI桥设备来说,连在它上游边的总线称之为它的原级总线(Primary Bus),连在它下游边的总线称之为它的次级总线(Secondary Bus),位于它的下游方向且最远的那条总线(也就是说如果它的下方还有若干层的话,编号最大的那条总线)称之为它的下属总线(Subordinate Bus)。
PCI桥用总线号寄存器提供的信息判断是否将一个配置事务传递到桥对面的总线上。
6、IO基点和存储器基点相关寄存器
这个类型的寄存器在01类型头标区的1Ch地址到38h地址中,凡是名字带有IO、Memory和ROM的寄存器都属于这个类型,是用来规定一个PCI桥的下游方向上的所有设备所占系统地址空间(可以是存储器空间,也可以是IO空间)的地址范围。当这个PCI桥的上下游总线上有存储器读写、IO读写的总线事务出现时,该PCI桥就根据这些寄存器规定的地址范围来判断是否将这个事务推向桥的另一边。
三、PCI的三种数据通信模式
1、Programmed I/O(程序IO)
程序IO就是任何数据传输都要经过CPU的一种通信方式。比如一个PCI设备需要向内存中写入数据,那么这个PCI设备首先会在PCI总线上发出一个中断,这个中断会通过HOST桥通知到CPU,CPU收到这个中断之后首先先发出前端总线的总线事务,借用HOST桥转换成PCI的总线事务,把该PCI设备的数据读到CPU内部的寄存器中,然后CPU再将数据从其内部寄存器写入到内存中,这个过程又要借用HOST桥把前端总线事务转换成内存协议(一般是DDR协议)的总线事务。显然,这种传输方式既要报告中断又需要CPU中转又要HOST桥中转,效率是很低的。
2、DMA(直接存储器访问)
DMA是Direct Memory Access,这是一种不需要CPU干预的数据传输方式。一个PCI设备如果要向内存中写入数据,则直接作为PCI主设备发起PCI的总线事务,经由HOST桥转换成内存协议的总线事务,这样子就可以直接访问内存。显然,这是一种高效的方式,现在的所有高速总线都支持这种方式。
3、Peer-to-Peer(点对点同级传输)
就是两个PCI设备之间的数据通信,可以不通过CPU直接进行(如果通过CPU就是程序IO),只要有一个设备经仲裁之后获得了PCI总线的控制权,它就是此时的主设备,另一个和它通信的设备就是此时的从设备。其实这种方式在本系列的第一篇文章中讲过的,PCI总线上的任何一个设备都可以申请成为主设备。
这三种通信模式如下图所示,原图地址在http://blog.chinaaet.com/justlxy/p/5100053095。
这两篇PCI的文章作为学习PCIe协议的基础,我个人认为已经足够了,PCIe的复杂程度远高于PCI,我以后会分成多篇文章慢慢讨论。如有任何错误,欢迎在留言区指出。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
