pcie内存地址空间/配置地址空间/io地址空间分析

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等低速总线一个最大的特色。

————————————————

原文链接:https://blog.csdn.net/RadianceBlau/article/details/81608729

基地址寄存器(BAR)在配置空间(Configuration Space)中的位置如下图所示:

blob.png
在这里插入图片描述
其中Type0 Header最多有6个BAR,而Type1 Header最多有两个BAR。这就意味着,对于Endpoint来说,最多可以拥有6个不同的地址空间。但是实际应用中基本上不会用到6个,通常1~3个BAR比较常见。

主要注意的是,如果某个设备的BAR没有被全部使用,则对应的BAR应被硬件全被设置为0,并且告知软件这些BAR是不可以操作的。对于被使用的BAR来说,其部分低比特位是不可以被软件操作的,只有其高比特位才可以被软件操作。而这些不可操作的低比特决定了当前BAR支持的操作类型和可申请的地址空间的大小。

一旦BAR的值确定了(Have been programmed),其指定范围内的当前设备中的内部寄存器(或内部存储空间)就可以被访问了。当该设备确认某一个请求(Request)中的地址在自己的BAR的范围内,便会接受这请求。

下面用几个简单的例子来熟悉BAR的机制:

例1. 32-bit Memory Address Space Request

如下图所示,请求一个4KB的NP-MMIO一般需要以下三个步骤:
在这里插入图片描述
blob.png

Step1:如图中(1)所示,未初始化的BAR的低比特(114)都是0,高比特(3112)都是不确定的值。所谓初始化,就是系统(软件)向整个BAR都写1,来确定BAR的可操作的最低位是哪一位。当前可操作的最低位为12,因此当前BAR可申请的(最小)地址空间大小为4KB(212)。如果可操作的最低位为20,则该BAR可申请的(最小)地址空间大小为1MB(220)。

Step2:完成初始化(写1操作)之后,软件便开始读取BAR的值,来确定每一个BAR对应的地址空间大小和类型。其中操作的类型一般由最低四位所决定,具体如上图右侧部分所示。

Step3:最后一步是,软件向BAR的高比特写入地址空间的起始地址(Start Address)。如图中所示,为0xF9000000。

例2. 64-bit Memory Address Space Request

下面是一个申请64MB P-MMIO地址空间的例子,由于采用的是64-bit的地址,因此需要两个BAR。具体如下图所示:

blob.png在这里插入图片描述

例3. IO Address Space Request

下面是一个申请IO地址空间的例子,如下图所示:

blob.png在这里插入图片描述

注:需要特别注意的是,软件对BAR的检测与操作(Evaluating)必须是顺序执行的,即先BAR0,然后BAR1,……,直到BAR5。当软件检测到那些被硬件设置为全0的BAR,则认为这个BAR没有被使用。

注:无论是PCI还是PCIe,都没有明确规定,第一个使用的BAR必须是BAR0。事实上,只要设计者原意,完全可以将BAR4作为第一个BAR,并将BAR0~BAR3都设置为不使用。

I/O端口是驱动程序与许多设备之间的通信方式,Linux的内核为我们提供了I/O端口分配的操作接口,但对PCI设备来讲,它的配置地址空间已经为其指定了I/O端口范围,不需要额外的分配操作。

下列代码通过访问I/O内存实现访问设备内存。

unsigned long mmio_start, addr1, addr2;
void __iomem *ioaddr;
mmio_start = pci_resource_start( pdev, 1);//获取bar1的首地址

ioaddr = pci_iomap(pdev, 1, 0); //内核对PCI bar1 内存映射的地址
addr1 = ioread32( ioaddr );
addr2 = ioread32( ioaddr + 4 );
printk(KERN_INFO “mmio start: %lX\n”, mmio_start);
printk(KERN_INFO “ioaddr: %p\n”, ioaddr);
————————————————

原文链接:https://blog.csdn.net/qq_22042587/article/details/78175648

### PCIe配置空间 PCIe配置空间是一段特殊的空间,用于为外设内存空间、I/O空间分配物理地址基地址,即配置BAR(Base Address Registers)[^2]。这段空间具有固定结构,在PCI总线扫描设备时会初始化并设置好这些寄存器的内容,从而使得各PCI设备可以被正确寻址而不会发生冲突。 ```cpp // 示例:读取PCI配置空间中的Vendor IDDevice ID uint16_t vendor_id; uint16_t device_id; vendor_id = pci_read_config_word(bus, slot, func, PCI_VENDOR_ID); device_id = pci_read_config_word(bus, slot, func, PCI_DEVICE_ID); ``` ### Mem32地址空间 Mem32指的是32位平台下的内存地址空间,其大小通常可达4GB(对于32位系统而言),主要用于访问系统的RAM以及映射到该范围内的外围设备资源[^3]。在外围设备方面,它允许CPU通过直接存储器访问(DMA)等方式高效地传输数据给外部硬件组件。 ```assembly ; 假设我们要向位于mem32空间某处的显卡帧缓冲区写入颜色值 mov eax, 0xAABBCCDD ; 设置要写的像素颜色 mov edi, [framebuffer_base_address]; 获取帧缓冲区起始位置 mov dword ptr [edi], eax ; 将颜色值写入指定位置 ``` ### I/O空间 相比之下,I/O空间则是专门用来访问外部设备寄存器的一片独立区域。这种设计让操作系统可以通过特定端口发送命令或查询状态信息给连接着的各种扩展板卡或其他类型的附加装置。由于采用了不同于常规内存的操作机制,因此即使是在同一套体系结构内也可能会有不同的指令集来处理这两种不同性质的数据交换过程。 ```c // C语言示例:通过inb/outb函数实现简单的串行通信控制 unsigned char status = inb(COM1_BASE_PORT); // 从COM1获取当前状态字节 outb(COM1_BASE_PORT, DATA_TO_SEND); // 向COM1发送一字节数据 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值