FreeRTOS: how to End and Restart the Scheduler

Most host or desktop systems (say Linux, Mac or Windows) have a normal use case where you start the operating system say in the morning and shut it down in the evening, and then you leave the machine. Embedded Systems are different: they are not attended, and they are supposed to run ‘forever’. Not every embedded system needs to run an OS (or in that world: Real-Time Operating System or RTOS), but the same applies here: after the RTOS is started, it is not intended that it will shutdown and restart. To the extend that you won’t they support the ‘shutdown’ and ‘restart’ functionality at all. In case of gathering coverage information this would be really useful:

coverage information from freertos application

coverage information from FreeRTOS application

In the case of FreeRTOS: what if I really need to shutdown the RTOS and restart it again, as by default this is not supported. This is what this article is about …

Introduction

Embedded Systems are fundamentally different from desktop systems: while it is kind of normal to shut down and restart a desktop or laptop system from time to time, this is not planned or intended for an embedded system: by its nature it should run ‘always’. This is further visible from the ‘main’ of an embedded system: typically the main never returns and stays running, something like this:

 
void main(void) {
  InitClocks();
  InitPins();
  InitDrivers();
  for(;;) {
    AppRun();
  }
  /* never leave main */
}

A similar thing applies to embedded systems if running with an RTOS, where this looks similar like this:

 
void main(void) {
  InitClocks();
  InitPins();
  InitDrivers();
  CreateInitialTasks();
  StartScheduler();
  /* should never get here as scheduler never terminates */
  for(;;) { }
  /* never leave main */
}

💡 Screenshots in this post are using the unlimited and free-of-charge Eclipse IDE NXP MCUXpresso IDE V10.3.0 with FreeRTOS V10.1.1

Why Shutdown and Restart?

Obviously the need for a RTOS shutdown or restart is probably not something needed the most for an embedded RTOS. Still I have found it very useful:

  • It makes sense to run some software *after* the RTOS has been shut down. For example if I collect coverage information (see “Adding GNU Coverage Tools to Eclipse” and “GNU Code Coverage on Embedded Target with Eclipse Neon and ARM gcc 5“), I don’t want to interfere that last process to dump the data with the RTOS. Additionally I need exclusive access to the system resources including lots of stack space: shutting down the RTOS gives me back all the RAM and stack space I need for the file I/O operations.

    writing gcov coverage information after scheduler shudown

    writing gcov coverage information after scheduler shutdown

  • It makes sense to run the RTOS while listening for updates. Running the RTOS while doing the update is certainly possible in some cases, but at some stage I have to stop it and restart it. This can be done with resetting and restarting the system (e.g. see “How to Reset an ARM Cortex-M with Software“). I have found it a better way to shutdown the RTOS, then doing the update outside of the RTOS and restart the system.

    ending the freertos scheduler from a task

    ending the FreeRTOS scheduler from a task

  • Another use case is for low power applications. While FreeRTOS is great in low power modes too (see “Low Power with FreeRTOS: Tickless Idle Mode“), the application can go even into lower power modes with more restricted functionality if I can turn off more of the active system. So having the ability to run the RTOS only in the phases of the life time of my application where it is making sense and not in other parts gives me greater flexibility and the opportunity for even lower power.

    vTaskStartScheduler()

    vTaskStartScheduler() followed by low power mode

So I hope you see why a shutdown and restart of the RTOS can make sense. Most RTOS including FreeRTOS have the ability to ‘silence’ the scheduler (vTaskSuspendAll() for example), but still the RTOS is there and uses system resources. But the ability to completely ‘remove’ it and restart it if needed would be cool thing :-).

FreeRTOS

FreeRTOS does have a scheduler start function (vTaskStartScheduler()) and even have a vTaskEndScheduler() function in its API:

freertos vtaskendscheduler api function

FreeRTOS vTaskEndScheduler() API function

But as noted in the forums and in the API description, it is only supported for the PC port (see API description):

NOTE: This has only been implemented for the x86 Real Mode PC port.

That’s true for the original FreeRTOS port. The port I have extended does support this and I’m using it in ARM Cortex-M and HCS08 application :-).

vTaskEndScheduler() and vTaskStartScheduler()

While the RTOS has the API call prepared to shut it down, FreeRTOS does not have the infrastructure in place to restart the RTOS after a vTaskEndScheduler() call. But this is exactly what I want: to restart the RTOS after it has been ended.

To have the ability to end the scheduler, the following macro has to be set to 1 in FreeRTOSConfig.h:

#define INCLUDE_vTaskEndScheduler                 1 

