PCI的IO空间和memory空间

参考:IO内存映射到内存中(PCI总线的地址空间的分配)   https://blog.csdn.net/zjy900507/article/details/81740345

https://blog.csdn.net/kunkliu/article/details/93992948

 参考  https://blog.csdn.net/insoonior/article/details/8011192#


 

IO空间和memory空间的不同点:

1、 IO空间有限;虽说X86处理器的IO空间可以寻址4G;但是windows系统里给外设分配的IO空间共64KB; 所以就要求单个PCI设备若使用了IO空间,其大小不要超过256字节;
  MEMORY空间则没有这个限制,受限于操作系统能够管理的内存大小;
2、 在驱动程序里面访问IO空间,可以直接使用IO指令访问;
  在驱动程序里面访问memory 空间,需要先将其物理地址(physical address)映射到虚拟地址(virtual address),然后才能访问;
3 、memory 空间访问的时候可以支持burst, IO空间访问的时候只能一个一个访问,所以从性能上看,memory空间访问的性能要高于IO空间。

IO内存空间,是指IO的内存和寄存器都通过映射到内存空间中,访问的时候直接用内存访问即可。

IO地址空间,访问需要专门的I/O指令,最大为64K(目前只有X86还存在IO端口访问,为了兼容,现在已经建议使用MMIO)

 

IO端口和IO内存的区别及分别使用的函数接口 

https://blog.csdn.net/insoonior/article/details/8011192#

(访问寄存器需要先匹配地址,IO端口即IO地址空间,IO内存即IO内存空间)

         每个外设都是通过读写其寄存器来控制的。外设寄存器也称为I/O端口通常包括:控制寄存器、状态寄存器和数据寄存器三大类。根据访问外设寄存器的不同方式,可以把CPU分成两大类。一类CPU(如M68K,Power PC等)把这些寄存器看作内存的一部分,寄存器参与内存统一编址访问寄存器就通过访问一般的内存指令进行,所以,这种CPU没有专门用于设备I/O的指令。这就是所谓的“I/O内存方式。另一类CPU(典型的如X86),将外设的寄存器看成一个独立的地址空间,所以访问内存的指令不能用来访问这些寄存器,而要为对外设寄存器的读/写设置专用指令,如IN和OUT指令。这就是所谓的“ I/O端口”方式。但是,用于I/O指令的“地址空间”相对来说是很小的,如x86 CPU的I/O空间就只有64KB(0-0xffff)。

        结合下图,我们彻底讲述IO端口和IO内存以及内存之间的关系。主存16M字节的SDRAM,外设是个视频采集卡,上面有16M字节的SDRAM作为缓冲区。

 

1.   CPU是i386架构的情况

 在i386系列的处理中,内存和外部IO是独立编址,也是独立寻址的。MEM的内存空间是32位可以寻址到4G,IO空间是16位可以寻址到64K。

 在Linux内核中,访问外设上的IO Port必须通过IO Port的寻址方式。而访问IO Mem就比较罗嗦,外部MEM不能和主存一样访问,虽然大小上不相上下,可是外部MEM是没有在系统中注册的。访问外部IO MEM必须通过remap映射到内核的MEM空间后才能访问。为了达到接口的同一性,内核提供了IO Port到IO Mem的映射函数。映射后IO Port就可以看作是IO Mem,按照IO Mem的访问方式即可。

3.    CPU是ARM或PPC架构的情况

在这一类的嵌入式处理器中,IO Port的寻址方式是采用内存映射,也就是IO bus就是Mem bus。系统的寻址能力如果是32位,IO Port+Mem(包括IO Mem)可以达到4G。


https://blog.csdn.net/baidu_24256693/article/details/54927523

简单介绍了下PCI设备的地址空间支持PIO和MMIO,即IO端口和IO内存。下面详细分析下这两种方式:

