4.2.7.1. FreeRTOS Usage Guidelines

This section has additional useful information related to FreeRTOS. It also compares some key points and config in FreeRTOS vs TI SysBIOS RTOS.

4.2.7.1.1. FreeRTOS configuration

  • In FreeRTOS, all applications need to provide a “FreeRTOSConfig.h” to specify the FreeRTOS kernel configuration, see https://www.freertos.org/a00110.html
  • All source code using FreeRTOS APIs, including FreeRTOS kernel itself MUST include “FreeRTOSConfig.h” before any other FreeRTOS .h files
  • Configuration includes things like below
  • Task scheduling options, Timer tick resolution, Number of priorities, Debug and trace hooks
  • Enable/disable RTOS features, mainly- task, mutex, recursive mutex, semaphore, etc
  • Default sizes for task stack, timer task, idle task, etc
  • PDK has a default predefined “FreeRTOSConfig.h” per SOC and CPU type
  • In general extreme fine-tuning of FreeRTOS config is not needed and a predefined config per SOC and CPU type would meet almost all use-cases and applications.
  • However, the user can modify this config if needed.
  • Also many config options are related to the inclusion/exclusion of RTOS modules to save code/data size. However, we can rely on the compiler to optimize out functions that are not called by applications.
  • The predefined config file per SOC and CPU can be found at below path

${PDK_INSALL_PATH}/packages/ti/kernelfreertos/config/{soc}/{cpu}/FreeRTOSConfig.h

4.2.7.1.2. Summary Comparison with SysBIOS

This section has a quick summary of the comparison between SysBIOS and FreeRTOS features. Refer rest of the sections on this page for more details

4.2.7.1.2.1. Key similarities between modules

SysBIOS Module FreeRTOS Module Additional Remarks
XDC based config FreeRTOSConfig.h #define based config, most code in C files for FreeRTOS
Static alloc Static alloc “construct” APIs in SysBIOS, “CreateStatic” APIs in FreeRTOS
Task Task Same scheduling policy as SysBIOS
Semaphore Semaphore, Mutex Same as SysBIOS. Binary, counting semaphore’s, recursive, priority inheritance mutex’s
Clock Timer Similar features, see details below
Event Event Similar features, see details below
Idle Idle Only one idle hook in FreeRTOS
Queue List Similar features, see details below
Mailbox Queue Similar features, see details below
Hwi HwiP in OSAL Similar features, see details below
Cache CacheP in OSAL Similar features, see details below
HeapMem MemoryP in OSAL Similar features, see details below
MPU/MMU MPU/MMU in CSL Similar features, see details below
Swi “Pend” function call Similar features, see details below
Load Run-time stats APIs Similar features, see details below

4.2.7.1.2.2. Key differences between modules

FreeRTOS difference vs SysBIOS Alternative in FreeRTOS
When configUSE_TIME_SLICING is 1, FreeRTOS will time slice between tasks at the same priority Keep this at 0 (PDK default config), if this is not desired
Timer callbacks are called in task context vs ISR context in SysBIOS Keep the timer task to highest priority so that timer callback get called immediately after all ISRs are done (PDK default config)
No SWI support in FreeRTOS Use “pend” function call, where functions are executed in timer task context. Again keep timer task as the highest priority (PDK default config), see xTimerPendFunctionCall() and xTimerPendFunctionCallFromISR()
No ability to create arbitrary heaps OSAL provides APIs to create arbitrary heaps to match SysBIOS
Only APIs ending with “FromISR()” can be called from within ISRs Porting Layer provides a function xPortInIsrContext() to detect if code is in ISR. This can be used to keep the application functions generic. OSAL takes care of this by default.
Number of task priorities is configurable (up to max 32) vs SysBIOS default of 16 TI default config keeps number of task priorities to 16

4.2.7.1.3. FreeRTOS Tasks

4.2.7.1.3.1. Overview

  • FreeRTOS has the ability to create tasks with the below parameters:
    • Entry function
    • One void * entry function argument
    • Stack memory (when NULL, FreeRTOS uses the default heap to alloc the stack memory)
    • Stack size in units of “stack words”, i.e 32b or 4 bytes in our case.
    • Priority (0 is lowest, configMAX_PRIORITY-1 is the highest)
      • TI default config is 16 to match SysBIOS priorities
  • Static task object memory alloc support
  • Scheduler policy is configurable
    • configUSE_PREEMPTION, TI default is 1 to match SysBIOS scheduler policy
      • This is what enables pre-emptive priority-based scheduling
    • configUSE_TIME_SLICING, TI default is 0 to match SysBIOS scheduler policy
      • When two tasks are of the same priority, FreeRTOS will time-slice between the tasks at units of the timer tick
      • NOTE, this is unlike SysBIOS where unless “Task_yeild” is called scheduler will not switch to another task at the same priority

