Skip to content

Latest commit

 

History

History
401 lines (281 loc) · 15 KB

File metadata and controls

401 lines (281 loc) · 15 KB

CMSIS Runtime Watchpoint 优化与完善路线

1. 文档目的

本文评估 cmsis-runtime-watchpoint 当前实现的成熟度,明确后续可以继续完善的功能、 优先级、设计边界与验收标准。

当前代码已经具备独立模块的基本形态:仅依赖 CMSIS-Core,不依赖 RTOS,不使用动态 内存,支持 DWTv1/DWTv2、动态观察点和 DebugMonitor 事件采集。它适合作为目标板 验证和项目集成的 MVP,但在作为通用开源组件或量产诊断组件之前,还需要补强硬件 资源所有权、能力探测和异常可靠性。

2. 当前基线

当前数据通路如下:

cwp_create()
    -> 分配静态 slot
    -> 配置 DWT COMP/FUNCTION
    -> 目标地址发生访问
    -> DebugMon_Handler
    -> cwp_debugmon_dispatch()
    -> 固定长度事件队列
    -> 主循环调用 cwp_poll_event()

已经具备的能力:

  • Cortex-M3/M4/M7 的 DWTv1 支持。
  • Cortex-M33/M35P/M52/M55/M85 的 DWTv2 支持。
  • 1、2、4 字节单地址读、写、读写观察点。
  • 固定资源池和代际 handle,可识别已经失效的 handle。
  • 观察点创建、删除、启用、禁用和全部清除。
  • DebugMonitor 中采集 PC、LR、xPSR,不在异常中打印或动态分配内存。
  • 基本栈帧和浮点扩展栈帧解析。
  • 队列满时丢弃新事件并累计丢失数量。
  • DWTv1/DWTv2 主机寄存器模拟测试。
  • 使用 CMSIS 6.3.0 core_cm4.hcore_cm33.h 的交叉编译测试。

3. 总体判断

功能仍有较大的优化空间,建议按以下顺序推进:

  1. 先保证不会破坏调试器或其他追踪组件正在使用的 DWT/DEMCR 资源。
  2. 再保证不同 Cortex-M 芯片上的能力探测和异常行为正确。
  3. 然后增加范围观察、触发策略、现场信息和跨编译器支持。
  4. 最后增加 RTOS 适配层、TrustZone、高可靠性和工程发布能力。

核心层应始终保持裸机和 CMSIS-Core 依赖;FreeRTOS、CMSIS-RTOS2、日志、持久化、 符号化都应作为可选适配层,不能反向侵入核心实现。

4. P0:生产化前必须完善

实施状态(2026-08-12):4.1、4.2、4.3 的单地址观察点部分和 4.6 错误码扩展 已完成,并通过 DWTv1/DWTv2 Mock 与 Cortex-M4/M33 交叉编译验证。4.4、4.5 仍属于 M2 目标板可靠性工作。

4.1 DWT 比较器所有权

优化前的 cwp_init() 会把探测到的所有比较器 FUNCTION 清零,可能覆盖调试器、 ETM、性能分析器或其他诊断模块的配置;当前实现已经改为显式资源认领。

已增加以下初始化策略:

  • CWP_CLAIM_FREE_ONLY:只使用当前未启用的比较器,推荐作为默认策略。
  • CWP_CLAIM_MASK:由调用者明确传入允许使用的比较器位图。
  • CWP_CLAIM_EXCLUSIVE:明确声明模块独占所有比较器,才允许全部清理。

模块只能修改自己成功认领的比较器。cwp_clear_all()cwp_deinit() 也只能清理 本模块拥有的资源。

验收标准:

  • 初始化前预置一个外部比较器,初始化、创建、删除和退出后其寄存器保持不变。
  • 指定位图以外的比较器永远不会被写入。
  • 资源不足时返回明确错误,不抢占外部比较器。

4.2 DEMCR 状态保存和恢复

TRCENAMON_EN 是系统级调试资源,可能由其他组件启用。模块不能假设自己是 唯一所有者。

