💡 本文是《STM32内核精讲》栏目的第十二篇。前十一篇我们学习了寄存器模型、异常处理、AAPCS、复位序列以及低功耗模式。从本篇开始,我们将进入内核的调试与跟踪世界——这是理解内核“运行时行为”的关键工具集。掌握调试与跟踪组件,你将能实现无侵入的printf调试、精准的性能计数、代码覆盖率分析,以及内核状态的实时监控。


📌 一、引言:调试不只是打断点

很多嵌入式开发者认为,调试就是“打断点、单步执行、看变量”。当程序停止时,这些手段确实有效。但当问题发生在程序全速运行时——例如偶发的中断延迟、罕见的竞争条件、或者系统进入低功耗模式后的异常唤醒——传统的断点调试往往无能为力。

Cortex‑M 处理器内置了一套名为 CoreSight 的调试与跟踪架构。通过这套架构,我们可以在程序全速运行时,实时收集指令流、数据访问、性能计数以及软件打印信息,而不干扰程序的正常执行。

本章将系统性地介绍 CoreSight 的核心组件:ITM、DWT、ETM、TPIU、SWO,以及它们在 STM32CubeIDE、Keil 等环境中的具体应用。读完本章,你将彻底理解:

  • 如何用 SWO 引脚实现无外设的 printf 调试。
  • 如何使用 DWT 的 CYCCNT 进行微秒级高精度计时和 CPU 负载统计。
  • 指令跟踪(ETM)和数据跟踪(DWT)的区别与适用场景。
  • 如何通过 SWV 实时监控异常统计和睡眠深度。

📌 二、CoreSight 架构概览:一个完整的片上调试系统

2.1 什么是 CoreSight?

CoreSight 是 ARM 推出的系统级调试与追踪架构,它为 SoC 提供了统一的调试和跟踪解决方案。其核心设计理念是:

  • 非干预性(Non‑invasive) :调试和跟踪操作不影响 CPU 的正常执行。即使程序全速运行,跟踪数据也能实时输出,这对于调试低功耗模式下的唤醒时序、高频外设中断等“必须全速运行才能复现”的问题至关重要。
  • 模块化与可扩展:CoreSight 的组件可以按需集成,不同厂商的组件遵循统一规范,保证了互操作性。
  • 自动拓扑发现:通过 ROM Table 自动识别所有调试组件,调试器无需预存硬件信息即可连接。

2.2 CoreSight 核心组件架构

CoreSight 遵循“源→链路→接收端”的数据流逻辑。

调试通路(Debug Path) 负责外部调试器对 SoC 的访问。外部调试器通过 JTAG 或 SWD 接口连接调试访问端口(DAP,Debug Access Port),DAP 将请求转换为片上总线访问,进而操作 CoreSight 组件或 SoC 外设/内存。DAP 内部又分为多个访问端口(AP,Access Port),包括 AHB‑AP、APB‑AP、AXI‑AP 等,用于连接不同的总线。

跟踪源(Trace Source) 是生成调试/跟踪数据的源头。主要包括:

  • ITM(Instrumentation Trace Macrocell) :软件跟踪宏单元,支持 printf 类调试输出和软件事件标记。
  • DWT(Data Watchpoint and Trace) :数据观察点与跟踪单元,提供数据访问跟踪、性能计数和 PC 采样。
  • ETM(Embedded Trace Macrocell) :嵌入式跟踪宏单元,跟踪 CPU 指令/数据执行流,支持完整的历史序列回放。

链路组件(Link) 负责转发和整合多源跟踪数据。主要包括:

  • Trace Funnel:将多个 ATB(Advanced Trace Bus)数据流合并为一路。
  • Replicator:将一路跟踪数据分发到多个接收端,实现“一源多收”。

接收端(Sink) 负责存储或输出跟踪数据到片外。主要包括:

  • TPIU(Trace Port Interface Unit):跟踪端口接口单元,支持两种输出模式:
    • 并行时钟模式(Clocked Mode):使用最多 4 条数据线,用于 ETM 指令跟踪等大数据量场景。
    • 串行线输出模式(SWO,Serial Wire Output):使用单引脚(在 STM32 中通常为 PB3)异步串行输出,是 SWV 功能的硬件基础,可同时输出 ITM 和 DWT 的跟踪数据。
  • ETB(Embedded Trace Buffer) :片上 RAM 存储跟踪数据,可通过 DAP 读取。
  • ETR(Embedded Trace Router) :通过 AXI 接口将跟踪数据转发到系统内存,适合大数据量跟踪。

