Performance and Runtime Analysis with FreeRTOS

One of the great things with the FreeRTOS operating system is that it comes with free performance analysis: It shows me how much time is spent in each task. Best of all: it shows it in a graphical way inside Eclipse too:

FreeRTOS Runtime Information in Eclipse

FreeRTOS Runtime Information in Eclipse

In the above output I see that my application is now 97.5% idle which is a good thing and matches my expectation, as this robot is just waiting to run on a track.

How to get that kind of information?

For the graphical view in Eclipse in need an Eclipse plugin (see Better FreeRTOS Debugging in Eclipse). That plugin is already pre-installed in the NXP MCUXpresso IDE.

Another way to see that information is using the ‘tasklist’ command which sends the output to a console (Segger RTT, UART, USB or similar):

tasklist command

tasklist command

This command is available on the McuOnEclipse FreeRTOS available on GitHub.

How does it work?

FreeRTOS records at the time of every task switch how much time has been passed (or consumed) by that task switched out. For this it uses a 32bit counter inside the task information structure. This is actually that counter which is shown by the ‘tasklist’ command under the ‘Runtime’ column. The percentage is then calculated based on the numbers which sum up as the total runtime.

💡 The counter value inside FreeRTOS is a 32bit value, so it is not really well suited for very long measurement periods.

In order to collect these numbers, two FreeRTOS configuration defines have to be set to 1 in FreeRTOSConfig.h:

 
#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 */

The two above configuration items have a graphical setting if using Processor Expert:

Processor Expert Settings for Performance Analysis

Processor Expert Settings for Performance Analysis

Counter

As mentioned above: FreeRTOS keeps track of the time spent for each task. But this is actually not the real time, it is just some kind of timer counter value if configGENERATE_RUN_TIME_STATS is turned on. In that case, the FreeRTOSConfig.h needs two configuration makros configured with two application functions provided:

 
 #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() AppConfigureTimerForRuntimeStats()
 #define portGET_RUN_TIME_COUNTER_VALUE()         AppGetRuntimeCounterValueFromISR()

The first one is to configure the timer at RTOS startup, the second one is to return tha actual timer counter value.

The general rule of thumb is that the timer used for measuring the task should be around 10x faster than the real tick counter for good results.

Users of Processor Expert have yet again an advantage: they can easily configure such a timer in the Processor Expert setting and everything is taken care of:

Processor Expert Timer for FreeRTOS

Processor Expert Timer for FreeRTOS

Below is the setting for a timer which runs 10 times faster than the 1 kHz RTOS tick timer:

Performance Counter

Performance Counter

Below is the resulting timer code:

/*
** ===================================================================
**     Method      :  FRTOS1_OnCounterRestart (component FreeRTOS)
**
**     Description :
**         This method is internal. It is used by Processor Expert only.
** ===================================================================
*/
void RTOSCNTRLDD1_OnCounterRestart(LDD_TUserData *UserDataPtr __attribute__((unused)))
{
  FRTOS1_RunTimeCounter++; /* increment runtime counter */
}

/*
** ===================================================================
**     Method      :  FRTOS1_AppConfigureTimerForRuntimeStats (component FreeRTOS)
**     Description :
**         Configures the timer for generating runtime statistics
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
#if configGENERATE_RUN_TIME_STATS
void FRTOS1_AppConfigureTimerForRuntimeStats(void)
{
#if configGENERATE_RUN_TIME_STATS_USE_TICKS
  /* nothing needed, the RTOS will initialize the tick counter */
#else
  FRTOS1_RunTimeCounter = 0;
  FRTOS1_RunTimeCounterHandle = RTOSCNTRLDD1_Init(NULL);
  (void)RTOSCNTRLDD1_Enable(FRTOS1_RunTimeCounterHandle);
#endif
}

#endif /* configGENERATE_RUN_TIME_STATS */
/*
** ===================================================================
**     Method      :  FRTOS1_AppGetRuntimeCounterValueFromISR (component FreeRTOS)
**     Description :
**         returns the current runtime counter. Function can be called
**         from an interrupt service routine.
**     Parameters  : None
**     Returns     :
**         ---             - runtime counter value
** ===================================================================
*/
uint32_t FRTOS1_AppGetRuntimeCounterValueFromISR(void)
{
#if configGENERATE_RUN_TIME_STATS
  #if configGENERATE_RUN_TIME_STATS_USE_TICKS
  return xTaskGetTickCountFromISR(); /* using RTOS tick counter */
#else /* using timer counter */
  return FRTOS1_RunTimeCounter;
  #endif
#else
  return 0; /* dummy value */
#endif
}

The interrupt service routine counts up a timer counter which then is used to measure the time spent inside a task.

If interrupts are a concern from a performance point of view and no high precision is needed, then the Processor Expert port again has a nice feature: instead of using a dedicated timer, it simply re-uses the tick timer of the RTOS. For this there is an extra setting to configure this:

#define configGENERATE_RUN_TIME_STATS_USE_TICKS 0 /* 1: Use the RTOS tick counter as runtime counter. 0: use extra timer */

The corresponding setting is the following in the UI:

Using Tick Counter

Using Tick Counter

With this, some basic measurement can be done. But this is not suitable for measuring short task execution time. Say the RTOS tick timer is 1 ms, then tasks running for less than 1 ms will be rarely measured.

Using ARM Cortex Cycle Counter

Another way to measure task execution time on ARM Cortex M (e.g. ARM Cortex-M4 or M7) is to use the Cortex Cycle Counter.

#include "KIN1.h"

static uint32_t prevCycleCounter, cycleCntCounter = 0;

void AppConfigureTimerForRuntimeStats(void) {
  cycleCntCounter = 0;
  KIN1_InitCycleCounter();
  prevCycleCounter = KIN1_GetCycleCounter();
}

uint32_t AppGetRuntimeCounterValueFromISR(void) {
  uint32_t newCntr, diff;

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

That approach measures the cycle counter difference between two calls to AppGetRuntimeCounterValueFromISR() and counts up a counter based on that value. In order not to count up too fast, the counter value is scaled down with a shift by 12 bits in above implementation (using a 120 MHz ARM Cortex-M4). For faster or slower running cores you might need to tweak that value.

Summary

FreeRTOS has built-in functions to track task execution time. It is implemented with a counter inside each task descriptor so does not need much RAM. The application has to provide a counter which is typically 10x faster than the tick time to get some reasonable measurements. But even using the tick counter itself gives some rough performance analysis data. Otherwise the application can offer a periodic timer counter. If using an ARM Cortex-M3/M4/M7, using the ARM Cortex Cycle counter is an alternative, as it does not need any timer or interrupts.

Happy Performing 🙂

Links

6 thoughts on “Performance and Runtime Analysis with FreeRTOS

  1. Hi erich I am trying to run FreeRTOS on Blue Pill Board (STM32f103c8t6) I am using St-link v2 debugger , I have created project using CubeMX and created 2 tasks which just toggle LEDs project is getting compiled without errors but only idleTask is running I have tried different configMAX_SYSCALL_INTERRUPT_PRIORITY, configKERNEL_INTERRUPT_PRIORITY, and different task priorities without success can you point me to some obvious and common mistakes i could be making and/or point me to a working code which I can use, Thanks in advance 🙂

    Like

  2. Pingback: Tutorial: Using Runtime Statistics with Amazon FreeRTOS V10 | MCU on Eclipse

  3. Pingback: Implementing FreeRTOS Performance Counters on ARM Cortex-M | MCU on Eclipse

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.