从cpu角度理解PCIe续集

  1. 概述

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

  1. 地址映射关系

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地址:

  1. 由于是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。
  2. 生成regions 块PCIe的基地址,该地址应pcie_base=OBOFFSET9_HI <<32 + OB_OFFSET9的bits[31:21] = 0x 3344 5566 56E0 0000。
  3. 计算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

  1. 扫描PCIe

扫描树的流程如下:

 

  1. 分配PCIe总线号
  2. 分配设备号
  3. 访问配置空间 (这里有一个原则读者需要注意,对PCIe设备配置空间访问时,一定要确定总线号、设备号、功能号、寄存器,不然无法找到设备)
  4. 读BAR0得到PCIe设备1空间大小(PCIe设备信息一般保存在片内ROM或者E2PROM中,上电由硬件逻辑完成从E2PROM中将程序自动初始化到PCI设备中)
  5. 分配PCIe地址,并写入到BAR空间
  6. 建立存储地址到PCIe地址映射 (映射方式上面段落已经讲解了,固定的PCIe配置空间映射)
    1. 分配总线号

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

                                                                                     图 7

    1. 分配设备号

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个设备了么。

    1. 访问配置空间

在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

 

  1. 分配PCIe地址

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

  1. 结语

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

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页