pcie 总线

Linux PCI设备驱动

cpu 对外设的访问方式

ram-like 接口设备访问

我们在写程序时,对变量的赋值,取值操作,实际上做了对内存进行读写。通过地址就可以对内存进行读写操作。实际上对芯片上的其它外设IO操作也是一样。

int* a=0x100000;
int* b=0x200000;
*a = 1000;
*b= *a;

CPU 与外设的连接结构图如下所示,通过地址线传输想要访问的地址,数据线传输数据,读写信号线确定你的操作是读/写。
可以看到总线上所挂的设备(ram、flash、gpio),它们对信号线都是共享的,那么怎么选中想要访问的设备呢? 它们每一个设备都有各自的地址段,当地址信号线上的地址属于自己的范围时,cs(片选信号)就会选中芯片,然后进行访问。
关键在于:内存控制器,它会根据地址范围发出对应的片选信号,选中对应的设备。
类似于这样的接口 叫ram-like接口,所有的ram-like 接口设备,cpu 都可以直接通过地址读写。
pcie 总线_第1张图片
下图是imx6ull 的片上外设控制器地址表(从0x0000_0000~ 0x8000_0000),可以看到不同的外设控制器各自有自己的地址段,0x8000_0000 是内存控制器。
pcie 总线_第2张图片
pcie 总线_第3张图片

无法直接用地址访问的设备

上图中的外设控制器都可以通过地址直接访问,但是它们管理的设备或其它设备是不能的。
例如我们在使用一个i2c设备时,需要读写i2c控制器,通过一定的协议才能完成对i2c设备的访问。i2c 协议
如emmc ,要像访问emmc 设备必须通过emmc 控制器,必须编写emmc控制器驱动。
这样的访问方式就会比较复杂,无法像内存一样轻松读写,因此pci 总线诞生。
pcie 总线_第4张图片

pci 总线

PCI总线(Peripheral Component Interconnect,外部设备互联),挂在pci 总线下的设备,cpu可以通过地址直接访问,让访问外设变得像内存一样简单,那么它是怎么做到的呢?
如图pci 控制器(host birdge:主桥又称北桥)跟其它片上设备一样挂在内存控制器下,可以通过地址直接访问,pci/pcie 则由它来管理。

当cpu 想要访问某个pci 设备时,cpu 发出的地址到达pci控制器后,它会将地址做偏移处理,转换成pci_addr。同样每一个pci设备都拥有属于自己的一段pci_addr,它们确认控制器发出的pci_addr属于自己的范围内,就会与其进行交互。
pci_addr = cpu_addr + offset
pcie 总线_第5张图片

pci 接口定义

pcie 总线_第6张图片
pci 接口定义如图所示:红色为必需信号线,蓝色为可选信号线。
必需的引脚中分为以下几个部分

  • 地址与数据
  1. AD[31:0] : 地址线与数据线是复用的。怎么控制它的功能是传输地址还是数据,使用 interface control 中的FRAME# 信号线。
  2. C/BE[3:0]# : 它也是一组复用信号线,当传输地址是它是命令 command;当传输数据时,它是字节使能 Byte enable,用于字节选择,可以进行单字节、字、双字访问;
  3. PAR : 奇偶校验信号,确保AD[31:00]和C/BE[3:0]#传递的正确性;
  • 控制接口
  1. FRAME# : 低电平有效,FRAME# 拉低的第一个时钟为AD 地址周期,下一个时钟为数据周期。
  2. IRDY#: 信号由PCI主设备驱动,信号有效时表示PCI主设备数据已经ready。
  3. TRDY#: 信号由目标设备驱动,信号有效时表示目标设备数据已经ready
  4. STOP# : 停止信号线,目标设备请求主设备停止当前总线事务。
  5. DEVSEL#: 设备选择。
  6. IDSEL : ID并非身份ID,而是Initialization Device Select(初始化设备选择),在读写设备配置空间时用于选中某个设备。
  • system
    CLK : 时钟信号线。
    RST#: 复位。

C/BE[3:0]# command 定义

在地址周期时,C/BE[3:0]# command 定义如下。
pcie 总线_第7张图片

