pcie简介

PCIE基础
PCI(Peripheral Component Interconnect)是Intel在1992年提出的一套总线协议。一颗典型的PCI总线树如图
树状图
从图中可以看出PCI总线主要分为三部分:
1. PCI 设备;2. PCI 总线;3. PCI 桥。

PCI标准的特点:
  1. 它是一个并行总线;2. PCI空间与处理器空间隔离;3. 扩展性强。

PCI Express,是在PCI的基础上发展而来的,它沿用现有的PCI编程概念及通信标准,但建基于更快的串行通信系统。PCIE总线使用的是高速差分总线,并采用端到端的连接方式(因此在每一条PCIe链路中只能连接两个设备)。现在的高速总线基本上都是串行总线,这样可以使用更高的时钟频率。
一个典型的PCIe系统框图如下:
系统框图
RC和Switch中的连接部分,都可以看作的是PCI TO PCI bridge(也就是说一个bus下如果要连多个设备,必须通过P2P),在linux驱动中,公用一套驱动

PCIE的特点:

  1. PCIE是总线结构,而PCIe是点对点结构。
    2. PCIe的连线是由不同的lane来连接的(x2/x4/x8/x16)
    3. PCIE配置空间从256B扩展为4k
    4.PCIe提供了很多特殊功能(配置空间express/extend)

    PCIE各版本的速率:
    速率
    PCIe总线的层次结构:
    PCIe总线是分层实现的,它包含多个层次(也可以理解成像是I2C,我们要发送器件地址,寄存器地址,这就相当于数据的header,类似于PCIE和TCP/IP的报文头),从上到下分别是应用层(也就是下图中的Device Core,PCIe Core HW/SW Interface),事务层(Transaction Layer),数据链路层(Data Link Layer),物理层(Physical Layer),其中,应用层并不是PCIe Spec所规定的内容,完全由用户根据自己的需求进行设计,另外三层都是PCIe Spec明确规范的,并要求设计者严格遵循的。PCIe的层次结构有点类似TCP/IP的协议实现,不过PCIe的各个层次都是通过硬件逻辑实现的。发送时数据报文先由应用层产生,然后经过事务层,数据链路层和物理层最终发送出去。接收端则是相反的一个步骤,数据先经过物理层,然后向上送给数据链路层,事务层,最后到达应用层。
    结果层次
    对于软件人员而言,我们操作PCIE的时候,比如写一个映射出来的地址,我们只需要通过地址赋值即可: *addr = data; 而这样的操作,会经过kernel的处理,最终在RC上形成一个PCIE的报文,在PCIE bus上寻址,并最终发送给addr对应的EP。
    报文最终的格式如下:
    报文格式
    绿色是物理层(Physical Layer)组装,蓝色是数据链路层(Data Link Layer)组装,红色是事务层(Transaction Layer)组装。

PCIE枚举/寻址:(I2C设备可以直接固化地址如0x50 0x51等,PCIE的地址通过BDF来寻找,RC scan/rescan可以扫描所有设备)
PCIE的地址是通过BDF(bus、device、function)来确认的
Pri:上级bus号
Sec:下级bus号
Sub:下级链路最大bus号
上电之后,PCIE总线确定每一个RC和EP的BDF,数据报文根据BDF来判断路径,发送到指定的设备。
地址
PCIE空间访问

对于软件人员而言,PCIE最需要关心的,就是PCIE空间的访问。PCIE空间可以分为bar空间和配置空间。
空间
配置空间:表明该PCIE设备的类型、设置、支持的功能等;【是什么,做什么】
bar空间: 实际功能的下发(比如bar映射的是SoC或LSW的寄存器)。

配置空间

配置空间
每个PCIE设备都有自己的独立的一段配置空间,该部分空间属于是这个设备的(可能是一段e2prom),设备在出厂时,配置空间是有默认初始值的。如上图所示,pci的配置空间是256字节,其中64字节是标准配置空间header,后面的192字节是Capability结构,展示pci能提供的能力。为了兼容PCI,PCIe的配置空间前256字节与PCI保持一致,256~4096字节是pcie 扩展配置空间,包含pcie的扩展能力如AER。
传统PCI机制访问(X86)
CPU 通过主桥(Host Bridge)中的 IO 映射地址端口(Address Port )和资料端口(Data Port)进行索引来间接访问 PCI 配置空间。
在这里插入图片描述
地址端口(Address Port )位于 IO 地址 CF8h-CFBh,是一个大小为32 bits的暂存器,需要填入CONFIG_ADDRESS,其格式如下
在这里插入图片描述
Bit 31表示Enable bit,如果不设起来就不会有作用。 (1<<31) = 0x80000000,所以通常会直接设起来,直接做 | 0x80000000的动作
Bit 8-23 是PCIe Device Address,是由 Bus Number+Device Number+Function Number所组成的
Bit0-7这边就填想到访问的PCIe 配置空间的Offset,做多就是0x00-0xff: 256 Bytes
所以CONFIG_ADDRESS 的值写作如下表示:
0x80000000 | bus << 16 | device << 11 | function << 8 | offset
CONFIG_ADDRESS 指定完需要访问的配置地址,会对资料端口映射的位置 CFCh-CFFh的CONFIG_DATA 暂存器的访问生成配置访问,将资料传入或传出 CONFIG_DATA 暂存器。

