Follow+Star Public Account, don’t miss exciting content
Source | Mike Tai Technology
FreeRTOS is a real-time operating system designed for microcontrollers and small microprocessors, distributed under the MIT license, emphasizing reliability and ease of use.
Safety regulations in markets such as automotive, medical, and industrial require designers to have an RTOS certified by relevant industry standards, but certified RTOS can be an expensive expenditure at the early stages of long-term safety projects.
SAFERTOS is a safety-critical RTOS that has passed IEC61508 and ISO26262 pre-certification. SAFERTOS uses the same functional model as FreeRTOS, built for safety. In safety products, project prototypes can be implemented using the FreeRTOS kernel and later transitioned to SAFERTOS during formal development.
This article illustrates how to migrate a FreeRTOS application to SAFERTOS through a simple example project.
🔸Hardware platform: NXP Freedom K64F development board – FRDM K64F
🔸Development environment: MCUXpresso IDE v11.1
🔸Example project can be downloaded for free from
www.highintegritysystems.com/down-loads/.

The project contains three projects:
1. FreeRTOS_Demo – Basic FreeRTOS project
2. RTOS_Demo – Privileged mode SAFERTOS project
3. RTOS_UnprvDemo – SAFERTOS project with unprivileged tasks
Main differences between FreeRTOS and SAFERTOS
Compared to FreeRTOS, SAFERTOS:
· Fewer API functions
· More error checks in functions
· Most API calls return status codes, other functions return data by reference
· Requires the application to provide all stack, task control block, and queue buffer memory
· Uses static allocation mechanisms, does not provide heap functions
· Default uses the processor’s MPU unit
· Completely redesigned to meet safety-critical software requirements
Therefore, when migrating FreeRTOS projects to SAFERTOS, some work is required to complete the kernel startup and operation.
FreeRTOS hides many conventional memory management operations, dynamically allocating stacks during task creation, allocating kernel buffers during kernel startup, etc. Static allocation can also be configured in FreeRTOS, with memory provided by the application, but most people prefer the simpler method of letting FreeRTOS handle it.
FreeRTOS also provides many compilation options and allows application designers to insert additional functional code into the kernel through hook macro mechanisms, running during task switches, for example, running additional hook functions during task deletion or creation.
API Differences
The type names defined by RTOS are different. When using FreeRTOS, application files need to include the corresponding header files for the API (tasks, queues, semaphores); in SAFERTOS, application files only need to include one SafeRTOS_API.h header file.
Static Allocation and MPU
SAFERTOS requires static allocation of memory needed by application tasks and kernel objects. Safety-critical systems recommend static allocation mechanisms, making it easier to prove sufficient memory space at runtime.
The vast majority of SAFERTOS ports assume the use of MPU. The use of MPU means application designers need to supervise the exact location of memory structure addresses, including kernel tasks and queue buffers. Additionally, the MPU must meet alignment and size constraints, and application engineers need to carefully allocate space to avoid wastage.
Therefore, when using FreeRTOS, ensure enough free space in the heap before calling xTaskCreate. With SAFERTOS, pre-allocated and aligned stacks and task TCB memory must be provided, and pointers to these structures passed to the corresponding parameters of xTaskCreate.
Task Privilege Mode and Kernel Function Encapsulation Layer
Each SAFERTOS task is assigned an operating privilege; privileged tasks have the same permissions as the kernel code. Many CPUs support privileged and unprivileged modes, restricting unprivileged mode’s instruction access, limited software traps, exceptions, and interrupts.
Typically, applications run in unprivileged mode, and each task provides a set of MPU parameters that configure the corresponding MPU domain during task switching.
When creating SAFERTOS tasks, an MPU domain is added to define the user task stack, ensuring tasks only access their own stack.
The kernel API operates in privileged mode, and each SAFERTOS API has a privilege-escalating wrapper layer, implemented by triggering exceptions (usually system calls), synchronizing interrupts, or CPU traps. The API encapsulation layer temporarily elevates task privileges, allowing unprivileged tasks to execute kernel APIs, which revert to the original task permissions after completion. Debugging is inconvenient because the actual API functions differ from their names at call time.
Although FreeRTOS also supports MPU functionality through a similar privilege escalation encapsulation mechanism, it only provides limited MPU porting references. In SAFERTOS, we assume application tasks run in unprivileged mode. In FreeRTOS MPU ports, tasks are typically assumed to run in privileged mode, but tasks can choose to create as restricted, i.e., unprivileged mode.
Applications Based on FreeRTOS
The example project includes a wizard-generated Amazon FreeRTOS project: FreeRTOS_Demo.
Automatically generated linker files
After building the project, linker positioning files are automatically generated. Since the SAFERTOS project requires modifying the linker file, we do not want the automatically generated linker files to overwrite the modified content. Move the generated linker file from the Debug directory to a separate directory, and disable auto-generation of linker files in project options, pointing to the new linker file directory.

