从CPU角度理解PCIe续集

原创内容,转载请注明:  [http://www.ssdfans.com]  谢谢!

  • 概述

上篇文章剩下两个问题,上电扫描PCIe树和存储地址到PCIe地址的映射,本篇文章将对这两个问题做出解答。本文可能会针对某一款芯片做出详细流程解答,读者可以只关注整个流程,具体映射机制和寄存器参考芯片datasheet。上篇文章已经了解到如何访问配置空间,前256Bytes可以通过寄存器方式访问,后面的256B~4k必须通过映射才能访问,映射无非就是把配置空间映射到存储地址空间,或者把PCIe设备空间映射到存储地址空间。下面开始讨论映射关系。

  • 地址映射关系

PCIe在存储域地址空间分为三部分,PCIe控制器本身的寄存器、PCIe设备的配置空间、PCIe设备空间。寄存器和配置空间由处理器本身决定存储地址范围,本款处理器地址范围如图 1所示,配置空间地址、寄存器地址、内存地址都已经确定。PCIe设备空间需要编程人员去配置Outbound和Inbound寄存器组,确定映射关系。

图1

Outbound在PCIe控制器中扮演的角色是将存储地址翻译到PCIe域的PCIe地址,Inbound是将PCIe地址翻译成存储地址,图 2是一个完整的RC和EP模型地址翻译模型,图中的地址数字仅仅代表一种形态,具体地址应该是什么在后文中讲解。当cpu需要访问EP的内存空间时,首先应该将存储地址转换成PCIe地址,在根据TLP到达指定的EP,进而将PCIe地址转换成EP端的存储地址。

图2

PCIe地址到存储地址之间的映射关系由三个寄存器决定(有两个寄存器组应该是32个寄存器)OB_SIZE、OB_OFFSET_INDEXn、OB_OFFSETn_HI,n的范围是0~31。在PCIe控制器中是把PCIe地址等分成32块regions (Regions 0 to 31),每个regions的大小是可以通过编程设置OB_SIZE寄存器确定大小,大小有1, 2, 4, or 8 MB,那么通过Outbound能够翻译的地址最大为8M*32=256M。存储域地址中有5位作为识别32个regions的index,OB_SIZE的大小决定这5位在32位地址上的位置。当OB_SIZE等于0,1,2,3时,index在存储地址中对应的位置是Bits[24:20], bits[25:21], bits[26:22], and bits[27:23],每个regions翻倍,是不是对应的地址应该按翻倍对齐呢,翻倍就是左移一位数据。OB_SIZE寄存器如图 3所示。

图3

OB_OFFSET_INDEXn寄存器结构如图 4所示,n是上一段落提到的index的值。该寄存器第0位是地址翻译使能位,第31~20位是第n个regions的基地址的31~20位,这里的取值取决regions的大小,当OB_SIZE 等于0,1,2,3时,bits[31:20], bits[31:21], bits[31:22], and bits[31:23]位相应被使用。OB_OFFSETn_HI寄存器的值是64位PCIe地址中第n个regions的基地址的63~32位,在32位PCIe地址中,该寄存器的值等于0。

图4

配置OutBound翻译的几个寄存器也做了详解,下面根据举例说明。图 5中配置空间存储地址由CPU本身架构所决定,这部分的地址映射才芯片内部完成,不需要由编程人员配置。PCIe设备空间被分成了32等分。假设region大小是2M,PCIe地址是64位,程序中需要对0x9D3A_1234存储地址做映射, 64位PCIe地址被使用在region 9上,初始化OBOFFSET9_HI值为0x3344 5566, OB_OFFSET9值56Ex xxxx(x的值这里不关心,看该寄存器结构就很清楚,第0位在地址翻译时候应该使能位1,这里仅仅用来讲解怎么做映射,不需要关心后面的Bits) ,下面分析怎么翻译到PCIe地址:

  • 由于是regions大小2M,那么index应该取地址的bits [25:21],提取0x9D3A_1234存储地址的bits [25:21得到01001b,该值等于9,那么该地址应该启用regions 9 翻译。存储地址的bits[20:00]是用做翻译到PCIe地址的bits[20:00]位,该值也可以理解成reginos 9内的偏移值,值是0x001A 1234。
  • 生成regions 块PCIe的基地址,该地址应pcie_base=OBOFFSET9_HI <<32 + OB_OFFSET9的bits[31:21] = 0x 3344 5566 56E0 0000。

计算PCIe地址,pcie_addr = pcie_base + 存储地址bits[20:0] =0x3344 5566 56FA 1234。

图5

从上面存储地址到PCIe地址映射可以看到,通过cpu寻址可以直接访问到PCIe设备空间,最多可以访问PCIe设备空间大小为256M,具体Outbound能够访问的大小根据芯片而定,当CPU与FPGA之间有大量数据交互时候也可以采用Inbound方式(Inbound地址翻译流程如图 6所示,这里就不在翻译),将CPU的内存映射到FPGA的寻址空间(这里是站在CPU角度看的,从图2可以理解具体映射大小还由EP决定),FPGA可以采用DMA方式访问cpu的内存,并且速度很快。有些芯片厂商干脆采用同核异构方式将CPU于FPGA集成在一起(有的将cpu与dsp集成在一起),两者之间采用AXI高速总线通讯。

图6

  • 扫描PCIe

扫描树的流程如下:

  • 建立存储地址到PCIe地址映射 (映射方式上面段落已经讲解了,固定的PCIe配置空间映射)
  • 分配PCIe总线号
  • 分配设备号
  • 访问配置空间 (这里有一个原则读者需要注意,对PCIe设备配置空间访问时,一定要确定总线号、设备号、功能号、寄存器,不然无法找到设备)
  • 读写BAR0确定PCIe设备1空间大小
  • 分配PCIe地址
    • 分配总线号

扫描PCIe总线树时,需要对这些PCIe总线进行编号,即初始化PCIe桥(在本文一律指透明桥)的Primary、Secondary和Subordinate Bus寄存器。在Linux内核中采用DFS算法对PCIe总线树进行遍历,DFS算法是按照深度优先的原则遍历PCIe树,局部代码如图 7所示,这里可以跟踪pci_scan_bridge函数,函数采用DFS算法对总线进行编号(后期会讲解搜索树,比较常见hash表、红黑树)。

图7

  • 分配设备号

PCIe设备的IDSEL信号与PCIe总线的AD[31:0]信号的连接关系决定了该设备在这条总线上的设备号。在配置读写总线事务的地址周期中,AD[10:0]已经被信号已经被功能号和寄存器号使用,因此PCIe设备的IDSEL只能与AD[31:11]信号连接。上一篇文章中谈到CONFIG_ADDR寄存器中的Device Number字段一共有5位,最大能够表示32个设备,这里只有21位,显然在两者之间不能建立一一映射关系。一个PCIe总线号下最多可以挂在21个PCIe设备,那么多个PCIe总线不就可以挂载32个设备了么。

  • 访问配置空间

在32位PCIe地址空间中,PCIe设备通常将PCIe配置存放在E2PROM中,PCIe设备进行上电初始化时,将E2PROM中的信息读到PCIe设备的配置空间作为初始值,由硬件自动完成。BAR0空间存储了PCIe设备空间的大小,某些位被设置成不可预读,当BAR0全部写入1时,然后在读取BAR0值,从数据低位看有多少连续位没有改变。没有改变的数据位数记录的该PCIe设备空间的大小,假如有n位没有改变,那么设备空间大小应该是2的n次方。第0位代表IO/Memory、第2,3位代表32/64位地址、第4位代表是否可预取,具体位定义格式可以直接参考内核PCIe总线代码,解析BAR函数如图 8所示。

图8

  • 分配PCIe地址

系统软件根据根据设备空间大小建立存储地址PCIe设备地址空间的映射,给PCIe设备分配的PCIe基地址写入到BAR0,如果是64位PCIe地址,那么BAR1是高32位地址。

  • 结语

写《从cpu角度理解PCIe》文章我参考了部分芯片的datasheet,并结合linux代码分析,本文仅仅起到分析流程的作用,具体映射机制和寄存器芯片参考相应芯片datasheet。如有错误,还望指正。

分类目录 SSD, 技术文章.
扫一扫二维码或者微信搜索公众号ssdfans关注(添加朋友->点最下面的公众号->搜索ssdfans),可以经常看到SSD技术和产业的文章(SSD Fans只推送干货)。
ssdfans微信群介绍
技术讨论群 覆盖2000多位中国和世界华人圈SSD以及存储技术精英
固件、软件、测试群 固件、软件和测试技术讨论
异构计算群 讨论人工智能和GPU、FPGA、CPU异构计算
ASIC-FPGA群 芯片和FPGA硬件技术讨论群
闪存器件群 NAND、3D XPoint等固态存储介质技术讨论
企业级 企业级SSD、企业级存储
销售群 全国SSD供应商都在这里,砍砍价,会比某东便宜20%
工作求职群 存储行业换工作,发招聘,要关注各大公司招聘信息,赶快来
高管群 各大SSD相关存储公司高管和创始人、投资人

想加入这些群,请微信扫描下面二维码,或搜索nanoarchplus,加阿呆为微信好友,介绍你的昵称-单位-职务,注明群名,拉你进群。SSD业界需要什么帮助,也可以找阿呆聊。