Skip to content

Latest commit

 

History

History
213 lines (158 loc) · 6.8 KB

File metadata and controls

213 lines (158 loc) · 6.8 KB

CMSIS Runtime Watchpoint 适配指南

本文面向将 cmsis-runtime-watchpoint 集成到裸机、FreeRTOS 或其他 RTOS 工程的开发者。 当前发布版本为 0.3.0。

1. 依赖和支持范围

核心模块只依赖 C99 和目标芯片对应的 CMSIS Device Header,不依赖堆内存或 RTOS。

  • DWTv1:Cortex-M3/M4/M7。
  • DWTv2:Cortex-M33/M35P/M52/M55/M85。
  • 支持单地址、对齐的 1/2/4 字节 READ、WRITE 和 READ_WRITE。
  • Cortex-M0/M0+/M23 不支持本模块所需的完整 DebugMonitor 路径。

FreeRTOS 是可选适配层。其他系统只需实现一个能够从 DebugMonitor 异常上下文唤醒 工作线程的回调,无需修改核心代码。

2. 集成核心模块

推荐使用 CMake:

set(CWP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
add_subdirectory(path/to/cmsis-runtime-watchpoint)
target_link_libraries(app PRIVATE cwp::core)
target_compile_definitions(app PRIVATE
    CWP_CMSIS_HEADER="your_device_header.h"
    CWP_PROVIDE_DEBUGMON_HANDLER=1)

也可以直接加入以下源码:

src/cwp.c
src/cwp_policy.c
src/cwp_debugmon_gcc.c       # 仅在使用模块提供的 GCC/Clang handler 时加入

并添加 include/ 到头文件搜索路径。CWP_CMSIS_HEADER 必须是最终芯片的 Device Header,例如 stm32f407xx.h,不要仅指定通用 core_cm4.h

如果应用已经拥有 DebugMon_Handler,不要定义 CWP_PROVIDE_DEBUGMON_HANDLER。现有 handler 应根据 EXC_RETURN 选择 MSP/PSP,再调用:

cwp_debugmon_dispatch(hardware_stack_frame, exc_return);

3. 初始化和 DWT 资源所有权

简单工程可调用:

cwp_status_t status = cwp_init(debug_monitor_priority);

产品工程推荐显式限定比较器:

cwp_init_config_t init = {
    .struct_size = sizeof(cwp_init_config_t),
    .api_version = CWP_INIT_CONFIG_VERSION,
    .debug_monitor_priority = 8,
    .claim_policy = CWP_CLAIM_MASK,
    .comparator_mask = (1UL << 0) | (1UL << 2)
};
cwp_status_t status = cwp_init_ex(&init);

默认 CWP_CLAIM_FREE_ONLY 不覆盖已经启用的外部比较器。退出时调用 cwp_deinit(), 模块会恢复自己接管的比较器、DEMCR 位和原 DebugMonitor 优先级。

连接 halting debugger 时,DWT 匹配通常由调试器接管。产品脱机验证应确保 CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk 为 0。

4. 创建观察点

static volatile uint32_t guarded_value;
cwp_handle_t write_guard;
cwp_watchpoint_config_t config = {
    .address = (uintptr_t)&guarded_value,
    .size = sizeof(guarded_value),
    .access = CWP_ACCESS_WRITE,
    .user_data = 0x1001U
};

status = cwp_create(&config, &write_guard);

要区分读和写,尤其是 DWTv1,必须分别创建 READ 与 WRITE 观察点。DWTv1 的 READ_WRITE 命中不能反推出本次实际访问类型。

5. 上下半段模型

DebugMon_Handler 是上半段,只负责保存事件并调用事件就绪回调。回调运行在异常 上下文,必须有界、不可阻塞、不可打印、不可分配内存。

裸机可以在主循环调用 cwp_poll_event()。其他 RTOS 可以注册自己的 ISR-safe 唤醒:

static void event_ready_from_exception(void *context)
{
    /* 仅调用当前系统允许在异常上下文使用的通知原语。 */
}

cwp_set_event_ready_callback(event_ready_from_exception, context);

下半段线程被唤醒后应循环调用 cwp_poll_event(),直到队列为空。一次通知可能对应 多个事件,不能假设通知次数与事件数严格相等。

6. FreeRTOS 适配

target_link_libraries(app PRIVATE cwp::freertos freertos_kernel)

worker 示例:

static cwp_freertos_notifier_t notifier;

static void watchpoint_worker(void *argument)
{
    cwp_event_t event;

    cwp_freertos_notifier_bind_current(&notifier);
    cwp_init(8);
    /* 在这里创建观察点。 */

    for (;;) {
        (void)cwp_freertos_wait(portMAX_DELAY);
        while (cwp_poll_event(&event)) {
            /* 当前为 task/PSP:分类、记录、告警或复位。 */
        }
    }
}

DebugMonitor 会调用 FreeRTOS FromISR API,因此其数值优先级必须不小于 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY。例如最大系统调用优先级为 5 时, 可选择 5~15;STM32F407 示例使用 8。

适配层默认使用 task notification slot 0。如需其他 slot,在编译所有相关源文件时 定义 CWP_FREERTOS_NOTIFICATION_INDEX,并保证小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。删除 worker 前必须先解绑 notifier。

7. 授权代码区和 __DSB()

若需要区分合法与非法访问,可以把已知合法函数放入 .cwp_authorized

static void authorized_write(volatile uint32_t *address, uint32_t value)
    CWP_AUTHORIZED_CODE;

static void authorized_write(volatile uint32_t *address, uint32_t value)
{
    *address = value;
    __DSB();
}

链接脚本必须导出半开区间:

.cwp_authorized :
{
    . = ALIGN(4);
    __cwp_authorized_start__ = .;
    KEEP(*(.cwp_authorized))
    KEEP(*(.cwp_authorized.*))
    . = ALIGN(4);
    __cwp_authorized_end__ = .;
} > FLASH

__DSB() 只用于让已知授权访问在离开授权 section 前接收 DebugMonitor,从而稳定 白名单分类。未知非法写入不需要、也不可能提前调用 __DSB();DWT 会自动捕获它。

验证工程中的“非法”测试函数为了让测试输出在不同优化等级下保持确定,也在访问后 执行了 __DSB()。这只是测试同步点,不是实际踩踏检测的使用要求。

8. PC 语义与踩踏定位

cwp_event_t.pc 是硬件异常栈中的异常返回 PC,并设置 CWP_EVENT_FLAG_PC_IS_EXCEPTION_RETURN。它不保证等于精确的 LDR/STR 地址。

下半段日志应使用 stacked_pc 名称,并结合 ELF 向前反汇编:

arm-none-eabi-addr2line -f -C -e firmware.elf 0x08001232
arm-none-eabi-objdump -dS firmware.elf \
  --start-address=0x08001220 --stop-address=0x08001240

DWT 是访问后的审计与定位工具。若必须在写入发生前阻止访问,需要 MPU 或 TrustZone;若必须获得完整指令执行历史,需要 ETM 等追踪能力。

9. 移植验收清单

  • CMSIS Device Header 与目标 MCU 一致。
  • 目标内核实现 DebugMonitor 和可用 DWT 数据地址比较器。
  • DebugMonitor handler 正确区分 MSP、PSP 和浮点扩展帧。
  • 不覆盖调试器、ETM 或其他组件使用的比较器。
  • FreeRTOS 下 DebugMonitor 优先级满足 FromISR 约束。
  • READ/WRITE 分开配置,并验证 8/16/32 位访问。
  • 验证队列溢出时 cwp_dropped_event_count() 可观测。
  • 断开 halting debugger 后重新上电验证。
  • 检查深睡眠唤醒后 DWT 寄存器是否需要重新配置。

完整 STM32F407 工程位于 examples/stm32f407_freertos/,对应真机输出见 docs/VALIDATION_STM32F407_FREERTOS.md