pci 设备的访问

pci 总线 与设备的连接结构如图,与前面一样许多设备都挂在同一条pci 总线上,它们对信号线是共享的,通过地址来对指定的设备访问。
问题1: 那这些设备怎么确认自己的地址段呢?在用地址访问之前需要对pci 设备做配置,也就是读写配置寄存器。
pcie 总线_第8张图片

pci 配置空间访问——非桥设备(type0)

先看以下配置空间的结构:
每一条pci支总线最多可以拥有32个设备(实际上负载不了这么多),每一个设备都有8个功能,每一个功能有一块 256 Byte 的配置空间(所谓的配置空间即许多寄存器的集合)。
PCI设备可以简单地分为PCI Agent、PCI Bridge、Cardbus Bridge:

  • PCI Agent:真正的PCI设备(比如网卡),是PCI树的最末端
  • PCI Bridge:桥,用来扩展PCI设备,必定有一个Root Bridge,下面还可以有其他Bridge。
  • Cardbus Bridge:用得不多,不介绍。

根据pci 设备类型的不同,pci 配置空间也有不同。pci配置空间一共有256 字节,其中64 字节为头部空间,配置过程就是头部寄存器的读写。

  • device id 与 vendor id: 各占2 字节,cpu读取它们就可以知道厂商和设备。
  • header type: 设备类型,1字节。普通设备它的值为0x00,桥设备为0x01。
  • BAR: 普通设备在其中声明自己需要多大的空间,同样cpu 会把首地址写回 BAR。
    pcie 总线_第9张图片
    pci 设备的简单配置过程:
  • 设备会在配置空间中声明自己需要多大的空间
  • cpu获取到这个值,就会给它分配指定长度的空间,并将首地址写回pci 设备配置空间。
  • 有了首地址后,该设备就有了自己的地址范围,首地址+长度= 地址范围;之后cpu就可以通过地址访问这个设备。
    注意: cpu 的地址和pci 的地址是不一样的,pci控制器会对地址做转换。

问题2: pci 总线上有那么多设备,如何能访问到其中某一个设备?

  • 通过IDSEL# 引脚选中设备,PCI 接口上有AD[31:0] 32条地址线,例如将AD31接到设备1的IDSEL、AD30——设备2 IDSEL、AD29——设备3 IDSEL,以此类推当选中某个设备时只要将对应的地址线拉低即可选中设备。(配置时地址线bit 31-11都是保留,可以与IDSEL相连)
    pcie 总线_第10张图片
    问题3:如何访问设备上的某一个功能?配置空间中的哪一个寄存器?
  • 下图有定义AD[10:8] 表示功能序号,AD[7:2]表示寄存器序号。
    pcie 总线_第11张图片
    pci 非桥设备配置过程:
  1. AD31——IDSEL# 拉低选中设备。
  2. FRAME# 拉低,第一个时钟发送地址 AD[10:8] 选择功能,AD[7:2] 选择寄存器地址。
  3. C/BE# 发送命令 1010 读取配置空间。C/BE# 读写配置空间命令
  4. TRDY#、IRDY# 双方设备准备就绪后开始传输数据。
  5. 读取device id与vendor id 得知设备信息,header type 得知设备类型(桥或非桥)。
  6. 非桥设备读取BAR 获取需要分配的空间大小。
  7. 分配一段空间,将首地址写回BAR。配置完成
  8. 以此类推配置bus0 下的每一个设备。
    pcie 总线_第12张图片

pci 配置空间访问——桥设备(type1)

0x00~0x0c 这一段寄存器,除了header type值不同外其它都与type 0相同,0x10 开始都不同。
BAR: 桥设备不需要多大的BAR空间,因为它只需要做转发工作。
primary bus: 上一级总线的总线号。
secondary bus: 自己的总线号。
subordlinate bus: 子总线中最大的总线号。
pcie 总线_第13张图片
问题4:如何配置桥设备?

  • 跟一般的PCI设备一样,通过IDSEL来选中它。
  • 通过type 0配置命令读取配置空间,发现它是PCI桥设备,把分配的总线号写给它。