当前实现会记录由模块首次开启的 DEMCR 位和原 DebugMonitor 优先级:

  • 仅设置本模块所需但尚未设置的位。
  • 退出时只撤销由本模块首次设置的位。
  • 如果进入模块前 MON_EN 已经开启,退出时保持开启。
  • 退出时只清理由本模块从 0 设置为 1 的 TRCENA/MON_EN 位;原本已开启的位保留。

验收标准:覆盖 DEMCR 初始值的四种组合,并验证退出后的状态恢复正确。

4.3 比较器能力探测

并非每个 DWT 比较器都一定支持相同类型的匹配。当前 DWTv2 检查 FUNCTION.ID, DWTv1 通过受保护的可写字段回读确认数据地址匹配是否实现。

建议能力模型至少包含:

typedef struct {
    uint32_t implemented_mask;
    uint32_t data_address_mask;
    uint32_t data_value_mask;
    uint32_t range_limit_mask;
    uint8_t comparator_count;
    uint8_t dwt_version;
} cwp_capabilities_t;

分配器应根据观察点类型选择满足能力要求的比较器,而不是简单选择第一个空 slot。

验收标准:使用 mock 构造不同能力组合,确认分配器不会把数据地址观察点放到不支持 该功能的比较器上。

4.4 DebugMonitor 接管与共存

当前可选实现会提供一个完整的 DebugMon_Handler。实际项目中,操作系统、调试代理 或安全组件可能已经拥有该异常入口。

建议明确支持三种集成方式:

  • 模块提供完整 handler,适合简单裸机工程。
  • 应用自己的 handler 调用 cwp_debugmon_dispatch(),作为推荐方式。
  • 注册前后置 hook,与现有 DebugMonitor 使用者进行链式分发。

还应增加异常重入保护。如果 DebugMonitor 处理过程中访问了另一个被监控地址,不能 造成无限嵌套或 lockup。重入时可选择只记录一次、累计计数或进入平台故障 hook。

验收标准:

  • 应用自有 handler 能正常调用模块。
  • 人为构造嵌套触发时不会无限递归。
  • 非本模块产生的 DebugMonitor 原因能够交给下一个处理器。

4.5 MATCHED 消费与事件去重

不同内核实现和调试状态下,需要验证 FUNCTION.MATCHED 的置位、读取和清除行为。 模块应避免一次硬件匹配被重复入队,也不能遗漏同一异常中多个比较器同时匹配的情况。

建议:

  • 为每个目标内核记录并验证 MATCHED 清除语义。
  • 一次 dispatch 先生成匹配位图,再统一生成事件。
  • 增加重复事件和多比较器同时匹配测试。
  • 记录原始匹配位图,便于现场诊断。

4.6 错误诊断能力

当前错误码可以覆盖基本 API 错误,但不足以区分硬件不支持、资源被占用、字段回读 失败或 DebugMonitor 不可用。

建议增加:

  • CWP_ERROR_RESOURCE_BUSY
  • CWP_ERROR_CAPABILITY
  • CWP_ERROR_REGISTER_VERIFY
  • CWP_ERROR_DEBUGMON_UNAVAILABLE
  • cwp_get_last_error_detail() 或由调用者提供的诊断结构

核心模块仍不直接打印日志,详细信息由上层决定如何输出。

5. P1:增强诊断能力

5.1 观察范围

增加硬件支持的地址范围观察:

  • DWTv1 使用 MASK 表达对齐的 2 的幂范围。
  • DWTv2 使用成对比较器表达地址起点和限制。
  • 分配器需要支持原子申请两个比较器,失败时不得留下半配置状态。
  • API 必须返回实际生效范围,避免硬件对齐规则造成误解。

不建议用多个单地址观察点静默模拟任意范围,因为这会快速耗尽有限的比较器。

5.2 触发策略

在观察点配置中增加可选策略:

  • 持续触发。
  • 单次触发后自动禁用。
  • 命中指定次数后禁用。
  • 只记录第一次现场,但继续统计命中次数。
  • 简单节流,避免高频地址快速填满队列。

触发策略必须在 DebugMonitor 中保持有界执行时间。

5.3 更丰富的事件现场

