总线地址译码与地址重映射:多外设/多芯片选择与窗口映射
更新时间:2026-09-19。前置:寄存器表与地址解码、AXI4-Lite 从机、AHB 总线矩阵。一篇讲「一个外设里寄存器怎么排」,本篇讲 SoC 顶层「一条地址总线怎么选中众多从设备」以及运行时改映射。
本文要回答的问题
- 高位地址如何译码成各从设备的片选,什么是部分译码和地址别名?
- 地址窗口、掩码、粒度怎么定义,重叠会怎样?
- 为什么要在启动后做地址重映射,BootROM 与 RAM 如何交换地址 0?
- 地址重映射窗口/MPU 和 decode 时序有哪些坑?
一、地址译码:从地址到片选
总线把地址广播给所有从设备,每个从设备有一个地址译码器判断「这次是不是找我」:
高位地址(基址比较/译码)
AHB HADDR/AXI ARADDR ──► &(addr[31:12]==BASE[31:12]) ──► 选中本从设备
低位地址 ─► 从设备内部寄存器/存储单元偏移- 全译码(fully decoded):所有高位都参与比较,每个地址唯一对应一个设备,无浪费也无别名。现代 SoC 用基址+掩码比较器(在 AXI/AHB crossbar 里集中做,见 AXI Crossbar、AHB 总线矩阵)。
- 部分译码(partial decoding):只用部分高位产生片选(老设计常见,用简单逻辑省门)。代价是同一设备在地址空间里出现多个地址别名(alias)——多根地址线没接,改那些位也选中同一设备,造成地址空间浪费和潜在歧义。
- 译码粒度通常按设备大小对齐:64KB 外设看
addr[31:16],低 16 位进设备做内部偏移。
片选与内部偏移
外设基址 0x4000_1000,大小 4KB
访问 0x4000_1234:
高位 0x40001 命中 → CS 有效
低 12 位 0x234 → 内部寄存器偏移一个存储设备(如 SRAM、外设)的地址范围必须按其自然大小对齐,否则译码比较器变复杂或产生非连续映射。
二、地址窗口与 Memory Map
SoC 地址空间被切成多个地址窗口(address region/window),常见布局(示意):
0x0000_0000 启动区(BootROM 或别名 RAM,可重映射)
0x0000_0000+ ITCM/DTCM/SRAM
0x2000_0000 外设 APB(AHB)
0x4000_0000 更大外设/AHB
0x6000_0000 外部存储器控制器(FMC/NOR/PSRAM)
0x8000_0000 DDR
...- 每个窗口有 base + size(掩码)+ 属性(cacheable/可执行/安全)。互连里的 decoder 表把窗口路由到对应从口;属性经保护位(AxPROT/PPROT)传递(见 APB 桥)。
- 未映射窗口(地址空洞):访问不到任何从设备时,必须有默认从设备/译码器返回错误(DECERR/总线错误)而不是挂死(见 AXI 错误与超时、AHB 默认从机)。
三、地址重映射(remap):为什么需要
经典问题:CPU 复位从地址 0 取指,但我们希望:
- 刚上电:地址 0 是 BootROM(不可改、含启动代码,见 BootROM 安全启动)。
- 启动完成后:地址 0 最好是快速 RAM(放中断向量表/关键代码),让异常入口零等待(见 TCM)。
同一块物理地址 0 不能同时是两者,于是用地址重映射:上电默认映射 BootROM 到 0,软件在启动早期写一个 REMAP 寄存器(或由硬件在跳转后置位),把地址 0 的窗口切到 RAM,BootROM 退到另一个高地址(或不可见)。
实现方式:
- 重映射 mux/窗口切换:remap 寄存器作为高位地址译码的选择位,切换「窗口 0 指向谁」。
- 多窗口别名 + MMU/MPU:同一物理外设可经多个逻辑窗口看到不同属性;系统地址重映射控制器(地址翻译窗口)在总线上做偏移加减。
- 启动区别名:BootROM 同时出现在 0(启动)和高地址(运行中可读),RAM 在 remap 后占据 0。
外部存储器控制器(FMC/SMC)也常提供多个片选窗口(bank/chip select region),每个窗口可配基址、时序,接不同外部芯片(NOR/PSRAM/NAND,见 HyperRAM、Raw NAND)。
四、重映射与时序、安全
- 重映射是全局可见的总线事件:切换瞬间若有未完成事务,可能被路由到错误的从设备。通常在启动早期(单线程、总线空闲)做一次,运行中不轻易切;需要切换时先停请求、排空总线、切窗口、再恢复(类似 外设复位前 quiesce)。
- 重映射窗口也用于安全隔离:安全/非安全世界看到的同一逻辑地址被翻译到不同物理区域,或非安全访问某窗口被拦截(见 总线防火墙、MPU/PMP、MMU/TLB)。
- decode 时序:基址比较在地址相位必须足够快(组合路径 addr→select→slave enable)。窗口多、比较器宽时是关键路径,常把译码打一拍或用前置寄存器(与 AXI register slice 同源),代价是增加地址到选中的延迟。
五、常见的坑
- 窗口重叠:两个从设备 decode 范围交叠,访问重叠区时两个都响应,总线冲突/数据不确定。
- 地址别名误用:部分译码下用别名地址访问,移植到全译码 SoC 失效。
- 未映射访问挂死:地址空洞无默认从机/超时返回,AXI/AHB 永久等待。
- 重映射竞态:切换时总线上有进行中事务,被错误路由;应在总线静止时切。
- 重映射后还在用旧地址:代码/链接脚本假设 BootROM 永远在 0,remap 后取指跑飞。
- 中断向量地址假设:remap 后向量表基址变了却没重配 VTOR/向量基址。
- 窗口未按大小对齐:基址不是 size 整数倍,译码比较复杂或映射非连续。
- 属性窗口配错:把设备内存标成 cacheable/可执行,产生配置总线访问被缓存、执行外设地址等问题(见 内存屏障)。
- 安全窗口泄漏:非安全主设备经重映射窗口访问到安全物理区,需防火墙配合。
- 外部片选窗口时序错:多个 bank 共用时序参数,慢速芯片按快速参数访问出错。
- decode 在关键时序路径:宽比较器拖慢地址相位,需要流水/分区。
- 复位默认态不一致:多复位源后 remap 寄存器默认值不同(见 复位控制器),导致冷启动/热启动内存布局不同。
相关与延伸
单外设内寄存器见 寄存器表与地址解码;集中译码互连见 AXI Crossbar、AHB 总线矩阵;启动映射见 BootROM;地址保护见 总线防火墙、MPU/PMP。
总结
总线地址译码用基址加掩码比较高位地址产生片选、低位地址做从机内部偏移,全译码无别名而部分译码会让同一设备占多个重复地址;SoC 把整个地址空间切成带基址/大小/属性的窗口并由互连集中路由,未命中窗口必须由默认从机返回 DECERR 防挂死;地址重映射则通过可配置窗口在「上电让 BootROM 占地址 0、启动后把 0 切到快速 RAM/TCM 放向量表」之间切换,外部存储控制器还提供多个独立时序的片选 bank,重映射必须在总线静止时完成并配合向量基址和安全窗口设置——窗口重叠、地址别名、空洞无响应、切换竞态和重映射后仍用旧地址是五大常见坑。