PCIe使用参考¶
REVISION HISTORY¶
| Revision No. | Description |
Date |
|---|---|---|
| 1.0 | 01/04/2026 |
1. 概述¶
PCIe(Peripheral Component Interconnect Express)是一种高速串行计算机扩展总线标准,用于连接各种外围设备到计算机系统。PCIe采用点对点串行连接,支持全双工通信,通过通道(Lane)实现数据传输。每个通道包含两对差分信号线,分别用于发送和接收,可以独立运行在不同速率上。
SGS PCIe控制器符合PCI Express基片规范,支持多种链路宽度和速率配置。该控制器支持Root Complex(RC)和Endpoint(EP)模式,可以作为根复合体连接下游设备,也可以作为端点设备连接到上游的Root Complex。支持Gen1、Gen2等多种速率规格,提供高性能的数据传输能力。
2. 关键字说明¶
-
Lane(通道)
PCIe链路的基本组成单元,包含一对差分发送信号线和一对差分接收信号线,支持全双工通信。PCIe链路可以配置为x1、x2等多种宽度。
-
Root Complex(RC,根复合体)
PCIe拓扑结构中的核心组件,连接CPU和内存子系统,负责发起和接收PCIe事务。RC可以连接多个Switch和Endpoint设备。
-
Endpoint(EP,端点)
PCIe拓扑结构中的终端设备,作为发起者或完成者参与PCIe事务。EP可以是网卡、存储控制器、显卡等各种外设。
-
Gen1/Gen2(代)
PCIe的不同速率规格。Gen1:2.5GT/s,Gen2:5GT/s。每个通道的理论带宽随速率提升而增加。
-
TLP(Transaction Layer Packet,事务层数据包)
PCIe事务层使用的数据包格式,用于在请求者和完成者之间传输信息。包括Memory Read/Write、IO Read/Write、Configuration Read/Write等类型。
-
BAR(Base Address Register,基地址寄存器)
用于配置PCIe设备的内存或IO映射地址。通过BAR,CPU可以访问PCIe设备内部寄存器和内存空间。
-
MSI/MSI-X(Message Signaled Interrupts,消息 signaled 中断)
PCIe设备使用的中断机制,通过写入特定内存地址来触发中断,替代传统的基于引脚的中断方式。MSI-X支持更多中断向量。
-
LTSSM(Link Training and Status State Machine,链路训练和状态机)
PCIe链路的训练和管理状态机,负责链路的初始化、速率协商、宽度协商和故障恢复。
-
Completion Timeout(完成超时)
PCIe事务的超时机制,当请求者在规定时间内未收到完成数据包时触发超时处理。
3. 功能描述¶
3.1 PCIe资源¶
MHERA PCIe控制器提供了2个独立的PCIe控制器(PCIe0和PCIe1),每个控制器可以配置为RC(Root Complex)或EP(Endpoint)模式。
各PCIe控制器的寄存器地址映射关系如下(基于mhera.dtsi):
| 寄存器区域 | PCIe0 | PCIe1 | 说明 |
|---|---|---|---|
| DBI | 0x1F356000 (4KB) | 0x1F35a000 (4KB) | Data Bus Interface,配置空间 |
| DBI2 | 0x1F357000 (512B) | 0x1F35b000 (512B) | DBI扩展寄存器 |
| ATU | 0x1F358000 (4KB) | 0x1F35c000 (4KB) | Address Translation Unit |
| CFG | 0x10000000 (4KB) | 0x40000000 (4KB) | 配置空间(RC模式) |
| 配置空间 | 0x10000000 (1GB) | 0x40000000 (1GB) | EP模式配置空间 |
| MAC | 0x1F2C7800 (512B) | 0x1F2C7a00 (512B) | MAC寄存器 |
| MAC2 | 0x1F2CD400 (512B) | 0x1F2CD600 (512B) | MAC扩展寄存器 |
| DMA | 0x1F359000 (4KB) | 0x1F35d000 (4KB) | DMA寄存器 |
注意: MHERA芯片配置了2个PCIe控制器,均支持x2链路宽度和Gen2速率。
3.2 功能支持¶
下表提供各PCIe控制器的功能支持情况
| 功能 | RC模式 | EP模式 | 链路宽度 | 最大速率 | MSI | MSI-X |
|---|---|---|---|---|---|---|
| 支持情况 | 支持 | 支持 | x1, x2 | Gen2 (5GT/s) | 支持 | 支持 |
链路速率支持情况如下表:
PCIe代数 |
编码方式 | 原始速率 | 单Lane带宽(x1) |
|---|---|---|---|
| Gen1 | 8b/10b | 2.5 GT/s | 250 MB/s |
| Gen2 | 8b/10b | 5.0 GT/s | 500 MB/s |
链路宽度配置支持情况如下:
链路宽度 |
Lane数量 | 理论带宽(Gen2) |
|---|---|---|
| x1 | 1 | 500 MB/s |
| x2 | 2 | 1.0 GB/s |
PCIe拓扑结构:
PCIe拓扑结构采用树形层次结构,以Root Complex(RC)为核心,通过Switch连接多个Endpoint设备。RC连接CPU和内存子系统,负责发起和接收PCIe事务。
3.3 注意事项¶
-
差分信号布线
PCIe使用差分信号对,需要严格控制阻抗匹配(通常为100Ω差分阻抗),并且同一差分对的两条线需要等长、等间距布线。
-
中断配置
MSI-X中断相比MSI中断支持更多的中断向量,建议在高性能场景下优先使用MSI-X。
3.4 带宽计算¶
PCIe的有效带宽取决于链路速率、链路宽度和编码效率。理论带宽计算公式如下:
理论带宽 = 链路速率 × 链路宽度 × 编码效率
对于Gen2 x2链路:
- 原始速率:5 GT/s
- 链路宽度:2 Lanes
- 编码效率:8/10 = 80%
- 理论带宽:5 GT/s × 2 × 0.8 = 8.0 Gb/s ≈ 1.0 GB/s
链路速率的配置
链路速率可以在设备树(DTS)中通过max-link-speed属性配置,也可以在运行时通过PCIe工具查看和调整。
查看当前链路速率:
lspci -vv -s <device_id> | grep LnkCap
4. 硬件连接¶
-
PCIe连接使用高速差分信号对,每个Lane包含一对发送差分线(TXP/TXN)和一对接收差分线(RXP/RXN)。发送和接收通过独立的差分对实现全双工通信。
-
PCIe链路宽度可以配置为x1、x2等,不同宽度对应不同数量的差分对。例如x2链路需要2对发送差分线和2对接收差分线。
-
MHERA芯片的PCIe硬件配置如下:
PCIe0配置:
- 链路宽度:x2
- 支持速率:Gen1, Gen2
- 差分信号对:PCIe0_TX0_P/N, PCIe0_TX1_P/N, PCIe0_RX0_P/N, PCIe0_RX1_P/N
PCIe1配置:
- 链路宽度:x2
- 支持速率:Gen1, Gen2
- 差分信号对:PCIe1_TX0_P/N, PCIe1_TX1_P/N, PCIe1_RX0_P/N, PCIe1_RX1_P/N
PCIe连接说明: PCIe通过高速差分信号对实现点对点串行连接。每个Lane包含一对发送差分线(TXP/TXN)和一对接收差分线(RXP/RXN),支持全双工通信。差分信号需要严格控制阻抗匹配(100Ω)和等长布线。
5. Kernel用法介绍¶
5.1 Kernel Config配置¶
在编译Kernel时需要通过menuconfig使能PCIe相关配置。进入kernel配置界面的方法:
make menuconfig
之后按照以下路径使能PCIe驱动:
Device Drivers --->
Sgs SoC platform drivers --->
Sgs PCIe Support --->
[*] Sgs PCIe Controller - RC mode # PCIe RC模式支持
[*] Sgs PCIe Controller - EP mode # PCIe EP模式支持
[*] Sgs PCIe HOTPLUG # PCIe热插拔支持
Sgs PCIe Rpmsg-lite ---> # Rpmsg-lite通信支持(可选)
Sgs PCIe Verify ---> # PCIe验证功能(可选)
配置项说明:
- Sgs PCIe Controller - RC mode: 使能PCIe Root Complex模式,允许芯片作为根复合体连接下游PCIe设备
- Sgs PCIe Controller - EP mode: 使能PCIe Endpoint模式,允许芯片作为端点设备连接到上游Root Complex
- Sgs PCIe HOTPLUG: 使能PCIe热插拔功能,支持运行时动态添加/移除PCIe设备
- Sgs PCIe Rpmsg-lite: 基于RPMsg的通信协议,用于PCIe设备间通信(可选功能)
- Sgs PCIe Verify: PCIe验证功能,用于调试和测试(可选功能)
注意: 以上配置项在默认defconfig中已使能(RC、EP和HOTPLUG),如需修改可按上述路径进行配置。
配置完成后保存并重新编译kernel:
make -j$(nproc)
5.2 DTS定义¶
mhera.dtsi中,pcie0与pcie1节点段落如下:
pcie0: pcie@1F356000 {
compatible = "sgs,dw-pcie-earlyparam"; /* [rc] "sgs,dw-pcie" [ep] "sgs,dw-pcie-ep" [bootargs] "sgs,dw-pcie-earlyparam" */
reg = <0x0 0x1f356000 0x1000>, /* 4K DBI */
<0x0 0x1f357000 0x200>, /* DBI2 */
<0x0 0x1f358000 0x1000>, /* atu */
<0x1 0x00000000 0x1000>, /* [only rc] 4K CFG */
<0x1 0x00000000 0x40000000>, /* [only ep] Configuration space */
<0x0 0x1f2c7800 0x200>, /* mac */
<0x0 0x1f2cd400 0x200>, /* mac2 */
<0x0 0x1f359000 0x1000>; /* dma */
reg-names = "dbi", "dbi2", "atu", "config", "addr_space", "mac", "mac2", "dma";
portid = <0>;
#address-cells = <3>;
#size-cells = <2>;
/* properties for RC */
device_type = "pci";
bus-range = <0x00 0xff>;
/* ranges: [flags] [pci addr] [cpu addr] [size] */
ranges = <0x81000000 0 0x00001000 0x1 0x00001000 0 0x0000f000 /* downstream I/O */
0x82000000 0 0x00010000 0x1 0x00010000 0 0x3fff0000>;/* non-prefetchable memory */
linux,pci-domain = <0>;
num-lanes = <2>;
max-link-speed = <2>;
interrupts = <GIC_SPI INT_IRQ_PCIE0_OUT IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI INT_IRQ_PCIE0_OUT IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI INT_IRQ_PCIE0_OUT IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI INT_IRQ_PCIE0_SUP_PHY_P0 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI INT_IRQ_PCIE0_SUP_PHY_P1 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "msi", "mac", "hdma", "sup_phy0", "sup_phy1";
#interrupt-cells = <1>;
dma_read_channel_number=<8>;
dma_write_channel_number=<8>;
clocks = <&CLK_pcie0>;
clk_source = <0>; /*0: internal, 1:internal_2, 2: external, 3: wire */
status = "okay";
};
pcie1: pcie@1F35a000 {
compatible = "sgs,dw-pcie-earlyparam"; /* [rc] "sgs,dw-pcie" [ep] "sgs,dw-pcie-ep" [bootargs] "sgs,dw-pcie-earlyparam" */
reg = <0x0 0x1f35a000 0x1000>, /* DBI */
<0x0 0x1f35b000 0x200>, /* DBI2 */
<0x0 0x1f35c000 0x1000>, /* atu */
<0x1 0x40000000 0x1000>, /* [only rc] 4K CFG */
<0x1 0x40000000 0x40000000>, /* [only ep] Configuration space */
<0x0 0x1f2c7a00 0x200>, /* mac */
<0x0 0x1f2cd600 0x200>, /* mac2 */
<0x0 0x1f35d000 0x1000>; /* dma */
reg-names = "dbi", "dbi2", "atu", "config", "addr_space", "mac", "mac2", "dma";
portid = <1>;
#address-cells = <3>;
#size-cells = <2>;
/* properties for RC */
device_type = "pci";
bus-range = <0x00 0xff>;
/* ranges: [flags] [pci addr] [cpu addr] [size] */
ranges = <0x81000000 0 0x00001000 0x1 0x40000000 0 0x00020000 /* downstream I/O */
0x82000000 0 0x00100000 0x1 0x40100000 0 0x3ff00000>; /* non-prefetchable memory */
linux,pci-domain = <1>;
num-lanes = <2>;
max-link-speed = <2>;
interrupts = <GIC_SPI INT_IRQ_PCIE1_OUT IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI INT_IRQ_PCIE1_OUT IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI INT_IRQ_PCIE1_OUT IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI INT_IRQ_PCIE1_SUP_PHY_P0 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI INT_IRQ_PCIE1_SUP_PHY_P1 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "msi", "mac", "hdma", "sup_phy0", "sup_phy1";
#interrupt-cells = <1>;
dma_read_channel_number=<8>;
dma_write_channel_number=<8>;
clocks = <&CLK_pcie1>;
clk_source = <0>; /*0: internal, 1:internal_2, 2: external, 3: wire */
status = "okay";
};
在上面的段落中,PCIe控制器的地址空间包括DBI、DBI2、ATU、配置空间、MAC、MAC2和DMA寄存器空间。
节点中属性的说明如下:
| 属性 | 说明 |
备注 |
|---|---|---|
| compatible | 用于匹配驱动注册 | "sgs,dw-pcie" (RC), "sgs,dw-pcie-ep" (EP), "sgs,dw-pcie-earlyparam" |
| reg | 寄存器地址和大小 | 包括DBI、DBI2、ATU、CFG、配置空间、MAC、MAC2、DMA |
| reg-names | 寄存器区域名称 | "dbi", "dbi2", "atu", "config", "addr_space", "mac", "mac2", "dma" |
| portid | 端口ID | 0: pcie0, 1: pcie1 |
| #address-cells | 地址单元格数 | 禁止修改 |
| #size-cells | 大小单元格数 | 禁止修改 |
| device_type | 设备类型 | 必须为"pci" |
| bus-range | PCIe总线号范围 | 根据实际配置 |
| ranges | 地址映射范围 | 根据实际配置 |
| linux,pci-domain | PCIe域号 | 区分不同PCIe控制器 |
| num-lanes | 链路宽度 | 1, 2 |
| max-link-speed | 最大链路速率 | 1=Gen1, 2=Gen2 |
| interrupts | 中断号 | 包括msi、mac、hdma、sup_phy0、sup_phy1 |
| interrupt-names | 中断名称 | "msi", "mac", "hdma", "sup_phy0", "sup_phy1" |
| dma_read_channel_number | DMA读通道数 | 默认8 |
| dma_write_channel_number | DMA写通道数 | 默认8 |
| clocks | 时钟节点 | 禁止修改 |
| clk_source | 时钟源选择 | 0: internal, 1: internal_2, 2: external, 3: wire |
| status | 节点使能 | "okay": enable "disabled": disable |
5.3 模块使用介绍¶
用户空间应用程序的使用,基本流程为:
-
扫描PCIe设备
-
访问PCIe设备配置空间
-
映射PCIe设备BAR空间进行读写
lspci工具说明:
系统自带的lspci命令是简化版本,功能相对有限。如果需要完整版本的lspci工具(包含更多功能和详细选项),请前往pciutils官网下载:
- 官网地址: https://pci-ids.ucw.cz/git/
- 下载路径: git://git.kernel.org/pub/scm/utils/pciutils/pciutils.git
- 编译方法:
make make install
完整版lspci工具提供更详细的设备信息解析、更多查询选项和更好的兼容性。
举例:使用lspci工具查看PCIe设备信息
/ # lspci -vv
00:00.0 PCI bridge: SGS Technologies PCIe Controller (rev 01)
Control: I/O+ Mem+ BusMaster+
Status: Cap+ 66MHz- UDF- FastB2B- OrdErr-
LnkCap: Port #0, Speed 5GT/s, Width x2
LnkSta: Speed 5GT/s, Width x2
/ # lspci -s 00:01.0 -vv
00:01.0 Ethernet controller: Ethernet Controller X
Region 0: Memory at 0x40000000 (32-bit, prefetchable)
Region 2: Memory at 0x40100000 (32-bit, non-prefetchable)
5.4 Sample code¶
此demo提供参考访问PCIe设备的配置空间和BAR空间,可根据需求更改。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#define PCI_CONFIG_SPACE_SIZE 256
#define PCI_BAR0_SIZE 0x10000
/* 读取PCIe配置空间 */
int pci_read_config(int fd, int offset, unsigned char *buf, int len)
{
if (lseek(fd, offset, SEEK_SET) == -1) {
perror("lseek");
return -1;
}
if (read(fd, buf, len) != len) {
perror("read config");
return -1;
}
return 0;
}
/* 写入PCIe配置空间 */
int pci_write_config(int fd, int offset, unsigned char *buf, int len)
{
if (lseek(fd, offset, SEEK_SET) == -1) {
perror("lseek");
return -1;
}
if (write(fd, buf, len) != len) {
perror("write config");
return -1;
}
return 0;
}
/* 映射BAR空间并访问 */
void* pci_map_bar(int bar_fd, size_t size)
{
void *mapped_addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED, bar_fd, 0);
if (mapped_addr == MAP_FAILED) {
perror("mmap");
return NULL;
}
return mapped_addr;
}
int main(int argc, char **argv)
{
int config_fd = -1;
int bar_fd = -1;
unsigned char config_buf[PCI_CONFIG_SPACE_SIZE];
void *bar_base = NULL;
char device_path[256];
unsigned int vendor_id, device_id;
/* 打开PCIe设备配置空间 */
snprintf(device_path, sizeof(device_path),
"/sys/bus/pci/devices/0000:00:01.0/config");
config_fd = open(device_path, O_RDWR);
if (config_fd < 0) {
perror("Failed to open PCI device config");
return -1;
}
/* 读取Vendor ID和Device ID */
pci_read_config(config_fd, 0x00, config_buf, 4);
vendor_id = config_buf[1] << 8 | config_buf[0];
device_id = config_buf[3] << 8 | config_buf[2];
printf("PCIe Device Information:\n");
printf(" Vendor ID: 0x%04x\n", vendor_id);
printf(" Device ID: 0x%04x\n", device_id);
/* 读取Class Code */
pci_read_config(config_fd, 0x08, config_buf, 4);
printf(" Class Code: 0x%02x%02x%02x\n",
config_buf[2], config_buf[1], config_buf[0]);
/* 读取BAR0地址 */
pci_read_config(config_fd, 0x10, config_buf, 4);
unsigned int bar0 = config_buf[3] << 24 | config_buf[2] << 16 |
config_buf[1] << 8 | config_buf[0];
printf(" BAR0: 0x%08x\n", bar0);
/* 打开BAR0资源文件进行内存映射 */
snprintf(device_path, sizeof(device_path),
"/sys/bus/pci/devices/0000:00:01.0/resource0");
bar_fd = open(device_path, O_RDWR | O_SYNC);
if (bar_fd < 0) {
perror("Failed to open BAR0 resource");
goto out;
}
/* 映射BAR空间 */
bar_base = pci_map_bar(bar_fd, PCI_BAR0_SIZE);
if (!bar_base) {
fprintf(stderr, "Failed to map BAR0\n");
goto out;
}
printf("\nAccessing BAR0 registers:\n");
printf(" BAR0 mapped at: %p\n", bar_base);
/* 读取BAR0偏移0x00的寄存器 */
unsigned int reg_value = *(unsigned int*)(bar_base + 0x00);
printf(" Register at 0x00: 0x%08x\n", reg_value);
out:
if (bar_base && bar_base != MAP_FAILED) {
munmap(bar_base, PCI_BAR0_SIZE);
}
if (bar_fd >= 0) {
close(bar_fd);
}
if (config_fd >= 0) {
close(config_fd);
}
return 0;
}
6. FAQ¶
Q1:PCIe链路训练失败
-
复现PCIe链路无法正常建立,dmesg显示链路训练失败:
sgs-pcie 1f35a000.pcie: PCIe1: mode=RC, clk_source=internal, ssc_enable=0 sgs-pcie 1f35a000.pcie: PCIe1: lanes=2, mac=ffffffc008e25a00, mac2=ffffffc008e2d600, dbi=ffffffc008e35000 sgs-pcie 1f35a000.pcie: sgs_pcie_phy_init232 sgs-pcie 1f35a000.pcie: host bridge /soc/pcie@1F35a000 ranges: sgs-pcie 1f35a000.pcie: IO 0x0140000000..0x014001ffff -> 0x0000001000 sgs-pcie 1f35a000.pcie: MEM 0x0140100000..0x017fffffff -> 0x0000100000 sgs-pcie 1f35a000.pcie: iATU unroll: enabled sgs-pcie 1f35a000.pcie: iATU regions: 8 ob, 8 ib, align 4K, limit 4G sgs-pcie 1f35a000.pcie: Phy link never came up -
原因分析
PCIe链路训练失败的常见原因包括:
1、硬件连接问题:差分信号线阻抗不匹配、线序错误或接触不良;
2、时钟配置问题:参考时钟频率不正确或时钟信号质量差;
3、设备树配置问题:max-link-speed和num-lanes配置与实际硬件不符。
-
解决方法
检查硬件连接和设备树配置,确保:
1、使用示波器或逻辑分析仪检查差分信号质量;
2、检查设备树中的max-link-speed和num-lanes配置;
3、尝试降低链路速率或链路宽度进行测试。
Q2:PCIe设备无法枚举
-
PCIe设备在启动后无法被系统识别,lspci命令看不到设备:
/ # lspci / # -
原因分析
PCIe设备无法枚举的可能原因:
1、PCIe控制器驱动未正确加载;
2、设备树配置错误,PCIe控制器未使能;
3、PCIe设备的PERST#信号未正确复位;
4、PCIe设备的供电未正常工作。
可以通过dmesg查看PCIe控制器的初始化状态。使用
dmesg | grep pci命令可以查看PCIe控制器的初始化日志和错误信息。 -
解决方法
1、检查PCIe控制器驱动是否正确加载;
2、确保设备树中PCIe控制器的status属性为"ok";
3、检查硬件设计中PERST#信号的时序,确保满足PCIe规范要求;
4、使用万用表检查PCIe设备的供电电压是否正常。
Q3:PCIe带宽未达到预期
-
PCIe链路已建立,但实际带宽远低于理论值:
/ # lspci -vv -s 00:01.0 | grep LnkSta LnkSta: Speed 2.5GT/s, Width x1
虽然配置为Gen2 x2,但实际工作在Gen1 x1。
-
说明
PCIe链路速率和宽度会自动协商,实际速率取决于:
1、双方设备支持的最大速率;
2、信号质量是否满足高速传输要求;
3、链路训练过程中是否检测到错误。
-
解决方法
1、检查设备树配置,确认max-link-speed和num-lanes设置正确;
2、检查硬件设计,确保差分信号线的阻抗匹配和布线质量;
3、检查对端设备是否支持所配置的速率和宽度;
4、查看dmesg日志,确认链路训练过程中的错误信息。