2.3 跟踪数据流的工作过程

ITM、DWT 和 ETM 生成的跟踪数据以数据包的形式通过 ATB 总线传输给 TPIU。TPIU 根据配置将数据格式化后,通过并行跟踪端口或 SWO 串行线输出。ETM 的指令跟踪必须使用并行跟踪端口,而数据跟踪和软件跟踪可以复用并行端口,也可以使用 SWO。

SWO 是 TPIU 串行输出模式下的物理引脚,可以同时输出 ITM 的软件跟踪数据(printf 调试)和 DWT 的硬件事件跟踪数据(PC 采样、数据访问、异常统计等)。这两类数据在 TPIU 中合并、格式化后,通过 SWO 引脚串行发送到调试器。

2.4 JTAG 与 SWD 的关系

JTAG 是行业标准的 5 引脚接口(TDI、TDO、TCK、TMS、可选 TRST),用于下载和调试程序。SWD 是 JTAG 的替代方案,仅使用两个引脚(SWCLK 和 SWDIO)即可提供相同的调试功能,并通过 SWO 引脚引入了数据跟踪功能。SWO 引脚在 JTAG 接口中与 TDO 复用,这意味着 SWO 跟踪只能在 SWD 接口模式下使用,JTAG 模式下无法访问 SWO。


📌 三、ITM:无侵入的 printf 调试

3.1 ITM 的原理与端口

ITM(Instrumentation Trace Macrocell,仪器跟踪宏单元)是 Cortex‑M 内核中的一个硬件模块,它的核心用途是支持调试信息的输出(例如 printf 格式输出)。ITM 包含 32 个刺激端口,允许不同的软件模块把数据输出到不同的端口,从而让调试主机可以把它们的信息分离开。每个端口都可以独立地使能/禁止。

ITM 的核心价值在于:

  • 非侵入性:printf 输出不占用 UART 等外设资源,也不依赖芯片厂商的外设驱动。
  • 实时性:即使程序全速运行,ITM 也可以通过 SWO 引脚将调试数据实时传输到调试器,不打断 CPU 执行。
  • 简单易用:CMSIS 提供了 ITM_SendChar() 函数(位于 core_cm3.h/core_cm4.h/core_cm7.h 中),只需重定向 __io_putchar 即可完成 printf 输出。

3.2 支持的 Cortex-M 内核

ITM 只支持 Cortex-M3、M4、M7 及更高版本的内核。Cortex-M0/M0+ 内核不支持 ITM,因此在 STM32F0、STM32L0 等型号上无法使用 SWO 调试。

3.3 SWO 引脚与硬件连接

SWO 是单引脚、异步串行通信接口。在 STM32 中,SWO 引脚通常与 PB3 复用,其复用功能为 TRACESWO(AF0)。具体引脚定义请查阅所用 MCU 的参考手册。

与一般的 SWD 调试接口需要 SWDIO 和 SWCLK 两根线相比,SWO 是额外的第三根线,用于输出跟踪数据。但注意,不是所有的开发板都把 SWO 引脚引出,在调试前需要确认硬件连接。

3.4 代码配置示例

将 printf 重定向到 SWV/ITM 需要以下步骤:

  1. syscalls.c 文件包含在工程中。printf() 会调用 _write() 函数,该函数在 syscalls.c 中有实现。
  2. 修改 __io_putchar 函数:
int __io_putchar(int ch) {
    ITM_SendChar(ch);
    return(ch);
}
  1. 使能跟踪时钟和睡眠模式下的调试保持:
// 使能跟踪时钟(TRACE_CLK_EN,位21)以及睡眠模式下的调试连接(DBG_SLEEP,位22)
DBGMCU->CR |= DBGMCU_CR_TRACE_CLK_EN | DBGMCU_CR_DBG_SLEEP;

// 使能 ITM 端口0 和 ITM 全局控制
ITM->TER |= 0x1;               // 使能刺激端口0
ITM->TCR |= ITM_TCR_ITMENA_Msk; // 使能 ITM 全局
  1. 配置 SWO GPIO(以 STM32 的 PB3 为例):
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef gpio_init = {0};
gpio_init.Pin = GPIO_PIN_3;          // SWO 引脚为 PB3
gpio_init.Mode = GPIO_MODE_AF_PP;
gpio_init.Pull = GPIO_PULLUP;
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
gpio_init.Alternate = GPIO_AF0_TRACE; // 复用功能 AF0(TRACE)
HAL_GPIO_Init(GPIOB, &gpio_init);