问题5:前面描述了如何配置 bus0 下的pci设备,那么如何配置bus1 下的pci设备呢?怎么通过桥转发到它的子设备。

  1. 主桥发出type 1的包。
  2. AD[23:16]:bus number 确定要访问哪个桥设备(哪一级总线)(桥的配置空间中有三个bus号分别是primary bus上级、secondary bus自己、subordlinate bus子级中最大)。
    当 bus number>=secondary bus && bus number <=subordlinate bus 时它就知道需要自己做转发。
  3. 如果bus number = secondary bus时,需要访问的桥设备号与当前桥号相等时,那么需要访问的设备就挂在这一级的总线下,桥设备发出type 0命令,这一级的桥通过ADXX——IDSEL选中设备,根据function number选择哪个功能,register number 选择寄存器,就可以对某一级桥下的设备进行读写。
  4. bus number > secondary bus && bus number <=subordlinate bus ,如果要访问的总线号大于当前桥设备的总线号,并且小于等于自己桥设备中最大的总线号,那么要访问的设备就在自己子级桥下,因此该桥会发出type1 命令找到下一级桥,以此类推直到发生步骤③。

这个过程类似于路由,需要到达的设备属于自己时,就转发给设备,不属于自己,就转发给下一级总线。

  • 桥:接收到Configuration Command type 1后,有2个选择
    • 对于跟它直连的PCI Agent
      • Configuration Command type 1转换为Configuration Command type 0
      • 使用IDSEL选择设备
    • 如果还需要通过下一级的桥才能访问此设备
      • 转发Configuration Command type 1

pcie 总线_第14张图片
pcie 总线_第15张图片

PCIE 总线

PCIE 总线即PCI Express(快速的pci)。
在我们的映像中并行的数据线(例如pci 的AD[31:0])相对传输速率比较高,但是当真正高速后,信号线之间会产生干扰,这种情况下反而使用串行信号线速度更快。
所以PCIE 的数据线改为串行的差分信号线(一个方向上有两根信号线tx+、tx-)。
PCIE 的接口引脚如图:
PCIE_TXP/N、PCIE_RXP/N,构成了一组发送与接收的信号线,被称为一个 lane ,P为+、N为-。
PCIE_REFCLKP/N 时钟也是差分信号。
pcie 总线_第16张图片
PCI接口的引脚时并行的,PCEe的是串行的,每个方向的数据使用2条差分信号线来传输,发送/接收两个方向就需要4条线,这被称为1个Lane:
pcie 总线_第17张图片
PCIE 设备之间可以有多个lane:

  • 两个PCIe设备之间有一个Link
  • 一个Link中有1对或多对"发送/接收"引脚,每对"发送/接收"引脚被称为"Lane"
  • 一个Lane:有发送、接收两个方向,每个方向用2条差分信号线,所以1个Lane有4条线
  • 一个Link最多可以有32 Lane

PCIE 总线结构

PCIE总线 与PCI 总线的相同点:虽然PCIE 信号线发生改变,但是对CPU 来讲它与PCI 总线是一样的,CPU仍让是使用地址即可以访问到PCIE设备,所以软件驱动上PCI 与PCIE 设备驱动是兼容的。
PCIE 仍然需要像PCI 一样配置设备,分配设备所需要的地址段。
对比PCI 与PCIE 结构图,PCI 总线 CPU 对接的是Host Bridge(主桥)、PCIE 总线CPU 对接的是Root Complex,实际上Root Complex已经将桥封装在内部,所以它们是对于CPU 来说是相同的。只是在桥后的部分不同。

pcie 总线_第18张图片
pcie 总线_第19张图片
** PCIe 与PCI的不同:**

  • 连接方式不同:在PCI系统里,多个PCI设备都链接到相同总线上。但是在PCIe系统里,是点对点传输,即一条PCIE 总线只能连接一个PCIE设备(不包括Root Complex、switch内部结构),要接多个PCIe设备,必须使用Switch进行扩展。
  • 通信方式不同:PCI 使用并行通信,数据与地址在AD[31:0] 上并行传输;PCIe 使用串行通信,数据是有先后的一个Byte一个Byte发送接收,因此PCIe 采用数据包方式传输。

