4、Trace技术入门:ETM/ITM/SWO原理、SWO引脚配置与数据流捕获、使用Orbuculum解析SWO数据

调试嵌入式系统,很多时候就像在黑屋子里摸开关。你设个断点,停下来看看变量,然后继续跑。但有些问题——比如任务调度颠簸、中断响应抖动、或者某个函数到底跑了多少时间——断点根本抓不住。

这时候,Trace技术就派上用场了。说白了,Trace就是让芯片一边跑一边往外吐内部状态信息,你可以在外面实时收数据,完全不打断程序执行。我当年第一次用SWO抓出任务切换时间线的时候,说实话,挺震撼的。

4.1 三种Trace机制:ETM、ITM、SWO

ARM Cortex-M/M33内核提供了几种不同的Trace能力。它们分工不同,别搞混了。

机制 全称 输出什么 带宽需求 典型用途
ETM Embedded Trace Macrocell 指令执行流(PC地址序列) 高(通常需要4~8位并行Trace端口) 代码覆盖率、执行路径分析、性能profiling
ITM Instrumentation Trace Macrocell 软件主动打印的调试信息(printf、事件标记) 中(通过SWO单线输出) 日志输出、事件时间戳、RTOS跟踪
SWO Serial Wire Output ITM/ETM数据经过串行化后的单线输出 低~中(最高约115.2kbps~2Mbps) 低成本单线Trace,适合大多数调试场景

我个人习惯把ETM当成“录像机”,它记录CPU每一步去了哪里。ITM则是“对讲机”,你的代码主动喊话,告诉外面发生了什么。SWO就是那根天线,把信号发出来。

关键认知: 不是所有Cortex-M芯片都支持ETM。M0/M0+通常没有,M3/M4/M7/M33部分有。但ITM+SWO几乎是标配。所以SWO是你最应该先掌握的Trace手段。

4.2 SWO引脚配置——让芯片开口说话

SWO在硬件上就是一根线。在Cortex-M的SWD调试接口里,除了SWDIO和SWCLK,还有一个可选的SWO引脚。它通常复用为某个GPIO,比如PA10或PB3,具体看芯片手册。

配置SWO,你需要做三件事:

  1. 使能TRACE时钟和引脚功能——在RCC和GPIO寄存器里打开。
  2. 配置SWO波特率——让芯片和调试器约定好速率。
  3. 使能ITM端口——告诉ITM哪些通道可以往外发数据。

下面是一段典型的初始化代码(以STM32F4为例):

/* 使能TRACE时钟 */
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
RCC->APB2ENR |= RCC_APB2ENR_DBGMCU_CLK;

/* 配置PB3为SWO功能(AF7) */
GPIOB->AFR[0] |= (7 << 12);  // PB3复用AF7
GPIOB->MODER &= ~(3 << 6);
GPIOB->MODER |= (2 << 6);   // 复用模式

/* 使能DBGMCU的TRACE */
DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN;

/* 配置TPIU/SWO波特率:假设HCLK=168MHz,目标SWO=2MHz */
uint32_t swoprescaler = (HCLK_FREQ / SWO_TARGET_BAUD) - 1;
TPIU->CSPSR = 0x1;           // 1位数据宽度
TPIU->ACPR  = swoprescaler;  // 分频值

/* 使能ITM */
ITM->LAR  = 0xC5ACCE55;     // 解锁ITM
ITM->TCR  = 0x0001000D;     // 使能ITM,使能DWT时钟,使能SWO输出
ITM->TER  = 0xFFFFFFFF;     // 使能所有ITM通道

我曾经踩过的坑: 波特率算错了,SWO出来的数据全是乱码。后来发现TPIU的ACPR寄存器公式是 分频值 = (HCLK / 目标波特率) - 1,不是直接除。另外,有些调试器(比如J-Link)会自动协商SWO速率,但如果你手动配置了,两边必须一致。

4.3 数据流捕获——调试器怎么把SWO数据捞出来

芯片开始往外吐数据了,接下来你得有个东西在另一端接着。调试器就是那个“接收器”。