4.2.7.1.3.2. Initial Tasks

  • Two tasks are created inside FreeRTOS on startup, idle task and timer task

  • Idle task, similar to any other task, only it runs at lowest priority.

  • Timer task (PDK default config enables timer task to match SysBIOS),

    • Timer callbacks and “deferred” interrupt handler functions are called in the context of the timer task

    • NOTE, unlike SysBIOS, SW timer callbacks are not called in ISR context

    • “deferred” handler functions, can be used as an equivalent of SW interrupt of SysBIOS

    • NOTE, unlike SysBIOS, there is no SWI module in FreeRTOS

    • PDK config keeps the priority of this task to the highest so that the timer function will run immediately after an ISR

    • Above can be controlled via FreeRTOS Config, PDK defaults are listed below

      #define configUSE_TIMERS               (1) /* enable timer task and SW timers */
      #define configTIMER_TASK_PRIORITY      (configMAX_PRIORITIES - 1) /* highest priority */
      #define configTIMER_TASK_STACK_DEPTH   (256) /* 1KB */
      

4.2.7.1.3.3. Important tips

  • Task function should not return in FreeRTOS, instead, it should call vTaskDelete(NULL) to destroy itself, e.g
    void myTaskMain(void *args)
    {
        ... do something ...
    
        vTaskDelete(NULL);
        /* do not call “return” */
    }
    
  • By default task switch does not save/restore FPU registers, tasks that need FPU should call vPortTaskUsesFPU once before using FPU operations. If in doubt always call this function

  • ISR handler does not save FPU state, so ISRs should not use floating-point operations

  • On task delete, FreeRTOS will free any memory allocated internally, if dynamically memory allocation mode was used. This memory free is done in the “IDLE” task, so “IDLE” needs to get the opportunity to run at some point.

  • vTaskStartScheduler() starts FreeRTOS and the highest priority task is executed first.

    • Tasks, semaphores, and OS objects can be created before vTaskStartScheduler() is called. Recommend to create one task and then call vTaskStartScheduler() and then do everything from within that task including creating other tasks.
    • `vTaskStartScheduler()` is similar to `BIOS_start()`, i.e it never returns and switches to the created tasks or idle task
  • .stack is the stack used by code, before FreeRTOS scheduler is started, i.e from _c_int00 to vTaskStartSchedular()

4.2.7.1.4. FreeRTOS Interrupts

4.2.7.1.4.1. Overview

  • Interrupt handler is not directly invoked by FreeRTOS Kernel, porting layer handles this
    • In theory, FreeRTOS can work without interrupts. To switch tasks, interrupts are not needed. However, that’s not very useful.
  • Porting layer does below
    • Setup (any) one timer to be configured at configTICK_RATE_HZ, typically 1ms
    • For R5F, we use one of the SOC level general-purpose DM timers.
    • Timer ISR is in porting layer
    • When the timer ISR happens the porting layer calls xTaskIncrementTick() FreeRTOS API to maintain the FreeRTOS timer tick state
  • Porting layer also implements the common interrupt entry and exit logic
    • Esp before interrupt exit, porting layer needs to invoke a task switch if during ISR handling a semaphore was posted that needed a task switch on ISR return
  • Interrupt nesting is also taken care of by this common interrupt handler
  • Porting layer also implements functions to protect the critical section of FreeRTOS via the below APIs
    • portENTER_CRITICAL, disable interrupt, track nesting of calls
    • portEXIT_CRITICAL, reenable interrupt, if nesting call depth is 0
    • portSET_INTERRUPT_MASK_FROM_ISR, interrupt disable and return old interrupt state
    • portCLEAR_INTERRUPT_MASK_FROM_ISR, restore interrupt state

4.2.7.1.4.2. Nested interrupts and ISR stack

  • On R5F,

    • When an interrupt is triggered, the CPU switches to IRQ mode and uses the IRQ stack.
    • IRQ interrupts are disabled by HW at this point.
    • In the ISR handler, some CPU state is saved to IRQ stack and mode is switched to SVC mode and therefore SVC stack
    • IRQs are then enabled, i.e nested interrupts are enabled
    • The user ISR code is executed
    • At this point, more IRQs can occur to interrupt the user ISR code
    • After all IRQs are handled, IRQ is disabled
    • Mode switched based to IRQ mode
    • If a task switch was requested, tasks are switched, and control returns from IRQ to the highest priority pending task
    • If a task switch was NOT requested, control returns from IRQ to the interrupted task
  • On R5F,

    • Thus the user ISR code gets called with SVC stack, so the size of the SVC stack MUST be kept large enough to handle worst-case ISR requirement.

    • The IRQ stack size itself can be kept small since it only saves few bytes (8 bytes) of state for every nested IRQ invocation.

    • This stack size is specified in the linker command file, a sample snippet is shown below,

      __IRQ_STACK_SIZE = 256; /* This is the size of stack when R5 is in IRQ mode */
      __FIQ_STACK_SIZE = 256;
      __SVC_STACK_SIZE = 4096; /* This is the size of stack when R5 is in SVC mode */
      __ABORT_STACK_SIZE = 256;  /* This is the size of stack when R5 is in ABORT mode */
      __UNDEFINED_STACK_SIZE = 256;  /* This is the size of stack when R5 is in UNDEF mode */
      