这是PCIE 总线详细的结构图,标注出了Root Complex、switch的内部结构。
Root Complex 由Host Bridge 和多个 PCI-to-PCI Bridge 组成。
Switch 由多个 PCI-to-PCI Bridge 组成,功能主要是扩展PCIE 能接更多的设备。
pcie 总线_第20张图片
pcie 总线_第21张图片

PCIE 设备之间数据包的传输

前面说到CPU 对于PCIE 设备仍然是使用PCI地址就可以访问到设备,那么它们需要像PCI 设备一样配置吗?
答案:是的
CPU 将如何配置它们?PCIE 的数据信号线变化,并且没有地址线,桥与PCIE 设备之间该以怎样的形式通信呢?
问题1:PCIE 设备之间如何通信(主要就是桥设备与普通设备),怎么传输地址、数据?
PCIE 总线数据线为串行信号线,属于字节流,只能一个一个Byte 发送,因此采用 数据包 方式传送。

PCIE 设备之间的数据通信,与网络协议栈非常相似,如图PCIE 协议栈一共有三层。
分别是Transaction层(事务层)、Data Link层(数据链路层)和 Physical层(物理层)。
事务层: 传输的是Transaction Layer Packet(TLP),其中包含 header 头部、data payload 要传输的数据内容、ECRC crc校验码,验证header 和data payload 传输正确性。头部中又包含了命令类型(什么方式的读/写)、地址,保证数据能发送到目的地
请添加图片描述
数据链路层: (发送回路)数据链路层在拿到 TLP 包后在两端加上序列号和LCRC,在发生传输错误时,拥有重传机制。(接收回路)接收到一个link 包,验证seq 与crc并传递给事务层。
请添加图片描述
物理层: (发送回路)物理层 拿到link 包,在两端加上开始信号和结束信号。(接收回路)接收到一个物理层的包,将两端的开始与结束信号拆分,传递给上层的数据链路层。
请添加图片描述
pcie 总线_第22张图片

TLP 头部

TLP 的通用格式如下:
pcie 总线_第23张图片
PCIe TLP头部决定 数据包的目标设备、目录地址、读写命令等等。
在 PCIE 的不同的读写情况下TLP 包的格式也有不同:

  • 命令类型:你是读内存还是写内存?读IO还是写IO?读配置还是写配置?
    在Header里面有定义。
  • 地址:对于内存读写、IO读写,地址保存在Header里。
  • Bus/Dev/Function/Regiser:对于配置读写,这些信息保存在Header里。
  • 数据:对于内存读、IO读、配置读,先发出请求,再得到数据。
    分为2个阶段:读请求报文、完成报文。 读请求报文,不含数据;完成报文,包含数据。

PCIE TLP头部完整格式:
Fmt 与type: 决定命令的类型,内存读/写、IO读/写、配置0读/写 或配置1读/写。参照表格
Bus Number: 要访问的总线号。(以下几个号都只适用于配置设备时的TLP头部)
Device Number: 要访问的设备号。
Function Number: 要访问的功能号。
Register Number: 要访问的寄存器号。
下图是PCIE 配置TLP头部:
pcie 总线_第24张图片
pcie 总线_第25张图片

PCIE 配置空间

PCIE总线 与PCI 一样,需要读取设备配置空间,获取到设备信息,分配地址并写入配置空间。
根据pcie设备类型不同,PCIE 配置空间分为两类:type0(非桥设备)和type1(桥设备)。
基本与PCI 设备配置空间相同,主要关注以下几个:

  • header type: 区分设备类型,非桥设备为0x00、桥设备为0x01。bit7 位决定PCIe 是多功能设备(一般endpoint才有多功能,桥设备一般为单功能),还是单功能设备。bit7=1 多功能,bit7=0 单功能。
    pcie 总线_第26张图片

  • Base Address: 需要分配的地址长度存放在BAR中,并且配置完会向BAR写入首地址。

  • Pirmary Bus Number: 上游总线号。

  • Secondary Bus Number: 自己的总线号。

  • Subordinate Bus Number: 下游总线号的最大数值。
    pcie 总线_第27张图片

配置PCIe 设备

配置桥设备

