4. 硬件断点寄存器详解:FPB 与 DWT 单元
硬件断点,说白了就是芯片内部专门用来“盯梢”的电路。你设一个地址,它就在那盯着,程序一跑到那就停。跟软件断点不一样,它不依赖内存里的指令替换,所以 ROM 里的代码、Flash 里的代码都能断。今天咱们就聊聊 Cortex-M 内核里两个核心的硬件调试单元——FPB 和 DWT。
4.1 FPB 单元:Flash 断点与补丁
FPB 的全称是 Flash Patch and Breakpoint。名字挺长,但功能就两个:断点和补丁。断点好理解,补丁是啥?它允许你把 Flash 中的某条指令“重定向”到 SRAM 中的另一条指令,实现运行时打补丁。不过说实话,补丁功能用得少,咱们重点看断点。
4.1.1 FPB 寄存器结构
FPB 单元包含一组控制/状态寄存器和多个比较器寄存器。以 Cortex-M4 为例,通常有 6 个比较器,其中 2 个用于字比较(32 位指令),4 个用于半字比较(16 位指令)。
| 寄存器名称 | 偏移地址 | 功能描述 |
|---|---|---|
| FP_CTRL | 0xE0002000 | 控制寄存器,使能 FPB、设置优先级 |
| FP_REMAP | 0xE0002004 | 重映射基址寄存器,用于补丁功能 |
| FP_COMP0 ~ FP_COMP5 | 0xE0002008 ~ 0xE000201C | 比较器寄存器,存放断点地址 |
每个 FP_COMP 寄存器的结构如下:
位段说明:
[31:1] : 断点地址(字对齐,bit[0] 固定为 0)
[0] : 使能位(1 = 使能该比较器)
4.1.2 FPB 断点配置流程
配置一个硬件断点,步骤其实很简单:
- 读取 FP_CTRL,获取 NUM_CODE 字段,确认比较器数量。
- 选择一个空闲的 FP_COMP 寄存器。
- 写入目标断点地址(注意字对齐),并将 bit[0] 置 1。
- 设置 FP_CTRL 的 ENABLE 位,使能 FPB 单元。
代码示例(伪代码风格):
// 假设我们要在 0x08001234 处设断点
uint32_t *fp_ctrl = (uint32_t *)0xE0002000;
uint32_t *fp_comp0 = (uint32_t *)0xE0002008;
// 步骤1:使能 FPB
*fp_ctrl |= (1 << 0); // ENABLE = 1
// 步骤2:配置比较器0
*fp_comp0 = (0x08001234 & 0xFFFFFFFE) | 0x01;
4.2 DWT 单元:数据监视与跟踪
DWT 全称 Data Watchpoint and Trace。它跟 FPB 是搭档——FPB 盯指令,DWT 盯数据。你想监控某个变量什么时候被修改?DWT 就是干这个的。
4.2.1 DWT 寄存器概览
DWT 单元包含多个寄存器,但咱们调试最常用的是下面这几个:
| 寄存器名称 | 偏移地址 | 功能描述 |
|---|---|---|
| DWT_CTRL | 0xE0001000 | 控制寄存器,使能 DWT 及各种计数器 |
| DWT_COMP0 ~ DWT_COMP3 | 0xE0001020 ~ 0xE000102C | 比较器寄存器,存放监视地址 |
| DWT_MASK0 ~ DWT_MASK3 | 0xE0001030 ~ 0xE000103C | 地址掩码寄存器,用于范围匹配 |
| DWT_FUNCTION0 ~ DWT_FUNCTION3 | 0xE0001040 ~ 0xE000104C | 功能寄存器,配置匹配动作 |
4.2.2 DWT 比较器的工作模式
每个 DWT 比较器都可以独立配置。DWT_FUNCTION 寄存器决定了匹配条件:
- 仅读匹配:当 CPU 读取目标地址时触发。
- 仅写匹配:当 CPU 写入目标地址时触发。
- 读写匹配:读或写都触发。
- 数据值匹配:不仅地址匹配,数据值也要匹配(这个功能比较高级)。
DWT_MASK 寄存器用来做地址范围匹配。MASK 值为 0 时,精确匹配单个地址;MASK 值为 n 时,匹配地址的低 n 位被忽略,相当于匹配 2^n 字节的范围。
4.2.3 DWT 数据断点配置示例
假设我们要监控地址 0x20000100 处的变量,当它被写入时触发断点:
uint32_t *dwt_ctrl = (uint32_t *)0xE0001000;
uint32_t *dwt_comp0 = (uint32_t *)0xE0001020;
uint32_t *dwt_function0 = (uint32_t *)0xE0001040;
// 步骤1:使能 DWT
*dwt_ctrl |= (1 << 0); // CYCCNTENA = 1
// 步骤2:设置监视地址
*dwt_comp0 = 0x20000100;
// 步骤3:配置功能——写匹配
// FUNCTION 字段:0x2 表示仅写匹配
*dwt_function0 = 0x2;
配置完成后,当 CPU 执行一条写入 0x20000100 的指令时,调试器就会收到事件,触发断点。
4.3 FPB 与 DWT 的协同使用
这两个单元其实可以配合起来用。比如你想在某个函数被调用时,检查一个全局变量的值——FPB 在函数入口断住,DWT 同时监控那个变量。这种组合拳在复杂 bug 定位时特别有效。
下面这张图展示了 FPB 和 DWT 在调试系统中的位置关系:
4.4 实际调试中的注意事项
用硬件断点这么多年,我总结了几条经验:
- 数量有限:FPB 一般 6 个,DWT 一般 4 个。别指望设几十个硬件断点,那是软件断点的事。
- 地址对齐:FPB 要求字对齐(32 位指令)或半字对齐(16 位指令)。不对齐的地址会被忽略。
- 低功耗模式:芯片进入睡眠或停止模式时,FPB 和 DWT 可能被关闭。调试前先确认电源状态。
- 多核芯片:每个核心有自己的 FPB 和 DWT,不能跨核共享。