4.2.7.1.4.3. Interrupts outside of FreeRTOS

  • On R5F,
    • When FreeRTOS enter its critical section, it only disables IRQ but not FIQ
    • Hence, FreeRTOS API calls MUST NOT be done inside FIQ.
    • FIQs can be used for extremely low latency interrupt bypassing the OS completely.
    • NOTE: this is same as the case with SysBIOS

4.2.7.1.4.4. Additional important tips

  • Only FreeRTOS APIs that end with FromISR can be called from the ISR context

    BaseType_t xHigherPriorityTaskWoken = 0;
    
    xSemaphoreGiveFromISR(pSemaphore->semHndl, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    
  • The FromISR API returns a flag xHigherPriorityTaskWoken to indicate task switch should be requested, portYIELD_FROM_ISR requests the task switch. The actual task switch happens after all nested ISRs have been executed.

  • This is different from SysBIOS, where the same APIs can be used inside ISR and outside ISR.

  • The OSAL APIs will take care of this internally as below, e.g,

    SemaphoreP_post(...)
    {
      ....
      if( xPortInIsrContext() )
      {
          BaseType_t xHigherPriorityTaskWoken = 0;
    
          xSemaphoreGiveFromISR(pSemaphore->semHndl, &xHigherPriorityTaskWoken);
          portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
      }
      else
      {
          xSemaphoreGive(pSemaphore->semHndl);
      }
      ...
    }
    

4.2.7.1.5. FreeRTOS Semaphores and Mutex

4.2.7.1.5.1. Overview

  • FreeRTOS has a textbook implementation of binary semaphore, counting semaphore, mutex with priority inheritance, and recursive mutex
  • Timeouts can be specified to unblock with timeout error code if semaphore/mutex is not available
  • Timeout is specified in units of OS ticks. Use pdMS_TO_TICKS() to convert from msec to OS ticks

4.2.7.1.5.2. Important tips

  • Binary and counting semaphores should be used to signal from ISR to task and task to task
    • PDK default config enables binary and counting semaphores
  • Mutex should be used for mutual exclusion of critical sections
    • PDK default config enables mutex and recursive mutexes
  • Once again, only APIs that end with FromISR can be used from within ISR. These are non-blocking APIs.

4.2.7.1.6. FreeRTOS Task Notification

4.2.7.1.6.1. Overview

  • Low overhead API to signal a task from ISR or another task
  • We see about a 25% reduction in cycles needed for task switch using task notification vs using semaphores in ideal standalone conditions.
  • This is a very freertos specific API and PDK drivers and OSAL will NOT use this.

4.2.7.1.6.2. Important tips

  • In practical use-cases, typically cache effects due to cache miss on the larger application code/data, will have a higher effect than overheads of task switch in the smaller OS code.
  • So we recommend to NOT use this feature unless really the savings is critical to the end application.
  • Here one needs to know the task handle to signal, so it’s difficult to use this API from within a driver library since driver ISR for example does not know the task in which the driver API is called
  • Thus use these APIs in applications after thorough analysis and profiling on benefit for the use-case.

4.2.7.1.7. FreeRTOS Timers

4.2.7.1.7.1. Overview

  • Allows users to create one-shot or periodic callbacks at timer resolution of one OS tick
  • Similar to “Clock” module in SysBIOS
  • A timer daemon task executes the callbacks, this is like any other task, with nothing special about it.
  • PDK default is the highest priority for this task so that after all ISRs this task will execute
  • This can be used as an alternative to SW interrupt from SysBIOS
  • A SW queue is used to post callbacks or functions to execute on timer expiry, the queue depth is the config option. PDK default is 16

4.2.7.1.7.2. Important tips

  • Since a single task executes all the multiple callbacks, the callbacks are recommended to not block and spend too much time, even though in theory blocking calls are allowed.
  • Use xTimerPendFunctionCall() to do “deferred ISR” handling, i.e do the most critical work in ISR and do the rest of the work in another function call which gets called right after all ISRs are done.
  • Note, do not use FromISR APIs in callbacks, use regular FreeRTOS APIs

4.2.7.1.8. FreeRTOS Heaps

4.2.7.1.8.1. Default heap

  • FreeRTOS kernel has multiple heap implementations as defined in portable/MemMang
  • A port should compile one of the below heap_n.c files to pick the required heap implementation
    • heap_1.c
      • linear heap allocation, free() not allowed. Typically used when true dynamic alloc is prohibited.
      • Obsolete now and not used anymore, since FreeRTOS natively support static alloc mode
    • heap_2.c
      • Linked list based heap but adjacent free blocks not merged
      • Obsolete now and not used anymore, heap_4.c is a better implementation
    • heap_3.c
      • Uses compiler provided, malloc() and free(), FreeRTOS only makes the calls thread-safe
      • Heap size specified via –heap compiler option and placed in .heap section in the linker command file.
    • heap_4.c PDK default
      • Linked list based heap with adjacent free block merging
      • More or less similar to malloc(), SysBIOS heap implementation
      • Memory needs to be provided as a global static array, and placed appropriately in the linker command file.
    • heap_5.c
      • Same as heap_4.c, + it allows memory to be specified as multiple memory blocks When memory in one block is full, it will alloc from the next block and so on.

4.2.7.1.8.2. Important tips

  • Use –heap in linker command file to specify heap size and place .heap section appropriate memory in the linker command file. This is used by FreeRTOS when it needs dynamic memory allocation.
  • In general recommend using FreeRTOS static alloc APIs for tasks, semaphores to keep the application deterministic.
    • PDK OSAL uses FreeRTOS static alloc APIs
  • Use PDK provided MemoryP.h to create arbitrary application heaps

4.2.7.1.9. FreeRTOS Additional Modules

4.2.7.1.9.1. Queues

  • Highly efficient SW queue implementation
  • Lowest level primitive in FreeRTOS used internally by almost all modules like tasks, semaphores, mutex
  • Thread/ISR safe
  • Blocking/non-blocking with timeouts
  • Fixed queue depth and fixed queue element size at queue create
  • Can be used by application users

4.2.7.1.9.2. Queue Sets, Event Groups, Stream Buffers, Co-routines

  • Croutines, used when tasks are deemed too expensive to use, almost unlikely we will ever use these
  • Queue Sets, more higher level than queues, built using queues, semaphores, even FreeRTOS recommends using these very carefully. https://www.freertos.org/Pend-on-multiple-rtos-objects.html
  • Event groups, similar to the Event module in SysBIOS, are not used by PDK drivers.
  • Stream buffers, built using queues, semaphores
  • PDK default config keeps these disabled

4.2.7.1.10. FreeRTOS Hook Functions

4.2.7.1.10.1. Debug Hook Functions

  • FreeRTOS provides “hook” function callbacks specified in FreeRTOS config.
  • These callbacks are invoked by FreeRTOS at specific points or when certain conditions are met
  • Stack overflow check, configCHECK_FOR_STACK_OVERFLOW PDK default enabled
    • Enables stack overflow checks, two options to detect overflows,
      • Option 1: Check if the stack pointer has gone beyond the limit
      • Option 2: Put a pattern at top of the stack and if the pattern is overwritten then call out stack overflow PDK default
      • NOTE: Depending on the nature of stack overflow, this logic may or may not catch all stack overflows
  • Malloc failed hook, configUSE_MALLOC_FAILED_HOOK PDK default disabled
    • Called when pvPortMalloc fails inside FreeRTOS, not very useful, since all APIs return error on memory alloc failure.
  • Assert, configASSERT PDK default enabled
    • Called when unrecoverable assert condition happens inside FreeRTOS or porting layer
  • PDK defines a new config flag configOPTIMIZE_FOR_LATENCY, this can be used to disable all debug hooks and some other configs in FreeRTOS config to minimize FreeRTOS overheads due to debug hooks,
  • Recommend enabling all hooks in debug mode and disable all hooks in release mode after applications are reasonably sure assert and overflows will not happen.
    • By default configOPTIMIZE_FOR_LATENCY is set to 0, i.e disbled

4.2.7.1.10.2. Statistics Hook Functions

  • FreeRTOS provides APIs to query and return time spent in each task and output the results in a nice formatted table
  • The hook functions for this feature are enabled in PDK default config when configOPTIMIZE_FOR_LATENCY is 0.
  • It uses the same timer as the tick timer to measure task load.
  • See also https://www.freertos.org/rtos-run-time-stats.html
  • This feature is still alpha quality in the current PDK release

4.2.7.1.10.3. Trace Hooks Functions

  • FreeRTOS provides more than 70+ hook macros at key points in the kernel to log trace info
  • Example,
    • traceTASK_SWITCHED_IN, called when a task is being switched into
    • traceTASK_SWITCHED_OUT, called when a task is being switched to another task
  • This is used by tools like TracealyzerTM to log and then visualize task execution and a lot more in a GUI tool, see https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_Trace/FreeRTOS_Plus_Trace.html
  • Not enabled in PDK default config