SWD协议中BANK机制详解

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);
}
复制代码
// 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的核心作用

  1. 地址空间扩展:解决SWD协议地址线不足的问题

  2. 寄存器组织:将寄存器按功能分组到不同BANK

  3. 性能优化:常用寄存器放在BANK0,减少切换开销

9.2 使用要点

  1. 默认BANK:大多数调试操作在BANK0完成

  2. 切换开销:BANK切换有性能开销,应尽量减少

  3. 状态保持:DP会记住当前BANK,直到下次切换

  4. 错误避免:访问寄存器前确认当前BANK是否正确

9.3 实际建议

复制代码
// 对于99%的调试场景,只需要BANK0
// 只有以下情况需要切换BANK:
// 1. 读取AP ID信息 (BANK15)
// 2. 读取配置信息 (BANK15)
// 3. 访问特定扩展功能寄存器

// 通用规则:
// 内存读写、寄存器访问 → BANK0
// 系统信息、配置查询 → BANK15

简单来说,BANK就是ARM CoreSight调试架构中用来扩展寄存器地址空间的"页表"机制,让有限的SWD地址线能够访问大量的调试寄存器。

相关推荐
逆小舟2 小时前
【SWM320】学习使用UART
单片机·学习·嵌入式软件
逐步前行2 小时前
STM32_SysTick_系统定时器
stm32·单片机·嵌入式硬件
逐步前行3 小时前
STM32_外部中断_寄存器操作
stm32·单片机·嵌入式硬件
安庆平.Я3 小时前
STM32——FreeRTOS - 任务创建和删除*
stm32·单片机·嵌入式硬件
yezhailiaoke13 小时前
【CANoe测试系列】-程控电源控制
单片机·嵌入式硬件
白掰虾17 小时前
一分钟上手STM32CubeMX2——STM32C5点灯
stm32·单片机·嵌入式硬件·stm2cubemx2·stm32c5
逐步前行18 小时前
STM32_NVIC_中断控制
stm32·单片机·嵌入式硬件
forAllforMe19 小时前
用STM32+LAN9252做etherCAT 运动控制从机方案
stm32·单片机·嵌入式硬件
WYH28719 小时前
FreeRTOS工程项目实践
c语言·单片机·嵌入式硬件·学习