3. BAR空间原理:BAR寄存器作用、内存映射与IO映射、BAR地址对齐与大小计算
Base Address Register(BAR)是PCIe设备配置空间中最关键的寄存器组,它决定了主机CPU如何访问设备内部的寄存器、FIFO或内存缓冲区。理解BAR的工作原理是编写正确PCIe驱动的基石。
3.1 BAR寄存器的作用与结构
每个PCIe Function最多支持6个BAR(Type 0 Header)或2个BAR(Type 1 Header,用于Bridge)。BAR的主要作用包括:
- 声明资源需求:设备通过BAR向系统报告它需要的内存空间或I/O空间的大小。
- 接受基地址分配:BIOS/UEFI或VxWorks PCIe总线枚举器将系统物理地址写入BAR,完成地址映射。
- 使能访问通路:一旦BAR被正确配置,CPU即可通过映射后的地址直接访问设备内部资源。
关键概念:BAR本身是一个可读写的寄存器,但它的只读位决定了资源类型和大小。写入全1再回读,是探测BAR大小的标准方法。
3.1.1 BAR寄存器位域定义(32位BAR示例)
| 位域 | 宽度 | 描述 |
|---|---|---|
| Bit 0 | 1 | 空间类型指示:0 = Memory Space,1 = I/O Space |
| Bit 2:1 | 2 | 内存类型(仅Memory BAR):00 = 32位,10 = 64位 |
| Bit 3 | 1 | 预取属性(仅Memory BAR):1 = Prefetchable,0 = Non-Prefetchable |
| Bit 31:4 | 28 | 基地址:实际映射的物理地址高28位(32位BAR) |
实战提示:64位BAR占用连续的两个BAR寄存器(如BAR0和BAR1),低BAR存放低32位地址,高BAR存放高32位地址。在VxWorks中处理64位BAR时,必须将两个BAR视为一个整体进行探测和配置。
3.2 内存映射与I/O映射
PCIe设备支持两种地址空间映射方式,它们在访问机制和性能上存在显著差异:
- Memory-Mapped I/O (MMIO):将设备寄存器映射到CPU的物理内存地址空间,CPU使用普通的load/store指令访问。这是现代PCIe设备的主流方式,支持突发传输和缓存一致性。
- I/O Space Mapping:映射到x86特有的I/O地址空间,必须使用in/out指令访问。I/O空间大小受限(64KB),且不支持预取,性能较低。
架构差异:在PowerPC或ARM架构下,通常不存在独立的I/O空间。VxWorks BSP在移植时,必须将I/O BAR请求转换为MMIO方式处理,否则设备无法正常工作。
3.3 BAR地址对齐与大小计算
PCIe规范要求BAR的基地址必须与BAR所请求的大小自然对齐。这意味着如果BAR请求16MB空间,其基地址的低24位必须为0(即16MB对齐)。
3.3.1 探测BAR大小的标准算法
- 保存BAR当前值(读回保存)。
- 向BAR写入全1(0xFFFFFFFF)。
- 读回BAR值,忽略低4位(Type位和属性位),对高位取反加1,即得到BAR请求的大小。
- 恢复BAR原始值。
计算公式:
对于64位BAR,需要将低BAR和高BAR组合为一个64位值进行计算。
BAR_SIZE = (~(BAR_VALUE & 0xFFFFFFF0) + 1)对于64位BAR,需要将低BAR和高BAR组合为一个64位值进行计算。
3.3.2 对齐规则与常见陷阱
| BAR请求大小 | 对齐要求 | 基地址低N位必须为0 |
|---|---|---|
| 4 KB | 4 KB 对齐 | Bit 11:0 = 0 |
| 1 MB | 1 MB 对齐 | Bit 19:0 = 0 |
| 16 MB | 16 MB 对齐 | Bit 23:0 = 0 |
| 64 MB | 64 MB 对齐 | Bit 25:0 = 0 |
| 256 MB | 256 MB 对齐 | Bit 27:0 = 0 |
常见陷阱:BAR大小必须是2的幂次方。如果设备设计时BAR大小不是2的幂次方(如3MB),PCIe枚举器将无法正确分配地址,导致驱动初始化失败。在VxWorks中,如果遇到BAR探测结果为0或异常大小时,应首先检查设备硬件设计是否符合2的幂次方对齐规则。
3.4 VxWorks中的BAR配置实战要点
在VxWorks中,PCIe总线枚举器(通常由BSP中的pciConfigLib或pciAutoConfigLib实现)负责自动探测和配置BAR。驱动开发者需要关注以下环节:
- 资源分配冲突:当多个设备请求的总空间超过系统地址窗口时,枚举器可能分配失败。此时需要检查BSP中的PCI内存窗口大小设置。
- 64位BAR处理:在32位系统上,64位BAR可能无法获得高位地址,导致映射失败。VxWorks 7支持通过MMU配置高位映射。
- 预取属性:Prefetchable BAR允许CPU合并写操作,但要求设备寄存器读取无副作用。如果设备寄存器读取有副作用(如清中断),必须设置为Non-Prefetchable。
调试技巧:在VxWorks shell中使用
pciDevShow 命令可以查看所有PCIe设备的BAR配置状态。如果某个BAR显示为0或无效地址,说明枚举阶段配置失败,需要检查硬件连接或BSP配置。
3.4.1 典型BAR配置代码片段
/* VxWorks PCIe BAR 探测与映射示例 */
STATUS pcieBarProbe
(
UINT32 busNo,
UINT32 deviceNo,
UINT32 funcNo,
UINT32 barIndex,
UINT32 *pBarSize
)
{
UINT32 barOrig;
UINT32 barMask;
UINT32 barValue;
/* 1. 读取BAR原始值 */
pciConfigInLong (busNo, deviceNo, funcNo,
PCI_CFG_BASE_ADDRESS_0 + (barIndex * 4),
&barOrig);
/* 2. 写入全1探测大小 */
pciConfigOutLong (busNo, deviceNo, funcNo,
PCI_CFG_BASE_ADDRESS_0 + (barIndex * 4),
0xFFFFFFFF);
/* 3. 回读并计算 */
pciConfigInLong (busNo, deviceNo, funcNo,
PCI_CFG_BASE_ADDRESS_0 + (barIndex * 4),
&barValue);
/* 4. 忽略低4位,取反加1 */
barMask = barValue & 0xFFFFFFF0;
*pBarSize = (~barMask) + 1;
/* 5. 恢复原始值 */
pciConfigOutLong (busNo, deviceNo, funcNo,
PCI_CFG_BASE_ADDRESS_0 + (barIndex * 4),
barOrig);
return OK;
}
核心要点总结:
• BAR寄存器通过只读位向系统声明资源需求,写入全1回读是探测大小的标准方法。
• MMIO是现代PCIe设备的主流映射方式,性能优于I/O映射。
• BAR基地址必须自然对齐到其请求的大小,大小必须是2的幂次方。
• VxWorks中通过pciConfigLib完成BAR配置,驱动开发者应关注64位BAR和预取属性的正确处理。
• BAR寄存器通过只读位向系统声明资源需求,写入全1回读是探测大小的标准方法。
• MMIO是现代PCIe设备的主流映射方式,性能优于I/O映射。
• BAR基地址必须自然对齐到其请求的大小,大小必须是2的幂次方。
• VxWorks中通过pciConfigLib完成BAR配置,驱动开发者应关注64位BAR和预取属性的正确处理。