跳转至

PCIe使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 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. 硬件连接

    1. PCIe连接使用高速差分信号对,每个Lane包含一对发送差分线(TXP/TXN)和一对接收差分线(RXP/RXN)。发送和接收通过独立的差分对实现全双工通信。

    2. PCIe链路宽度可以配置为x1、x2等,不同宽度对应不同数量的差分对。例如x2链路需要2对发送差分线和2对接收差分线。

    3. 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 模块使用介绍

    用户空间应用程序的使用,基本流程为:

    1. 扫描PCIe设备

    2. 访问PCIe设备配置空间

    3. 映射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链路训练失败

    1. 复现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
      
    2. 原因分析

      PCIe链路训练失败的常见原因包括:

      1、硬件连接问题:差分信号线阻抗不匹配、线序错误或接触不良;

      2、时钟配置问题:参考时钟频率不正确或时钟信号质量差;

      3、设备树配置问题:max-link-speed和num-lanes配置与实际硬件不符。

    3. 解决方法

      检查硬件连接和设备树配置,确保:

      1、使用示波器或逻辑分析仪检查差分信号质量;

      2、检查设备树中的max-link-speed和num-lanes配置;

      3、尝试降低链路速率或链路宽度进行测试。

    Q2:PCIe设备无法枚举

    1. PCIe设备在启动后无法被系统识别,lspci命令看不到设备:

      / # lspci
      / #
      
    2. 原因分析

      PCIe设备无法枚举的可能原因:

      1、PCIe控制器驱动未正确加载;

      2、设备树配置错误,PCIe控制器未使能;

      3、PCIe设备的PERST#信号未正确复位;

      4、PCIe设备的供电未正常工作。

      可以通过dmesg查看PCIe控制器的初始化状态。使用 dmesg | grep pci 命令可以查看PCIe控制器的初始化日志和错误信息。

    3. 解决方法

      1、检查PCIe控制器驱动是否正确加载;

      2、确保设备树中PCIe控制器的status属性为"ok";

      3、检查硬件设计中PERST#信号的时序,确保满足PCIe规范要求;

      4、使用万用表检查PCIe设备的供电电压是否正常。

    Q3:PCIe带宽未达到预期

    1. PCIe链路已建立,但实际带宽远低于理论值:

      / # lspci -vv -s 00:01.0 | grep LnkSta LnkSta: Speed 2.5GT/s, Width x1

      虽然配置为Gen2 x2,但实际工作在Gen1 x1。

    2. 说明

      PCIe链路速率和宽度会自动协商,实际速率取决于:

      1、双方设备支持的最大速率;

      2、信号质量是否满足高速传输要求;

      3、链路训练过程中是否检测到错误。

    3. 解决方法

      1、检查设备树配置,确认max-link-speed和num-lanes设置正确;

      2、检查硬件设计,确保差分信号线的阻抗匹配和布线质量;

      3、检查对端设备是否支持所配置的速率和宽度;

      4、查看dmesg日志,确认链路训练过程中的错误信息。