The challenge is: after the scheduler has been started, it is not easy to return to the code right after the vTaskStartScheduler() call. Because the tasks with their own stacks, plus the standard port for FreeRTOS and M3, M4 and M7 even do reset the MSP stack pointer (see this forum discussion). So if I want to return to the place where the scheduler has been started, I need to prevent resetting the MSP stack pointer on ARM Cortex. This is why I have added a setting to FreeRTOS to configure this:

reset msp setting

reset MSP  setting

This sets the following configuration define:

 
#ifndef configRESET_MSP
  #define configRESET_MSP                         (0)
   /*!< 1: reset MSP at scheduler start (Cortex M3/M4/M7 only); 0: do not reset MSP */
#endif

With this setting set to 0, my port does *not* reset the MSP at scheduler starting point.

port code to reset msp stack pointer

port code to reset msp stack pointer

This means that not the full MSP stack will be available for interrupts (see “ARM Cortex-M Interrupts and FreeRTOS: Part 3“), but that’s usually ok too.

To be able to jump back to the starting point of the scheduler, I use a cool feature of the C library: setjmp/longjmp (see http://web.eecs.utk.edu/~huangj/cs360/360/notes/Setjmp/lecture.html).

First I need a jump buffer variable:

#if INCLUDE_vTaskEndScheduler
#include <setjmp.h>
static jmp_buf xJumpBuf; /* Used to restore the original context when the scheduler is ended. */
#endif

Inside xPortStartScheduler() I setup the jump buffer:

#if INCLUDE_vTaskEndScheduler
    if(setjmp(xJumpBuf) != 0 ) {
      /* here we will get in case of a call to vTaskEndScheduler() */
      __asm volatile(
        " movs r0, #0         \n" /* Reset CONTROL register and switch back to the MSP stack. */
        " msr CONTROL, r0     \n"
        " dsb                 \n"
        " isb                 \n"
      );
      return pdFALSE;
    }
#endif
  vPortStartFirstTask(); /* Start the first task. */
  /* Should not get here! */
  return pdFALSE;

setjmp() returns 0 if establishing the jump buffer, and shall return !=0 in case of setjmp() called which will get called during vTaskEndScheduler().

void vTaskEndScheduler( void )
{
  /* Stop the scheduler interrupts and call the portable scheduler end
     routine so the original ISRs can be restored if necessary.  The port
     layer must ensure interrupts enable bit is left in the correct state. */
  portDISABLE_INTERRUPTS();
  xSchedulerRunning = pdFALSE;
  vPortEndScheduler();
}

The vPortEndscheduler() then does all the cleanup and reset of the RTOS. The reset is needed not to confuse any RTOS awareness in the debugger:

void vPortEndScheduler(void) {
  vPortStopTickTimer();
  vPortInitializeHeap();
  uxCriticalNesting = 0xaaaaaaaa;
  /* Jump back to the processor state prior to starting the
     scheduler.  This means we are not going to be using a
     task stack frame so the task can be deleted. */
#if INCLUDE_vTaskEndScheduler
  longjmp(xJumpBuf, 1);
#else
  for(;;){} /* wait here */
#endif
}

With this, the scheduler is terminated gracefully and I’m back on the main stack again:

on msp stack after calling vtaskendscheduler()

on msp stack after calling vTaskEndScheduler()

I find that very cool :-).

Summary

By default for embedded targets, FreeRTOS does not support ending the scheduler or restarting the scheduler after ending it. This article describes a port for ARM Cortex-M which implements vTaskEndScheduler() and the ability to call vTaskStartScheduler() again without a power-on reset. Ending and Starting the scheduler is useful for low-power applications, use cases where an RTOS is not needed during the live cycle of an application or for bootloader applications. The concept is used on different ARM Cortex-M cores from NXP including on the 8/16bit S08 microcontroller, but can be easily used for any other microcontroller architectures.

Happy Scheduling 🙂

Links

Advertisements

2 thoughts on “FreeRTOS: how to End and Restart the Scheduler

  1. For the case of needing low power modes, I normally do something like set a long interrupt interval on a low power timer (like 1 second or more) and then disable the RTOS interrupt. We just had a 38 hour power outage here and the heating system control I built came back with the right time and all settings with only a small super capacitor powering the processor.

    Like

    • Yes, I’m doing that kind of low power handling too in combination with tickless idle mode. There is a crossing point between keeping the system (and especially the RAM) alive and ramping things down and up again. For most applications silencing the RTOS is the best approach imho.

      Like

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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