当前位置: 首页 > 科技观察

MCU上的代码执行时间

时间:2023-03-12 02:54:45 科技观察

在许多实时应用程序中,80/20规则不起作用,CPU可以将95%(或更多)的时间花在少于5%的代码上。这适用于电机控制、发动机控制、无线通信和许多其他时间敏感型应用。这些嵌入式系统通常是用C语言编写的,开发人员通常被迫手动优化代码,可能会回退到汇编语言,以满足性能需求。测量代码段的实际执行时间有助于找到代码中的热点。本文将说明如何在基于Cortex-M的MCU上轻松测量和显示实时执行时间。测量代码执行时间有许多方法可以测量代码执行时间。作为一名嵌入式工程师,使用一个或多个数字输出和示波器是很常见的。您需要在执行要监视的代码之前将输出设置为高电平,然后将输出设置为低电平。当然,在执行任何一项操作之前,需要进行大量设置工作:找到一个或多个空闲输出,确保它们易于访问,将端口配置为输出,编写代码,编译,设置范围等。一旦你有了一个信号,您可能需要监视它一段时间以查看最小值和最大值。数字存储示波器使这个过程更容易,但还有其他更简单的方法。另一种测量执行时间的方法是使用可跟踪的调试接口。只需运行代码,查看轨迹,计算增量时间(通常手动),并将CPU周期转换为微秒。不幸的是,此跟踪给出了一个执行实例,可能必须进一步向下跟踪捕获才能找到最坏情况下的执行时间。这是一个乏味的过程。Cortex-M周期计数器大多数Cortex-M处理器上的调试端口包含一个32位自由运行计数器,用于计算CPU时钟周期。计数器是调试监视和跟踪(DWT)模块的一部分,可轻松用于测量代码执行时间。下面的代码可用于启用和初始化此功能。#defineARM_CM_DEMCR(*(uint32_t*)0xE000EDFC)#defineARM_CM_DWT_CTRL(*(uint32_t*)0xE0001000)#defineARM_CM_DWT_CYCCNT(*(uint32_t*)0xE0001004)if(ARM_CM_DWT_CTRL!=0){//Seeif=CM/DEM/DEM可用setbit24ARM_CM_DWT_CYCCNT=0;ARM_CM_DWT_CTRL|=1<<0;//setbit0}使用DWT循环计数器测量代码执行时间可以通过读取目标前后循环计数器的值来测量和计算代码段的执行时间代码,如下所示。当然,这意味着必须设置代码,但是可以获得非常准确的值。uint32_tstart;uint32_tstop;uint32_tdelta;start=ARM_CM_DWT_CYCCNT;//Codetomesurestop=ARM_CM_DWT_CYCCNT;delta=stop–start;因为使用了无符号运算,delta代表被测代码的实际执行时间(CPU时钟周期)。在测量开始和停止读数之间的代码执行时间时可能会发生中断,因此序列的每次执行都可能有不同的值。在这种情况下,可能需要在测量期间禁用中断,但请注意,禁用中断是暂时的并且仅用于测量目的。即便如此,中断的任务也应该包括在内,因为它们会影响代码的最大执行时间。DisableInterrupts;start=ARM_CM_DWT_CYCCNT;//Codetomesurestop=ARM_CM_DWT_CYCCNT;EnableInterrupts;delta=stop–start;如果被测代码包含条件语句、循环或任何可能导致变化的东西,获得的值可能并不代表最坏情况下的执行时间。要纠正此问题,需要添加峰值检测器,如下图所示。当然,在进行任何测量之前,需要声明max并将其初始化为最小值(即0)。start=ARM_CM_DWT_CYCCNT;//Codetomesurestop=ARM_CM_DWT_CYCCNT;delta=stop–start;if(maxdelta){min=delta;}```与Cortex-M4处理器和Cortex-M7一样,执行时间也取决于CPU是否配备缓存。如果系统中使用了指令或数据缓存,则同一段代码的多次测量可能会不一致。此时,考虑禁用缓存以衡量最坏情况。大多数调试器都允许显示这些变量值。如果是这样,则需要全局声明显示变量,以保留其值并允许实时监控。不幸的是,这些值代表CPU时钟周期,大多数调试器还不够成熟,无法缩放变量以用于显示目的。假设一个16MHz的CPU时钟速度,显示70.19微秒比显示1123个周期要方便得多。实际上有一种更好的方式来显示这些变量,它还提供缩放功能,以更易读的形式查看它们。经过时间模块当然,可以将代码片段嵌入到应用程序中,但一个简单的模块也是可能的。elapsedtime.c和elapsedtime.h,它只包含4个函数。方法如下:按照惯例,#include在使用elapsedtime.c中的其他函数之前,调用elapsedtime_init()通过设置“ELAPSEDTIMEMAX_SECTIONS”来定义时间测量结构的最大数量。这对应于用停止/开始代码包裹的不同代码部分调用elapsedtimestart()并传递要观看的代码部分的索引(即0到ELAPSEDTIMEMAX_SECTIONS-1)调用elapsedtimestop()并传递运行时使用的相同内容startedIndex如果调试器允许监控变量(即当target运行时),你可以显示elapsedtimetbl[]并查看相应索引的elapsedtime结构重复步骤4到6并将代码放在最坏和最坏的情况下,所以ELAPSED_TIME数据结构中的Min和max字段可以很好地表示被测代码片段的执行时间。如何影响感知执行时间。voidmain(void){//Somecodeelapsed_time_init();//初始化模块//Somecode}voidMyCode(void){//Somecodeherelapsed_time_start(0);//Startmeasurementofcodesnippet#0//Codebeingmeasured_time_stop(0);//Stopand//Someothercode}Of当然,最小和最差执行时间取决于测量的频率以及代码是否分别受最小和最差条件的限制。另外,不需要显示起始字段,因为它仅用于记录测量开始时DWT循环计数器的值,但是可以使用起始字段来显示它。换句话说,当您看到此值发生变化时,您就知道正在进行测量。使用uc/probe的示例显示了使用elapsed_time.c和uc/probe来测量代码片段的执行时间。图1|IAR和uc/probe的树视图图1显示了使用IAR的LiveWatch(左)和uc/probe的树视图(右)。屏幕截图是在不同时间截取的,是一个数组,用于存储不同代码片段的测量值。可以将最小/最大/电流分配给仪表和数字指示器,如图2所示。CPU以80mhz运行,值以微秒显示,应用比例因子0.0125。左侧的按钮用于重置统计数据,强制重新计算最小值和最大值。图2|Instrumentationusinguc/probeshowing***ExecutionTimeuc/probe的一个强大功能是能够与MicrosoftExcel接口以在电子表格中(实时)显示这些值,如图3所示。图3|使用Excel显示实时数据总结作为嵌入式开发人员,有许多工具可用于测试和验证设计。对于代码执行时间,可以轻松使用Cortex-M处理器的众多功能之一,即DWT周期计数器。uc/probe提供了许多功能,允许使用仪表、仪表板、数字指示器、Excel界面或图表监视应用程序中的许多变量。借助内置的示波器功能,一旦满足触发条件,最多可以捕获7个额外的变量值。附件代码elapsed_time.c#include#include/*****************************************************************************************皮质-M-DWTTIMER************************************************************************************/#defineARM_CM_DEMCR(*(uint32_t*)0xE000EDFC)#defineARM_CM_DWT_CTRL(*(uint32_t*)0xE0001000)#defineARM_CM_DWT_CYCCNT(*(uint32_t*)0xE0001004)/********************************************************************************************数据结构************************************************************************************/typedefstructelapsed_time{uint32_tstart;uint32_tcurrent;uint32_tmax;uint32_tmin;}ELAPSED_TIME;/*****************************************************************************************经过时间测量的存储空间*************************************************************************************/staticELAPSED_TIMEelapsed_time_tbl[ELAPSED_TIME_MAX_SECTIONS];/****************************************************************************************模块初始化**注意(s):必须在此模块中的任何其他函数之前调用***************************************************************************************/voidelapsed_time_init(void){uint32_ti;if(ARM_CM_DWT_CTRL!=0){//SeeifDWTisavailableARM_CM_DEMCR|=1<<24;//Setbit24ARM_CM_DWT_CYCCNT=0;ARM_CM_DWT_CTRL|=1<<0;//Setbit0}for(i=0;icurrent=stop-p_tbl->start;if(p_tbl->maxcurrent){p_tbl->max=p_tbl->current;}if(p_tbl->min>p_tbl->current){p_tbl->min=p_tbl->current;}}/**************************************************************************************清除测量统计************************************************************************************/voidelapsed_time_clr(uint32_ti){ELAPSED_TIME*p_tbl;p_tbl=&elapsed_time_tbl[i];p_tbl->start=0;p_tbl->current=0;p_tbl->min=0xFFFFFFFF;p_tbl->max=0;}elapsed_time.h/*****************************************************************************************模块测量执行时间***************************************************************************************//****************************************************************************************最大耗用时间测量值科目*******************************************************************************************/#defineELAPSED_TIME_MAX_SECTIONS10/*****************************************************************************************功能原型*****************************************************************************************/voidelapsed_time_clr(uint32_ti);//清除测量值voidelapsed_time_init(void);//模块初始化voidelapsed_time_start(uint32_ti);//开始测量voidelapsed_time_stop(uint32_ti);//停止测量参考https://www.micrium.com/ucprobe/about/httpswww.iar.com/iar-embedded-workbench/https://www.arm.com/products/processors/cortex-m【本文来自专栏作者“老曹”的原创文章》,作者微信公众号:OhHomeArchiSelf,id:wrieless-com】点此阅读作者更多好文