3.5 在 IDE 中查看输出

以 Keil MDK 为例,完成配置后,进入调试模式,打开 Debug (printf) Viewer 窗口,即可观察到 MCU 通过 ST-LINK 向编译器输出的打印信息。

在 STM32CubeIDE 中,需要在 Run → Debug Configuration 中启用 SWV 相关的 Trace 配置,然后在 SWV Data Trace 窗口中查看 ITM 输出的数据。


📌 四、DWT:数据观察点与性能计数器

4.1 DWT 简介

DWT(Data Watchpoint and Trace,数据观察点与跟踪)是 CoreSight 中的另一个核心组件,主要用于系统调试及跟踪。DWT 包含多个计数器,典型地用于程序代码的“性能速写”(profiling)。通过编程它们,可以让计数器在溢出时发出跟踪数据包。

DWT 的主要功能包括:

  • 数据观察点(Data Watchpoint) :监视特定内存地址的访问(读/写),当条件匹配时触发调试事件。
  • 性能计数器:包括 CYCCNT(时钟周期计数器)、CPI(每条指令平均周期数)计数器、异常开销计数器等。
  • PC 采样(PC Sampling) :周期性地记录程序计数器值,用于代码覆盖率分析和性能瓶颈定位。
  • 事件跟踪:记录异常进入/退出、数据访问等事件。

4.2 CYCCNT:高精度时钟周期计数器

DWT 中最常用的组件是 CYCCNT 寄存器。它是一个 32 位向上的计数器,记录的是内核时钟运行的个数——内核时钟跳动一次,该计数器就加 1,精度非常高。

精度 = 1 / 内核时钟频率
最长计时时间 = 2^32 / 内核时钟频率

例如,如果内核时钟是 72 MHz,精度就是 1/72M ≈ 14ns,最长计时约 59.65 秒。对于 STM32H7 等 400 MHz 主频的芯片,计时精度高达 2.5ns。CYCCNT 溢出后会自动清零重新向上计数,因此在使用过程中需要考虑溢出处理。

4.3 如何使能 DWT 和 CYCCNT

使能 DWT 和 CYCCNT 需要经过以下三个步骤。

  1. 使能 DWT 外设:通过内核调试寄存器 DEMCR 的位 24 控制,写入 1 使能 DWT 功能。DEMCR 地址为 0xE000EDFC
#define DEMCR (*(unsigned int *)0xE000EDFC)
#define TRCENA (0x01 << 24)
DEMCR |= TRCENA;
  1. 清空 CYCCNT 寄存器:CYCCNT 的基地址是 0xE0001004。在使能 CYCCNT 之前,先将其清零。
#define DWT_CYCCNT (*(unsigned int *)0xE0001004)
DWT_CYCCNT = 0;
  1. 使能 CYCCNT 计数器:通过 DWT 控制寄存器的位 0(CYCCNTENA)控制,写入 1 使能 CYCCNT 开始计数。
#define DWT_CTRL (*(unsigned int *)0xE0001000)
#define CYCCNTENA (0x01 << 0)
DWT_CTRL |= CYCCNTENA;

4.4 DWT 的实用场景

高精度计时与延时:利用 CYCCNT 可以实现微秒级的高精度延时。由于 CYCCNT 直接记录内核时钟周期,不受 SysTick 配置或中断优先级的影响,精度远高于普通循环延时。

uint32_t DWT_GetCycle(void) {
    return DWT_CYCCNT;
}

void DWT_DelayUs(uint32_t us) {
    uint32_t ticks = (SystemCoreClock / 1000000) * us;
    uint32_t start = DWT_GetCycle();
    while ((DWT_GetCycle() - start) < ticks);
}

代码执行时间测量:通过读取 CYCCNT 的前后差值,可以精确测量任意代码块的执行周期数。这在优化算法、对比不同实现方式的性能时非常有用。

CPU 负载统计:在 RTOS 中,可以利用 DWT 统计 CPU 使用率。在空闲任务中读取 CYCCNT 并计算空闲时间占比,从而得出 CPU 负载。

PC 采样与代码覆盖率:DWT 可以周期性地采样 PC 值,记录程序运行路径,用于分析代码覆盖率和定位执行热点。


📌 五、ETM:指令跟踪与代码覆盖率

5.1 ETM 的原理与作用

ETM(Embedded Trace Macrocell,嵌入式跟踪宏单元)是 CoreSight 中功能最强大的跟踪组件。它能够收集连续执行的每一条指令序列,并以跟踪数据包的形式输出到片外。