可以按编译配置增加以下字段:

  • 事件序号和时间戳。
  • 当前异常号 IPSR
  • CONTROLPRIMASKBASEPRIFAULTMASK
  • MSP、PSP 和 EXC_RETURN。
  • CFSR、HFSR、MMFAR、BFAR 等故障状态寄存器快照。
  • 同一次异常的匹配比较器位图。
  • 用户指定的观察点标签或 32 位上下文 ID。

时间戳应通过平台 hook 获取。不要强制占用 DWT CYCCNT,因为 CYCCNT 也可能属于其他 性能分析组件。

5.4 事件队列策略

当前环形队列为了区分空和满,实际可用容量是 CWP_EVENT_CAPACITY - 1。后续可以改用 单调递增读写序号,使配置容量全部可用,并提供可选溢出策略:

  • 丢弃最新事件。
  • 覆盖最旧事件。
  • 只保留第一条并统计后续命中。
  • 调用平台紧急处理 hook。

需要继续保持单生产者中断、单消费者主循环模型;如果允许多个消费者,应使用明确的 临界区或原子操作,而不是仅依赖 volatile

5.5 生命周期与电源管理

增加以下接口或平台 hook:

  • 系统进入深睡眠前保存模块状态。
  • 唤醒后重新检查 DWT 寄存器并恢复本模块拥有的观察点。
  • 软件复位或 warm reset 后检测 DWT 是否保留旧配置。
  • 时钟或调试域关闭时返回可诊断状态。

不能假设所有芯片在低功耗模式下都会保留 DWT 配置。

6. P2:通用组件能力

6.1 跨工具链异常入口

当前提供 GCC/Clang Arm Thumb handler。后续可以补充:

  • Arm Compiler 6。
  • IAR Embedded Workbench。
  • 不提供汇编入口的纯 C 集成模板。

每种实现都必须验证 MSP/PSP、EXC_RETURN、基本栈帧和浮点扩展栈帧。

6.2 可选 RTOS 适配层

核心代码保持不变,在 adapters/ 下增加独立适配:

adapters/
├── freertos/
│   ├── cwp_freertos.c
│   └── cwp_freertos.h
└── cmsis_rtos2/
    ├── cwp_cmsis_rtos2.c
    └── cwp_cmsis_rtos2.h

适配层可提供:

  • 将事件转交到诊断任务。
  • 记录当前任务句柄或任务编号。
  • 在任务切换时实现观察点复用。
  • 将事件写入 RTOS 队列或流缓冲区。

不得在核心层直接包含 FreeRTOS.hcmsis_os2.h

6.3 TrustZone

针对 Cortex-M33/M55/M85 增加 Secure/Non-secure 使用说明:

  • 明确模块编译在哪个安全域。
  • 验证目标地址的安全属性和调试认证状态。
  • 不允许 Non-secure 侧尝试管理仅 Secure 可访问的 DWT 资源。
  • 分别测试 Secure 和 Non-secure 异常入口。

6.4 数据值匹配

在硬件比较器明确支持时,可增加按写入值触发:

  • 等于指定值。
  • 不等于指定值。
  • 地址与数据值组合匹配。

此功能与比较器配对和能力限制关系紧密,必须建立在 4.3 的能力分配器完成之后。

6.5 工程质量

建议补充:

  • clang-format 和统一编码规则。
  • clang-tidy、Cppcheck 或同类静态分析。
  • MISRA C:2012 偏差记录,可作为可选安全配置。
  • API 文档生成和版本号。
  • CMake install/export、pkg-config 或 CMSIS-Pack 描述。
  • 发布包 SBOM、许可证清单和来源说明。
  • 真实目标板持续集成;主机 mock 不能替代硬件测试。

7. 推荐 API 演进

为了后续增加策略而不频繁破坏 ABI,建议从单个优先级参数演进为带版本和长度的配置:

typedef struct {
    uint16_t struct_size;
    uint8_t api_version;
    uint8_t debug_monitor_priority;
    uint32_t comparator_claim_mask;
    uint32_t flags;
} cwp_init_config_t;

typedef struct {
    uint16_t struct_size;
    uint8_t api_version;
    uint8_t size;
    uintptr_t address;
    cwp_access_t access;
    uint32_t trigger_limit;
    uintptr_t user_data;
    uint32_t flags;
} cwp_watchpoint_config_v2_t;

