zoukankan      html  css  js  c++  java
  • 在ARM Cortex-M上实现FreeRTOS性能计数器

      说明:本文翻译自Erich Styger的文章《Implementing FreeRTOS Performance Counters on ARM Cortex-M》,文章的权属属于原作者。

      当使用像FreeRTOS这样的RTOS时,迟早要问一个问题:每个任务花费多少时间?基于Eclipse的MCUXpresso IDE具有很好的视图,准确显示了此类信息:

     

    FreeRTOS运行时信息

      为了使FreeRTOS(或“任务列表”视图)显示非常有用的信息,开发人员必须提供帮助,以便RTOS可以收集此信息。本文说明如何在ARM Cortex-M上完成此操作。

    1、概述

      不久前,我从处理器专家的角度讨论了FreeRTOS的性能和运行时分析中的该主题。这次是关于使用“本机” FreeRTOS和使用NXP MCUXpresso SDK,但是相同的原理将适用于Cortex-M处理器和微控制器的所有其他环境。至于FreeRTOS端口,我正在使用https://github.com/ErichStyger/McuOnEclipseLibrary中的端口,因为该端口已经存在所有需要的钩子。GitHub上提供了本文中使用的所有文件和源。

    2、如何工作

      操作系统使用计数器来衡量任务执行时间。因此,在任务上下文切换时间,此计数器用于确定该任务使用的时间。重要的一点是,该时间不是绝对的(例如37毫秒),而是一些“滴答声”(例如241个滴答声)。RTOS知道总体上使用了多少“滴答声”。RTOS知道系统中有多少个任务,因此它可以显示每个任务花费了总时间的百分比。另一个要注意的是,时间*包括*在中断中花费的时间。

      这是一种非常简单但功能强大的估算任务执行时间的方法,通常就是您所需要的。它可以通过一种非常简单的方式来实现:使用一个使计数器递增的计时器和一个用于读取计数器值的函数。

      要打开性能测量,我必须启用两个FreeRTOS配置设置:

    1 #define configUSE_TRACE_FACILITY 1 /* 1: include additional structure members and functions to assist with execution visualization and tracing, 0: no runtime stats/trace */
    2 
    3 #define configGENERATE_RUN_TIME_STATS 1 /* 1: generate runtime statistics; 0: no runtime statistics */

      要配置计时器并读取计数器,我必须使用两个宏来告诉函数名称:

    1 #define configGET_RUNTIMER_COUNTER_VALUE_FROM_ISR   AppGetRuntimeCounterValueFromISR
    2 
    3 #define configCONFIGURE_TIMER_FOR_RUNTIME_STATS     AppConfigureTimerForRuntimeStats

    3、使用“滴答”计数器

      一种非常简单的衡量任务执行情况的方法是使用FreeRTOS滴答计数器本身。可以通过以下方式启用

    1 #define configGENERATE_RUN_TIME_STATS_USE_TICKS     (1)

      但是,这仅在任务执行时间超过RTOS滴答周期时才能测量任务执行时间。对于更快的任务,此方法没有用。根据Nyquist-Shannon采样定理,我最好使用2倍(更好:10倍)的测量频率。

    4、使用Cortex-M周期计数器

      实现该计数器的另一种方法是使用Cortex-M周期计数器,该计数器已在许多设备上实现,并给出了很好的结果。最好的是:无需中断或额外的计时器。可能的实现如下所示:

     1 static uint32_t prevCycleCounter, cycleCntCounter = 0;
     2  
     3 void AppConfigureTimerForRuntimeStats(void) {
     4   cycleCntCounter = 0;
     5   McuArmTools_InitCycleCounter();
     6   prevCycleCounter = McuArmTools_GetCycleCounter();
     7 }
     8  
     9 uint32_t AppGetRuntimeCounterValueFromISR(void) {
    10   uint32_t newCntr, diff;
    11  
    12   newCntr = McuArmTools_GetCycleCounter();
    13   diff = newCntr-prevCycleCounter;
    14   prevCycleCounter = newCntr;
    15   cycleCntCounter += diff>>12; /* scale down the counter */
    16   return cycleCntCounter;
    17 }

    5、使用定期定时器中断

      标准方法是使用定期中断计时器,该计时器增加计数器。对于1 kHz滴答计时器,推荐的频率是FreeRTOS滴答计时器频率的10倍,在这种情况下为10 kHz(100 us):

     1 static uint32_t perfCounter = 0;
     2  
     3 #define PIT_BASEADDR       PIT
     4 #define PIT_SOURCE_CLOCK   CLOCK_GetFreq(kCLOCK_BusClk)
     5 #define PIT_CHANNEL        kPIT_Chnl_0
     6 #define PIT_HANDLER        PIT0_IRQHandler
     7 #define PIT_IRQ_ID         PIT0_IRQn
     8  
     9 void PIT_HANDLER(void) {
    10   PIT_ClearStatusFlags(PIT_BASEADDR, PIT_CHANNEL, kPIT_TimerFlag);
    11   perfCounter++;
    12   __DSB();
    13 }
    14  
    15 void AppConfigureTimerForRuntimeStats(void) {
    16   pit_config_t config;
    17  
    18   PIT_GetDefaultConfig(&config);
    19   config.enableRunInDebug = false;
    20   PIT_Init(PIT_BASEADDR, &config);
    21   PIT_SetTimerPeriod(PIT_BASEADDR, PIT_CHANNEL, USEC_TO_COUNT(100U, PIT_SOURCE_CLOCK));
    22   PIT_EnableInterrupts(PIT_BASEADDR, PIT_CHANNEL, kPIT_TimerInterruptEnable);
    23   NVIC_SetPriority(PIT_IRQ_ID, 0);
    24   EnableIRQ(PIT_IRQ_ID);
    25   PIT_StartTimer(PIT_BASEADDR, PIT_CHANNEL);
    26 }
    27  
    28 uint32_t AppGetRuntimeCounterValueFromISR(void) {
    29   return perfCounter;
    30 }

    6、摘要

      FreeRTOS包含一项功能,可以测量相对于系统中其他任务的任务执行时间。我需要提供的是计时器或某种计数器的初始化例程,以及获取计数器值的方法。如果您对检查FreeRTOS计时的其他方式感兴趣,请查看Percepio Tracealyzer或Segger SystemView。如果您希望应用程序本身显示性能数据,请查看“ 使用FreeRTOS进行性能和运行时分析”中介绍的Shell / Commandline实现。

    7、链接

    欢迎关注:

  • 相关阅读:
    Linkerd 2.10(Step by Step)—将 GitOps 与 Linkerd 和 Argo CD 结合使用
    Linkerd 2.10(Step by Step)—多集群通信
    Linkerd 2.10(Step by Step)—使用 Kustomize 自定义 Linkerd 的配置
    Linkerd 2.10(Step by Step)—控制平面调试端点
    Linkerd 2.10(Step by Step)—配置超时
    Linkerd 2.10(Step by Step)—配置重试
    Linkerd 2.10(Step by Step)—配置代理并发
    本地正常运行,线上环境诡异异常原因集合
    Need to invoke method 'xxx' declared on target class 'yyy', but not found in any interface(s) of the exposed proxy type
    alpine 安装常用命令
  • 原文地址:https://www.cnblogs.com/foxclever/p/12151472.html
Copyright © 2011-2022 走看看