ETM 的核心价值在于:

  • 历史序列调试:可以回放程序执行的历史,定位那些“抓不住”的偶发故障。
  • 代码覆盖率分析:通过跟踪执行的指令,精确计算哪些代码被执行了,哪些没有。
  • 性能瓶颈分析:结合 DWT 的性能计数器,分析流水线停顿、分支预测失败等微架构行为。

5.2 ETM 与其他跟踪组件的区别

组件 跟踪内容 数据量 适用场景
ITM 软件打印信息 小(由软件控制) printf 调试、事件标记
DWT 数据访问、性能计数、PC 采样 中等 性能分析、内存访问跟踪
ETM 完整指令流 巨大(每条指令都产生数据) 代码覆盖率、历史序列调试

由于 ETM 产生的跟踪数据量巨大,通常需要使用并行跟踪端口(Clocked Mode)输出,而非 SWO 的单引脚串行模式。ETM 的指令跟踪必须使用并行跟踪端口

5.3 ETM 的局限性

  • 引脚占用较多:并行跟踪模式需要 1~4 条数据线,占用较多 GPIO 引脚。
  • 并非所有 Cortex‑M 系列都支持:Cortex‑M0/M0+ 不支持 ETM;M3、M4、M7 部分型号支持;M33、M55、M85 支持更高级的跟踪特性。
  • 需要专用的跟踪捕捉器:普通的 J-Link 或 ST-Link 无法捕捉 ETM 的指令跟踪流,需要专用的 Trace Port Analyzer(TPA)。

对于大多数日常调试,ITM 和 DWT 已足够使用。ETM 主要用于深度性能分析和代码覆盖率测试。


📌 六、SWO 与 SWV:跟踪数据的输出通道

6.1 SWO 与 SWV 的关系

SWO(Serial Wire Output)是 TPIU 的一种工作模式,使用单引脚异步串行输出。SWV(Serial Wire Viewer)是调试器侧的功能名称——通过 SWO 引脚接收并解析跟踪数据,在 IDE 中实时显示的程序计数器采样、数据跟踪、事件跟踪和仪器跟踪信息。

SWO 输出的跟踪数据包括:

  • PC 采样:显示 CPU 周期统计信息。
  • 数据跟踪:记录数据读/写访问,用于时序分析。
  • 事件跟踪:异常和中断执行统计。
  • ITM 跟踪信息:printf 样式的调试输出。

JTAG 接口中的 TDO 引脚与 SWO 复用,因此 SWO 跟踪只能在 SWD 接口模式下使用。

6.2 SWO 支持的跟踪源

SWO 是 TPIU 串行输出模式下的物理引脚,可以同时输出 ITM 的软件跟踪数据(printf 调试)和 DWT 的硬件事件跟踪数据(PC 采样、数据访问、异常统计等)。这两类数据在 TPIU 中合并、格式化后,通过 SWO 引脚串行发送到调试器。

6.3 调试器的 SWO 支持

  • ST-LINK:支持 SWV,但需要正版 ST-Link V2 或以上版本。
  • J-Link:支持 SWO/SWV,但需要特定的引脚连接配置。
  • ULINKplus:支持 SWO 跟踪。

并非所有的开发板都引出了 SWO 引脚,在项目规划时需要确认硬件支持。

6.4 SWO 输出速率配置

SWO 的串行输出速率需要通过调试器配置,通常设置为 2 MHz ~ 4.5 MHz。过高的速率可能导致信号完整性问题,过低则可能无法及时输出跟踪数据。

在 Keil MDK 中,需要在 Trace Settings 中配置 SWO 的时钟频率和输出速率。在 STM32CubeIDE 中,需要在 Debug Configuration → SWV 选项卡中进行配置。


📌 七、内核状态跟踪:异常统计与睡眠深度

7.1 使用 DWT 统计异常执行

DWT 模块提供了专门的事件跟踪能力,可以记录异常和中断的执行信息。通过 DWT 的异常开销计数器,可以监控:

  • 每种异常(IRQ 或系统异常)被触发的次数。
  • 异常进入和退出的耗时(异常开销)。
  • 中断嵌套深度等。

调试器工具(如 STM32CubeIDE 的 SWV 功能)可以实时显示这些统计信息。