Application Code
The application includes three LED tasks and one control task. The control task updates the target brightness values of each LED in a global array and uses a mutex semaphore to monitor access to the global array. The global array is not the best method for communication between tasks, but our goal is to provide a simple example of how to transition from FreeRTOS to SAFERTOS.
Migration from FreeRTOS to SAFERTOS
1. Upgrade the project to SAFERTOS, all code runs in privileged mode
1. Replace FreeRTOS kernel code with SAFERTOS
Delete the code in the amazon-freertos directory of the project, replacing it with SAFERTOS libraries and header files, modify the include path in project options – C/C++ Build/Settings -> Tool Settings -> MCU C compiler -> includes.

In the automatically generated project by the wizard, the C/C++ General -> Paths and Symbols -> includes path information also needs to be modified.

2. Edit the linker file to export the symbols needed by SAFERTOS
SAFERTOS requires the segments and variable symbols defined in the linker file to set the MPU region protecting kernel code and data. The required segments and symbols can be found in the portmpu.h file.
The kernel function segment is named kernel_func, and the kernel data segment is kernel_data. In GCC, the kernel functions and data are placed in the respective segments through segment attributes.
Kernel functions are typically placed right after the vector table. Kernel data is placed at some location in RAM, which must comply with MPU alignment requirements.
The linker file must also export segment start and end symbols, as well as the start address, end address, and size of ROM and RAM.
The following symbols are needed in portmpu.h:
· lnkStartFlashAddress
· lnkEndFlashAddress
· lnkStartKernelFunc
· lnkEndKernelFunc
· lnkStartKernelData
· lnkEndKernelData
· lnkRAMEnd (RTOS_Demo_Debug_memory.ld)
· lnkRAMStart (RTOS_Demo_Debug_memory.ld)
3. Install the interrupts and exceptions required by SAFERTOS
K6xxF porting requires SysTick, PendSV, and SVC interrupts, which are handled by implementations defined by CMSIS, with the corresponding handling entry located at the default vector table position. RTOS can name its own exception handling functions.
In SAFERTOSConfig.h, by defining, implement SAFERTOS exception handling to replace CMSIS definitions, but the SAFERTOS library file cannot be modified; the startup file vector table’s CMSIS names can be redefined.
In the project code, the vector table definition is located in the startup_mk64f12.c file. Insert in this file:
/* SAFERTOS system tick, SVC and PendSV handlers */
#define SysTick_Handler vTaskProcessSystemTickFromISR
#define SVC_Handler vSafeRTOSSVCHandler
#define PendSV_Handler vSafeRTOSPendSVHandler
Unlike FreeRTOS, SAFERTOS checks the vector table entries at startup; if they are missing or incorrect, it refuses to run.
4. Kernel hook functions
SAFERTOS provides limited hook functions, the most important of which is the Error Hook function. When the system detects an unrecoverable error, it enters a safe error state. For example, if a corrupted TCB or stack overflow is detected, the error hook will be called. In the project, the error hook is a simple infinite loop (located in HookFunctions.c).
5. Kernel tasks, kernel configuration, and startup
In addition to the addresses of hook functions, the addresses and sizes of stacks and TCBs must also be passed to the idle task and timer task (optional). The kernel task also requires MPU parameters, timer command queue information, etc. SAFERTOS passes parameters to a dedicated API to configure the kernel through a dedicated structure. The most complex part of migrating applications from FreeRTOS to SAFERTOS is configuring and starting the scheduler.
Kernel configuration (including kernel task stack and TCB) information is placed in a separate SafeRTOSConfig.c file.
At each stage of kernel startup, when the configuration structure is passed to the kernel configuration function, the kernel starts, and the API returns the corresponding error code. The meaning of the error codes returned by the function can be found in the projdefs.h file in the kernel include directory. This file lists all error code information.
For the example application, a “global” MPU region is also needed, establishing a global MPU region to access the onboard LED’s GPIO so that we do not have to elevate the corresponding task’s privileges to use GPIO resources. In SafeRTOSConfig.c, the “global” MPU region is set by calling xMPUConfigureGlobal Region(), allowing read/write access to GPIO registers (in fact, this region contains all peripheral address space).
6. Application task TCB and stack
Set the stack and TCB for each application task, filling in other task parameters.
In FreeRTOS, the six parameters required to create a task are directly passed to the xTaskCreate() function, which returns the handle of the newly created task or pdFAIL error information. In SAFERTOS, there are more task parameters, passed to xTaskCreate() through a pointer to a port-specific xTaskParameters structure, with the second parameter receiving the handle of the newly created task, and the function returning pdPASS or an error code.
In the example, it can be seen that the task names and parameters of FreeRTOS and SAFERTOS are different. The task parameters in SAFERTOS also include each task’s privilege level and MPU region settings. In the first step, we run all tasks at the privileged level, so no additional MPU parameters are currently needed, and these parameters are set to 0/null.
7. Update the called API
For each API call, check and modify the corresponding function names and return values in SAFERTOS.
8. Type updates
In addition to different API function names, the type names defined in the porting layer also differ. You can use the IDE’s search & replace functionality to replace them.
2. Modify application tasks to unprivileged mode
We have allowed all tasks to access the GPIO registers used through a “global MPU domain.” What access rights are needed when a task is marked as unprivileged?
Mark a task as unprivileged and then load and run the application. If the application eventually enters the MPU fault handler, the fault address can be identified by checking the debugger’s registers. You can try to force a return from the handler using the debugger to quickly identify the instruction causing the MPU fault.
In the example, each LED task needs to access the shared “brightness request” array, handling the mutex for accessing that data, with the mutex buffer only accessed by kernel code. How do tasks achieve access to the shared RAM region?
Create a named segment in the linker file, export the start address and size symbols, and add placement attributes to specify the positioning of functions or variables, as follows in GCC:
__attribute__ ( ( section ( “section_name”)))
Finally, use the linker-exported segment symbols to define the MPU domain, adding them to the task MPU parameters to gain access rights. Note that the exported linker symbols are addresses, and after declaring them as extern variables, we can obtain the symbols’ addresses, or declare them as array types, where the array name will be its address.
Conclusion
This article explores the differences between FreeRTOS and SAFERTOS through a simple example project and migration process.
Through the example project, at each stage of converting FreeRTOS to SAFERTOS, analyzing the pre-configured project source code and running each version can analyze the differences between the original FreeRTOS platform application and the final unprivileged version.
Clearly, even very simple code has many different SAFERTOS conversion implementations, and early design decisions can affect the simplicity of conversion. It is recommended to minimize the use of FreeRTOS-specific APIs, use kernel-managed inter-task communication mechanisms, and use type names shared by FreeRTOS and SAFERTOS to make things easier.
Considering the tasks to run in unprivileged execution mode from the start of the application will make it easier when upgrading to safety applications.
Selected Summary | Column | Directory | Search
Selected Summary | ARM, Cortex-M
Selected Summary | ST Tools, Download Programming Tools
Click “Read the original text” to see more sharing, welcome to share, collect, like, and view.