配置PCIe 设备首先需要配置桥设备,因为需要通过桥的转发才能到达endpoint。
例如配置与主桥相连的A桥:
根据Fmt 与type选择有两种类型的TLP 配置包:分别为type0和type1。它们的区别是当需要到达的设备与桥直连时(比如host 与A、A与C),该桥会发出type0的包,直接到达设备;当需要到达的设备在它的下游,并且不与他直连时(比如A 与最下端设备),该桥会发出type1的包,type1经过一层层桥的转发,到达与该设备直连的桥时,该桥会发出type0 的包,到达设备。
在这里插入图片描述
① A与主桥相连,所以主桥发出type0 的包,并且标明Device 、Function 和Register Number。
② 根据设备号(Root Complex 为封装好的硬件,所以设备号已经在硬件上固定了,switch 也是一样)、功能号和寄存器号,就可以找到想要读写的寄存器。
③ 读取配置空间header type,发现A 设备为桥,将其的总线号分别配置为 Pirmary Bus Number=0、Secondary Bus Number=1、Subordinate Bus Number=255(暂定为255,待扫描完下游所有的桥设备后,修改为下游最大桥设备号)。
这样一个桥设备就配置完成了。

注意:

  • type0 需要到达的设备与桥直连,因此type0 的TLP 头部中应该不用注明Bus Number,因为这样没有意义。
  • 一个PCIe 设备中最多可以有8个功能,每个功能拥有一个配置空间。
    pcie 总线_第28张图片

配置非桥设备

非桥设备如上图中的 Bus3 Dev0设备,这种设备一般被叫做endpoint
配置Bus3 Dev0:
① Host Bridge 发出type1 的TLP 数据包,Fmt=010、type=00101,Bus Number=3,Device Number=0,Function Number=0,Register Number=xxx。
② type1 TLP 包经过桥A->C->D桥,最终桥D发出type0 数据包,到达Bus3 Dev0。
③ 读取Bus3 Dev0 Function0 的配置寄存器,发现为普通设备
④ 读取BAR,获得设备需要的内存大小。
⑤ 分配指定大小内存,并将首地址写入设备0 BAR。配置完成。
在这里插入图片描述
pcie 总线_第29张图片

PCIe 系统完整配置过程示例

5.2.1 硬件拓扑结构

以下图中的设备的配置过程为例,给大家做示范。
pcie 总线_第30张图片

5.2.2 配置过程演示

