硬件开发——PCIe
小狼@http://blog.youkuaiyun.com/xiaolangyangyang
一、引脚定义
- SMBUS:10kHz~100KHz,用于传输PCIe卡信息(如智能电源管理)
- JTAG:
- 设插拔:PRSNT1#和PRSNT2#用于PCIe热插拔,在PCIe卡上,PRSNT1#和PRSNT2#短接,而在处理器主板中,PRSNT1#信号接地,PRSNT2#信号通过上拉电阻拉高,PCIe卡插入时,处理器端的PRSNT2#被接地,拔出时,PRSNT2#被电阻上拉。如下图所示:
二、配置空间及枚举过程
- 访问配置寄存器
- x86 I/O访问(CAM模式):使用CONFIG_ ADDRESS(0xCF8)、CONFIG_ DATA(0xCFC)寄存器进行访问,x86一般在BIOS阶段进行枚举和地址分配;
- ARM MMIO访问(ECAM模式): IO端口只能访问256(2^6)字节的配置空间,PCIe的配置空间扩展到4K,IO端口的方法无法访问到扩展的配置空间,所以使用一段MMIO空间(一般为256MB(256bus * 32dev * 8func * 4k)空间给ECAM机)用来访问配置空间,大小256M(256*256*4096),ARM一般在Linux内核进行枚举和地址分配。
CONFIG _ ADDRESS(0xCF8)寄存器定义
ECAM空间地址定义
- 配置空间枚举过程
枚举的意义:
- 树形结构的连接线分配总线号;
- 对PCIe Device 和 Bridge 分配BDF (Bus: Device. Function);
- 为PCIe bridge分配primary、secondary、subordinate bus number。
枚举的过程:
- 首先,以BDF=0:0.0 读取设备A的配置空间寄存器vendor ID和device ID(软件通过配置空间读取得方式判断PCIe设备是否存在);
- 如果设备A的Vendor ID和device ID有效,继续读设备A的配置空间寄存器header type;判断设备C是Bridge还是endpoint,该示例为桥设备,桥设备则初始化设备A配置空间寄存器Primary bus=0,Secondary bus=1,Subordinate bus=255(注意Subordinate bus 后面会修改为正确值);
- 以BDF=1:0.0 读取设备C的配置空间寄存器 vendor ID和device ID;
- 设备C的vendor ID和device ID有效,继续读设备C的配置空间寄存器header type,判断设备C是桥设备还是endpoint,该示例为桥设备,桥设备则初始化设备C配置空间寄存器Primary bus=1,Secondary bus=2,Subordinate bus=255;
- 以BDF=2:0.0 读取设备D的配置空间寄存器vendor ID和device ID;
- 设备D的vendor ID和device ID有效,继续读设备D的配置空间寄存器header type,判断设备D是桥设备还是endpoint,该示例为桥设备,桥设备则初始化设备D配置空间寄存器Primary bus=2,Secondary bus=3,Subordinate bus=255;
- 以BDF=3:0.0 读取设备的配置空间寄存器vendor ID和device ID,设备的vendor ID和device ID有效,继续读设备的配置空间寄存器header type,判断设备是桥设备还是endpoint,该示例为endpoint,继续读设备的配置空间判断是single-function设备还是multi-function设备;
- 编号7中的设备为multi-function,然后以BDF=3:0.1 读取设备的配置空间寄存器vendor ID和device ID,设备的vendor ID和device ID有效,然后做一些初始化操作;
- Bus 3 的设备遍历结束之后,发现桥设备D下最大的bus number为3,则将设备D的配置空间寄存器Subordinate bus=3;
- Bus3遍历结束后,回到Bus 2,以BDF=2:1.0 读取设备E,和步骤5类似;
- 步骤11和步骤6类似,步骤12和步骤7类似,步骤13&14&15和步骤9类似。
- BAR寄存器初始化
向BAR寄存器写入0xFFFF_FFFF,如果读出是0xFFF0_0000,BAR的[19:0]写不进数据,说明BAR需要的Size是1M,此时:
BAR初始化为0x0000_0000,则占用PCIe域0x0000_0000~0x000F_FFFF的1M空间;
BAR初始化为0x0010_0000,则占用PCIe域0x0010_0000~0x001F_FFFF的1M空间;
可见初始化的BAR地址要与BAR Size整数倍对齐。
- 地址映射
使用inbound/outbound
如果仅需RC访问EP的MEM(包括读写/DMA),则需要配置RC的outbound+EP的inbound
如果仅需EP访问RC的DDR(包括读写/DMA),则需要配置RC的inbound+RC的outbound
- 数据传输
RC和EP访问映射过后的CPU域地址即可,访问指令会被PCIe IP转换成TLP包在PCI域进行传输,TLP传输时的地址是PCI域的地址,在RC端和EP端都会进行地址映射。
三、协议(TLP包传输)
1、TLP结构
TLP Header基本结构
Memory read/write TLP(Mwr/Mrd包)
Completion/Completion with Data TLPs
2、TLP包类型
- Memory:Transfer data to/from a memory-mapped location
- IO:Transfer data to/from an I/O-mapped location
- Configuration:Device Function configuration/setup
- Message:From event signaling mechanism to general purpose messaging
3、路由方式
- 地址路由:switch和ep中均保存有地址范围,可通过地址单位进行路由,TLP的目的地址在范围内则接收,反之拒收
- ID路由:使用Bus Number、Device Number和Function Number组成的16位BDF进行寻址
- peer-to-peer:EP与EP之间传送消息
- 隐式路由(Implicit routing):INTx中断、电源管理和错误等消息,这些消息直接发向RC(直接向下转发),或者来自RC(直接向上转发),或则是Local: terminate at receiver(终结消息)
五、问题总结
1、TI达芬奇方案inbound、outbound和BAR写入的地址是什么
DSP作为RC,FPGA作为EP,DSP的0x60000000~0x6FFFFFFF(256M)地址用来做PCIe地址映射,
OB_SIZE=1M时:将0x60000000~0x61FFFFFF分成32个region,每个region1M;
OB_SIZE=2M时:将0x60000000~0x63FFFFFF分成32个region,每个region2M;
OB_SIZE=4M时:将0x60000000~0x67FFFFFF分成32个region,每个region4M;
OB_SIZE=8M时:将0x60000000~0x6FFFFFFF分成32个region,每个region8M;
OB_OFFSET_INDEX[n]寄存器写入的是PCIe域地址。
如:OB_SIZE = 1M,OB_OFFSET_INDEX[0] = 0x7000000,DSP[0x60000000] -> PCIe[0x70000000],长度1M
如果BAR0写入地址是0x70000000,则DSP[0x60000000]映射到BAR0,长度1M
OB_SIZE = 1M,OB_OFFSET_INDEX[1] = 0x7000000,DSP[0x60100000] -> PCIe[0x70000000],长度1M
如果BAR0写入地址是0x70000000,则DSP[0x60100000]映射到BAR0,长度1M
inbound、outbound及BAR写入的都是PCIe域地址
2、Synopsys方案inbound、outbound如何进行映射
Synopsys支持8个inbound和8个outbound,每个bound使用如下寄存器进行映射:
- IATU_REGION_CTRL_1_OFF_OUTBOUND_i:控制寄存器1
- IATU_REGION_CTRL_2_OFF_OUTBOUND_i:控制寄存器2
- IATU_LWR_BASE_ADDR_OFF_OUTBOUND_i:soc域地址[31:0]
- IATU_UPPER_BASE_ADDR_OFF_OUTBOUND_i:soc域地址[63:32]
- IATU_LIMIT_ADDR_OFF_OUTBOUND_i:长度
- IATU_LWR_TARGET_ADDR_OFF_OUTBOUND_i:pcie域地址[31:0]
- IATU_UPPER_TARGET_ADDR_OFF_OUTBOUND_i:pcie域地址[63:32]
- IATU_REGION_CTRL_3_OFF_OUTBOUND_i:控制寄存器3
- IATU_UPPR_LIMIT_ADDR_OFF_OUTBOUND_i:长度
- IATU_REGION_CTRL_1_OFF_INBOUND_i:控制寄存器1
- IATU_REGION_CTRL_2_OFF_INBOUND_i:控制寄存器2
- IATU_LWR_BASE_ADDR_OFF_INBOUND_i:soc域地址[31:0]
- IATU_UPPER_BASE_ADDR_OFF_INBOUND_i:soc域地址[63:32]
- IATU_LIMIT_ADDR_OFF_INBOUND_i:长度
- IATU_LWR_TARGET_ADDR_OFF_INBOUND_i:pcie域地址[31:0]
- IATU_UPPER_TARGET_ADDR_OFF_INBOUND_i:pcie域地址[63:32]
- IATU_REGION_CTRL_3_OFF_INBOUND_i:控制寄存器3
- IATU_UPPR_LIMIT_ADDR_OFF_INBOUND_i:长度
Synopsys将配置空间放在pcie域的0x5600_0000地址,采用ECAM方案,访问前需要outbound映射到soc域,且在映射时,将soc域地址的[31:24]固定为了0x56,soc可用的地址为0x5600_0000~0x56ff_ffff的16M地址,即这16M soc空间是专门预留给pcie子系统的,配置空间和bar空间都只能映射到soc域的这个范围。因为所使用到的EP数量很少,所以不需要整个256M那么大的配置空间。写入bar寄存器的bar空间地址为pcie域地址,访问前需要映射到soc域才能使用。
RC和EP的dma使用的都是是pcie域地址。
3、inbound、outbound寄存器是在什么时候进行配置的
inbound、outbound寄存器都是在驱动里进行MEM映射时进行配置
4、中断和DMA
大部分DMA位于PCIe板卡,BAR空间有DMA寄存器映射,可在上位机驱动内使用板卡的DMA,DMA使用的PCIe域地址,使用上位机MEM地址时需要先映射到PCIe域地址。
5、xilinx PCIe IP(RC、EP)使用
6、BAR空间是怎么初始化的(假设BAR空间为1M,0x100000)?
向BAR寄存器写入0xFFFFFFF,读回的值取反加1即为BAR空间长度;
1、写入0xFFFFFFF,即向BAR写入的地址是0xFFFFFFF;
2、但这个地址是无效的,实际能设置的最大地址是0xFFF00000;
3、0xFFF00000就是BAR寄存器能设置的最大地址,所以0x100000为BAR长度。
7、BAR的地址是从哪里分配的,分配原则是什么,mem映射的地址分配原则是什么?
8、任务
1、编译lspci,了解其使用;
2、xilinx高速采集卡系列文章;
3、配置Xilinx PCIe IP EP(包括中断、DMA);
4、Xilinx PS端PCIe模块寄存器;
5、查看Linux内核中的枚举代码;
6、写一个Nvme盘读写驱动。
9、lspci命令
40000000-400003ff : 0000:00:1f.1
a)40000000-400003ff:地址范围
b)0000(16bit):域
c)00(8bit):总线编号
d)1F(5bit):设备号
e)1(3bit):功能号
10、IMX6的PCIe
- 配置空间:使用ECAM机制
- 地址映射
- 首先指定region和direction,后面的control、base、limit、target寄存器就属于指定的region和direction
- outbound region范围(CX_ATU_NUM_OUTBOUND_REGIONS):4
- inbound region范围(CX_ATU_NUM_INBOUND_REGIONS):4,1
- 最小映射size(CX_ATU_MIN_REGION_SIZE):64KB
11、PCIe速度级别
12、 prefetchable和nonprefetchable
prefetchable:可预取为memory,读写没有副作用,系统可加缓存等优化;
nonprefetchable:不可预取为寄存器,读写有副作用,如读清中断,系统不会进行优化操作。
PCIE Inbound Outbound 地址配置,DMA传输
pcie inbound和outbound关系
教程:FPGA PCIE调试及DSP代码的讲解
【干货】从CPU角度理解PCIe续集
PCIe学习(二):PCIe DMA关键模块分析之一
dma与pcie
PCIe网卡驱动实现分析(一)--- PCIe基础知识
NVMe驱动解析-关键的BAR空间
PCIE 之RC与EP之间数据传输
PCI设备BAR空间的初始化
米联客ZYNQ——Linux系统移植教程之脚本文件解读
PCIE配置空间设置
lspci
linux lspci命令详解(linux查看硬件配置命令)
lspci命令整理
Xilinx ZYNQ Ultrascale+ 性能测试之 PL/PS PCIe Root Port NVMe
PCIe总线-简介(一)
PCIe总线-存储器域和PCIe总线域访问流程分析(二)
PCIe总线-配置空间介绍(三)
PCIe总线-MPS MRRS RCB参数介绍(四)
PCIe总线-事物层之TLP通用格式介绍(五)
PCIe总线-事物层之TLP请求和完成报文格式介绍(六)
PCIe总线-事物层之TLP路由介绍(七)
PCIe总线-RK3588 PCIe子系统简介(八)
PCIe总线-RK3588 PCIe驱动设备树介绍(九)
PCIe总线-RK3588 PCIe平台驱动分析(十)
【PCIE】PCIE TLP包解析
PCIe协议之-TLP路由基础
PCIe 协议基础实战
PCI外部设备互连&PCIE
读取PCI配置空间数据并操作其映射的物理内存