PCIE 基础
参考资料
相关书籍下载方式:
链接:https://pan.baidu.com/s/1zzWWt9ujVTr9oJSaJNP_mA
提取码:ahax
PCIE总线概述
注:由于自己是软件人员,关注重点更靠近X86环境下PCIE的状态和作用,本来很多部分都是由FPGA和硬件人员注重关心,但软件人员最好也知道些,也知道其所以然。
PCIE金手指接口及说明
PCIE速率
GT/s —— Giga transation per second (千兆传输/秒),即每一秒内传输的次数。重点在于描述物理层通信协议的速率属性,可以不和链路宽度等关联。
参考PCIE2.0/PCIE3.0/PCIE4.0/PCIE5.0接口的带宽、速率计算和PCIE扫盲
PCIE带宽计算: 吞吐量 = 传输速率 * 编码方案
比如,PCIE4.0是16 GT/s,128b/130b编码,所以 1Lane吞吐率 = 16*128/130=15.7GBps=1.96GMByte/s
端到端数据传输
- PCIE使用端到端链接
- TX和RX使用差分信号,且TX/RX分开
- 使用GT(Gigatransfer) 计算峰值带宽 GT=总线频率x数据位宽x2
- PCIE链路使用串行方式进行数据传输
PCIE总线信号
这里只需要看PCIE X1信号即可
电源信号
参考PCI-Express接口供电能力详解,可知:
- 接口本身最高的供电能力为75W
- 接口看到有5根+12V和3根3.3V针脚(+3.3V aux可选)。
- 文档中规定了每根针脚最高通过电流为1.1A, 因此所说的75W限制其实是,12V x (5 x 1.1A) = 66W再加上3.3V x (3 x 1A) = 9.9W,一共75.9W
- 另外,PCIE插槽也分多种类插槽: | 电压 | 10W插槽 | 25W插槽 | 75W插槽 | | —- | —- | —- | —- | | +3.3V | 3A | 3A | 3A | | +12V | 0.5A | 2.1A | 5.5A |
PCIE主要使用Vcc供电,Vaux-3.3V主要是电源管理相关,为了降低功耗和缩短恢复时间使用。
RESET#信号和WAKE#信号(可选)
CPU通过RESET#信号来复位PCIE内部逻辑
WAKE# PCIE设备休眠时,Vcc不进行供电。PCIE设备使用该信号让主机给PCIE进行供电唤醒。-必须支持Vaux供电
REFCLK+/REFCLK-
主板使用外部晶振给PCIE插槽提供参考时钟,设备作为Add-in卡与PCIE链接/或者内置PCIE模块,来与处理器进行同步。
时钟值: 100MHz
注:配置空间中 LINK Control Registers中”Common Clock Configuration”位
- 0 - 默认值,表示设备与链路对端使用时钟异步
- 1 - 表示设备与链路对端使用时钟同步
SMBUS与JTAG
支持SMBUS和JTAG信号
PRSTN1#和PRSTN2#信号
这两个信号用来进行热插拔, Add-in卡中信号相连,插槽中PRSTN1接地,PRSTN2上拉。
- 状态1-没插卡: 插槽中PRSTN1接地为低,PRSTN2上拉为高
- 状态2-插卡:Add-in卡短接,所以PRSTN1=PRSTN2为低
- 状态3-拔卡:恢复到没插卡状态,插槽中PRSTN1接地为低,PRSTN2上拉为高
且PCIE这两个信号使用长短针设计,可以保证在其他金手指插上后才能接触。拔出时优先断开。
PCIE总线层次结构
- 核心层:产生数据和接受数据
- 事物层:定义了PCIE总线使用总线事物,接收核心层的数据,封装成TLB数据包传输到数据链路层。
- 链路层:报数报文的可靠性,添加SeqNum和CRC前后缀,使用ACK/NAK来保证报文可靠传输。
- 物理层:链接PCIE设备的物理设备,以及链路训练,识别等。
PCIE链路拓展
因为PCIE设备只支持端对端的传输,所以必须使用Switch进行拓展。
SWITCH是一个特殊的设备,**支持1个上游端口-链接RC或上层SWITCH,2-n个下游端口-链接EP**。
另外,还有两个端口(与数据流向有关):
- Egress:发送数据端口(数据离开switch端口)
- Igress:接收数据端口(数据进入switch端口)
注:这两个概念是相对的,比如上层的swtich的Egress是下层switch的Igress。
cross Link(了解):
多个主机间通过PCIE互联,将上游端口和上游端口对接。
PCIE设备初始化-复位
PCIE支持两种复位方式:传统复位方式和FLR复位方式(可选)
- 传统复位方式:Cold,Warm,Hot Reset
- FLR复位:可以给PCIE设备配置空间提供一个寄存器,该寄存器的Function Level Reset写1时,PCIE设备单纯复位内部逻辑.不影响LTSSM链路训练状态机。
传统复位介绍:
- Cold复位:Vcc上电后,RESET#信号触发,无条件进入初始状态。
- Warm复位:用户自定义,比如使用watchdog进行复位等。
- HotReset:当PCIE设备异常时,可通过软件进行复位,比如通过桥设备复位子设备,
PCIE设备组成部分
PCIE主要由:RC Switch,PCIE-to-PCI桥,EP组成
基于PCIe架构的处理器系统
不同处理器架构,PCIE体系结构实现不同,这里Intel X86为例:
虚拟PCI:提供CPU地址域域PCI地址域转换。
FSB-to-Pcie桥:FSB兼容PCIE,因此可以直接转换。
在X86上,我们将:PCIE总线端口,地址转换,存储器控制器 统称为RC。所以X86上所有外设都是链接在PCIE上转的。
注:RC可以引出多个PCIE端口,称为多端口RC
在X86上,RC由ICH和MCH组成:
- ICH(Input/Output Controller Hub,输入/输出控制集线器)
- MCH(Memory Controller Hub,内存控制器集线器)
RC的组成结构
PCIE总线没有规定RC的设计,所以五花八门的。这里重点关注X86的PCIE设计;
以X86为例,RC由:
- PCI的HOST主桥作用:地址转换和隔离
- RCRB 管理存储器系统的寄存器组
- Event Collector 处理PCIE设备的消息报文和PME消息
- MR-IOV/SR-IOV相关
SWITCH桥-难点
因为PCIE使用端对端传输,所以需要PCIE SWITCH进行链路拓展-简单说就是PCIE HUB
- switch由多少端口,就有多少虚拟PCI桥
- switch内部有一条虚拟的PCI总线,用于链接各个虚拟PCI桥
- 系统软件初始化时,需要为这条虚拟PCI总线分配编号
TC和VC
为了保证报文得QoS,PCIE采用虚拟多通路方式:
- 每一条数据链路存在8个VC,
- 报文头有3-bit得TC(Traffic Class)标签-8类
- 8个TC和8个VC由软件绑定,属于 “多对一”
PCIE设备配置空间
X86使用CONFIG_ADDRESS寄存器与CONFIG_DATA寄存器访问PCIE 0x00-0xFF配置空间
使用ECAM方式访问0x100-0xFFF配置空间
首先PCIE设备也有PCI的0X00-0X3F基础配置空间-必须支持
这里注意:0x34的Capabilities Pointer指向了0x40-0xFF的配置空间
其次PCIE设备也支持0x40-0xFF的配置空间-必须支持(可选使用)
根据ID不同来区分不同的功能,主要关心的是:电源管理,MSI/MSI-X管理
最后PCIE设备可选支持0x100-0xFFF 4K大小的拓展配置空间-可选
这里截取PCI主桥的一段配置说明,或者参考PCIe设备在一个系统中是如何发现与访问的:
Host 主桥配置
(base) baiy@baiy-All-Series:u-boot-2020.07$ sudo lspci -s 00:00.0 -vv
00:00.0 Host bridge: Intel Corporation Xeon E7 v3/Xeon E5 v3/Core i7 DMI2 (rev 02)
Subsystem: Intel Corporation Xeon E7 v3/Xeon E5 v3/Core i7 DMI2
Control: I/O- Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Interrupt: pin A routed to IRQ 0
NUMA node: 0
Capabilities: [90] Express (v2) Root Port (Slot-), MSI 00
DevCap: MaxPayload 128 bytes, PhantFunc 0
ExtTag- RBE+
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop-
MaxPayload 128 bytes, MaxReadReq 128 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend-
LnkCap: Port #0, Speed 5GT/s, Width x4, ASPM L1, Exit Latency L0s <512ns, L1 <16us
ClockPM- Surprise+ LLActRep+ BwNot+ ASPMOptComp+
LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- CommClk-
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed unknown, Width x0, TrErr- Train- SlotClk- DLActive- BWMgmt- ABWMgmt-
RootCtl: ErrCorrectable- ErrNon-Fatal- ErrFatal- PMEIntEna- CRSVisible-
RootCap: CRSVisible-
RootSta: PME ReqID 0000, PMEStatus- PMEPending-
DevCap2: Completion Timeout: Range BCD, TimeoutDis+, LTR-, OBFF Not Supported ARIFwd-
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled ARIFwd-
LnkCtl2: Target Link Speed: 2.5GT/s, EnterCompliance- SpeedDis-
Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1-
EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [e0] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [100 v1] Vendor Specific Information: ID=0002 Rev=0 Len=00c <?>
Capabilities: [144 v1] Vendor Specific Information: ID=0004 Rev=1 Len=03c <?>
Capabilities: [1d0 v1] Vendor Specific Information: ID=0003 Rev=1 Len=00a <?>
Capabilities: [280 v1] Vendor Specific Information: ID=0005 Rev=3 Len=018 <?>
Capabilities: [300 v1] Vendor Specific Information: ID=0008 Rev=0 Len=038 <?>
(base) baiy@baiy-All-Series:u-boot-2020.07$ sudo lspci -s 00:00.0 -xxxx
00:00.0 Host bridge: Intel Corporation Xeon E7 v3/Xeon E5 v3/Core i7 DMI2 (rev 02)
00: 86 80 00 2f 00 04 10 00 02 00 00 06 10 00 00 00
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 86 80 00 00
30: 00 00 00 00 90 00 00 00 00 00 00 00 00 01 00 00 # Capabilities Pointer = 0x90
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
50: 00 e0 ff fb 00 00 00 00 00 00 00 00 00 00 00 00
60: 05 90 02 01 00 00 00 00 00 00 00 00 00 00 00 00
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
90: 10 e0 42 00 00 80 00 00 00 00 00 00 42 38 7a 00 # 0x90的ID=10,next=0xe0
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
b0: 00 00 00 00 9e 13 00 00 00 00 00 00 06 00 00 00
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
e0: 01 00 03 00 08 00 00 00 00 00 00 00 00 00 00 00 # 0xe0的ID=0x01,next=0x00-结束
f0: 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00
PCIE支持多少种Capability?
// linux-5.7.14/include/uapi/linux/pci_regs.h
/* Capability lists */
#define PCI_CAP_LIST_ID 0 /* Capability ID */
#define PCI_CAP_ID_PM 0x01 /* Power Management */
#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */
#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */
#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */
#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */
#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */
#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */
#define PCI_CAP_ID_HT 0x08 /* HyperTransport */
#define PCI_CAP_ID_VNDR 0x09 /* Vendor-Specific */
#define PCI_CAP_ID_DBG 0x0A /* Debug port */
#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */
#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */
#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */
#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */
#define PCI_CAP_ID_SECDEV 0x0F /* Secure Device */
#define PCI_CAP_ID_EXP 0x10 /* PCI Express */
#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
#define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Conf. */
#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */
#define PCI_CAP_ID_EA 0x14 /* PCI Enhanced Allocation */
#define PCI_CAP_ID_MAX PCI_CAP_ID_EA
PCIE总线的事务层(重点)
PCIE总线链路层和物理层
PCIE链路层
链路层作用:监控物理层的状态,保证TLP数据的准确传输。
PCIE链路层状态
链路层通过物理层监控PCIE状态,并维护Data Link Control and Management State Machine (DLCMSM) 状态机;
- DLCMSM三种状态分别代表: 未链接,链接中,可使用。
- DLCMSM根据链接状态,向事务层设置标记: DL_Down和DL_Up
DLLP格式
DLLP数据产生于链路层,终止于链路层,目的是保证TLP有效传输,
PCIE TLP数据传输
链路层源设备和目标设备在一条PCIE的两端;
数据传输过程:
- 链路层将事务层的TLP报文 加上数据包序列号和LCRC,放入REPLAY BUFFER-用户自定义buffer;
序列号:发送端12位计数器 NEXT_TRANSMIT_SEQ,在DL_INACTIVATE->DL_ACTIVATE时为0,每发送一条,计数加1 - 接收端12位计数器NEXT_RCV_SEQ,在接收到TLP报文时,对比计数器值与TLP 数据包序列号。
- 接收端接收一定量的数据/或者超时后,统一回复一次ACK DLLP;太频繁影响性能。
- 如果序列号比较正确,且CRC正确,接收端回复ACK DLLP,发送端则清除已发送buffer
- 如果序列号比较错误,或者CRC错误,
PCIE的链路训练过程
参考硬件设备识别扫盲篇 和PCIe设备发现过程
1、PCIE 链路训练、枚举扫描、配置BAR的顺序?
上电复位后,首先进行链路训练,之后进行枚举扫描、最后进行基地址寄存器BAR的配置。
完成基地址配置后,就可以通过memory TLP读写进行寄存器的访问了。
2、链路训练、枚举扫描、配置BAR的过程
PCIE首先进行链路训练,上电复位后,链路训练状态机进入L0状态时链路训练完成后进入gen1模式,如果双方支持更高的速率,则立即进行gen2/3、4速率的训练,当链路训练状态机再次进入L0状态,链路训练完成。
链路训练完成开始进行枚举扫描,枚举扫描主要目的是CPU需要知道系统中有哪些PCIE 设备,并且为每个设备分配总线号。PCIE的配置读TLP报文中包含响应设备的bus_number、function_number以及device_number,因此PCIE设备需要知道自己的bus_number是多少。
枚举扫描过程中,CPU会通过配置读TLP读取PCIE配置空间的的verdor id和头标类型寄存器以及其他配置寄存器,此时PCIE返回的读返回TLP报文中complete_id(bus_number、function_number、device_number)为全0,因此此时PCIE不知道自己的bus_number。枚举过程中,CPU完成对PCIE的配置读后,会发起配置写TLP,此时PCIE接受到CPU发来的配第一次置写TLP,会从TLP中解析出bus_number字段存下来。随后的配置读TLP返回中,就会使用bus_number、和function_number和device_number拼接成complete_id。
枚举扫描完成后,会配置基地址寄存器,给PCIE分配地址空间。通过对BA0/1、BAR2/3等基地址寄存器先进行写全32’hffff_ffff,得到信息确认PCIE想申请32bit地址还是64bit地址以及向获得的地址空间,从而分配基地址。
https://blog.csdn.net/qq_24722893/article/details/109272114
https://m.sohu.com/a/234024205_781333
https://www.sohu.com/a/234290359_781333
https://www.sohu.com/a/234436659_781333
PCIE得复位机制
基础介绍
FLR
PCIe总线自V2.0加入了功能层复位(Function Level Reset,FLR)的功能。该功能主要针对的是支持多个功能的PCIe设备(Multi-Fun PCIe Device),可以实现只对特定的Function复位,而其他的Function不受影响。当然,该功能是可选的,并非强制的(SRIOV得PF是必须得)。软件可以通过查询配置空间中的设备功能寄存器(Device Capability Register)来查询该PCIe设备是否支持FLR。如下图所示:
之前看Synopsys的IP: 错误理解:支持FLR方式的PCIe设备需要在其BAR空间中提供一个寄存器,当系统软件对该寄存器的Function LevelReset位写1时,PCIe设备将使用FLR方式复位PCIe设备的内部逻辑。
这种做法好像很无语,因为标准规定:100ms内必须完成FLR复位,在内核代码 pci_flr中根本没 用户自定的操作接口,只能按照标准版本去进行FLR, 不允许有自定义的操作。
FLR的应用场景
在一个大规模的并行处理系统中,系统软件使用分区的概念管理所以硬件资源,包括处理器资源和所以IO资源,这些IO资源中通常会包含PCIe设备。
在这种处理器系统中,任务在指定的分区中运行,当这个任务执行完毕后,系统软件需要调整硬件资源的分区。此时受到影响的PCIe设备需要使用FLR方式复位内部的逻辑,以免造成对新的分区的资源污染,并保护之前任务的结果。
当PCIe设备使用FLR方式进行复位时,有些与PCIe链路相关的状态和寄存器并不会被复位:
Ø Sticky Registers。与传统的复位方式相同,FLR方式不能复位这些寄存器,但是系统软件对部分Sticky Registers进行修改。当Vaux(备用电源)被移除后,这些寄存器中的保存的数据才会丢失。
Ø HwIint类型的寄存器。在PCIe设备中,有效配置寄存器的属性为HwIint,这些寄存器的值由芯片的配置引脚决定,后者上电复位后从EEPROM中获取。Cold和Warm Reset可以复位这些寄存器,然后从EEPROM中从新获取数据,但是使用FLR方式不能复位这些寄存器。
Ø 此外,还有一些特殊的配置寄存器不能被FLR方式复位,如Max_Payload_Size、RCB和一些与电源管理、流量控制和链路控制直接相关的寄存器。
Ø FLR方式不会影响LTSSM状态机。
FLR的机制
FLR只复位对应Function的内部状态和寄存器(使其暂时不变化,Making it quiescent),但是并不影响Sticky bits、有硬件初始化的值(Hardware-initialized bits)和链路专用寄存器(比如Captured Power,ASPM Control、Max Payload Size以及VC等寄存器)。
如果该设备在FLR前,发出了Assert INTx中断消息,必须在开始FLR之前在发出对应的Deassert INTx消息,除非该INTx已经被与其他Function共享了。当收到FLR后,该Function的所有的其他功能都应被立即停止(Required to cease)。
此外,PCIe Spec还明确给出了FLR的完成时间应在100ms以内。
PCIe Spec还明确规定了,当某个Function处于FLR状态时的一些特性:
· 该Function不能有任何与外界通信的(外部)接口;
· 该Function必须将任何软件可读取的状态(可能包括加密信息等)打乱。换句话说,任何内部存储都必须被清零或者随机化;
· 该Function必须可以被另一个Diver配置为一般模式;
· 该Function必须为其收到的包含有FLR信息的配置写(Configuration Write)返回一个Completion,然后再进行FLR操作。
在进入FLR状态后,还需要:
· 该Function接收到的任何请求都应该被直接丢弃,且不登记(Logging),也不报错误。但是FC Credits必须要被更新,以维持链路的正常操作;
· 该Function接收到的任何Completion都应该被当做Unexpected Completions,然后直接丢弃,且不登记,也不报错。
FLR复位代码实现
pcie_has_flr检测 Device Capabilities Register (Offset 04h) 是否支持flr功能
pcie_flr 给Device Control Register (Offset 08h) bit[15]写1,然后轮训60s检测是否能重新读取到command配置
pci_reset_function
__pci_reset_function_locked(dev);
pcie_has_flr
pcie_flr
pci_wait_for_pending_transaction(dev) // 读取PCIE的状态寄存器,判断是否在pending状态
pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); // 写1复位
pci_dev_wait
not ready %dms after %s; waiting
not ready %dms after %s; giving up
PASID-TBD
注:主要是IO虚拟化用到了这部分,所以需要简单看下。
PASID(进程地址空间ID Process Address Space ID)是一项可选功能,可在 多个进程之间共享单个Endpoint设备 ,同时为每个进程提供完整的64位虚拟地址空间。 实际上,此功能增加了对TLP前缀的支持,该TLP前缀包含可以添加到内存事务TLP的20位地址空间。
PASID TLP Prefix
参考:PCI Express Base_r5_1 P618页,6.20章节