简单来说,例如我们想要读取"bus0 dev0 fun0, offset 0x00"的位置,那他的CONFIG_ADDRESS 就是0x80000000, 我们把它写入地址端口0xCF8,那该位置的资料就可以从资料端口0xCFC读到资料
Linux下x86的读取例子如下:
/* Direct PCI access. This is used for PCI accesses in early boot before
the PCI subsystem works. */
在这里插入图片描述
PCIe ECAM访问(ARM)
PCI Express Enhanced Configuration Access Mechanism (ECAM)是访问PCIe配置空间的一种机制。是将PCIE的配置空间映射到mem空间,使用mem访问其配置空间的一种实现。
系统会给PCI设备分配一段内存空间,早期每个PCI设备分配的内存大小仅有256个Bytes。到后来的PCIE时期,随着设备性能增强,PCIE设备的配置空间扩展至4K个Bytes。PCIE一共支持256条Bus,32个Dev,8个Fun。因此在满负载的情况下,共需内存大小 = 4k * 256 328 = 256K Bytes = 256M,这个256M的内存空间是为PCIE设备准备的空间,系统不可用,这也是内存实际可用的总是会小于标称的主要原因之一。硬件根据ECAM的方式将某个Memory空间映射给PCI配置空间,CPU访问对应的memory空间即可以操作PCIE配置空间。

在这里插入图片描述
空间访问格式如下:
在这里插入图片描述
地址值格式如下:
BaseAddress + bus << 20 | device << 15 | function << 12 | offset
有了地址之后,可以直接用指针的方式取值,例如地址为Addr:
unsigned int data = *Addr;

无论是哪种方式读/写配置空间,最终都是生成了一个PCIE报文(图为TLP header):
在这里插入图片描述
然后再通过PCIE总线进行发送和接收。(CPU要访问该PCIe设备空间,只需访问对应的内存空间。CPU发出一个物理地址,RC检查该地址,如果发现该内存空间地址是某个PCIe设备空间的映射,就会触发其产生TLP,去访问对应的PCIe设备,读取或者写入PCIe设备)

BAR空间
bar空间的地址在配置空间上可以获取到,分为type0(EP)和type1(RC)。
在这里插入图片描述
bar 仅用于地址解码。它没有实现内存,只有访问内存的地址窗口。对于每个 bar,系统将读取所需内存窗口的大小并分配一个物理地址范围以访问该窗口。
bar地址的32bit格式如下:
在这里插入图片描述
如下图所示,请求一个4KB的NP-MMIO一般需要以下三个步骤:
在这里插入图片描述

  1. 如图所示,未初始化的BAR的低比特(11~ 4 )都是0,高比特(31~12)都是不确定的值(用 X 表示)。所谓初始化,就是系统(软件)向整个bar都写1,来确定BAR的可操作的最低位是哪一位。当前可操作的最低位为12,因此当前bar可申请的(最小)地址空间大小为4KB(2 ^ 12 )如果可操作的最低位为20,则该bar可申请的(最小)地址空间大小为1MB( 2^20)。
    2. 完成初始化(写1操作)之后,软件便开始读取BAR的值,来确定每一个BAR对应的地址空间大小和类型。其中操作的类型一般由最低四位所决定,具体如上图右侧部分所示。
    3. 最后一步是,系统软件在读到 size和type信息后,分配了对应的地址空间,并向BAR的高比特写入地址空间的起始地址(Start Address)。如图中所示,为0xF9000000(物理地址)。

空间读写

配置空间的读写,可以查看和修改设备属性,bar空间的读写,可以控制设备功能。

配置空间:
使用命令:lspci -s xx:xx.x –vvv 查看Capabilities:

在这里插入图片描述
64B~256B的格式如下:

在这里插入图片描述
256~4K格式如下:
在这里插入图片描述
其中Link Control可以配置Link Up/Down
在这里插入图片描述

Link Control偏移了16bytes,所以起始地址是60,link disable在第四个bit上,目前link Control的值是0x0000。
Link Down操作需要link disable位置1,置1后link Control值应为0x0010。通过setpci命令设置:
查看:setpci -s 00:15.3 60.b,其值为0;设置:setpci -s 00:15.3 60.b=10
设置之后再确认是否写入:setpci -s 00:15.3 60.b
在这里插入图片描述
lspci查看结果
在这里插入图片描述
BAR空间
bar空间可以映射SoC寄存器、LSW寄存器等,就如同FPGA上操作效果一样,都是一个基地址,加上一个offset,访问对应的寄存器来实现功能,这里不再赘述。
驱动和编码
Linux中,PCIE设备和driver各有一个表:
[dev1, dev2, dev3, … devN]
[driver1, driver2, driver3, … driverN]
当有新设备加入时,会遍历driver list,查找对应的驱动,并执行probe;
当有新驱动加入时,会遍历dev list,查找对应的设备,并执行probe;

PCIE的驱动主要是实现2个接口,1个适配dev的table,例如:
在这里插入图片描述
id_table是用来匹配驱动对应的设备(vendor id,device id);
probe里是对PCIE的初始化;
remove是卸载驱动。

probe中有一套基本的流程:
在这里插入图片描述
pci_enable_device:启用 PCIe 设备,并开启设备的中断。

pci_request_regions:申请bar资源

pci_resource_start, pci_resource_len, pci_resource_flags: bar属性

pci_enable_msi(x): 启用中断

ioremap:映射bar地址到虚拟地址

提供出去的常用接口就是对bar的读写,通常是read/write(bar, offset, data)

  • 8
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值