目录
PCIe系统
在一个PCIe系统中,最多支持256条Bus,每条Bus最多可以挂32个设备(Device) ,每个设备最多可以实现8个Function。
BDF地址:指的是在PCIe域中可以根据Bus、Device、Function可以定位到具体的Function。
配置空间和内部空间
每个Function都会有一个自己的配置空间(如下图)。整个配置空间是一系列寄存器的集合。
- 主机软件可以通过读取配置空间获得每个设备的每个Function的信息。
- 主机软件可以通过配置空间配置设备。
同时,每个设备的每个Function会有若干个内部空间。空间的大小和属性存放于配置空间Header中的Base Address寄存器中。下图是Header的寄存器描述:一行是4个Byte,总共64Byte。这里我们只需要关注Base Address Register部分。
CPU如何访问每个内部空间
CPU只能直接访问主机内存( Memory)空间(或者IO空间),不能对PCIe等外设进行直接操作。Root Complex(以下简称RC)可以帮助CPU实现与外设进行数据交互。
- 如果CPU想读PCIe外设的数据,先叫RC通过TLP把数据从PCIe外设读到主机内存,然后CPU从主机内存读数据;
- 如果CPU要往外设写数据,则先把数据写入内存,然后通知RC通过TLP(Transaction Layer packet,具体请翻阅PCIe传输层部分内容)把数据写入到PCIe设备。
具体来说,CPU读取某个PCIe设备(以下论述默认设备只有一个function)数据过程:
- 系统把PCIe设备内部空间映射到内存地址空间
- CPU访问对应的内存地址空间;
- RC检查该内存地址,如果发现该内存地址空间是目标PCIe设备空间的映射,就会产生Memory Read TLP,去访问对应的PCIe设备;
- PCIe设备产生Completion TLP(包括数据和一些附加信息)送回RC;
- RC修改内存地址空间的数值;
- CPU直接读取这个地址空间的数据。
关键点:系统如何把PCIe设备内部空间映射到内存地址空间?
- 上电时,系统软件首先会读取PCIe设备的BAR0;
如何读取参考下一节- 系统软件往该BAR0写入全1;
BAR寄存器有些bit是只读的,是PCIe设备在出厂前就固定好的, 写全1进去,如果值保持不变,就说明这些bit是厂家固化好的,这些固化好的bit提供了这块内部空间的一些信息:
低12位没变,则[31:12]表示的是基地址,这个基地址指的是内部空间在主机内存中的基地址,暂时还没写入有效数据。由于[11:0]为固定位,则表示该设备空间大小是4KB( 2^12Byte)。[3:0]存放的是内部空间的一些属性:
( IO映射还是内存映射[0]? 32bit地址还是64bit地址[2:1]?能否预取[3]?有些寄存器只要一读,数据就会清掉。对这样的空间,是不能预读的,因为预读会改变原来的值)
- 系统根据这些信息,在系统内存空间中选择一块地方来映射这4KB的空间,分配的空间的基地址则写入Base Address Register[31:12]中。
CPU如何访问配置空间
- 系统已经为所有可能的配置空间做了内存映射:
PCI ECAM也叫做PCIBAR,是PCIe配置地址空间的映射地址。它的起始地址可调,台式机BIOS一般会把它设置得很高,这样4G以下内存会比较大,方便32位Windows使用。它的大小可以修改,一般可以设为64MB和128MB。
系统软件想访问哪个Configuration,只需指定相应Function对应的内存空间地址, RC发现这个地址是Configuration的映射空间地址,就会产生相应的Configuration Read TLP(映射地址→BDF)去获得相应Function的配置空间。
内存地址空间 PCIe配置地址空间 BDF
上图是Configuration Read TLP的Header,Bus Number+Device+Function就唯一决定了目标位置;Ext Reg Number+Register Number相当于配置空间的偏移。找到设备,然后指定配置空间的偏移,就能找到具体想访问的配置空间的某个位置(寄存器)。
只有RC才能发起配置空间的访问请求,其他设备是不允许对别的设备进行配置空间读写的。
参考文献
1、深入浅出SSD — 固态存储核心技术原理与实战
2、内存是怎么映射到物理地址空间的?内存是连续分布的吗?By 老狼