在许多实时应用中,如电机控制、发动机控制、无线通信等时间敏感的应用,CPU可能花费不到5%的时间执行代码。这些嵌入式系统通常用C语言编写,开发人员可能会使用到汇编语言优化代码,以满足时间期限(deadline)要求。测量部分代码的实际执行时间可以帮助我们找到代码中的时间关键点。
本文将展示如何轻松测量和显示基于Cortex-M MCU的代码片段的执行时间。
测量执行时间的方法
有很多方法可以测量代码执行时间。嵌入式工程师经常使用数字输出和示波器。我们可以在监视的代码执行之前,设置输出为高电平,代码执行后将输出设置为低电平。当然,在执行此操作之前需要进行大量设置工作:找到易于探测的输出引脚,将端口配置为输出,编写代码,编译等等。获得信号后,你可能需要对其进行一段时间的监测以查看其运行时间的最小值和最大值。
测量执行时间的另一种方法是使用具有跟踪功能的调试工具。你只需运行代码、查看跟踪、手动计算增量时间并将CPU周期转换为微秒。不幸的是,跟踪仅提供一次执行实例,你可能需要进一步查看跟踪捕获以找到最坏情况的执行时间,这可能是一个乏味的过程。
Cortex-M周期计数器
Cortex-M的处理器上的 CoreSight调试端口包含一个32位自由运行计数器,用于CPU时钟周期计数。计数器是调试监视和跟踪 (DWT) 模块的一部分,可轻松用于测量代码的执行时间。启用和初始化该功能所需的代码如下:
使用DWT周期计数器测量代码执行时间
我们可以通过读取代码段前后的循环计数器的值来测量并计算代码片段的执行时间,如下所示。
无符号数delta表示被测代码的实际执行时间(以CPU时钟周期为单位)。
在代码执行期间可能会发生中断,因此每次执行此序列获得时间值可能并不相同,我们也可以在测量期间禁用中断以移除中断的影响。但建议在测量中允许中断,因为它们会影响代码的时间期限。
如果被测量的代码包含条件语句、循环或任何可能导致变化的东西,那么获得的值可能不代表最坏情况下的执行时间。要解决此问题,可以简单地添加一个峰值检测,如下所示。在进行任何测量之前,需要声明 max 并将其初始化为最小值(即 0)。
同样,如果需要了解最短执行时间,min只需在测量之前声明并初始化为最大计数值(即 0xFFFFFFFF)。代码如下:
执行时间还取决于 CPU是否配备cache,某些Cortex-M4和Cortex-M7处理器集成了Cache。如果 系统使用了指令或数据cache,则同一代码段的多次测量结果可能会不一致。你可以考虑禁用cache来衡量最坏的情况。
大多数调试器能够实时显示这些变量值。我们可以使用Global声明显示的变量以保留其值并允许实时监控。这些值代表CPU时钟周期,但大多数调试器无法为显示目的缩放变量。假设CPU时钟速度为16 MHz,显示70.19微秒比显示1123个周期要方便得多。实际上有一种更好的方式来显示实时变量,使用μC/Probe实时监控工具,它还提供了缩放能力,以易读的形式查看变量值。
使用μC/Probe显示测量值
在应用中添加测量,监控四个代码片段的执行时间,并通过μC/Probe显示测量结果。
下图显示了使用 IAR的LiveWatch(左)和 μC/Probe 的Tree View控件(右)的原始测量值。elapsed_time_tbl[] 是一个数组,用于存储不同代码片段的测量值。
你还可以将min/max/current值分配给gauge和numeric indicator控件,如下图所示。这里的值以微秒为单位显示,示例中CPU以80 MHz 运行,使用了0.0125的缩放因子。仅显示最长执行时间。
uC/Probe还可以与Microsoft的Excel连接,从而在电子表格中显示实时值,如下图所示。
总结
作为嵌入式开发人员,我们有许多工具可以用来测试和验证我们的设计。μC/Probe提供了许多功能,允许用户使用gauge、meter、numeric indictor、Excel或graph/plots来监控应用程序中的变量。基于Cortex-M处理器的DWT 周期计数器功能和μC/Probe,我们可以轻松实现Cortex-M MCU应用的设计验证工作。