MMIO
MMIO(Memory mapping I/O)即内存映射I/O,它是PCI规范的一部分,I/O设备被放置在内存空间而不是I/O空间。 从处理器的角度看,内存映射I/O后系统设备访问起来和内存一样。这样访问AGP/PCI-E显卡上的帧缓存,BIOS,PCI设备就可以使用读写内存一样的汇编指令完成,简化了程序设计的难度和接口的复杂性。
MMIO就是通过将外围设备映射到内存空间,便于CPU的访问。
I/O作为CPU和外设交流的一个渠道,主要分为两种,一种是Port I/O,一种是MMIO(Memory mapping I/O)。
PCIe总线中有两种MMIO:P-MMIO和NP-MMIO。P-MMIO,即可预取的MMIO(Prefetchable MMIO);NP-MMIO,即不可预取的MMIO(Non-Prefetchable MMIO)。其中P-MMIO读取数据并不会改变数据的值。
Port IO
端口映射I/O(port-mapped I/O), CPU使用专门的I/O指令对设备进行访问, 并把设备的地址称作端口号. 在执行其中的一条指令时,CPU使用地址总线选择所请求的I/O端口,使用数据总线在CPU寄存器和端口之间传送数据。
主要目的是提供对I/O编程的统一方法,但又不牺牲性能。为了达到这个目的,每个设备的I/O 端口都被组织成一组专用寄存器。CPU把要发给设备的命令写入控制寄存器(control register),并从状态寄存器(status register)中读出表示设备内部状态的值。CPU还可以通过读取输入寄存器(input register)的内容从设备取得数据,也可以通过向输出寄存器(output register)中写入字节而把数据输出到设备。
IO地址空间&内存地址空间
IO地址空间
访问外部设备寄存器的地址区域,(PCI支持4GB的IO空间,但是x86平台为 64KB),因为X86平台只支持64KB的IO空间,Endpoint为了能在X86上使用,只 能把自己的消耗的IO资源限制在64KB以内。由于Endpoint纷纷把IO资源的消耗 限制在64KB,因此,大部分其他架构的CPU也把IO空间限制到64KB以内了。
内存地址空间
访问memory的地址空间,32位平台为4G。 此memory空间和main memory(平 时常说的内存或者主存)是两个概念,32bit平台下CPU memory地址总线只能 寻址到4G,这4G空间包括main memory、外设IO 、空间映射(MMIO)等,不 能全给main memory,因此32bit的CPU是无法配置4G内存的。
PortIO和MMIO 的主要区别
-
前者不占用CPU的物理地址空间,后者占有。
-
前者是顺序访问。也就是说在一条I/O指令完成前,下一条指令不会执行。
-
使用方式不同由于port I/O有独立的64K I/O地址 空间,但CPU的地址线只有一套,所以必须区分地址属于物理地址空间还是I/O地址空间。
MMIO内存映射
DRAM Controller View就是内存控制器能够控制的内存地址的范围,那顾名思义,内存控制器的这个控制范围主要肯定就是内存的大小范围,在内存还没有增长到今天这个范围的以前,内存控制器能够识别的大小可能只有2G,而今天它能够识别的范围远远超过之前。
System View是指从CPU的角度来看,当前系统的地址范围到底是多大。MMIO是将IO设备也同时映射到内存地址空间进行统一的编址,这样的话CPU能发现的地址范围肯定是要大于内存控制器所能发现的地址范围的,所以在上图中也能够看到System View是远大于DRAM Controller View的。
0-1M:被分配成DOS Legacy Address Range
1M-TOLUD (Top of Low Usable Dram ):被分配成Low Main Memory Address Range,这一部分主要是Main Memory,也就是对应DRAM,但是在这段memory中间还有一些小小的地址被占用作他用,比如ISA Hole TSEG等,这些被占用的地址是极少部分,绝大部分都是被分配用在了memory上,所以这些地址大部分是可以直接被DRAM Controller访问找到对应的DRAM的
TOLUD-4G:被分配成为PCI Memory Address Range (MMIO Low),其中主要分了三个部分 Flash APCI等,DMI Interface 和 PCIe Configuration Space 。这部分地址就是被MMIO所占用的,所以这部分地址就不能让DRAM Controller在DRAM上找到对应的位置了,因为这地址表示的就不是DRAM
4G -TOUUD (Top of Upper Usable DRAM) :Upper Main Memory Address Range, 这部分就比较容易理解,就是DRAM中超过4G的部分,所对应的地址
TOUUD-TOUUD+X : Reclaim Address 对4G以下MMIO占用部分内存的重新映射,利用这部分地址就能够重新利用之前浪费掉的地址空间。
TOUUD+X- MAX range : MMIO High 这部分都可以用来作为PCIE MMIO 的地址了 并且利用这部分地址也不会再占用内存地址造成内存空间的浪费了
CPU通过MMIO访问外设
CPU如果想访问某个设备的空间,由于它不能访问PCIe设备配置空间,会让RC通过TLP把数据从PCIe外设读到Host内存,
然后CPU从Host内存读数据;
如果CPU要往外设写数据,则先把数据在内存中准备好,
然后RC通过TLP写入到PCIe设备。
下图例子中,最左边虚线的表示CPU要读Endpoint A的数据,RC则通过TLP(经历Switch)数据交互获得数据,并把它写入到系统内存中,然后CPU从内存中读取数据(紫色箭头所示),从而CPU间接完成对PCIe设备数据的读取。
具体实现就是上电的时候,系统把PCIe设备开放的空间(系统软件可见)映射到内存空间,CPU要访问该PCIe设备空间,只需访问对应的内存空间。RC检查该内存地址,如果发现该内存空间地址是某个PCIe设备空间的映射,就会触发其产生TLP,去访问对应的PCIe设备,读取或者写入PCIe设备。
一个PCIe设备,可能有若干个内部空间(属性可能不一样,比如有些可预读,有些不可预读)需要映射到内存空间,设备出厂时,这些空间的大小和属性都写在Configuration BAR寄存器里面,然后上电后,系统软件读取这些BAR,分别为其分配对应的系统内存空间,并把相应的内存基地址写回到BAR。
下图例子,一个PCIe Endpoint,只支持Memory Map,它有两个不同属性的内部空间要开放给系统软件,因此,它可以分别映射到系统内存空间的两个地方;还有一个Legacy Endpoint,它既支持Memory Map,还支持IO Map,它也有两个不同属性的内部空间,分别映射到系统内存空间和IO空间。
32bit memory address space request
PCIe设备,系统软件是如何为其分配映射空间的 (32 Bit memory address)
上电时,系统软件首先会读取PCIe设备的BAR0,得到数据:(1)
然后系统软件往该BAR0写入全1,得到:(2)
BAR寄存器有些bit是只读的,是PCIe设备在出厂前就固定好的bit,写全1进去,如果值保持不变,就说明这些bit是厂家固化好的,这些固化好的bit提供了这块内部空间的一些信息
解读:低12没变,表明该设备空间大小是4KB(2的12次方),然后低4位表明了该存储空间的一些属性,这些都是PCIe设备在出厂前都设置好的,提供给系统软件的信息。
然后系统软件根据这些信息,在系统内存空间找到这样一块地方来映射这4KB的空间,把分配的基地址写入到BAR0:(3)
从而最终完成了该PCIe空间的映射。一个PCIe设备可能有若干个内部空间需要开放出来,系统软件依次读取BAR1,BAR2。。。,直到BAR5,完成所有内部空间的映射。
64bit memory address space request
64bit memory address 和32bit memory address 申请方式类似,区别在于需要用到2个连续的BAR
IO address space request
IO地址空间申请和MMIO区别就是申请的地址空间是IO地址
Linux系统下设备的内存映射
参考链接:
https://blog.csdn.net/weixin_43921686/article/details/129106423
https://blog.csdn.net/mushui_954745742/article/details/128578112