Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

FreeRTOS Task Switching, State Transitions, and Interrupt Handling on STM32

Tech 1

The Role of FreeRTOS

Diagram 1 Diagram 2

Diagram 3

Diagram 4

Diagram 5

On an STM32 running FreeRTOS, the flow from reset to task startup and multitasking operation is as follows: Flow Diagram

  1. Reset → ② → ③: Start the first task by calling vTaskStartScheduler(), which triggers an SVC interrupt to launch the first task.
  2. ④ → ③: Multitask switching occurs via the SysTick timer interrupt, which then generates a PendSV interrupt. Alternatively, a regular interrupt might occur while a task is running.
  3. ② → ⑤: When running bare-metal, a regular interrupt can occur.

Processor Operating Modes and Privilege Levels

ARM SP, LR, PC registers, along with all other registers and processor operating modes are discussed. The difference between privileged and unprivileged modes lies in which registers can be accessed or modified. For Cortex-M3/M4 cores, detailed explanation of privileged vs. unprivileged modes: Privilege Levels

Cortex-M3 Dual Stack: MSP and PSP

Cortex-M3 uses Main Stack Pointer (MSP) and Process Stack Pointer (PSP). When does the M3 core use MSP vs. PSP?

Privileged mode can use both MSP and PSP. Unprivileged mode can use PSP; it's unclear if MSP is accessible. In exception handlers, only MSP is used.

Dual Stack

What is the Difference Between Regular Interrupts and SVC/PendSV Interrupts?

Regular interrupts automatically push certain CPU registers onto the stack: xPSR, PC (task entry address), R14 (LR), R12, R3, R2, R1, R0 (task parameter). On return from interrupt, the CPU automatically pops these values. Interrupt Stacking

When an interrupt occurs in an STM32, the processor automatically pushes the following registers onto the stack to save the current context:

  1. Program Counter (PC): Holds the address of the next instruction.
  2. xPSR (Program Status Register): Contains status flags and interrupt state.
  3. Link Register (LR): Holds the return address.
  4. R0-R3: General-purpose registers used for parameter passing and return values.
  5. R12: Another general-purpose register.

The stack frame structure is:

+-----------------+
| xPSR            |
+-----------------+
| PC (Return Addr)|
+-----------------+
| LR              |
+-----------------+
| R12             |
+-----------------+
| R3              |
+-----------------+
| R2              |
+-----------------+
| R1              |
+-----------------+
| R0              |
+-----------------+

These values are saved before the ISR starts. After the ISR completes, the processor restores them. If the ISR uses registers R4-R11, they must be manually saved and restored at the start and end of the ISR.

However, SVC and PendSV interrupts require manual saving of R4-R11 registers inside the interrupt handler.

Why Don't Regular Interrupts Need to Save R4-R11?

Regular interrupt functions typically use few local variables, so they seldom require R4-R11. In multitasking, however, more data is processed, leading to more local variables and function calls.

Thread Mode Privileged ↔ Handler Mode Privileged (bare-metal interrupts): Only processor mode changes occur at the start and end of exception handling: Bare-metal Interrupt

In RTOS multitasking switching: Thread Mode Unprivileged ↔ Handler Mode Privileged (PendSV interrupt for context switching): When CONTROL[0]=1 (unprivileged thread mode), both processor mode and privilege level change at exception entry and exit: RTOS Interrupt

In RTOS development with an MMU, thread mode unprivileged ↔ thread mode privileged resembles Linux user mode to kernel mode. Thread Mode Unprivileged ↔ Thread Mode Privileged & Handler Mode Privileged: Mode Switching

After chip reset, the system enters thread mode privileged. Switching betweeen thread mode privileged and handler mode occurs via exception/interrupt entry and exit. Privileged code can set CONTROL[0] to enter unprivileged mode. Handler mode is always privileged. Any exception runs at privileged level; after return, the system reverts to the privilege level that existed when the exception occurred. Handler mode is entered via exception/interrupt and exits back to thread mode. On exception return, you can set CONTROL[0] (unprivileged thread mode) or clear it (privileged thread mode) to change the returning thread mode's privilege level. Unprivileged thread mode code cannot modify CONTROL[0] to return to privileged mode; it must trigger an exception to handler mode, where the handler clears CONTROL[0], so that upon return, the thread mode becomes privileged.

During exception return, besides clearing CONTROL[0], you can also modify the LR register (cannot modify CONTROL[1] in an interrupt handler) so that the returned thread mode is privileged and uses PSP.

Exception Return

xTaskCreate()

xTaskCreate Flow

  1. prvInitialiseNewTask() prvInitialiseNewTask
  2. prvAddNewTaskToReadyList()
    • Increments global task counter uxCurrentNumberOfTasks.
    • If pxCurrentTCB is NULL, point it to the newly created task.
    • If it's the first task, call prvInitialiseTaskLists() to initialize task-related lists (currently only ready list).
/* Initialize task-related lists */
void prvInitialiseTaskLists( void )
{
    UBaseType_t uxPriority;
    for ( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
    {
        vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
    }
}

vListInitialise() sets pxList->xListEnd.xItemValue = portMAX_DELAY.

List Initialization

Lists and List Items

The list header xLIST is a global variable (e.g., ready list pxReadyTasksLists). List items are xLIST_ITEM members of TCB_t allocated in heap.

When static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) calls listINSERT_END( pxList, pxNewListItem ), it inserts the xLIST_ITEM from the heap-allocated TCB_t into the xLIST (ready list).

List Insertion

xListEnd.xItemValue is initialized during list initialization: void vListInitialise( List_t * const pxList ). ListItem2.xItemValue is set in the delay function prvAddCurrentTaskToDelayedList() via listSET_LIST_ITEM_VALUE.

vListInsertEnd() insertion:

  • pxIndex points to the list header. The list is a circular doubly-linked list; inserting at the end places the new item after pxIndex.

vListInsertEnd vListInsertEnd detail

vListInsert() insertion:

  • Sorts by xItemValue and inserts accordingly.

vListInsert vListInsert detail

For further analysis, refer to: "From Single-Chip to Operating System ⑤—FreeRTOS List & List Item Source Code Interpretation"

Priority Bitmap uxTopReadyPriority Supports multiple priorities.

FreeRTOS Task Scheduler

1. Starting the Task Scheduler Initialization and Launching the First Task

For detailed code analysis of each function, refer to: "09_FreeRTOS Task Scheduler"

[FreeRTOS] 2. SVC System Call

vTaskStartScheduler() This function internally implements the following steps:

  1. Create idle task.
  2. If software timers are enabled, create timer task.
  3. Disable interrupts to prevent interference before or during scheduler start; interrupts will be re-enabled when the first task runs.
  4. Initialize global variables and set scheduler running flag.
  5. Initialize the time base timer for task runtime statistics.
  6. Call xPortStartScheduler().

xPortStartScheduler() This function handles hardware-specific configuraton for starting the scheduler and launching the first task.

Internal implementation:

  1. Check interrupt configuration in FreeRTOSConfig.h.
  2. Set PendSV and SysTick interrupt priorities to the lowest.
  3. Call vPortSetupTimerInterrupt() to configure SysTick.
  4. Initialize critical section nesting counter to 0.
  5. Call prvEnableVFP() to enable FPU (M3 lacks FPU), and then prvStartFirstTask() to launch the first task.

void prvStartFirstTask() This function launches the first task.

Used for ...

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.