2. PCIe协议基础回顾:PCIe拓扑结构、事务层、数据链路层、物理层、配置空间与BAR机制
在开始PCIe驱动从x86向ARM的移植适配之前,必须对PCIe协议的核心层次与机制建立精确的理解。本章将系统回顾PCIe的拓扑结构、分层协议栈(事务层、数据链路层、物理层)、配置空间结构以及BAR(基地址寄存器)机制。这些知识是后续分析硬件差异、调试驱动问题的基石。
2.1 PCIe拓扑结构
PCIe采用树形拓扑,由根复合体(Root Complex, RC)、交换器(Switch)和端点(Endpoint, EP)组成。与传统的PCI共享总线不同,PCIe使用点对点串行链路,每个设备通过独立的逻辑通道与上游通信。
2.2 PCIe分层协议栈
PCIe协议栈分为三个逻辑层:事务层(Transaction Layer)、数据链路层(Data Link Layer)和物理层(Physical Layer)。每个层都有独立的职责和协议数据单元(PDU)。
| 层次 | PDU名称 | 核心功能 | 驱动关注点 |
|---|---|---|---|
| 事务层 | TLP(事务层包) | 生成/解析读写请求、完成报文;处理端序、地址转换 | BAR映射、DMA描述符、MSI/MSI-X中断 |
| 数据链路层 | DLLP(数据链路层包) | 链路管理、错误检测(CRC)、重传(ACK/NAK) | 链路状态寄存器、错误日志(AER) |
| 物理层 | PLP(物理层包) | 串行/解串、8b/10b或128b/130b编码、链路训练 | 链路宽度/速率协商、LTSSM状态机 |
2.3 配置空间与BAR机制
PCIe配置空间是驱动与硬件交互的核心接口。每个PCIe功能(Function)都拥有独立的4KB配置空间(传统PCI只有256字节)。前64字节是配置头,包含Vendor ID、Device ID、Class Code、BAR等关键字段。
2.3.1 配置空间布局
| 偏移地址 | 寄存器名称 | 宽度 | 说明 |
|---|---|---|---|
| 0x00 | Vendor ID / Device ID | 16+16 bit | 厂商与设备标识,驱动匹配的关键 |
| 0x04 | Command / Status | 16+16 bit | 控制I/O、内存、总线主控使能 |
| 0x08 | Revision ID / Class Code | 8+24 bit | 设备分类(如网络控制器、存储控制器) |
| 0x10 - 0x24 | BAR0 - BAR5 | 32 bit each | 基地址寄存器,定义设备地址空间 |
| 0x2C | Subsystem Vendor / Subsystem ID | 16+16 bit | 子系统标识,用于精确匹配 |
| 0x34 | Capabilities Pointer | 8 bit | 指向能力链表(MSI、PCIe、AER等) |
| 0x3C | Interrupt Line / Pin | 8+8 bit | 传统中断路由信息(ARM平台可能无效) |
2.3.2 BAR机制详解
BAR(Base Address Register)是设备向系统声明其所需地址空间的机制。每个BAR可以映射内存空间或I/O空间(I/O空间在ARM平台上通常不支持)。驱动通过读取BAR值获取系统分配的物理地址,然后将其映射到内核虚拟地址空间。
- 向BAR写入全1(0xFFFFFFFF)。
- 回读BAR值,低比特位表示空间类型和大小信息。
- 根据回读值计算所需地址空间大小(取反后加1)。
- 恢复原始BAR值。
- 系统固件(BIOS/UEFI/设备树)在枚举时分配实际基地址。
2.4 事务层协议:TLP结构与路由
TLP(事务层包)是PCIe通信的基本单元。一个TLP由头部(Header)、数据负载(Data Payload)和可选的摘要(Digest)组成。头部长度根据地址格式分为3DW(双字)或4DW。
/* TLP 头部格式 (3DW, 内存读请求) */
struct tlp_header_mrd {
u32 fmt_type; // [31:24] FMT, [23:16] Type
u32 length; // [9:0] 数据负载长度 (以DW为单位)
u32 requester_id; // [31:16] Bus/Device/Function
u32 tag; // [15:8] 事务标签
u32 first_be; // [7:4] 首双字字节使能
u32 last_be; // [3:0] 尾双字字节使能
u32 address; // [31:2] 32位地址, [1:0] 保留
};
TLP路由方式有三种:地址路由(基于地址)、ID路由(基于Bus/Device/Function)和隐式路由(用于消息)。驱动开发中最常用的是地址路由(内存读写)和ID路由(配置读写)。
2.5 数据链路层与物理层关键机制
数据链路层负责链路完整性,包括CRC校验、重传和流量控制。物理层则管理链路训练状态机(LTSSM),负责协商链路宽度和速率。
| 状态 | 描述 | 驱动调试意义 |
|---|---|---|
| Detect | 检测对端设备是否存在 | 检查硬件连接和复位时序 |
| Polling | 发送训练序列,协商位锁定 | 确认参考时钟和信号完整性 |
| Configuration | 协商链路宽度和通道极性反转 | 检查链路宽度是否达到预期 |
| L0 | 正常工作状态 | 驱动正常收发TLP |
| Recovery | 链路错误后重新训练 | 检查错误日志和AER寄存器 |
lspci -vvv 或直接读取RC的链路状态寄存器(如 PCI_EXP_LNKSTA)确认当前链路宽度和速率。
2.6 本章小结
PCIe协议栈的层次化设计为驱动移植提供了清晰的抽象边界。在x86到ARM的移植过程中,需要重点关注:
- 拓扑差异: ARM的RC实现方式多样,配置空间访问方式(ECAM vs. 自定义)是首要确认点。
- 配置空间: ARM平台可能依赖设备树进行地址分配,而非BIOS/UEFI。
- BAR映射: 必须使用
ioremap()并注意DMA地址掩码设置。 - 链路训练: 硬件复位时序和参考时钟配置是常见问题根源。