4. AXI内存映射与地址分配:系统地址空间划分,图像帧缓冲区的地址映射,多主设备仲裁
好,咱们今天聊点实在的。内存映射和地址分配,说白了就是给系统里的每个模块发一张“房产证”,告诉它们哪块地址归谁管。做图像加速的时候,这块要是搞不清楚,轻则数据写错地方,重则整个系统死给你看。
4.1 系统地址空间划分
一个典型的SoC里,地址空间是分层的。我习惯把整个32位或64位地址空间想象成一个大楼,每层住着不同的住户:
- 底层(0x0000_0000 - 0x3FFF_FFFF):通常是DDR内存,放帧缓冲区和操作系统数据
- 中层(0x4000_0000 - 0x7FFF_FFFF):外设寄存器空间,比如I2C、SPI、UART
- 高层(0x8000_0000 - 0xBFFF_FFFF):专用加速器,比如我们的图像处理IP
- 顶层(0xC000_0000 - 0xFFFF_FFFF):系统控制、Boot ROM、安全区域
你想想看,如果两个设备抢同一块地址,那不乱套了?所以地址译码器(Address Decoder)就是那个“物业管理员”,谁访问哪块地址,它说了算。
关键点:地址划分必须保证无重叠、无空洞。我在一个项目里见过有人把两个加速器映射到同一块地址,结果调试了三天才发现——两个IP同时响应,总线直接挂死。
4.2 图像帧缓冲区的地址映射
图像加速里最核心的就是帧缓冲区(Frame Buffer)。说白了,就是一块连续的内存区域,存着一帧一帧的图像数据。
举个例子,一个1080p的RGB图像,每像素24位:
帧大小 = 1920 × 1080 × 3 = 6,220,800 字节 ≈ 6 MB
如果我们要做双缓冲(Double Buffering),就需要12 MB。我建议把两个缓冲区放在连续的地址空间里:
# 假设DDR基地址为0x8000_0000
Buffer 0: 0x8000_0000 - 0x805F_3FFF
Buffer 1: 0x8060_0000 - 0x80BF_7FFF
为什么要连续?因为DDR的burst传输喜欢连续地址,跳来跳去会降低效率。嗯,这里要注意:帧缓冲区的对齐也很重要。我一般要求64字节对齐,这样AXI的burst长度能最大化。
个人经验:我曾经在一个项目中,帧缓冲区没做对齐,结果每次DMA传输都要多读几个字节,带宽浪费了15%。后来改成64字节对齐,问题立刻解决。
4.3 多主设备仲裁
图像加速系统里,主设备可不止一个。CPU、GPU、DMA、图像处理IP,它们都想访问DDR。谁先谁后?这就是仲裁器(Arbiter)的活。
AXI总线支持多种仲裁策略:
- 固定优先级:简单粗暴,但低优先级设备可能饿死
- 轮询(Round-Robin):公平,但实时性差
- 加权轮询:给图像处理IP更高的权重,保证帧率
- 基于QoS:每个主设备带一个优先级标签,仲裁器动态调整
我个人偏爱加权轮询。为什么呢?因为图像处理对带宽要求高,但偶尔延迟大一点没关系;而CPU对延迟敏感,但带宽需求小。加权轮询可以兼顾两者。
避坑指南:我曾经在一个4K视频处理项目里,用了固定优先级,结果DMA一直抢不到总线,导致视频卡顿。后来改成加权轮询,给DMA设了最高权重,问题才解决。记住:没有最好的仲裁策略,只有最合适的。
4.4 地址映射的实战案例
咱们来看一个实际的地址映射表。假设一个图像加速系统,包含:
| 设备 | 起始地址 | 结束地址 | 大小 | 说明 |
|---|---|---|---|---|
| DDR (4GB) | 0x0000_0000 | 0xFFFF_FFFF | 4 GB | 主存,含帧缓冲区 |
| 图像处理IP | 0x4000_0000 | 0x4000_FFFF | 64 KB | 配置寄存器 |
| DMA控制器 | 0x4001_0000 | 0x4001_0FFF | 4 KB | 描述符和状态 |
| 显示控制器 | 0x4002_0000 | 0x4002_FFFF | 64 KB | 时序和图层控制 |
你看,外设都挤在0x4000_0000附近,方便地址译码。帧缓冲区放在DDR里,由操作系统动态分配。
4.5 多主设备仲裁的硬件实现
仲裁器通常用状态机实现。我画个简单的结构图,帮你理解:
你看,四个主设备都想访问DDR,仲裁器就是那个“交通警察”。它根据预设的权重,决定谁先通过。我习惯在仲裁器里加一个超时计数器——如果一个设备等太久,就强制提升它的优先级,防止饿死。
核心要点:
- 地址空间划分要无重叠、无空洞
- 帧缓冲区要连续、对齐,提高DDR效率
- 多主设备仲裁选加权轮询,兼顾公平和实时
- 加超时保护,防止低优先级设备饿死
好了,这一章的内容就到这儿。地址映射和仲裁是AXI总线的基础,也是图像加速的基石。搞懂了这些,后面的章节咱们才能聊得更深入。