DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • The Impact of AI Agents on Modern Workflows
  • A Developer’s Guide to Multithreading and Swift Concurrency
  • Mastering AI Agents: How Agentic Design Patterns Make Agents Smarter
  • Deep Work for Site Reliability Engineers

Trending

  • When Airflow Tasks Get Stuck in Queued: A Real-World Debugging Story
  • The Future of Java and AI: Coding in 2025
  • Implementing Explainable AI in CRM Using Stream Processing
  • Distributed Consensus: Paxos vs. Raft and Modern Implementations

Performance and Runtime Analysis With FreeRTOS

Love using FreeRTOS? Love performance monitoring? Then take a look at how to configure both for your projects as well as the ARM Cortex Cycle Counter.

By 
Erich Styger user avatar
Erich Styger
·
Feb. 28, 18 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
12.1K Views

Join the DZone community and get the full member experience.

Join For Free

One of the great things about the FreeRTOS operating system is that it comes with free performance analysis: It shows me how much time is spent on 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 expectations — as this robot is just waiting to run on a track.

How to Get That Kind of Information

For the graphical view in Eclipse, I 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 32-bit counter inside the task information structure. This is actually the counter 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 32-bit 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 config GENERATE_RUN_TIME_STATS is turned on. In that case, the FreeRTOSConfig.h needs two configuration macros with the 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 the 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 another 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 that 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 it:

#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 measuring can be done. But this is not suitable for measuring short task execution times. 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

  • MCUXpresso IDE web page: http://www.nxp.com/mcuxpresso/ide
  • MCUXpresso IDE community: http://www.nxp.com/mcuxpresso/ide/forum
  • Better FreeRTOS Debugging in Eclipse
  • McuOnEclipse Library project: https://github.com/ErichStyger/McuOnEclipseLibrary/tree/master/lib/FreeRTOS/Source
  • ARM Cortex-M Cycle Counter: https://mcuoneclipse.com/2017/01/30/cycle-counting-on-arm-cortex-m-with-dwt/
FreeRTOS Task (computing)

Published at DZone with permission of Erich Styger, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • The Impact of AI Agents on Modern Workflows
  • A Developer’s Guide to Multithreading and Swift Concurrency
  • Mastering AI Agents: How Agentic Design Patterns Make Agents Smarter
  • Deep Work for Site Reliability Engineers

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!