FreeRTOS has many cool features, and one is that it can report the CPU percentage spent in each task. The downside is that to get this kind of information some extra work is needed. In this article I show how to do this for the NXP i.MX1064.
Outline
In “Tutorial: Using Runtime Statistics with Amazon FreeRTOS V10” I described the approach how to measure task runtime and execution time. Basically the RTOS needs an extra timer to measure task execution time. This timer value is checked at task context switch time and stored in the task control block information section. With little overhead the RTOS knows how the time is spent between the tasks (this does not separate out time spent in interrupts).
In this article I show how this can be accomplished using the NXP i.MXRT1064 EVK board. The example project used in this article is available on GitHub.
Configuring the RTOS
To collect the runtime information, it is necessary to configure a few #defines in FreeRTOSConfig.h:
First configure to collect runtime statistics:
#define configGENERATE_RUN_TIME_STATS 1 /* 1: generate runtime statistics; 0: no runtime statistics */
It is possible to use the Systick timer, but this will not give us precise results, so we disable that one:
#define configGENERATE_RUN_TIME_STATS_USE_TICKS 0 /* 1: Use the RTOS tick counter as runtime counter. 0: use extra timer */
Next we need to configure how to use the timer: For this we specify the name of a timer configuration function and a function to get the timer value:
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() McuRTOS_AppConfigureTimerForRuntimeStats() #define portGET_RUN_TIME_COUNTER_VALUE() McuRTOS_AppGetRuntimeCounterValueFromISR()
I’m going with the default Implementation in the McuRTOS module which already covers the basics. That’s it.
The next step is to implement Initialize the timer and implement the ISR for it.
Initializing Timer Functions
💡 I could overwrite the McuRTOS_AppConfigureTimerForRuntimeStats() instead of doing a local AppConfigureTimerForRuntimeStats(). I prefer the local variant because this means less work.
First we need to include the needed driver for the driver. I’m going to use the GPT (General Purpose Timer) of the i.MX RT:
#include "fsl_gpt.h"
Next I need a 32bit counter which is used to measure the time:
uint32_t McuRTOS_RunTimeCounter; /* runtime counter, used for configGENERATE_RUNTIME_STATS */
Next I configure the timer to 0.1 ms (10 times the frequency of the RTOS tick timer which is set to 1 kHz:
static void AppConfigureTimerForRuntimeStats(void) { uint32_t gptFreq; gpt_config_t gptConfig; GPT_GetDefaultConfig(&gptConfig); /* Initialize GPT module */ GPT_Init(GPT2, &gptConfig); /* Divide GPT clock source frequency by 3 inside GPT module */ GPT_SetClockDivider(GPT2, 3); /* Get GPT clock frequency */ gptFreq = CLOCK_GetFreq(kCLOCK_PerClk); /* GPT frequency is divided by 3 inside module */ gptFreq /= 3; /* Set GPT module to 10x of the FreeRTOS tick counter */ gptFreq = USEC_TO_COUNT(100, gptFreq); /* FreeRTOS tick is 1 kHz */ GPT_SetOutputCompareValue(GPT2, kGPT_OutputCompare_Channel1, gptFreq); /* Enable GPT Output Compare1 interrupt */ GPT_EnableInterrupts(GPT2, kGPT_OutputCompare1InterruptEnable); /* Enable at the Interrupt and start timer */ EnableIRQ(GPT2_IRQn); GPT_StartTimer(GPT2); }
Next the implementation of the timer interrupt service routine. Here the counter gets incrememented:
void GPT2_IRQHandler(void) { /* Clear interrupt flag.*/ GPT_ClearStatusFlags(GPT2, kGPT_OutputCompare1Flag); McuRTOS_RunTimeCounter++; /* increment runtime counter */ #if defined __CORTEX_M && (__CORTEX_M == 4U || __CORTEX_M == 7U) __DSB(); #endif }
Finally I have to call the Timer initialization function, for example just before starting the scheduler:
That’s it! Now the RTOS is collecting runtime information.
Getting the Information
The information is displayed in the ‘Runtime’ column of the FreeRTOS Tasklist in Eclipse (shown for MCUXpresso IDE):
Using a serial connection (UART, USB, SEGGER RTT) the same information can be retrieved using a command line interface too, no debug connection needed:
Summary
Collecting runtime information in FreeRTOS usually requires a timer (the SysTick can be used too). The collected information gives a good overview about where CPU time is spent so can help to check the system load.
Happy Runtiming 🙂
Links
- How to measure runtime in FreeRTOS: https://mcuoneclipse.com/2018/08/02/tutorial-using-runtime-statistics-with-amazon-freertos-v10/
- Project on GitHub: https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/MCUXpresso/i.MX%20RT1064_EVK/MIMXRT1064_FreeRTOS
- NXP i.MX RT1064: https://mcuoneclipse.com/2018/12/28/first-steps-with-the-nxp-i-mx-rt1064-evk-board/
One thing I’ve been meaning to try to add is an elapsed idle time counter for each task. Would be nice to be able to see how long a task has been blocked. Seems like it ought to be easy enough, just haven’t had a chance to work on it.
LikeLike
Is this not already provided? I mean if a task is using x% of the CPU time, it has been blocked (100-x)% by other tasks. Means if a task shows 20% usage in the Runtime, it has been blocked 80%.
Or are you thinking about something different?
LikeLike
No, I mean like “this thread has been blocked for 16.4 seconds”.
LikeLike
hmm, the data is there: how many ticks (time) the system is running, and the precentage (or number of ticks) each task is running (or have been blocked).
So it really would be about to present it somehow?
LikeLike
I’m not looking for a total, just the number of ticks elapsed since the current blocking event began.If the hooks are there for the transitions it should be easy, I just haven’t looked at it closely.
Something like my network task will normally have a very small percentage of the runtime. Seeing how long the task has been blocked would let me see if it’s actually inactive or not.
LikeLike
So really what you want is a time stamp when the task was active the last time?
LikeLike
Yeah, that’s basically it.
LikeLike
Hello Erich, I saw that you wrote the FreeRTOS port to the i.MX-RT1050 processor. I am trying to port Lib Metal onto that processor and I was wondering if you have any advice on how to achieve this or where the best place would be to get sources on this?
LikeLike
Hi Nicole,
not sure what you mean with ‘Lib Metal’?
Erich
LikeLike
Hi Erich,
It is an abstraction layer across different OSs and hardware platforms environments to manage devices, handle device interrupts, and request memory access. It was developed by OpenAmp. The source code for Libmetal is in Github under OpenAMP/libmetal.
Nicole
LikeLike
Hi Nicole,
best if you check out the NXP MCUXpresso SDK sources.
LikeLike