往期内容
本文章相关专栏往期内容,PCI/PCIe子系统专栏:
- 嵌入式系统的内存访问和总线通信机制解析、PCI/PCIe引入
- 深入解析非桥PCI设备的访问和配置方法
- PCI桥设备的访问方法、软件角度讲解PCIe设备的硬件结构
- 深入解析PCIe设备事务层与配置过程
Uart子系统专栏:
- 专栏地址:Uart子系统
- Linux内核早期打印机制与RS485通信技术
– 末片,有专栏内容观看顺序interrupt子系统专栏:
- 专栏地址:interrupt子系统
- Linux 链式与层级中断控制器讲解:原理与驱动开发
– 末片,有专栏内容观看顺序pinctrl和gpio子系统专栏:
专栏地址:pinctrl和gpio子系统
编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用
– 末片,有专栏内容观看顺序
input子系统专栏:
- 专栏地址:input子系统
- input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
– 末片,有专栏内容观看顺序I2C子系统专栏:
- 专栏地址:IIC子系统
- 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
– 末篇,有专栏内容观看顺序总线和设备树专栏:
- 专栏地址:总线和设备树
- 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
– 末篇,有专栏内容观看顺序
目录
- 往期内容
- 1.三种路由方式
- 2.基于ID的路由
- 2.1 PCIe设备(Endpoint)的配置空间
- 2.2 PCIe桥的配置空间
- 2.3 组合
- 3.基于地址的路由
- 3.1 内存读写/IO读写
- 3.2 完成报文
- 3.3 示例
- 4.隐式路由
1.三种路由方式
数据传输时,最先要确定的是:怎么找到对方?
所谓"路由",就是怎么找到对方,PCIe协议中有三种路由方式:
- 基于ID的路由
- 基于地址的路由
- 隐式路由
TLP中怎么表示自己使用哪种路由?TLP头部就表明了:
访问PCIe设备时,要先配置,才能读写数据:
-
配置读、配置写:使用基于ID的路由,就是使用<Bus, Device, Function>来寻找对方。配置成功后,每个PCIe设备都有自己的PCIe地址空间了。
-
内存读、内存写或者IO读、IO写:
- 发出报文给对方:使用基于地址的路由
- 对方返回数据或者返回状态时:使用基于ID的路由
-
各类消息,比如中断、广播等:使用隐式路由
2.基于ID的路由
TLP中含有<Bus number, Device number, Function number>,这就是ID。
配置成功后,每个PCIe设备,包括虚拟的PCIe桥,都分配到了地址空间:
- PCIe设备(Endpoint):地址空间是自己的,别的设备访问这个地址范围时,就是想访问它
- PCIe桥:地址空间是下面的PCIe设备的,别的设备访问这个地址范围时,就是想访问它下面的设备
2.1 PCIe设备(Endpoint)的配置空间
Endpoint的配置空间里有"Base Address Regiseters":
- 一开始:这些寄存器用来声明:需要多大的地址空间、是内存空间还是IO空间
- 被配置后:系统软件为它分配出地址空间,把首地址写入这些寄存器
下图来自《PCI Express_ Base Specification Revision 4.0 Version 0.3 ( PDFDrive ).pdf》:
2.2 PCIe桥的配置空间
PCIe桥的配置空间里有"Primary Bus Number、Secondary Bus Number、Subordinate Bus Number":
- 配置PCIe桥的时候,系统软件会填充这些信息
- 对于ID路由,将根据这些信息转发报文
PCIe桥的配置空间里有"Memory Base、Prefetchable Memory Base、I/O Base":
-
表示该桥下游所有设备使用的三组空间范围
- 存储器空间
- 可预取的存储器空间
- I/O空间
-
对于地址路由,将根据这些信息转发报文
下图来自《PCI Express_ Base Specification Revision 4.0 Version 0.3 ( PDFDrive ).pdf》:
2.3 组合
假设2号设备写入到配置空间的地址范围为AB,3号设备写入到配置空间地址范围是CD,那么1号桥设备所记录的地址空间范围就得包含下游设备的地址范围AB和CD。
3.基于地址的路由
PCIe设备(EndPoint)被配置后,它记录有分配给它的基地址。
PCIe桥也记录有下游PCI子树的基地址。
- PCIe桥监测总线上的信号,根据TLP里的地址决定是否转发到自己下面的总线上
- PCIe设备监测总线上的信号,根据TLP里的地址决定是否访问自己
- PCIe设备发出回应的报文时,使用基于ID的路由
3.1 内存读写/IO读写
需要注意的是Requester ID,这个是设备回应时所不可缺少的,报文回应的是基于ID的路由
3.2 完成报文
上图里面的Requester ID、TAG,被称为"Transaction ID"。
主设备要给EndPoint的内存写数据,它发出"内存写报文",不需要对方回应。
主设备要读EndPoint的内存数据,它发出"内存读报文",需要对方回应。
主设备要给EndPoint的IO写数据,它发出"IO写报文",需要对方回应。
主设备要读EndPoint的IO数据,它发出"IO读报文",需要对方回应。
- PCIe设备要回应时,回应谁?给"Requester ID",使用基于ID的路由
- 发起PCIe传输的设备(主设备),对每次传输都分配一个独一的Tag,并且在硬件内部保存当前TLP
- 接收到回应报文后,才会根据Tag清除掉内存中保存数据
- 如果没接收到回应,或者失败了:会把硬件中保存的TLP重新发送出去
回应的完成报文,可以含有数据,也可以不含数据,格式如下:
3.3 示例
TLP1:TLP1是要发送给EP1,那么其Requester ID就是RC的信息,RC回应EP21时,靠的就是Requester ID找到EP1,只有接收到回应,EP1中保存的发送的TLP信息才会删除,否则就会进行重发直到收到回应,而这个离不开TLP中的Tag字段(请求标签,用于追踪该请求)
4.隐式路由
隐式路由意味着无需显式指定目标设备(不使用Bus、Device、Function号),而是通过其他信息(如目标地址或ID)确定数据的传输路径。
根据不同的路由方式,PCIe消息可以在不同设备间传递,或直接发送至根复杂设备(Root Complex, RC)。
消息报文的头部格式如下:
消息报文中头部的Type字段里低3位表示隐式路由方式:
000:路由到Root Complex (RC)
- 这种类型的消息直接路由到PCIe总线的根复杂设备(RC),通常用于管理或状态查询的请求,RC负责处理这些消息。
- 示例:例如,PCIe设备请求RC进行特定的配置或查询操作。
001:使用地址路由
- 消息根据目标的内存地址或I/O地址来进行路由,基于配置阶段中分配的地址范围。地址路由在正常的数据读写操作中比较常见,但消息使用这种方式较为少见。
- 示例:地址范围确定消息应该送到哪个设备,通过检查内存或I/O地址来定位目标设备。
002:使用ID路由
- 根据设备的Bus、Device、Function(BDF) ID信息来确定消息的传输路径。此时,系统中的交换机或桥设备会解析消息中的ID信息,将消息转发到对应的目标设备。
- 示例:典型用于PCIe设备之间的配置操作,如读取某个设备的配置寄存器或发起特定的设备管理请求。
011:来自Root Complex的广播报文
- 当根复杂设备发出这种类型的广播消息时,所有下游的PCIe桥设备都会转发该消息给其下游设备。
- 示例:在系统中的所有PCIe设备需要接收某个全局配置或广播命令时,RC会发出这样的广播消息。
100:本地消息
- 本地消息在到达目的设备后结束,不会被再次转发。这种消息通常用于在设备内部处理一些特定的控制或管理任务,而不是跨越多个设备进行传递。
- 示例:设备内部的状态或控制消息,如内部的电源管理指令等。
101:路由到Root Complex,仅用于电源管理
- 这种类型的消息专门用于与Root Complex进行电源管理相关的事务。比如,当某个设备进入低功耗状态时,它可能会向RC发送此类消息以告知其状态。
- 示例:设备进入睡眠模式时,会向RC报告自身的电源状态。
隐式路由通常由根复杂设备或中间的PCIe桥通过解析TLP(Transaction Layer Packet)中的地址或ID字段来确定路由路径。
Type字段低3位提供了不同类型的路由选择,包括直接路由到RC、使用地址或ID进行路由,以及特殊的广播或本地消息。