保留当前 cwp_init(priority) 作为便捷包装函数,新项目使用 cwp_init_ex()。这样可以 兼容现有调用者,同时为资源认领、溢出策略和平台 hook 留出扩展空间。

8. 推荐实施里程碑

M1:资源共存与能力探测

范围:完成 4.1、4.2、4.3 和错误码扩展。

状态:已完成代码实现、主机验证和 STM32F407 目标板确认;实板已验证外部繁忙比较器 保持不变、动态认领和退出恢复。

退出条件:模块不会修改未认领的 DWT 比较器,能基于能力选择比较器,并正确恢复 DEMCR 状态。

M2:非法读取判定与目标板异常可靠性

范围:完成 4.4、4.5;增加基于最终链接地址的授权代码区和非法读取分类;在至少一块 DWTv1 和一块 DWTv2 目标板验证。

状态:策略层、主机测试和 STM32F407 DWTv1 验证工程已实现;DWTv2 实板与嵌套异常 压力测试仍待完成。

退出条件:裸机脱机运行时能稳定捕获读、写、读写访问;授权读取与非法读取能依据 链接器边界正确分类;调试器连接、断开、单步时的行为有明确记录;嵌套异常不会造成 lockup。

M3:RTOS 下半段与诊断增强

范围:系统无关事件就绪回调、可选 FreeRTOS task-notification 适配层、读写授权策略、 PC 语义标志;后续继续完成触发策略、完整容量队列和低功耗恢复。

状态:通用回调、FreeRTOS 适配层、读写策略均已实现;STM32F407、FreeRTOS V11.3.0 实板验证通过。4 次事件分别唤醒 worker 4 次,授权读写和非法读写分类正确, 事件丢弃数为 0。

退出条件:DebugMonitor 上半段只采集并通知,FreeRTOS worker task 能可靠完成分类和 上报,事件丢失可观测。该基线已完成;高频压力和休眠唤醒恢复列为发布后可靠性增强。

M4:通用发布

范围:跨工具链、RTOS 适配、静态分析、版本化和发布材料。

状态:0.3.0 已完成版本化、许可证边界、适配指南、FreeRTOS 示例和真机日志归档; 跨工具链、CMSIS-RTOS2、静态分析基线与自动发布仍待后续版本完成。

退出条件:核心模块仍可独立裸机编译,所有适配层均为可选依赖,并具有明确的支持 矩阵和许可证清单。

9. 目标板测试矩阵

类别 必测场景
基础访问 8/16/32 位读、写、读写,地址对齐错误
资源 0/1/多个比较器,全部占用,混合能力比较器
生命周期 重复初始化、重复退出、失效 handle、禁用后重新启用
异常栈 MSP、PSP、基本帧、浮点扩展帧、栈对齐填充
并发 主循环消费时触发、临界区触发、多比较器同时匹配
溢出 队列填满、持续高频触发、丢失计数回绕
共存 调试器观察点、ITM/ETM、CYCCNT、外部 DebugMonitor handler
调试状态 不连接调试器、连接但运行、断点暂停、单步
电源 浅睡眠、深睡眠、唤醒、warm reset
安全 Secure/Non-secure 地址和调试认证状态(适用时)

10. 暂不建议加入核心层的功能

以下功能有价值,但应保留在应用或适配层:

  • UART、RTT、文件系统等具体日志输出。
  • ELF 符号化和源码行解析。
  • 自动上传崩溃日志。
  • 直接创建 FreeRTOS 任务或消息队列。
  • 在 DebugMonitor 中执行用户回调、格式化字符串或复杂恢复动作。
  • 依赖堆内存的动态扩容。

这些功能进入核心层会增加异常延迟、锁依赖和平台耦合,削弱当前模块作为独立 CMSIS 裸机组件的价值。

11. 最终建议

M1~M3 已完成,并通过 STM32F407 + FreeRTOS V11.3.0 真机闭环。0.3.0 可以作为首个 公开版本发布,用于开发期内存踩踏诊断和访问审计。后续工作不再阻塞首发:优先增加 高频压力与重入测试、触发任务上下文、DWTv2 真机覆盖和低功耗恢复;跨工具链、 CMSIS-RTOS2 与发布自动化归入 M4 增强。