Cortex-M4测量函数执行耗时两种方法的优缺点对比
Cortex-M4平台两种函数执行耗时测量方案对比
在搭载Cortex-M4内核的SOC上测量函数执行耗时,你找到的两种方案都是内核寄存器级的原生实现,不需要外挂外设,各自的适用场景、优劣势差异很明显:
方案1:基于DWT_CYCCNT周期计数器实现
参考实现代码如下:
REGISTER(DEMCR_ADDR) |= 1 << 24 ; // 开启DWT组件的TRACE访问权限 REGISTER(DWT_CTRL) |= 1; // 启动CYCCNT计数 startTime = REGISTER(DWT_CYCCNT); // 运行待测量的目标函数/逻辑 elapsedTime = REGISTER(DWT_CYCCNT) - startTime; REGISTER(DWT_CTRL) &= ~1; // 关闭DWT计数
优势
- 精度为单个CPU时钟周期,是Cortex-M平台能达到的最高计时精度,测量纳秒级、微秒级的短函数时几乎没有精度损失
- 计数过程完全不产生中断,测量逻辑本身不会触发上下文切换、中断进出栈等额外开销,对被测代码的运行干扰极小
- CYCCNT是32位递增计数器,以Cortex-M4常见的168MHz主频计算,单次计数溢出周期约25秒,覆盖绝大多数普通函数的执行时长范围,短时间测量不需要额外写溢出处理逻辑
- 整个测量过程不需要修改SysTick的原有配置,不会和已经占用SysTick做系统节拍的RTOS、裸机延时组件产生冲突,接入成本极低
缺陷
- DWT属于内核调试组件,部分车规、高安全等级的量产SOC会永久锁死DWT的访问权限,量产固件中可能无法正常开启该功能
- DWT计数的时钟和内核时钟绑定,当CPU进入休眠停钟、动态降频场景时,DWT计数会随内核时钟暂停/变慢,测量结果和实际墙钟时间会有偏差,不适合带低功耗逻辑的长流程测量
- 原生配置没有溢出中断支持,如果要测量超过25秒(168MHz主频下)的长流程,需要额外实现轮询溢出的逻辑,使用起来不如带中断的定时器方便
方案2:基于SysTick系统定时器实现
参考实现代码如下,注意原示例里的耗时计算逻辑存在错误:SysTick是递减计数器,正确的计数值差应该用初始值减去结束值,否则会得到负数结果:
// 测量前初始化 SysTick->LOAD = SysTick_LOAD_RELOAD_Msk; /* 配置重装载值为计数器最大量程 */ SysTick->VAL = 0UL; /* 清零当前计数 */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; /* 选择内核时钟作为时钟源,启动定时器 */ startTime = SysTick->VAL; // 运行待测量的目标函数/逻辑 elapsedTime = startTime - SysTick->VAL; // 原示例写为SysTick->VAL - startTime为逻辑错误 // 测量结束后复位寄存器 SysTick->LOAD = SysTick_LOAD_RELOAD_Msk; SysTick->VAL = 0UL; SysTick->CTRL = 0UL; // 关闭SysTick
优势
- SysTick是Cortex-M架构强制要求标配的内核外设,所有量产Cortex-M4芯片100%支持,不存在DWT那样被厂商锁死无法访问的问题,兼容性最好
- 自带溢出中断功能,即使测量数分钟级别的长流程,只需要在中断服务函数里累加溢出次数就能实现无回绕的连续计时,长时测量逻辑实现简单
- 时钟源可配置,既可以选内核时钟,也可以选外部常开门的参考时钟,部分低功耗场景下即使内核休眠停钟,也能通过外部时钟获得准确的墙钟时间
缺陷
- 直接操作SysTick寄存器做测量会覆盖系统原有配置:绝大多数裸机框架、RTOS都依赖SysTick产生系统调度节拍、毫秒级延时,测量过程中修改SysTick配置会直接导致系统延时不准、任务调度异常,除非系统专门预留了计时接口,否则适配成本很高
- SysTick的计数寄存器只有24位,配置为最大量程时,以168MHz主频计算单次无溢出计数窗口仅约0.1秒,测量执行时间稍长的函数就必须处理溢出,否则结果完全错误
- 如果开启SysTick中断处理溢出,中断响应、进出栈的固定开销会给被测流程引入额外误差,测量微秒级短函数时,这个误差的占比会非常高,结果参考价值低
- 测量初始化和收尾需要操作3个寄存器,测量逻辑本身的代码开销比DWT方案高,测极短函数时,测量代码本身的耗时占比更明显,容易拉高结果误差
内容的提问来源于stack exchange,提问作者RDF
相关产品推荐
相关产品推荐

