4. CoreSight组件之ETM:触发逻辑、ATB总线与Trace数据格式化

ETM,全称是Embedded Trace Macrocell。说白了,它就是芯片内部的一个“硬件探针”。你想想看,CPU跑得飞快,软件断点根本跟不上。这时候ETM就派上用场了。它能实时抓取CPU执行的指令流和数据流,然后通过ATB总线送出去。

我个人习惯把ETM理解成一个“黑匣子”。飞机上有黑匣子,记录飞行数据。ETM就是SoC的黑匣子,记录程序执行轨迹。调试的时候,我们回放这些轨迹,就能知道程序到底是怎么跑的,哪里出了问题。

4.1 ETM的触发逻辑:什么时候开始抓?

ETM不是一直抓数据的。那样数据量太大,根本存不下。它需要一套触发逻辑,来决定“什么时候开始抓”、“抓到什么时候停”。

触发逻辑的核心,是资源选择器和触发条件的组合。我画了一张图,帮你理清这个关系。

ETM触发逻辑结构图 地址比较器 数据比较器 上下文ID 计数器/时序 触发逻辑单元 AND/OR组合 状态机控制 启动/停止条件 过滤规则 (可编程配置) Trace使能信号 控制数据采集 💡 触发逻辑可以级联:先通过地址匹配触发计数器,计数器溢出后再启动Trace采集。

触发逻辑的配置,通常通过ETM的寄存器来完成。核心寄存器包括:

寄存器 功能 我常用的配置
ETMCR 控制寄存器,使能ETM 设置bit[0]为1启动
ETMTRIGGER 触发事件寄存器 配置地址匹配+数据匹配
ETMTSSCR 时间戳控制 周期采样模式
ETMFFCR FIFO控制,防溢出 设置阈值,避免丢数据

我的经验:配置触发条件时,别一上来就设得太复杂。我习惯先设一个简单的地址断点,确认ETM能正常抓数据。然后再加计数器、加过滤条件。一步步来,出问题了也好定位。

4.2 ATB总线协议:数据怎么传出去?

ETM抓到了Trace数据,接下来要把它送出去。这就用到了ATB总线——Advanced Trace Bus。它是CoreSight体系内部专用的总线,专门用来传输Trace数据。

ATB总线其实不复杂。它跟AHB有点像,但更简单。核心信号就这几个:

  • ATCLK:时钟,跟CPU时钟可以不同步
  • ATDATA:数据总线,宽度可配(8/16/32位)
  • ATID:源ID,标识数据来自哪个ETM
  • ATVALID:发送端有效信号
  • ATREADY:接收端准备好信号

ATB的握手协议很简单:发送端拉高ATVALID,接收端拉高ATREADY,一个数据就传过去了。如果接收端忙,ATREADY拉低,发送端就得等。

⚠️ 注意:ATB总线上可以挂多个ETM。每个ETM有唯一的ATID。我曾经遇到一个问题:两个ETM配了相同的ATID,结果Trace数据混在一起,根本没法解析。排查了好久才发现是ID冲突。所以,ATID的分配一定要仔细核对。

ATB总线上还有一个重要的概念——数据包。ETM产生的Trace数据不是裸数据,而是打包成ATB包。包的类型包括:

  • 原子包:最基本的指令执行信息
  • 数据包:包含数据读写地址和值
  • 同步包:用于对齐和同步
  • 触发包:标记触发事件发生

4.3 Trace数据的产生与格式化:抓到的数据长什么样?

ETM抓到的原始数据,不能直接用。它需要经过格式化,变成调试工具能识别的格式。这个过程,我称之为“从硬件信号到调试信息的翻译”。

Trace数据的产生流程,大致是这样的:

  1. 指令跟踪:ETM监控CPU的指令执行流水线
  2. 分支记录:只记录分支指令(跳转、调用、返回)
  3. 压缩编码:用变长编码压缩数据,减少带宽
  4. 打包输出:封装成ATB包,加上时间戳

这里有个关键点——分支跟踪。ETM不会记录每一条指令,那样数据量太大了。它只记录“分支”信息。比如程序从地址A跳到了地址B,ETM就记下这个跳转。调试工具根据分支信息,结合程序镜像,就能重建出完整的执行路径。

核心原理:ETM输出的Trace数据,本质上是“程序执行路径的增量信息”。它不告诉你每条指令是什么,只告诉你“从哪里跳到了哪里”。调试工具拿到这个增量信息,再结合ELF文件,就能还原出完整的指令流。

Trace数据的格式,ARM定义了一套标准——ETMv4架构的包格式。常见的包类型有:

包类型 编码 含义 长度
Atom 0b1xxx 原子执行信息 1字节
Branch Address 0b01xx 分支目标地址 2-8字节
Data Trace 0b001x 数据读写信息 可变
Timestamp 0b0001 时间戳 2-8字节
Exception 0b0000 异常事件 2字节

举个例子。假设程序执行了这样一段代码:

0x1000: MOV R0, #1
0x1004: ADD R1, R0, #2
0x1008: BL func
0x100C: SUB R2, R1, #3

ETM抓到的Trace数据,可能只有一条:Branch Address: 0x2000(跳转到func函数)。调试工具收到这个包,就知道程序在0x1008处调用了func,目标地址是0x2000。然后工具去ELF文件里查0x2000处的指令,就能继续跟踪了。

避坑指南:我曾经调试一个死循环问题。程序在某个循环里出不来了。用ETM抓Trace,发现分支地址一直在两个值之间来回跳。一看代码,原来是循环条件写错了。如果没有ETM,这种问题靠断点根本查不出来——断点一停,循环状态就变了。

Trace数据的格式化,还有一个重要环节——时间戳。ETM可以配置为周期性地插入时间戳包。这样调试工具就能知道每条指令执行的精确时间。对于分析性能瓶颈,时间戳非常有用。

嗯,到这里,ETM的核心机制就讲清楚了。触发逻辑决定抓什么,ATB总线决定怎么传,格式化决定数据长什么样。这三者配合起来,ETM才能成为一个强大的调试工具。