<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1596188457259603&amp;ev=PageView&amp;noscript=1">
TruePERSPECTIVES_logo.png

Visualizing run-time statistics using FreeRTOS and Atollic TrueSTUDIO Pro

Posted by Mattias Norlander on Oct 24, 2017 2:11:48 PM

Atollic TrueSTUDIO Pro (v.8.1.0 and later version) have extended kernel-aware debugging support by allowing to visualise FreeRTOS run-time statistics. FreeRTOS can optionally be configured to collect information about each tasks execution time. In Atollic TrueSTUDIO the execution time for each task can be visualised as the percentage of the total execution time for the application using the kernel-aware debug views for FreeRTOS.

freeRTOS statistics.png

In this article we describe how to make the run-time statistics work in Atollic TrueSTUDIO Pro 8.1.0.

There are three steps to set up the system so that run-time statistics will display in the Atollic TrueSTUDIO kernel-aware debugging window.

1. Set up a hardware timer

The execution time is measured using a hardware timer. The timer will be used to increment a counter that is used to measure how much execution time is used by each timer.

The timer used for collecting these statistics must use a higher resolution time base than the tick interrrupt that is driving the scheduler, otherwise the statistics will be too inaccurate or even useless. FreeRTOS recommends the time base to be from 10 to 100 times faster than the tick interrupt, as stated on the FreeRTOS website:

"The faster the time base the more accurate the statistics will be - but also the sooner the timer value will overflow." FreeRTOS website

In this example we will use an STM32F429I-Discovery and the old Std Periph firmware driver APIs. We will setup a a basic timer (TIMER7) to generate an update interrupt on each tick. The timer will run at ~20KHz which should be OK since it is within the range of 10-100 times faster than the tick interrupt. The TrueSTUDIO  kernel-aware debugger support shows the relative execution time value, not the absolute in ms.

void SetupRunTimeStatsTimer(void)
{
  /* Set interrupt priority and enable TIMER7 interrupt in NVIC */

NVIC_SetPriority(TIM7_IRQn, NVIC_PriorityGroup_0);
  NVIC_EnableIRQ(TIM7_IRQn);

/* Clock the TIMER7 peripheral */

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
  TIM_TimeBaseInitTypeDef TimBaseStruct;
  /* Setup a pre-scaler (PSC) and a timer period (ARR) to generate an interrupt at ~20KHz */
  TimBaseStruct.TIM_Prescaler = 0x1194;
  TimBaseStruct.TIM_Period = 0x1;
  TIM_TimeBaseInit(TIM7, &TimBaseStruct);
  TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);

  /* Start TIMER7 */

  TIM_Cmd(TIM7, ENABLE);
}

2. Enable run-time statistics in FreeRTOSConfig.h

The following macros must be defined:

#define configUSE_TRACE_FACILITY                   1
#define configGENERATE_RUN_TIME_STATS              1

The symbol configUSE_TRACE_FACILITY is a pre-requisite for all kernel-aware debug support. It adds structure members and functions to assist with visualization and tracing of the kernel.

When the project is built with the symbol configGENERATE_RUN_TIME_STATS set to 1, the API function vTaskStartScheduler() will contain a call to the macro portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(). With this call the user can assign a function to be called to configure a hardware timer. We previously defined a function called SetupRunTimeStatsTimer() which will be used. Therefore add the following macros to your FreeRTOSConfig.h:

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()   SetupRunTimeStatsTimer()

Also add a variable that can be incremented by the HW-timer and used to determine how much execution time that is spent in each task. In FreeRTOSConfig.h add:

#define portGET_RUN_TIME_COUNTER_VALUE()           ulHighFrequencyTimerTicks

 

3. Increment the timer counter

In the ISR (interrupt service routine) clear the pending bit and increment the variable used to measure execution time.

volatile unsigned long ulHighFrequencyTimerTicks = 0;

void TIM7_IRQHandler(void)
{
    /* Clear the pending bit in NVIC and TIMER7 */
    NVIC_ClearPendingIRQ(TIM7_IRQn);
    TIM_ClearITPendingBit(TIM7, TIM_IT_Update);

    /* Increment the counter used to mease execution time */
    ulHighFrequencyTimerTicks++;
}

Now start a debug session and try run-time statistics!

If you managed to get this far you should now be able to see run-time statistics in the FreeRTOS Task List view in TrueSTUDIO Pro. It could look something like this:

freertos_run_time_stat.png

You may need to adjust the frequence of your timer to find the best fit resolution for your application.

Is the Run Time column still not listing any numbers, only N/A even though you have followed this guide in detail? This problem can a arise if your are building your project with optimization above -O0. The reason is quite likely found in the declaration of ulTutoralRunTime in tasks.c

#if ( configGENERATE_RUN_TIME_STATS == 1 )

	PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL;	/*< Holds the value of a timer/counter the last time a task was switched in. */
	PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL;		/*< Holds the total amount of execution time as defined by the run time counter clock. */

#endif

Either declare the variable as volatile:
PRIVILEGED_DATA volatile static uint32_t ulTotalRunTime = 0UL;		/*< Holds the total amount of execution time as defined by the run time counter clock. */

Or simply change the optimization level only for tasks.c by right-clicking it in File Explorer --> Properties --> C/C++ Build --> Settings --> Tool Settings --> Optimization --> Optimization Level == -O0.

 

Happy debugging with Atollic TrueSTUDIO Pro and FreeRTOS!

Topics: freertos, run-time stastistics, kernel-aware debug, profiling