In-Depth Analysis of FreeRTOS Kernel Source Code: From Task Scheduling to Interrupt Handling

In-Depth Analysis of FreeRTOS Kernel Source Code: From Task Scheduling to Interrupt Handling

Introduction

As a representative of embedded real-time operating systems, FreeRTOS has an elegant and efficient kernel implementation. This article will delve into the key implementations of the FreeRTOS kernel, including task scheduling, memory management, interrupt handling, and other core mechanisms.

In-Depth Analysis of FreeRTOS Kernel Source Code: From Task Scheduling to Interrupt Handling

Task Management Implementation

Task Control Block (TCB) Structure

typedef struct tskTaskControlBlock {
    volatile StackType_t *pxTopOfStack;  // Stack top pointer
    ListItem_t xStateListItem;           // State list item
    ListItem_t xEventListItem;           // Event list item
    UBaseType_t uxPriority;              // Task priority
    StackType_t *pxStack;                // Stack starting address
    char pcTaskName[configMAX_TASK_NAME_LEN];  // Task name
    
    #if ( portSTACK_GROWTH > 0 )
    StackType_t *pxEndOfStack;           // Stack end pointer
    #endif
    
    #if ( configUSE_MUTEXES == 1 )
    UBaseType_t uxBasePriority;          // Base priority (for priority inheritance)
    #endif
} tskTCB;

typedef tskTCB TCB_t;

Task Creation Process

BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,
                      const char * const pcName,
                      const configSTACK_DEPTH_TYPE usStackDepth,
                      void * const pvParameters,
                      UBaseType_t uxPriority,
                      TaskHandle_t * const pxCreatedTask)
{
    TCB_t *pxNewTCB;
    BaseType_t xReturn;

    // Allocate TCB memory
    pxNewTCB = (TCB_t *)pvPortMalloc(sizeof(TCB_t));
    
    if (pxNewTCB != NULL) {
        StackType_t *pxStack = NULL;
        
        // Allocate task stack
        pxStack = (StackType_t *)pvPortMalloc(usStackDepth * sizeof(StackType_t));
        
        if (pxStack != NULL) {
            // Initialize stack
            memset(pxStack, 0xa5U, usStackDepth * sizeof(StackType_t));
            
            // Initialize TCB
            pxNewTCB->pxStack = pxStack;
            
            // Initialize task stack frame
            pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxStack + usStackDepth - 1,
                                                          pxTaskCode,
                                                          pvParameters);
                                                          
            // Set task attributes
            prvInitialiseTCBVariables(pxNewTCB,
                                    pcName,
                                    uxPriority);
                                    
            // Add task to ready list
            prvAddTaskToReadyList(pxNewTCB);
            
            xReturn = pdPASS;
        }
    }
    
    return xReturn;
}

Scheduler Implementation

Scheduler Initialization

void vTaskStartScheduler(void)
{
    // Create idle task
    xIdleTaskHandle = xTaskCreateStatic(prvIdleTask,
                                      "IDLE",
                                      configMINIMAL_STACK_SIZE,
                                      NULL,
                                      portPRIVILEGE_BIT,
                                      xIdleTaskStack,
                                      &xIdleTaskTCB);
                                      
    #if (configUSE_TIMERS == 1)
    // Create timer service task
    prvInitialiseTimerTaskAndQueue();
    #endif
    
    // Set system clock interrupt
    xPortStartScheduler();
    
    // Start the first task
    prvStartFirstTask();
}

Core Implementation of Context Switching

void vTaskSwitchContext(void)
{
    // Get the highest priority ready task
    UBaseType_t uxTopPriority = uxTopReadyPriority;
    
    // Find the highest priority ready list
    while (listLIST_IS_EMPTY(&(pxReadyTasksLists[uxTopPriority]))) {
        --uxTopPriority;
    }
    
    // Get the next task to run
    List_t * const pxList = &(pxReadyTasksLists[uxTopPriority]);
    ListItem_t * const pxListItem = listGET_OWNER_OF_NEXT_ENTRY(pxList);
    TCB_t *pxTCB = listGET_LIST_ITEM_OWNER(pxListItem);
    
    // Update current task pointer
    pxCurrentTCB = pxTCB;
}

Memory Management Implementation

Heap Memory Manager

// Memory block structure
typedefstruct A_BLOCK_LINK {
    struct A_BLOCK_LINK *pxNextFreeBlock;// Next free block
    size_t xBlockSize;                     // Block size
} BlockLink_t;

