在大多数固件开发过程中,需要有某种形式的诊断日志/跟踪。如果在传统串口上使用简单的“printf”调用实现日志,每次日志调用的开销通常是多个毫秒,这种时间消耗将干扰应用程序的行为,在异常处理程序或快速控制循环等时间关键代码中是不可接受的。 为了解决这个问题,很多MCU,如ARM Cortex-M3, M4或M7内核的MCU,已经在处理器中内置了解决方案,即ITM – Instrumentation Trace Macrocell。
ITM是一个硬件单元,可以传输两种类型的诊断数据:
1、由DWT单元生成的调试事件,例如异常事件和数据观察点事件。
2、软件插装(SWIT)事件,即代码记录的自定义数据。
使用ITM,只需将数据写入相应的内存映射寄存器,用户即可将任何类型的数据作为SWIT事件传输到主机PC。通过相应的调试器,传输只需几个时钟周期,ITM还支持硬件完成的时间戳。许多IDE允许用户在调试窗口中查看这些数据,或者将数据写入文件供以后分析。数据可以是字符形式的文本数据(来自printf输出),但也可以发送二进制数据,ITM每次写入最多支持32位数据。
ITM单元为SWIT事件提供了32个逻辑通道,每个通道都有一个相应的stimulus寄存器,用于接受输入。这些通道将诊断数据分成不同的类别。例如,ARM推荐通道0用于文本数据,通道31用于RTOS事件,而其他通道可以用于任何目的。所有ITM通道共享一个公共FIFO缓冲区,该缓冲区依次连接到一个或两个输出端口。如果使用完整跟踪端口和跟踪调试器,则ITM数据包含在指令跟踪(ETM)结果中,但也可以通过调试端口中常用的串行线输出(SWO)接口获得。大多数针对ARM MCU的调试器都支持SWO接口。
ITM FIFO缓冲区非常小,只有10个字节,因此如果使用低速的调试器,如果过于频繁地向ITM端口写入数据,可能会造成数据丢失。这可以在写入之前通过检查ITM FIFO是否有空闲空间来防止,在没有空间的情况下延迟写入(参见下面的“fputc”代码示例)。但是,如果调试器接收数据太慢,这种方式可能会对系统的时间产生重大影响。
避免数据丢失和阻塞的更好方法是使用快速调试器,如SEGGER J-Link,Keil ULINK等允许60-100 MHz的SWO采样率。
在代码中使用ITM
ITM stimulus寄存器位于地址0xE0000000(端口0)到0xE000007C(端口31)。要写入数据,需要做的就是在IDE中启用ITM跟踪,并将数据写入相应的寄存器。
如果使用的板级支持包包含ARM的CMSIS API,那么写入数据很简单:
#include”myMCU.h” // Includes CMSIS
ITM->PORT[0].u32 = mydata;
如果系统中没有使用CMSIS API,可以直接为ITM stimulus寄存器定义宏,如下所示:
#defineITM_PORT(n) (*((volatile unsigned long *)(0xE0000000+4*n)))
ITM_PORT(0) = mydata;
在使用ITM跟踪之前,需要使能ITM跟踪。基于Keil µVision的示例如下,该系统已配置为在所有32个ITM stimulus端口启用ITM跟踪。如果可能的话,在“Trace port”中选择Manchester模式,它允许比UART/NRZ模式更高的SWO时钟频率。
为了让你的“printf”调用写入ITM端口0,需要像这样定义“fputc”函数:
struct __FILE { int handle; /* Add whatever needed */ };
FILE __stdout;
FILE __stdin;
int fputc(int ch, FILE *f)
if(DEMCR & TRCENA) // Only ifITM isavailable
while(ITM_PORT(0) == 0); // Blockuntil room inITM FIFO
ITM_PORT(0) = ch; // Writethe data
return(ch);
ITM是大多数ARM开发工具支持的重要调试技术,它在调试中有许多应用,并且易于入门!
Percepio Tracealyzer利用ITM的性能,实现RTOS实时数据跟踪。Tracealyzer允许用户跟踪和可视化RTOS任务,异常(ISR)和其他软件事件的执行,提供超过30个相互关联的视图,为基于RTOS的固件的运行时世界提供惊人的视觉洞察,加速开发,验证和调试。
Tracealyzer支持ARM Cortex-M3, M4和M7 MCU的ITM跟踪,实现FreeRTOS,µC/OS-III和SafeRTOS等RTOS的可视化分析。