RTOS基础 -- 基于 DWT CYCCNT 的 M4 内核代码耗时统计与 CPU 频率获取方法

基于 DWT CYCCNT 的 M4 内核代码耗时统计与 CPU 频率获取方法


一、背景介绍

在嵌入式开发中,准确测量代码执行耗时是优化性能、评估实时性的重要手段。对于基于 ARM Cortex-M4 的 NXP 处理器,如 i.MX RT 或 i.MX8M 的 M4 核,我们可以使用 ARM 内部的 DWT CYCCNT(CPU周期计数器) 实现 纳秒~微秒级别的高精度耗时统计

本文将系统介绍:

  • 如何使用 DWT CYCCNT 测量代码运行时间
  • 如何获取 M4 核的 CPU 主频(SystemCoreClock)
  • 如何根据周期数换算微秒时间
  • 实用示例与注意事项

二、DWT CYCCNT 原理简介

什么是 DWT CYCCNT?

DWT(Data Watchpoint and Trace)模块是 ARM Cortex-M3/M4/M7 内核的调试组件,内部包含一个 CYCCNT 计数器,可以记录 CPU 自上电以来经历的时钟周期数。

  • 精度:1 个时钟周期(周期数 = 时钟频率 × 时间)
  • 常用于:代码耗时统计、性能分析、任务调度评估

三、初始化 DWT CYCCNT

默认 DWT 计数器是关闭的,需在代码中初始化:

void DWT_Init(void)
{
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;    // 开启 DWT 模块
    DWT->CYCCNT = 0;                                    // 清零周期计数器
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;                // 启动周期计数器
}

建议在 main() 或系统初始化流程中调用一次:

DWT_Init();

四、获取 M4 内核的 CPU 主频

方法一:通用 CMSIS 方法

extern uint32_t SystemCoreClock;

SystemCoreClockUpdate();           // 刷新当前时钟
uint32_t core_clk = SystemCoreClock;

方法二:NXP MCUXpresso SDK 推荐方式

#include "fsl_clock.h"

uint32_t core_clk = CLOCK_GetFreq(kCLOCK_CoreSysClk);

常见时钟项:

获取函数说明
CLOCK_GetFreq(kCLOCK_CoreSysClk)M4 核心时钟
CLOCK_GetFreq(kCLOCK_AhbClk)AHB 总线时钟
CLOCK_GetFreq(kCLOCK_IpgClk)外设 IPG 时钟

五、根据周期数换算时间(微秒)

原理推导:

假设:

  • delta_cycles 为执行代码所花费的周期数
  • SystemCoreClock 为当前 M4 的主频(单位 Hz)

则:

delta_us = (float)delta_cycles / (SystemCoreClock / 1e6f);

这个公式将周期数换算为微秒(us)。

举例说明:

假设:

  • M4 主频为 600 MHz(SystemCoreClock = 600000000)
  • 代码执行了 3000 个周期

则:

delta_us = 3000 / (600000000 / 1e6) = 3000 / 600 = 5.0 微秒

六、完整示例代码

#include <stdio.h>
#include "fsl_clock.h"

void DWT_Init(void)
{
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CYCCNT = 0;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}

void measure_example(void)
{
    uint32_t start, end, delta_cycles;
    float delta_us;

    // 获取 M4 主频
    uint32_t core_clk = CLOCK_GetFreq(kCLOCK_CoreSysClk); // or use SystemCoreClockUpdate();

    // 开始测时
    start = DWT->CYCCNT;

    // 待测代码段
    for (volatile int i = 0; i < 1000; i++);

    // 结束测时
    end = DWT->CYCCNT;

    delta_cycles = end - start;
    delta_us = (float)delta_cycles / (core_clk / 1e6f);

    printf("执行耗时:%.2f us\n", delta_us);

    // 判断是否超时
    if (delta_us > 50.0f) {
        printf("超时警告!\n");
    }
}

七、常见问题与注意事项

问题说明
CYCCNT 始终为 0未使能 DWT,需调用 DWT_Init()
测量值不准确未调用 SystemCoreClockUpdate() 或未正确配置 PLL 时钟
变量溢出DWT->CYCCNT 是 32 位无符号整型,大约 7 秒会溢出一次 @600MHz
与 FreeRTOS 冲突?没有冲突,DWT 是独立模块,可安全使用

八、优化建议

为避免频繁计算,可预先定义:

#define US_PER_CYCLE (1e6f / SystemCoreClock)
#define CYCLES_TO_US(cycles) ((cycles) * US_PER_CYCLE)

或者定义一组测时辅助函数:

static inline void start_timing(void) {
    DWT->CYCCNT = 0;
}

static inline float stop_timing_us(void) {
    return DWT->CYCCNT / (SystemCoreClock / 1e6f);
}

九、总结

  • DWT CYCCNT 是 Cortex-M4 提供的高精度耗时计数器
  • 获取当前 M4 CPU 主频是准确计算时间的前提
  • (float)delta_cycles / (SystemCoreClock / 1e6f) 是周期数到微秒的标准换算公式
  • 在 NXP 平台,推荐使用 CLOCK_GetFreq() 获取主频
  • 可用于性能分析、超时判断、精度对比等各种场景
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值