SWO数据流的结构很简单:

  • 每个数据包以源端口号开头(ITM端口0~31)。
  • 后面跟着数据载荷(1~4字节)。
  • 如果是printf输出,通常走ITM端口0,数据是ASCII字符。
  • 如果是事件时间戳,走ITM端口1~31,数据是时间值或事件ID。

调试器捕获SWO数据的方式有两种:

方式 说明 适用场景
实时流模式 调试器持续接收SWO数据,通过USB转发到PC 长时间日志记录、RTOS跟踪
缓冲模式 调试器内部FIFO缓存,PC端定时读取 短时突发数据、低功耗场景

我一般用J-Link的SWO功能配合Ozone或J-Scope来收数据。但如果你想要更灵活的工具链,Orbuculum是个好东西。

4.4 使用Orbuculum解析SWO数据

Orbuculum是一套开源工具,专门用来接收、解析和转发SWO数据流。它包含几个小工具:

  • orbtop:实时显示ITM端口流量,类似任务管理器。
  • orbdump:把SWO数据转存为文件。
  • orbcat:把ITM端口0的数据当作串口打印出来。
  • orbtrace:解析ETM指令流(如果你有ETM的话)。

基本用法很简单。假设你已经用J-Link或ST-Link连上了目标板,并且SWO引脚已经配置好:

# 1. 启动J-Link SWO服务器(后台运行)
JLinkSWOViewer -device STM32F407VG -speed 4000 -if SWD -swo 2000000

# 2. 用orbdump捕获原始SWO数据
orbdump -d /dev/ttyACM0 -b 2000000 -o swo_raw.bin

# 3. 用orbcat实时查看ITM端口0的printf输出
orbcat -d /dev/ttyACM0 -b 2000000

# 4. 用orbtop查看各ITM端口流量
orbtop -d /dev/ttyACM0 -b 2000000

小技巧: 如果你用orbcat发现输出乱码,先检查波特率是否匹配。如果波特率没错,看看ITM的TER寄存器是不是把所有端口都打开了。有时候你只想看端口0,但其他端口也在疯狂发数据,把流给冲乱了。

Orbuculum最实用的地方在于,它能把SWO数据转成标准格式,喂给其他工具。比如你可以把时间戳数据导出来,用Python画任务切换时间线。我做过一次,把FreeRTOS的任务切换抖动从微秒级抓出来了,老板看了直点头。

4.5 知识结构总览

下面这张图把ETM、ITM、SWO的关系和Orbuculum在其中的位置画清楚了:

Trace数据流与Orbuculum工具链 Cortex-M 内核 ETM(指令流) ITM(软件事件) DWT(数据观察) TPIU(串行化) SWO 引脚 单线串行输出 调试器 J-Link / ST-Link SWO接收 USB转发到PC Orbuculum orbdump orbcat orbtop 数据流方向: ETM/ITM → TPIU串行化 → SWO引脚 → 调试器捕获 → USB → Orbuculum解析 Orbuculum输出:ITM端口0的printf(orbcat)、端口流量统计(orbtop)、原始数据(orbdump) ETM数据需要更高带宽,通常走并行Trace端口,SWO主要用于ITM和DWT事件

4.6 实战建议与避坑指南

最后,分享几个我实际用SWO做Trace时总结的经验:

  • 先确认硬件支持:不是所有开发板都把SWO引脚引出来了。我有一块板子,SWO和某个外设复用了,跳线没焊,折腾了半天才发现。
  • 波特率别贪高:SWO的速率受限于调试器和线缆质量。2Mbps以上容易丢包。我一般用115200或1Mbps,稳定第一。
  • ITM端口0留给printf:其他端口用来发事件ID和时间戳,别混在一起,否则解析起来很痛苦。
  • Orbuculum的-d参数:指定的是调试器虚拟出来的串口设备,不是目标芯片的串口。在Windows上是COM口,Linux上是/dev/ttyACM0或/dev/ttyUSB0。
  • 如果数据全是0xFF:大概率是SWO引脚没配置对,或者调试器没使能SWO接收。检查GPIO复用和DBGMCU寄存器。

一句话总结: SWO是你用最低成本获得实时调试能力的入口。配置好引脚和波特率,用Orbuculum收数据,你就能看到程序运行时内部发生了什么——而不是靠猜。