1. BANK是什么?
BANK是ARM CoreSight调试架构中用于扩展AP寄存器地址空间的一种机制。
1.1 核心问题
-
SWD协议中,AP地址总线只有A[3:2]两位
-
这两位只能寻址4个寄存器(00, 01, 10, 11)
-
但实际调试需要更多寄存器(CSW、TAR、DRW、配置寄存器、状态寄存器等)
1.2 解决方案:BANK
通过DP SELECT寄存器的APBANKSEL[3:0]字段(4位)
可以扩展为 16个BANK × 每个BANK 4个寄存器 = 64个寄存器地址
2. BANK机制工作原理
2.1 地址构成
完整AP寄存器地址 = BANK + 寄存器偏移
BANK选择: DP SELECT寄存器的APBANKSEL[3:0] (4位, 16个BANK)
寄存器选择: SWD请求包的A[3:2] (2位, 每个BANK 4个寄存器)
总地址空间: 16 × 4 = 64个寄存器位置
2.2 BANK映射表
每个BANK占用16字节地址空间(4个32位寄存器)
BANK0: 偏移0x00-0x0F
A[3:2]=00 (0x00): CSW - 控制/状态字
A[3:2]=01 (0x04): TAR - 传输地址寄存器
A[3:2]=10 (0x08): DRW/BD0 - 数据寄存器0
A[3:2]=11 (0x0C): DRW/BD1 - 数据寄存器1
BANK1: 偏移0x10-0x1F
A[3:2]=00 (0x10): BD2 - Banked Data 2
A[3:2]=01 (0x14): BD3 - Banked Data 3
A[3:2]=10 (0x18): BD4 - Banked Data 4
A[3:2]=11 (0x1C): BD5 - Banked Data 5
BANK2: 偏移0x20-0x2F
A[3:2]=00 (0x20): BD6
A[3:2]=01 (0x24): BD7
A[3:2]=10 (0x28): BD8
A[3:2]=11 (0x2C): BD9
... 以此类推
BANK15: 偏移0xF0-0xFF
A[3:2]=00 (0xF0): 保留
A[3:2]=01 (0xF4): CFG - 配置寄存器
A[3:2]=10 (0xF8): BASE - ROM表基地址
A[3:2]=11 (0xFC): IDR - ID寄存器
3. BANK的实际用途
3.1 扩展寄存器空间
如果没有BANK机制:
- 只能访问4个寄存器
- 功能严重受限
有了BANK机制:
- 可以访问多达64个寄存器位置
- 支持复杂的调试功能
3.2 不同类型寄存器的组织
常用寄存器放在BANK0(快速访问)
不常用寄存器放在其他BANK(需要时切换)
BANK0(常用):
CSW - 每次内存访问都需要
TAR - 设置目标地址
DRW - 数据读写
BANK15(系统):
CFG - 配置信息(很少修改)
BASE - ROM表地址(初始化时读取)
IDR - 识别信息(很少访问)
4. BANK切换操作
4.1 如何切换BANK
// 切换到BANK0(访问CSW、TAR、DRW)
swd_write_dp(SELECT, 0x000000F0);
// APBANKSEL = 0x0, APSEL = 0x0
// 切换到BANK15(访问CFG、BASE、IDR)
swd_write_dp(SELECT, 0x00000FF0);
// APBANKSEL = 0xF, APSEL = 0x0
// 然后通过A[3:2]选择具体寄存器
// A[3:2]=01 (0x04) -> 读取BANK15的0xF4地址(CFG)
4.2 实际使用示例
// 示例1:读取IDR寄存器(在BANK15)
void read_ap_idr(uint32_t *idr) {
// 1. 切换到BANK15
swd_write_dp(SELECT, 0x00000FF0);
// 2. 读取IDR寄存器(偏移0xFC,A[3:2]=11)
// SWD请求包: A[3:2]=11, APnDP=1, RnW=1
swd_read_ap(0x0C, idr); // 注意:这里传入的是寄存器在BANK内的偏移
}
// 示例2:常规内存读取(使用BANK0)
void read_memory(uint32_t addr, uint32_t *data) {
// 1. 确保在BANK0(通常已经是)
// 2. 写CSW
swd_write_ap(CSW, 0x23000052);
// 3. 写TAR
swd_write_ap(TAR, addr);
// 4. 读DRW
swd_read_ap(DRW, data);
}
5. BANK与性能优化
5.1 BANK切换的开销
每次BANK切换需要:
1. 写DP SELECT寄存器(一个完整的SWD包)
2. 可能的方向切换和空闲周期
3. 总开销:~10-20个时钟周期
5.2 优化策略
// 不好的做法:频繁切换BANK
for(int i = 0; i < 100; i++) {
swd_write_dp(SELECT, BANK0); // 切换到BANK0
swd_write_ap(TAR, addr[i]);
swd_read_ap(DRW, &data[i]);
swd_write_dp(SELECT, BANK15); // 切换到BANK15
swd_read_ap(CFG, &cfg); // 不需要每次都读CFG
}
// 好的做法:批量处理相同BANK的操作
swd_write_dp(SELECT, BANK0);
for(int i = 0; i < 100; i++) {
swd_write_ap(TAR, addr[i]);
swd_read_ap(DRW, &data[i]);
}
// 只在需要时读CFG
swd_write_dp(SELECT, BANK15);
swd_read_ap(CFG, &cfg);
6. 不同类型AP的BANK使用
6.1 MEM-AP(内存访问端口)
BANK0: 主要工作寄存器
CSW, TAR, DRW
BANK1-14: 通常未使用或用于特定功能
可能包含:调试控制、断点、观察点寄存器
BANK15: 系统寄存器
CFG, BASE, IDR
6.2 系统控制AP
可能使用更多BANK来组织各种系统控制寄存器
BANK0: 基本控制
BANK1: 电源管理
BANK2: 时钟控制
BANK3: 复位控制
...
7. BANK相关常见问题
7.1 忘记切换BANK
错误:当前在BANK0,尝试访问CFG寄存器(在BANK15)
结果:实际访问的是BANK0的DRW寄存器
现象:读取到错误数据,可能引发FAULT
7.2 BANK切换竞争条件
// 在多线程/中断环境下
线程A: swd_write_dp(SELECT, BANK0);
// 中断发生
线程B: swd_write_dp(SELECT, BANK15);
// 返回线程A
线程A: swd_read_ap(DRW, ...); // ❌ 实际在BANK15,读取的是CFG
解决方案:
// 使用临界区保护BANK切换
enter_critical_section();
swd_write_dp(SELECT, desired_bank);
// 执行AP操作
exit_critical_section();
8. 实际调试器中的BANK处理
8.1 Keil/ARM MDK
// Keil内部维护当前BANK状态
static uint8_t current_bank = 0xFF;
uint32_t mdk_read_ap_register(uint8_t bank, uint8_t reg_offset, uint32_t *data) {
if (current_bank != bank) {
swd_write_dp(SELECT, (0 << 24) | (bank << 4)); // 假设AP#0
current_bank = bank;
}
uint8_t a_bits = reg_offset >> 2; // 转换为A[3:2]
return swd_read_ap(a_bits, data);
}
8.2 DAPLink
// DAPLink通常采用保守策略
uint32_t daplink_read_ap_register(uint8_t ap_num, uint8_t bank, uint8_t reg_offset, uint32_t *data) {
// 总是先设置SELECT
uint32_t select_val = (ap_num << 24) | (bank << 4);
if (!swd_write_dp(SELECT, select_val)) {
return 0;
}
uint8_t a_bits = reg_offset >> 2;
return swd_read_ap(a_bits, data);
}
9. 总结
9.1 BANK的核心作用
-
地址空间扩展:解决SWD协议地址线不足的问题
-
寄存器组织:将寄存器按功能分组到不同BANK
-
性能优化:常用寄存器放在BANK0,减少切换开销
9.2 使用要点
-
默认BANK:大多数调试操作在BANK0完成
-
切换开销:BANK切换有性能开销,应尽量减少
-
状态保持:DP会记住当前BANK,直到下次切换
-
错误避免:访问寄存器前确认当前BANK是否正确
9.3 实际建议
// 对于99%的调试场景,只需要BANK0
// 只有以下情况需要切换BANK:
// 1. 读取AP ID信息 (BANK15)
// 2. 读取配置信息 (BANK15)
// 3. 访问特定扩展功能寄存器
// 通用规则:
// 内存读写、寄存器访问 → BANK0
// 系统信息、配置查询 → BANK15
简单来说,BANK就是ARM CoreSight调试架构中用来扩展寄存器地址空间的"页表"机制,让有限的SWD地址线能够访问大量的调试寄存器。