在实际调试中,可以使用 SWV 的数据时间线窗口实时观察中断的触发频率和执行时长。例如,当怀疑某中断执行时间过长导致系统响应延迟时,可以开启 DWT 的中断事件跟踪,在 SWV 数据时间线中直观地看到中断进入和退出的时刻点,从而精确计算中断服务函数的执行耗时。

7.2 跟踪睡眠深度与功耗模式

Cortex‑M 支持通过 DWT 跟踪系统进入低功耗模式的频率和时长。DWT 中的睡眠计数器(SLEEPCNT)是一个 8 位寄存器,记录处理器处于睡眠模式时的时钟周期数,每 256 个周期溢出一次。它可以用于跟踪睡眠深度,但测量较长睡眠时间时需要处理溢出中断或配合软件累加。

当 CPU 执行 WFI 或 WFE 指令时,DWT 可以记录睡眠状态的进入和退出事件。结合本章的低功耗知识和 Tickless 模式,开发者可以通过 DWT 验证:

  • CPU 是否成功进入了预期的睡眠模式。
  • 唤醒延迟是否符合预期。
  • 是否存在未预期的频繁唤醒(功耗异常的根本原因)。

7.3 使用 SWV 进行实时内核状态监控

SWV 串行线查看器提供的典型信息包括:

  • PC 采样:显示 CPU 周期统计信息。
  • 事件计数器:用于跟踪中断和异常的频率。
  • 数据跟踪:数据读写时序分析。
  • ITM 跟踪信息:软件 printf 输出。

在调试低功耗问题时,可以通过 SWV 实时查看系统何时进入睡眠、何时被唤醒、以及唤醒源是哪个中断。这些信息对于优化功耗和定位异常唤醒问题至关重要。


📌 八、总结

8.1 本篇核心要点

  1. CoreSight 架构:ARM 的系统级调试与跟踪解决方案,遵循“源→链路→接收端”的数据流逻辑。调试通路通过 DAP(SWJ-DP 和各 AP)访问内核和外设,跟踪通路通过 ITM/DWT/ETM 生成数据,经 ATB 总线传输,由 TPIU 通过并行端口或 SWO 串行线输出至调试器。
  2. ITM 软件跟踪:支持无外设占用的 printf 调试。ITM 包含 32 个刺激端口,通过 SWO 引脚输出,CMSIS 提供了 ITM_SendChar() 函数,仅需重定向 __io_putchar 即可实现。
  3. DWT 性能计数器:提供 CYCCNT 高精度周期计数器(精度可达 2.5ns),可进行微秒级延时、代码执行时间测量、PC 采样和异常统计。使能 DWT 需要配置 DEMCR.TRCENA 和 DWT_CTRL.CYCCNTENA。
  4. ETM 指令跟踪:提供完整的指令流跟踪,用于代码覆盖率分析和历史序列调试,但需要并行跟踪端口和专业捕捉器支持。
  5. SWO/SWV 跟踪数据流:SWO 是 TPIU 的串行输出模式,可同时输出 ITM 的软件跟踪和 DWT 的硬件事件跟踪数据。SWV 是调试器侧的功能名称,用于解析和显示这些数据。
  6. 内核状态跟踪:通过 DWT 和 SWV 可实时监控异常统计、中断延迟和睡眠深度,是调试低功耗问题和系统性能瓶颈的有力工具。注意 DWT 的 SLEEPCNT 是 8 位寄存器,测量长时间睡眠需配合溢出处理。

8.2 下篇预告:《内核性能指标 —— CoreMark、DMIPS、中断延迟实测》

从下一篇开始,我们将进入内核性能评测的领域:CoreMark 和 DMIPS 的由来与测试方法、如何精确测量中断延迟、以及代码密度的对比分析。


💬 读者问题专栏 · 问题征集

你在内核调试或跟踪过程中,是否遇到过:

  • SWO 没有数据输出,不知道从哪里排查?
  • DWT 的 CYCCNT 计数器不计数,检查过 DEMCR 和 DWT_CTRL 了吗?
  • 想测量中断延迟或代码执行时间,不知道用 DWT 还是用逻辑分析仪更合适?
  • 调试低功耗时,如何确认 CPU 确实进入了预期的睡眠模式?

欢迎留言,我会在 《Cortex‑M 有问必答》 中专题解答。


📢 关于作者与更多内容

我是 BackCatK Chen,长期关注嵌入式底层、国产半导体与 AI 算力芯片。

如果你对芯片架构、行业趋势感兴趣,欢迎关注我的公众号,获取更多宏观技术观察。


文章标签Cortex-M CoreSight ITM DWT ETM SWO 调试 跟踪

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