下文中BDF表示Bus,Device,Function,用这三个数值来表示设备。

  1. 软件设置Host/PCI Bridge的Secondary Bus Number为0,Subordinate Bus Number为255(先设置为最大,后面再改)。
  2. 从Bus 0开始扫描:先尝试读到BDF(0,0,0)设备的Vendor ID,如果不成功表示没有这个设备,就尝试下一个设备BDF(0,1,0)。一个桥下最多可以直接连接32个设备,所以会尝试32次:Device号从0到31。注意:在Host/PCI Bridge中,这些设备的Device号是硬件写死的。
  3. 步骤2读取BDF(0,0,0)设备(即使图中的A)时,发现它的Header Type是01h,表示它是一个桥、单功能设备
  4. 发现了设备A是一个桥,配置它:
    • Primary Bus Number Register = 0:它的上游总线是Bus 0
    • Secondary Bus Number Register = 1:从它发出的总线是Bus 1
    • Subordinate Bus Number Register = 255:先设置为最大,后面再改
  5. 因为发现了桥A,执行"深度优先"的配置过程:先去枚举A下面的设备,再回来枚举跟A同级的B
  6. 软件读取BDF(1,0,0)设备(就是设备C)的Vendor ID,成功得到Vendor ID,表示这个设备存在。
  7. 它的Header Type是01h,表示这是一个桥、单功能设备。
  8. 配置桥C:
    • Primary Bus Number Register = 1:它的上游总线是Bus 1
    • Secondary Bus Number Register = 2:从它发出的总线是Bus 2
    • Subordinate Bus Number Register = 255:先设置为最大,后面再改
  9. 继续从桥C执行"深度优先"的配置过程,枚举Bus 2下的设备,从BDF(2,0,0)开始
  10. 读取BDF(2,0,0)设备(就是设备D)的Vendor ID,成功得到Vendor ID,表示这个设备存在。
  11. 它的Header Type是01h,表示这是一个桥、单功能设备。
  12. 配置桥D:
    • Primary Bus Number Register = 2:它的上游总线是Bus 2
    • Secondary Bus Number Register = 3:从它发出的总线是Bus 3
    • Subordinate Bus Number Register = 255:先设置为最大,后面再改
  13. 继续从桥D执行"深度优先"的配置过程,枚举Bus 2下的设备,从BDF(3,0,0)开始
  14. 读取BDF(3,0,0)设备的Vendor ID,成功得到Vendor ID,表示这个设备存在。
  15. 它的Header Type是80h,表示这是一个Endpoing、多功能设备。
  16. 软件枚举这个设备的所有8个功能,发现它有Function0、1
  17. 软件继续枚举Bus 3上其他设备(Device号1~31),没发现更多设备
  18. 现在已经扫描完桥D即Bus 3下的所有设备,它下面没有桥,所以桥D的Subordinate Bus Number等于3。扫描完Bus 3后,回退到上一级Bus 2,继续扫描其他设备,从BDF(2,1,0)开始,就是开始扫描设备E。
  19. 读取BDF(2,1,0)设备(就是设备E)的Vendor ID,成功得到Vendor ID,表示这个设备存在。
  20. 它的Header Type是01h,表示这是一个桥、单功能设备。
  21. 配置桥E:
    • Primary Bus Number Register = 2:它的上游总线是Bus 2
    • Secondary Bus Number Register = 4:从它发出的总线是Bus 4
    • Subordinate Bus Number Register = 255:先设置为最大,后面再改
  22. 继续从桥D执行"深度优先"的配置过程,枚举Bus 4下的设备,从BDF(4,0,0)开始
  23. 读取BDF(4,0,0)设备的Vendor ID,成功得到Vendor ID,表示这个设备存在。
  24. 它的Header Type是00h,表示这是一个Endpoing、单功能设备。
  25. 软件继续枚举Bus 4上其他设备(Device号1~31),没发现更多设备
  26. 已经枚举完设备E即Bus 4下的所有设备了,更新设备E的Subordinate Bus Number为4。然后继续扫描设备E的同级设备:Bus=2,Device从2到31,发现Bus 2上没有这些设备。
  27. 软件更新设备C即Bus 2的桥,把它的Subordinate Bus Number设置为4。然后继续扫描设备C的同级设备:Bus=1,Device从1到31,发现Bus 1上没有这些设备。
  28. 软件更新设备A即Bus 1的桥,把它的Subordinate Bus Number设置为4。然后继续扫描设备A的同级设备:Bus=0,Device从1到31,发现Bus 0上的设备B。
  29. 配置桥B:
    • Primary Bus Number Register = 0:它的上游总线是Bus 0
    • Secondary Bus Number Register = 5:从它发出的总线是Bus 5
    • Subordinate Bus Number Register = 255:先设置为最大,后面再改
  30. 再从桥B开始,执行"深度优先"的配置过程。

PCIe 路由方式

PCIe 的三种路由方式

数据传输时,最先要确定的是:怎么找到对方?
所谓"路由",就是怎么找到对方,PCIe协议中有三种路由方式:

  • 配置读、配置写:使用基于ID的路由,就是使用来寻找对方。配置成功后,每个PCIe设备都有自己的PCIe地址空间了。
  • 内存读、内存写或者IO读、IO写:
    发出报文给对方:使用基于地址的路由
    对方返回数据或者返回状态时:使用基于ID的路由
  • 各类消息,比如中断、广播等:使用隐式路由

不管是什么路由方式,我们最终关心的是TLP的格式。

地址路由

