3. BAR大小计算与对齐规则
好,咱们进入第三章。这一章我把它叫做“地基工程”。
为什么?因为 BAR 的大小和对齐,直接决定了后续地址能不能对上,机器能不能跑起来。我记得刚带团队的时候,有个同事把 BAR 的大小算错了,结果验证工程师查了三天,最后发现是地址空间没对齐。嗯,这种坑,被人踩一次就够了。
这一章我会把计算方法拆开,再把对齐规则讲透。你跟着走一遍,以后看到 BAR 就不会发怵了。
3.1 BAR 大小计算的核心逻辑
先想一个问题:当你向 PCIe 总线申请一块地址空间时,硬件怎么知道它该多大?
答案其实很简单——写全1。
你往 BAR 寄存器里写 0xFFFFFFFF,再读回来,看到哪些位是 0,哪些位是 1。芯片内部固化了不可编程的位,这些位就决定了 BAR 的最小对齐大小。
核心公式: BAR 可编程位从第一个 1 开始,决定了 size = 2ⁿ,对齐地址也等于这个 size。
我举个实际的例子。假设你配置了一个 4KB 的 BAR,那么低 12 位就被硬件写死了(读回来是 0),高位才是可编程段。12 位地址对应 4KB。你看,本质上这就是 2 的幂次。
3.2 对齐规则的三种常见形式
对齐规则说白了就是一句话:你的 BAR 基地址,必须能被它的大小整除。
为什么?因为总线只认识页对齐、自然对齐。你想想看,如果 BAR 大小是 1MB,它却放在 512KB 的边界上,那地址范围就会和邻居打架。
我个人习惯把对齐规则归纳为以下三类:
- 自然对齐: base_addr % size == 0。比如 4KB BAR,基址必须是 4KB 的倍数。
- Prefetchable & Non-prefetchable 的统一: 两种 BAR 都遵循同样的对齐规则,没有例外。
- 64-bit BAR 的特殊性: 两个连续的 32-bit 寄存器视为一个整体,高位寄存器的值决定了基址的高 32 位,对齐要求还是按照总的 64-bit size 来算。
提示: 64-bit BAR 在实际设计中很常见。我的经验是,不要在低 32 位乱动,把高位和低位一起当成一个 64 位数来算对齐,不容易搞错。
3.3 大小计算的避坑指南
我曾经遇到一个案例:某 BAR 声明了 8KB,但实际只用了 4KB。硬件描述里不小心写错了 mask,结果读回来只有 4KB 空间可用,验证怎么会过?
这种问题其实很容易犯,因为很多工程师直接复制前一个模块的代码。这里我给你列几个要点:
- 确认最终需求:你需要的地址空间有多大,包括对齐后的浪费。
- 检查 mask 宽度:写全 1 后,可编程位的范围必须等于 size 的二进制表示。
- 验证边界地址:base + size - 1 不能溢出 64-bit 地址空间。
- 注意合并 BAR:同一外设如果有连续空间,尽量合并成一个 BAR,减少地址碎片。
嗯,实际上,大多数问题都是第三条引起的。溢出真的是个坑,地址写爆了,总线就挂在那里,谁也找不到原因。
3.4 对齐规则流程图
下面这张图把这整套逻辑串了起来。你看着它,基本上就能手算任何 BAR 的大小和对齐地址了。
3.5 实战中的计算步骤
好了,理论看完了,我们来看看实际操作。假设你有一个 BAR,要求分配 16KB 的地址空间。
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 确定大小 | 16KB = 2^14 字节 → size = 0x4000 |
| 2 | 计算 mask | 低 14 位为 0,高位可编程 → mask = 0xFFFFC000 |
| 3 | 选择基址 | 基址必须是 0x4000 的倍数,比如 0x10000 |
| 4 | 验证地址范围 | 0x10000 ~ 0x13FFF,不与现有 BAR 重叠 |
| 5 | 配置 BAR 寄存器 | 写入基址值(注意低 4 位控制位) |
你看,只要一步接一步来,一点都不复杂。
警告: 基址的低 4 位用于控制(比如 Memory Space Indicator、Prefetchable 等),写入基址时必须保证低 4 位为 0 或者正确的控制值,否则硬件会忽略你的配置。
3.6 总结一下重点
- BAR 大小等于 2^n,n 由可编程位的最高位位置决定。
- 对齐规则 = 基址能被 size 整除。
- 64-bit BAR 整体对齐,别分开算。
- 写全 1 读回来的值,就是最好的说明书。
说实话,这一章讲的内容,我自己在项目里反复用到。很多看起来复杂的问题,比如地址碰撞、空间浪费,根子往往就是这三条规则没遵守。你只要记住这个流程,多练几遍,以后设计 BAR 就像喝水一样自然。
嗯,这次就到这里。拿起你的电路图,试着算几个 BAR 的大小,你会发现问题比你想的简单。