PCIe的内存地址空间、I/O地址空间和配置地址空间

PCIe的内存地址空间、I/O地址空间和配置地址空间

pci设备与其它接口的设备(如i2c设备)最大的不同是存在内存地址空间和配置地址空间,本文分析一下它们的用途。

首先区分一下IO空间和内存空间
cpu会访问的设备一般有内存和外设寄存器,如下图所示。x86架构采用独立编址将内存操作与外设IO操作分开了才有了内存空间和IO空间的区分。x86平台cpu内部对内存和外设寄存器访问的指令也是不同的。arm等其他平台都才有统一编址,不区分内存和外设的访问,我个人觉得这才是合理的。
IO空间:访问外部设备寄存器的地址区域,x86平台为64k
内存空间:访问内存的地址空间,32位平台为4G

pci设备的内存空间是怎么回事呢
常见的设备都只提供寄存器供cpu访问,对于低速外设这样的模式是足够的。但是对于需要大量、高速数据交互的外设就需要引入外设内存空间了。在网卡、显卡这样的pci高速外设中不仅有寄存器还有了一块内存

pci设备的配置空间和IO空间有什么区别呢
现在我们知道外设内存空间的大概用途了,那配置空间和IO空间不都是外设的寄存器吗,怎么还弄两个名字呢,它们有什么区别呢?
IO空间就和i2c设备的寄存器空间一样,用来获取外设状态、配置外设。
配置空间是一段特殊的IO空间,它的作用是为外设内存空间、IO空间分配物理地址基地址,即配置BAR(Base Address Registers)。这里类似linux对虚拟地址的映射了,但现在分配的却是物理地址。因为外设内部的内存地址都是从0开始编址的,当pci控制器接入多个pci设备时如何确保pci上的内存地址不混乱呢,这就是配置空间的一个作用,配置空间有固定的结构,在pci总线扫描设备时配置好BAR,这样各个pci设备的内存空间和IO空间才可访问,而不至于和其他设备物理地址冲突。
在pci总线之前的ISA总线是使用跳线帽来分配外设的物理地址的,每插入一个新设备都要改变跳线帽以分配物理地址,这是十分麻烦且易错的,但这样的方式似乎我们更容易理解。能够分配自己总线上挂载设备的物理地址这也是PCI总线相较于I2C、SPI等低速总线一个最大的特色

为什么只有pci设备可以有外设内存空间,i2c、usb等设备没有?
说起外设上可以有设备专属的内存时,指的都是pci设备。我们更为常用的i2c、usb、spi、uart设备怎么都没有这样的说法呢?
这是因为在cpu内部架构中会有系统总线和外设总线。系统总线一般是连接cpu与主存的,外设总线是cpu连接外部设备的。在有pci控制器的cpu架构中pci总线就取代了cpu内部的外设总线的位置,所有外设都是挂在不同层级的pci桥上的,再由pci转成了usb、i2c、uart等接口。因此pci设备相当于直接连在cpu总线上了,这样pci设备中就可以拥有cpu总线上的物理地址,和主存共享cpu的物理地址空间,当然就可以存在外设内存(相当于pci设备与其他外设的控制器在一个层级上)。i2c等外设只能通过i2c控制器间接的与cpu通信,pci设备拥有cpu的物理地址,可以直接与cpu对话。
可简要理解为pci总线类似cpu内部总线的延伸,i2c、usb等都仅仅是纯粹的外设总线,社会层级不同。

linux中对pci外设内存空间的使用
pci设备的内存空间配置好了物理地址就能在kernel中通过ioremap映射到内核的虚拟地址空间上去正常使用了。
ioremap就是kernel提供的用来映射外设寄存器到内核空间的函数。
读写相应的外设内存还是不能像实际的内存一样直接对地址直接赋值的,如*(int *)(0xfc430001)= 0x00002345。而需要使用内核提供的readl、writeb等函数,这同外设寄存器的操作一样
当对物理内存执行读写时,实际上是从物理ddr线上发起读写请求,对pci外设的内存执行读写操作时,实际是需要从pci总线发起读写请求的。pcie总线速率在物理上与ddr相差不大,但pci协议相较于ddr复杂一些(ddr总线上任务单一,每个时钟可以完成两次数据读写)速率应该会稍逊色一些,以后有机会在深入研究。

猜你喜欢

转载自blog.csdn.net/RadianceBlau/article/details/81608729
今日推荐