// Memory allocation implementation
void *pvPortMalloc(size_t xWantedSize)
{
    BlockLink_t *pxBlock;
    void *pvReturn = NULL;
    
    vTaskSuspendAll();
    {
        // Align size
        if (xWantedSize > 0) {
            xWantedSize += heapSTRUCT_SIZE;
            if ((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0) {
                xWantedSize += (portBYTE_ALIGNMENT - 
                              (xWantedSize & portBYTE_ALIGNMENT_MASK));
            }
        }
        
        // Find suitable memory block
        if (xWantedSize <= xFreeBytesRemaining) {
            pxBlock = pxFirstFreeBlock;
            
            while ((pxBlock->xBlockSize < xWantedSize) &amp;&amp;
                   (pxBlock->pxNextFreeBlock != NULL)) {
                pxBlock = pxBlock->pxNextFreeBlock;
            }
            
            if (pxBlock != NULL) {
                // Allocate memory block
                pvReturn = (void *)(((uint8_t *)pxBlock) + heapSTRUCT_SIZE);
                
                // Update free list
                prvRemoveBlockFromFreeList(pxBlock);
            }
        }
    }
    xTaskResumeAll();
    
    return pvReturn;
}

Interrupt Management

Interrupt Nesting Count

// Interrupt nesting counter
static UBaseType_t uxInterruptNesting = 0;

// Enter interrupt
void vPortEnterCritical(void)
{
    portDISABLE_INTERRUPTS();
    uxInterruptNesting++;
    
    if (uxInterruptNesting == 1) {
        // First level interrupt
        xSchedulerRunning = pdFALSE;
    }
}

// Exit interrupt
void vPortExitCritical(void)
{
    if (uxInterruptNesting > 0) {
        uxInterruptNesting--;
        if (uxInterruptNesting == 0) {
            // Last level interrupt
            xSchedulerRunning = pdTRUE;
        }
    }
    
    if (uxInterruptNesting == 0) {
        portENABLE_INTERRUPTS();
    }
}

Deferred Interrupt Handling

typedef struct xPENDING_INTERRUPT {
    PendedFunction_t pxFunction;  // Function to be executed later
    void *pvParameter1;           // Parameter 1
    uint32_t ulParameter2;        // Parameter 2
} PendingInterrupt_t;

static PendingInterrupt_t xPendingInterrupts[configMAX_PENDING_INTERRUPTS];

// Register deferred interrupt
BaseType_t xTimerPendFunctionCallFromISR(PendedFunction_t xFunctionToPend,
                                        void *pvParameter1,
                                        uint32_t ulParameter2,
                                        BaseType_t *pxHigherPriorityTaskWoken)
{
    BaseType_t xReturn = pdFAIL;
    
    if (uxPendedInterrupts < configMAX_PENDING_INTERRUPTS) {
        // Save interrupt information
        xPendingInterrupts[uxPendedInterrupts].pxFunction = xFunctionToPend;
        xPendingInterrupts[uxPendedInterrupts].pvParameter1 = pvParameter1;
        xPendingInterrupts[uxPendedInterrupts].ulParameter2 = ulParameter2;
        
        uxPendedInterrupts++;
        xReturn = pdPASS;
        
        // Notify timer task
        vTaskNotifyGiveFromISR(xTimerTaskHandle, pxHigherPriorityTaskWoken);
    }
    
    return xReturn;
}

Synchronization Mechanism Implementation

Core Implementation of Semaphores

typedef struct xSEMAPHORE {
    QueueHandle_t xQueue;           // Queue-based implementation
} Semaphore_t;

// Create semaphore
SemaphoreHandle_t xSemaphoreCreateBinary(void)
{
    Semaphore_t *pxSemaphore;
    
    // Create a queue of length 1
    pxSemaphore = (Semaphore_t *)xQueueCreate(1, semSEMAPHORE_QUEUE_ITEM_LENGTH);
    
    if (pxSemaphore != NULL) {
        // Initialize as empty semaphore
        pxSemaphore->xQueue = xQueueCreate(1, 0);
        if (pxSemaphore->xQueue == NULL) {
            vPortFree(pxSemaphore);
            pxSemaphore = NULL;
        }
    }
    
    return (SemaphoreHandle_t)pxSemaphore;
}

// Take semaphore
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
                         TickType_t xBlockTime)
{
    Semaphore_t *pxSemaphore = (Semaphore_t *)xSemaphore;
    BaseType_t xReturn;
    
    // Receive data from queue
    xReturn = xQueueReceive(pxSemaphore->xQueue,
                           NULL,
                           xBlockTime);
                           
    return xReturn;
}

Timer Services

Software Timer Implementation

typedef struct tmrTimerControl {
    constchar *pcTimerName;        // Timer name
    ListItem_t xTimerListItem;      // Timer list item
    TickType_t xTimerPeriodInTicks; // Timer period
    void *pvTimerID;                // Timer ID
    TimerCallbackFunction_t pxCallbackFunction; // Callback function
    UBaseType_t uxAutoReload;       // Auto-reload flag
} Timer_t;

// Create timer
TimerHandle_t xTimerCreate(const char * const pcTimerName,
                          const TickType_t xTimerPeriodInTicks,
                          const UBaseType_t uxAutoReload,
                          void * const pvTimerID,
                          TimerCallbackFunction_t pxCallbackFunction)
{
    Timer_t *pxNewTimer;
    
    // Allocate timer structure
    pxNewTimer = (Timer_t *)pvPortMalloc(sizeof(Timer_t));
    
    if (pxNewTimer != NULL) {
        // Initialize timer attributes
        pxNewTimer->pcTimerName = pcTimerName;
        pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;
        pxNewTimer->pvTimerID = pvTimerID;
        pxNewTimer->pxCallbackFunction = pxCallbackFunction;
        pxNewTimer->uxAutoReload = uxAutoReload;
        
        vListInitialiseItem(&amp;(pxNewTimer->xTimerListItem));
        listSET_LIST_ITEM_OWNER(&amp;(pxNewTimer->xTimerListItem),
                               (void *)pxNewTimer);
    }
    
    return (TimerHandle_t)pxNewTimer;
}

Conclusion

Key features of the FreeRTOS kernel implementation:

  1. Concise and efficient code

  • Minimized critical path
  • Optimized memory access
  • Reduced context switch overhead
  • Portability design

    • Isolation of hardware-related code
    • Unified portability interface
    • Conditional compilation support
  • Strong configurability

    • Rich configuration options
    • Modular design
    • Flexible feature trimming
  • Key point optimization

    • Interrupt latency handling
    • Priority inheritance
    • Memory management strategies

    It is recommended to study the following aspects in depth:

    • Scheduling algorithm implementation
    • Interrupt handling mechanisms
    • Synchronization primitive implementation
    • Memory management strategies
    • Time management services

    By studying the source code, deepen your understanding of core RTOS concepts.

    Leave a Comment