PIO 

 IO端口的编址是独立于系统的地址空间,其实就是一段地址区域,所有外设的地址都映射到这段区域中。就像是一个进程内部的各个变量,公用进程地址空间一样。不同外设的IO端口不同。访问IO端口需要特殊的IO指令,OUT/IN,OUT用于write操作,in用于read操作。在此基础上,操作系统实现了读写不同大小端口的函数。为什么说是不同大小呢??因为前面也说到,IO端口实际上是一段连续的区域,每个端口理论上是字节为单位即8bit,那么要想读写16位的端口只能把相邻的端口进行合并,32位的端口也是如此。

例如下面的汇编指令:

OUT 21h,al

0x21是8259A中断控制器的中断屏蔽寄存器,该指令将Intel X86处理器的al寄存器中的值写到中断寄存器的控制寄存器中,从而达到屏蔽某些中断信号的目的。类似的,在下面的汇编指令中:

IN al,20h

0x20是8259A中断控制器的正在服务寄存器,该指令将此寄存器中的状态值传递到处理器的al寄存器中。

无论是windows还是Linux都会上述指令做了封装以满足读写不同长度端口的需要。不过这种方式缺点也比较明显,一般情况CPU分配给IO端口的空间都比较小,在当前外设存储日益增大的情况下很难满足需要。另一方面,

 

MMIO

IO内存是直接把寄存器的地址空间直接映射到系统地址空间,系统地址空间往往会保留一段内存区用于这种MMIO的映射(当然肯定是位于系统内存区),这样系统可以直接使用普通的访存指令直接访问设备的寄存器,随着计算机内存容量的日益增大,这种方式更是显出独特的优势,在性能至上的理念下,使用MMIO可以最大限度满足日益增长的系统和外设存储的需要。所以当前其实大多数外设都是采用MMIO的方式。

 

还有一种方式是把IO端口空间映射到内存空间,这样依然可以通过正常的访存指令访问IO端口,但是这种方式下依然受到IO空间大小的制约,可以说并没有解决实际问题。

 

但是上述方案只适用于在外设和内存进行小数据量的传输时,假如进行大数据量的传输,那么IO端口这种以字节为单位的传输就不用说了,IO内存虽然进行了内存映射,但是其映射的范围大小相对于大量的数据,仍然不值一提,所以即使采用IO内存仍然是满足不了需要,会让CPU大部分时间处理繁琐的映射,极大的浪费了CPU资源。那么这种情况就引入了DMA,直接内存访问。这种方式的传输由DMA控制器控制,CPU给DMA控制器下达传输指令后就转而处理其他的事务,然后DMA控制器就开始进行数据的传输,在完成数据的传输后通过中断的方式通知CPU,这样就可以极大的解放CPU。当然本次讨论的重点不在DMA,所以对于DMA的讨论仅限于此。


每一个PCI设备最多支持8种功能(Function),每一条PCI总线最多支持32个设备,而每一个PCI总线系统最多又支持256个子总线(通过PCI桥)。因此,总的Configuration Address Space的大小为:256 Bytes/function x 8 functions/device x 32 devices/bus x 256 buses/system = 16MB。

那么CPU是怎么访问这些配置寄存器的呢?要知道配置寄存器指定了设备存储寄存器的映射方式以及地址区间,但是配置寄存器本身的访问就是一个问题。为每个设备预留IO端口或者IO内存都是不现实的。

暂且不说x86架构下IO端口地址空间只有区区64K,就是内存,虽然现在随着科技的发展,内存空间越来越大,但是也不可能为每个设备预留空间。那么折中的方式就是为所有设备的配置寄存器使用同一个IO端口,(虽然通过同一个端口访问配置空间,但可以通过配置寄存器中的总线号、设备号、功能号来区别是哪一个逻辑设备)系统在IO地址空间预留了一段地址就是0xCF8~0xCFF一共八个字节,前四个字节做地址端口,后四个字节做数据端口。CPU访问某个设备的配置寄存器时,先向地址端口写入地址,然后从数据端口读写数据。这里的地址是一个综合地址,结构如下:

这里就可以解释我们总线数量和逻辑设备数量的限制了。在寻找某一个逻辑设备时,先根据总线号找到总线,然后根据设备号找到总线上的某个接口,最后在根据功能号定位某一个逻辑设备。

  • 10
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值