MCU上的代码执行时间
作者:网友投稿 时间:2018-07-23 16:09
在许多实时应用程序中,二八原则并不生效,CPU 可以花费95%(或更多)的时间在不到5% 的代码上。电动机控制、引擎控制、无线通信以及其他许多对时间敏感的应用程序都是如此。这些嵌入式系统通常是用c编写的,而且开发人员常常被迫对代码进行手工优化,可能会回到汇编语言,以满足性能的需求。测量代码部分的实际执行时间可以帮助找到代码中的热点。本文将说明如何可以方便地测量和显示在基于Cortex-M MCU的实时执行时间。
测量代码的执行时间
测量代码执行时间的方法有很多。作为一个嵌入式工程师,经常使用一个或多个数字输出和一个示波器。需要在执行要监视的代码之前设置一个高的输出,然后将输出降低。当然,在做这些之前有相当多的设置工作: 找到一个或多个自由输出,确保它们可以轻松访问,将端口配置为输出,编写代码,编译,设置范围等等。一旦有了一个信号,你可能需要对它进行一段时间的监视,以便看到最小值和最大值。 数字存储示波器使这个过程更容易,但是还有其他更简单的方法。
另一种测量执行时间的方法是使用可跟踪调试接口。只需要运行代码,查看跟踪,计算 delta时间(通常是手动的) ,并将CPU周期转换为微秒。不幸的是,这个跟踪给了一个执行的实例,可能不得不在追踪捕获中进一步查找最坏情况下的执行时间。这是一个乏味的过程。
Cortex-M 周期计数器
在大多数Cortex-M的处理器中调试端口包含一个32位的自由运行计数器,它可以计算 CPU 的时钟周期。计数器是 Debug 观察和跟踪(DWT)模块的一部分,可以很容易地用于测量代码的执行时间。下面的代码是启用和初始化这个特性非常有用。
#define ARM_CM_DEMCR (*(uint32_t *)0xE000EDFC)
#define ARM_CM_DWT_CTRL (*(uint32_t *)0xE0001000)
#define ARM_CM_DWT_CYCCNT (*(uint32_t *)0xE0001004)
if (ARM_CM_DWT_CTRL != 0) { // See if DWT is available
ARM_CM_DEMCR |= 1 << 24; // Set bit 24
ARM_CM_DWT_CYCCNT = 0;
ARM_CM_DWT_CTRL |= 1 << 0; // Set bit 0
}
使用DWT周期计数器来测量代码执行时间
可以通过在目标代码之前和之后读取周期计数器的值来测量和计算代码段的执行时间,如下所示。 当然,这意味着必须设置代码,但能够得到一个非常准确的值。
uint32_t start;
uint32_t stop;
uint32_t delta;
start = ARM_CM_DWT_CYCCNT;
// Code to measure
stop = ARM_CM_DWT_CYCCNT;
delta = stop – start;
因为使用的是无符号运算,delta表示所测量代码的实际执行时间(CPU 时钟周期)。
在测量开始和停止读数之间的代码执行时间时,可能会发生中断,所以每次执行这个序列很可能会有不同的值。在这种情况下,可能希望在测量过程中禁用中断,但是要清楚禁用中断是暂时的,只用于测量。尽管如此,也许应该把中断的任务包括进来,因为它们会影响到代码的最后执行时间。
Disable Interrupts;
start = ARM_CM_DWT_CYCCNT;
// Code to measure
stop = ARM_CM_DWT_CYCCNT;
Enable Interrupts;
delta = stop – start;
如果所测代码包含条件语句、循环或任何可能导致变化的东西,那么获得的值可能不代表最坏情况下的执行时间。为了纠正这个问题,需要添加一个峰值检测器,如下图所示。当然,在进行任何测量之前,需要将 max 声明并初始化为最小值(即0)。
start = ARM_CM_DWT_CYCCNT;
// Code to measure
stop = ARM_CM_DWT_CYCCNT;
delta = stop – start;
if (max < delta) {
max = delta;
}



