在ARM Cortex-M上實現FreeRTOS性能計數器

說明:本文翻譯自Erich Styger的文章《Implementing FreeRTOS Performance Counters on ARM Cortex-M》,文章的權屬屬於原作者。

當使用像FreeRTOS這樣的RTOS時,遲早要問一個問題:每個任務花費多少時間?基於EclipseMCUXpresso 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配置設置:

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

#define configGENERATE_RUN_TIME_STATS 1 /* 1: generate runtime statistics; 0: no runtime statistics */

要配置計時器並讀取計數器,我必須使用兩個宏來告訴函數名稱:

#define configGET_RUNTIMER_COUNTER_VALUE_FROM_ISR   AppGetRuntimeCounterValueFromISR

#define configCONFIGURE_TIMER_FOR_RUNTIME_STATS     AppConfigureTimerForRuntimeStats

3、使用滴答計數器

一種非常簡單的衡量任務執行情況的方法是使用FreeRTOS滴答計數器本身。可以通過以下方式啓用

#define configGENERATE_RUN_TIME_STATS_USE_TICKS     (1)

但是,這僅在任務執行時間超過RTOS滴答週期時才能測量任務執行時間。對於更快的任務,此方法沒有用。根據Nyquist-Shannon採樣定理,我最好使用2倍(更好:10倍)的測量頻率。

4、使用Cortex-M週期計數器

實現該計數器的另一種方法是使用Cortex-M週期計數器,該計數器已在許多設備上實現,並給出了很好的結果。最好的是:無需中斷或額外的計時器。可能的實現如下所示:

static uint32_t prevCycleCounter, cycleCntCounter = 0;

void AppConfigureTimerForRuntimeStats(void) {
  cycleCntCounter = 0;
  McuArmTools_InitCycleCounter();
  prevCycleCounter = McuArmTools_GetCycleCounter();
}

uint32_t AppGetRuntimeCounterValueFromISR(void) {
  uint32_t newCntr, diff;

  newCntr = McuArmTools_GetCycleCounter();
  diff = newCntr-prevCycleCounter;
  prevCycleCounter = newCntr;
  cycleCntCounter += diff>>12; /* scale down the counter */
  return cycleCntCounter;
}

5、使用定期定時器中斷

標準方法是使用定期中斷計時器,該計時器增加計數器。對於1 kHz滴答計時器,推薦的頻率是FreeRTOS滴答計時器頻率的10倍,在這種情況下爲10 kHz100 us):

static uint32_t perfCounter = 0;

#define PIT_BASEADDR       PIT
#define PIT_SOURCE_CLOCK   CLOCK_GetFreq(kCLOCK_BusClk)
#define PIT_CHANNEL        kPIT_Chnl_0
#define PIT_HANDLER        PIT0_IRQHandler
#define PIT_IRQ_ID         PIT0_IRQn

void PIT_HANDLER(void) {
  PIT_ClearStatusFlags(PIT_BASEADDR, PIT_CHANNEL, kPIT_TimerFlag);
  perfCounter++;
  __DSB();
}

void AppConfigureTimerForRuntimeStats(void) {
  pit_config_t config;

  PIT_GetDefaultConfig(&config);
  config.enableRunInDebug = false;
  PIT_Init(PIT_BASEADDR, &config);
  PIT_SetTimerPeriod(PIT_BASEADDR, PIT_CHANNEL, USEC_TO_COUNT(100U, PIT_SOURCE_CLOCK));
  PIT_EnableInterrupts(PIT_BASEADDR, PIT_CHANNEL, kPIT_TimerInterruptEnable);
  NVIC_SetPriority(PIT_IRQ_ID, 0);
  EnableIRQ(PIT_IRQ_ID);
  PIT_StartTimer(PIT_BASEADDR, PIT_CHANNEL);
}

uint32_t AppGetRuntimeCounterValueFromISR(void) {
  return perfCounter;
}

6、摘要

FreeRTOS包含一項功能,可以測量相對於系統中其他任務的任務執行時間。我需要提供的是計時器或某種計數器的初始化例程,以及獲取計數器值的方法。如果您對檢查FreeRTOS計時的其他方式感興趣,請查看Percepio TracealyzerSegger SystemView。如果您希望應用程序本身顯示性能數據,請查看“ 使用FreeRTOS進行性能和運行時分析介紹的Shell / Commandline實現。

7、鏈接

歡迎關注:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章