sl_main Baremetal to RTOS Application Transition#
Table of Contents#
Overview#
This document provides a guide to help developers transition from baremetal to RTOS-based applications using the sl_main software module. It covers the differences between these application types, explains the initialization flow variations, outlines the step-by-step migration process, and addresses common issues that might arise during the transition.
The guide is intended for developers who need to:
Understand the structural differences between baremetal and RTOS applications in sl_main
Convert an existing baremetal application to use an RTOS
Implement proper initialization sequences in an RTOS environment
Configure and optimize RTOS applications using sl_main
By following this migration guide, developers will be able to successfully transition their applications while maintaining proper system initialization and operation.
Key Differences Between sl_main baremetal and RTOS Based Applications#
Feature | Baremetal | RTOS |
|---|---|---|
Main loop | Explicit super-loop in | No super-loop; task-based execution |
Execution model | Sequential processing | Concurrent task execution |
Initialization flow | All initialization occurs in sequence | Two-stage initialization with kernel start between stages |
Event processing | Called explicitly in main loop | Handled in separate tasks |
RTOS Structures and the Main Function#
In baremetal applications, the main function contains the infinite loop that drives the application. With RTOS-based applications using sl_main:
The
mainfunction is called from an initial RTOS task called the start task, which is created by sl_main. By default, this task has the highest possible priority. The priority of the start task can be changed, See Step 4: Configure start task for more details.This task runs all the initialization countained in (
sl_main_second_stage_initandapp_init)The application execution is driven by the RTOS scheduler and application-specific tasks
Application Architecture#
Baremetal Architecture#
Baremetal applications using sl_main have a sequential architecture:
main() → sl_main_init() → app_init() → while(1) loop:
├─ sl_main_process_action()
├─ app_process_action()
└─ power management/sleepAll code executes sequentially in a single execution context
Processing is driven by the super-loop in
mainEach component gets processing time in a predetermined order
The system sleeps when no processing is required
RTOS Architecture#
RTOS applications using sl_main have a concurrent, task-based architecture:
sl_main_init() → RTOS kernel start → Start task:
├─ sl_main_second_stage_init()
└─ app_init() → Create application tasks
[Optional: Start task deletes itself]
Task 1 ─┐
Task 2 ─┼─ Scheduled by the RTOS
Task N ─┘Multiple tasks execute concurrently based on priority and state
Processing is driven by the RTOS scheduler
Components can have dedicated tasks or shared tasks
The system sleeps when no tasks are ready to run
Application Flow Differences#
Baremetal Application Flow#
In a baremetal application, the initialization flow is straightforward:
sl_main_init- System initialization:Memory management initialization
Chip initialization routine for revision errata workarounds. (
CHIP_Init)User pre-clock initialization (
app_init_pre_clock)Interrupt management system setup
Infrastructure initialization (oscillators, clocks, DCDC, powermanager, and Sleeptimer)
User early initialization (
app_init_early)System permanent memory allocations (platform components and stacks)
sl_main_second_stage_init()
sl_platform_init- Platform initializationsl_driver_init- Driver initializationsl_service_init- Service initializationsl_stack_init- Protocol stack initializationsl_internal_app_init- Internal application initialization
app_init() - Application-specific initialization
Main loop
sl_main_process_action- System component processingapp_process_action- Application processingPower management / sleep
RTOS Application Flow#
In an RTOS application, the initialization flow is split between pre-RTOS and post-RTOS contexts:
sl_main_init- Pre-kernel initialization:Memory management initialization
Chip initialization routine for revision errata workarounds.
CHIP_InitUser pre-clock initialization (
app_init_pre_clock)Interrupt management system setup
Infrastructure initialization (oscillators, clocks, DCDC, powermanager and Sleeptimer)
User early initialization (
app_init_early)System permanent memory allocations (platform components and stacks)
RTOS kernel structures initialization (
osKernelInitialize)Start task creation with highest priority
RTOS kernel starts
The scheduler begins running the start task
In the start task context:
sl_main_second_stage_initsl_platform_init- Platform initializationsl_driver_init- Driver initializationsl_service_init- Service initializationsl_stack_init- Protocol stack initializationsl_internal_app_init- Internal application initialization
app_init- Application-specific initialization, including task creation
RTOS scheduler
Application tasks run according to their priorities and states
Start task either terminates or continues based on configuration
Migration Process#
Step 1: Save previous code and delete main.c file#
Before migrating, save your application code but prepare to replace the main.c file:
Save your main.c content for reference, especially:
Your
app_initimplementationYour
app_process_actionimplementationAny custom code in
main
Make a backup of any custom initialization you've implemented
Delete your main.c file so that it's replaced by the new template
Step 2: Add RTOS of choice and regenerate project#
Modify your project configuration to include an RTOS:
In Simplicity Studio, add either "FreeRTOS Kernel" or "Micrium OS Kernel" components to your project
If using the command line, modify your .slcp file to include the desired RTOS components
Regenerate your project:
In Simplicity Studio, clicking on the install button will trigger a project regeneration event
With the CLI, run
slc generateto regenerate project filesKeep in mind that a new main.c file will be added at the root of your project
Step 3: Update application code structure#
Implement or update your
app_initfunction:Write task creation code here
Create required RTOS primitives (semaphores, queues, etc.)
Initialize application-specific resources
void app_init(void) { // Initialize application resources initialize_app_resources(); // Create application tasks xTaskCreate(app_task_function, "App Task", APP_TASK_STACK_SIZE, NULL, APP_TASK_PRIORITY, NULL); // Create RTOS primitives app_semaphore = xSemaphoreCreateBinary(); app_queue = xQueueCreate(10, sizeof(message_t)); }Replace app_process_action() with task functions:
Move code from app_process_action() to appropriate task functions
Use RTOS primitives for synchronization and communication between tasks
void app_task_function(void *p_arg) { (void)p_arg; while (1) { // Code moved from app_process_action() process_application_logic(); // Use RTOS primitives for synchronization xSemaphoreTake(app_semaphore, portMAX_DELAY); // Yield or delay as appropriate vTaskDelay(pdMS_TO_TICKS(10)); } }Optionally implement
app_init_early,app_init_pre_clockorapp_permanent_memory_allocfor respectively pre-RTOS initialization, pre clock initialization and permanent memory allocation:void app_init_early(void) { // Pre-RTOS initialization if needed // This runs before the kernel is started } void app_init_pre_clock(void) { // Before essential infrastructure components are initialized // like (clock, oscillators, interrupt management, etc.). } void app_permanent_memory_alloc(void) { // Calls to memory manager permanent memory allocation. }
Step 4: Configure start task#
If you are in Simplicity Studio, please open the sl_main component. Otherwise you can configure the start task properties in sl_main_start_task_config.h:
Adjust the start task stack size if needed:
#define SL_MAIN_START_TASK_STACK_SIZE_BYTES 4096If you decide to keep the start task running, you may change its priority by setting the following configurations:
#define SL_MAIN_ENABLE_START_TASK_PRIORITY_CHANGE 1 #define SL_MAIN_START_TASK_PRIORITY osPriorityNormalIn the file of your choice:
Optionally, keep the start task running, implement
sl_main_start_task_should_continue:bool sl_main_start_task_should_continue(void) { return true; // Return true to keep the start task running }
Troubleshooting Common Issues#
Kernel Structure Initialization and Use#
Problem: RTOS structures (semaphores, mutexes, etc.) are used before being properly initialized.
Solution:
Ensure RTOS structures are created in
app_initor within tasks, not inapp_init_earlyDouble-check that any system component that requires RTOS primitives is initialized after the RTOS kernel starts
Review initialization order to ensure dependencies are satisfied
Start Task Priority and Configuration#
Problem: The start task priority causes issues with initialization sequence or other tasks preempt initialization.
Solution:
Remember that the start task runs at maximum priority (osPriorityRealtime7) during sl_main_init() and sl_main_second_stage_init() but not during app_init if you enabled
SL_MAIN_ENABLE_START_TASK_PRIORITY_CHANGE.If keeping the start task running (sl_main_start_task_should_continue() is redefined to return true), make sure SL_MAIN_ENABLE_START_TASK_PRIORITY_CHANGE is enabled and choose an appropriate priority in SL_MAIN_START_TASK_PRIORITY
If initialization is being preempted, check that other tasks aren't starting too early
Check for priority inversions if using RTOS mutexes
Memory Allocation Considerations#
Problem: Stack overflow on start task process.
Solution:
Adjust SL_MAIN_START_TASK_STACK_SIZE_BYTES if the default 4096 bytes is insufficient
Process Action Handling#
Problem: Functionality that worked in baremetal's process_action is not working in RTOS tasks.
Solution:
When migrating from baremetal, the
sl_main_process_actioncall in the super-loop must be replaced by appropriate task structuresCreate dedicated tasks for components that were previously serviced by process_action calls
Use RTOS primitives (semaphores, queues, notifications) to trigger processing instead of relying on the main loop
Ensure periodic processing is handled by task delays (vTaskDelay) rather than polling
Components that expected to be called regularly from the main loop may need redesigning to work in a task-based environment