本帖最后由 SourFish 于 2024-9-7 15:20 编辑
如上图中所示,H7系列都有Embedded Trace Buffer。依据型号不同,Buffer的大小也不同。H750、H7B0有4KB,但H730就只有2KB。实际上该片区域在参考手册中叫做Embedded trace FIFO (ETF),意思是这片存储区域使用了先进先出,可以滚动存储最新的指令,其实是一个意思。这片区域还有其他的名字,比如Micro Trace Buffer(MTB),本质是指同一片区域。

根据SEGGER对ETB的描述,该Buffer可以存储受限于自身大小的最新的指令跟踪的数据:
The Embedded Trace Buffer (ETB) is a small, circular on-chip memory area where trace information is stored during capture. It contains the data which is normally exported via trace pins, immediately after it has been captured from the ETM. The buffer can be read out through regular debug interface device once capture has been completed. No additional special trace port is required, so that the ETB can be read via a normal J-Link. The trace functionality via J-Link is limited by the size of the ETB. While capturing runs, the trace information in the buffer will be overwritten every time the buffer size has been reached. The result of the limited buffer size is that not more data can be traced than the buffer can hold. Because of this limitation, an ETB is not a fully alternative to the direct access to an ETM via J-Trace.
SEGGER还提到了这片区域“can be read via a normal J-Link”,但是对于H7来说,这个“normal J-Link”也是有限制的。经过实测,Ozone V3.36版本,J-Link V9、J-Link OB RA4M2、J-Link OB F072都可以进行跟踪,各位如果不能跟踪,可以考虑升级Ozone版本,因为在V3.34a时,J-Link V9就不可以进行跟踪,反而是J-Link RA4M2可以。

简单介绍一下怎样使用Ozone进行跟踪,以h750为例(我也没别的)。
在ST STM32H7 - SEGGER Wiki中,有如下工程:
下载该压缩包,包内的.pex文件就是H7 trace需要的脚本。
接下来在创建Ozone工程时,在如下界面添加跟踪脚本:
如果没在该界面添加或者是已有工程,可以在如下界面再次添加:
接下来进行指令跟踪演示,以该篇帖子的测试为例:
【性能测评】DSP库,MDK5的AC5,AC6,IAR和Embedded Studio的三角函数性能(2022-06-04更新) - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz! (armbbs.cn)
使用CubeMX生成CMake工程,使用RelWithDebInfo优化。调出Instruction Trace与Timeline窗口:
Code Profile是不必要的,因为有限的缓存不可能记录函数的利用率。
打断点并运行:

可以看到arm_sin_f32函数消耗30个指令周期,约60ns。

并且不同的输入也有相同的执行时间。

在这里顺便帮硬汉捉个虫,使用CMake的RelWithDebInfo优化,如果sinf函数中填入的是立即数,会被编译器优化成立即数,sinf函数压根不会被调用,汇编中的只是直接赋值,所以不会有运行时间或者运行时间极短。
图中刚好十个立即数,使用浮点数格式解析,与sin计算结果相符。硬汉可以看下MDK的AC6是否将该函数提前计算好了。
解决这种问题的办法也很简单,就是将立即数改为变量赋值:
[C] 纯文本查看 复制代码 float32_t src[10] = {3.141593f,
1.570796f,
1.047198f,
0.785398f,
0.628319f,
0.523599f,
0.448799f,
0.392699f,
0.349066f,
0.314159f};
res[0] = sinf(src[0]);
res[1] = sinf(src[1]);
res[2] = sinf(src[2]);
res[3] = sinf(src[3]);
res[4] = sinf(src[4]);
res[5] = sinf(src[5]);
res[6] = sinf(src[6]);
res[7] = sinf(src[7]);
res[8] = sinf(src[8]);
res[9] = sinf(src[9]);
这样sinf函数就不会被编译器优化。
使用变量赋值的方式测量sinf运行时间:
这样就能得到sinf的平均运行时间。
但是对于sinf函数来说,求和平均的方式也是不太对的。
图中可以看出,根据输入值不同,sinf的计算时间也不同。
根据以上十个数据的样本,可以得出sinf的指令总数为35+12或17+12个。
另外还有一个奇怪的点:arm_sin_f32的指令总数是30个,但是dwt计算的tick却是60左右,感觉有些对不上,不清楚为什么。
以及,如果arm_sin_f32也采用变量的形式赋值,在同等优化下,似乎会快一点点。推测立即数存储在代码中,即Flash中,而变量存储在ram中,取ram中的变量要比取flash中的立即数来的快。

这种Trace的局限性在于,只能获取断点前的或者各种Fault中断前的一部分指令数据,不过通常来说也够用了。在全速运行时,J-Link不能读取到Buffer里的内容,因此不能实时跟踪,这种方式可以算作事后分析。
关于HardFualt或者memManage Fault那些错误分析,这里就不再演示了。
|