内存读、内存写或者IO读、IO写属于地址路由。
下图是内存读写与IO读写的TLP 头部格式:内存读写数据格式有64bit和32bit两种,IO读写32位bit。
Address:代表要读写设备的地址(pci地址)。
requester ID:请求读写的设备id,其中
tag:表示一次pcie的通信。
pcie的每一次发送或读取信息都是通过TLP包,tag就表示TLP包的唯一序号。在设备发出包后会有一段缓存专门存放TLP包,如果对方回应这个TLP 那么设备会释放这段缓存,假如等待一段时间没有回应,设备将会重传这个TLP。就是通过tag判断。
pcie 总线_第31张图片
pcie 总线_第32张图片
想要了解PCIe 地址的路由方式首先要了解桥设备的配置空间(type1):
Memory Base 和Memory Limit:Base表示一段内存的首地址,Limit表示大小,合起来就描述了一段地址空间。描述下游设备所分配的所有地址段(一个桥下游设备所分配的地址应该是连续的,所以Base表示这些设备地址中最首的地址,Limit 表示它们的总长 )。
Prefetchable Memory Base、Limit:表示下游设备可预取的内存空间。
IO Base 和IO Limit:表示所有子设备所分配的IO 地址空间。
pcie 总线_第33张图片

地址路由完成报文

下图里面的Requester ID、TAG,被称为"Transaction ID"。
pcie 总线_第34张图片
主设备要给EndPoint的内存写数据,它发出"内存写报文",不需要对方回应。

主设备要读EndPoint的内存数据,它发出"内存读报文",需要对方回应。

主设备要给EndPoint的IO写数据,它发出"IO写报文",需要对方回应。

主设备要读EndPoint的IO数据,它发出"IO读报文",需要对方回应。

  • PCIe设备要回应时,回应谁?给"Requester ID",使用基于ID的路由
  • 发起PCIe传输的设备(主设备),对每次传输都分配一个独一的Tag,并且在硬件内部保存当前TLP。
  • 接收到回应报文后,才会根据Tag清除掉内存中保存数据。
  • 如果没接收到回应,或者失败了:会把硬件中保存的TLP重新发送出去。
    回应的完成报文,可以含有数据,也可以不含数据,TLP头部格式如下:
    pcie 总线_第35张图片

地址路由寻址过程

场景①:最常见的场景。cpu 想要访问nedpoint1,cpu给他分配的空间是A-B,那么桥P-P2的Memory base和Limit描述的大小就是A-B(因为它的下游只有一个设备。如桥p-p1他的下游有多个设备分配了空间,分别是A-B和C-D,那么它的base和Limit 描述的这段空间就是A-D )。

  1. cpu 发出的指令首先到达RC,RC将cpu_addr 转换为 pci_addr,并发出TLP包。
  2. TLP 到达p-p1,对比自己base 和Limit 知道,这条消息是要访问自己的下游设备,于是往下一级总线转发。
  3. 同样p-p2 发现要访问的是自己的下游设备,往下转发;p-p3 发现不是便不转发。
  4. 最终到达EP1。当EP1 回应cpu 消息时使用的是ID路由,RC 发给EP1的TLP 中包含了requester ID,通过它就将回应的消息返回给RC,cpu最终从RC读取。

场景②:EP1 与EP2之间的通信

  1. EP2 发出含有EP1地址的TLP包,P-P3 桥发现自己不能处理,于是便往上一级总线转发。
  2. P-P2 发现自己可以处理,于是往自己的下一级转发,最终到达EP1。

场景③:EP2 通过DMA 直接访问内存(主存储器)

  1. EP2 发出含有主存储器 地址的TLP包,P-P3 桥发现自己不能处理,于是便往上一级总线转发。
  2. 同样P-P1 也往上一级转发,最终通过RC的地址转换(pci_addr to cpu_addr),到达主存储器。

pcie 总线_第36张图片

隐式路由

消息报文的头部格式如下:
pcie 总线_第37张图片
消息报文中头部的Type字段里低3位表示隐式路由方式:

  • 000:路由到RC
  • 001:使用地址路由(使用地址路由的消息不常见)
  • 010:使用ID路由
  • 011:来自RC的广播报文,底下的PCIe桥会向下游转发此消息
  • 100:本地消息,消息到达目的设备后结束,不会再次转发
  • 101:路由到RC,仅用于电源管理

pcie 总线_第38张图片

你可能感兴趣的:(pcie,总线,Linux驱动,linux)