FreeRTOS源码分析一:task创建(RISCV架构)


前言

这里有 FreeRTOS 的移植需求,故对 FreeRTOS 做个源码分析,着重结合 RISCV 架构实现做分析。

本篇主要分析任务创建和启动部分。对 RISCV 架构有简单了解即可。

另外,移植工作中我只需考虑单核,所以我们暂不考虑多核情况。


启动 Demo

官方示例中提供了可在 qemu 上运行的 Demo。按以下命令执行:

git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules
cd FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/ && make
qemu-system-riscv32 -machine virt -bios none -nographic -kernel build/RTOSDemo.axf 

正常运行得到结果:
在这里插入图片描述

主函数

主函数创建两个任务,然后开始调度。任务优先级分别为 2 和 1。

#define tskIDLE_PRIORITY  0

#define mainQUEUE_RECEIVE_TASK_PRIORITY    ( tskIDLE_PRIORITY + 2 )
#define mainQUEUE_SEND_TASK_PRIORITY       ( tskIDLE_PRIORITY + 1 )

int main_blinky( void )
{
    vSendString( "Hello FreeRTOS!" );

    /* Create the queue. */
    xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( uint32_t ) );

    if( xQueue != NULL )
    {
        /* Start the two tasks as described in the comments at the top of this
         * file. */
        xTaskCreate( prvQueueReceiveTask, "Rx", configMINIMAL_STACK_SIZE * 2U, NULL,
                     mainQUEUE_RECEIVE_TASK_PRIORITY, NULL );
        xTaskCreate( prvQueueSendTask, "Tx", configMINIMAL_STACK_SIZE * 2U, NULL,
                     mainQUEUE_SEND_TASK_PRIORITY, NULL );
    }

    vTaskStartScheduler();

    return 0;
}

这里我们不关注队列 Queue。只关注 task 相关内容。

创建任务

调用 prvCreateTask 创建任务所需结构体,由 prvCreateTask 填充结构体内容。另外把新的任务加入就绪队列。

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

        pxNewTCB = prvCreateTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask );

        if( pxNewTCB != NULL )
        {
            prvAddNewTaskToReadyList( pxNewTCB );
            xReturn = pdPASS;
        }
        else
        {
            xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
        }

        return xReturn;
    }

这里我们只需关注 prvCreateTask 和 prvAddNewTaskToReadyList 这两个函数即可。

prvCreateTask 创建任务

根据宏portSTACK_GROWTH 考虑栈增长方向申请堆栈和 TCB空间并调用 prvInitialiseNewTask 初始化任务

    static TCB_t * prvCreateTask( TaskFunction_t pxTaskCode,
                                  const char * const pcName,
                                  const configSTACK_DEPTH_TYPE uxStackDepth,
                                  void * const pvParameters,
                                  UBaseType_t uxPriority,
                                  TaskHandle_t * const pxCreatedTask )
    {
        TCB_t * pxNewTCB;

        /* If the stack grows down then allocate the stack then the TCB so the stack
         * does not grow into the TCB.  Likewise if the stack grows up then allocate
         * the TCB then the stack. */
        #if ( portSTACK_GROWTH > 0 )
        {
			// 不成立宏,不考虑
        }
        #else /* portSTACK_GROWTH */
        {
            StackType_t * pxStack;

            /* Allocate space for the stack used by the task being created. */
            /* MISRA Ref 11.5.1 [Malloc memory assignment] */
            /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
            /* coverity[misra_c_2012_rule_11_5_violation] */
            pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );

            if( pxStack != NULL )
            {
                /* Allocate space for the TCB. */
                /* MISRA Ref 11.5.1 [Malloc memory assignment] */
                /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */
                /* coverity[misra_c_2012_rule_11_5_violation] */
                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

                if( pxNewTCB != NULL )
                {
                    ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );

                    /* Store the stack location in the TCB. */
                    pxNewTCB->pxStack = pxStack;
                }
                else
                {
                    /* The stack cannot be used as the TCB was not created.  Free
                     * it again. */
                    vPortFreeStack( pxStack );
                }
            }
            else
            {
                pxNewTCB = NULL;
            }
        }
        #endif /* portSTACK_GROWTH */

        if( pxNewTCB != NULL )
        {
            prvInitialiseNewTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
        }

        return pxNewTCB;
    }

这个函数我们先简单考虑一个问题:

栈增长方向

portSTACK_GROWTH 控制堆栈向上还是向下增长,小于 0 向下增长。以下是增长模型:

“向下增长” = 地址数值变小 = 从高地址向低地址移动。先 alloc 堆栈后 alloc TCB 即可使堆栈位于低地址,TCB 位于高地址。这个后面讨论。

低地址  ┌─────────────┐ ← pxStack (栈内存)
        │             │
        │   栈空间    │   ▲ 栈向这个方向增长
        │             │   │
        └─────────────┘
        ┌─────────────┐ ← pxNewTCB (TCB内存)
        │   TCB结构   │
高地址  └─────────────┘

“向上增长” = 地址数值变大 = 从低地址向高地址移动

具体例子

假设内存地址范围是 0x1000 - 0x2000:

栈向下增长 (portSTACK_GROWTH = -1) - 当前配置
0x2000 ← 栈开始位置(栈基址)
  │
  ▼ 栈增长方向(地址减小)
0x1900 ← 当前栈顶
0x1800
0x1700
  ...
0x1000
栈向上增长 (portSTACK_GROWTH > 0)
0x2000
  ...
0x1700
0x1800
0x1900 ← 当前栈顶  
  ▲ 栈增长方向(地址增大)
  │
0x1000 ← 栈开始位置(栈基址)

prvInitialiseNewTask 真正初始化任务结构体的函数

初始化堆栈内容并设置 TCB 成员,调用 pxPortInitialiseStack 为堆栈栈底填入预设好的堆栈内容之后返回。

static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
                                  const char * const pcName,
                                  const configSTACK_DEPTH_TYPE uxStackDepth,
                                  void * const pvParameters,
                                  UBaseType_t uxPriority,
                                  TaskHandle_t * const pxCreatedTask,
                                  TCB_t * pxNewTCB,
                                  const MemoryRegion_t * const xRegions )
{
    StackType_t * pxTopOfStack;
    UBaseType_t x;

    /* 填充堆栈 */
    #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
    {
        /* 填充的是0xa5 */
        ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) uxStackDepth * sizeof( StackType_t ) );
    }
    #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */

    // 栈向下增长的处理
    #if ( portSTACK_GROWTH < 0 )
    {
    	// 计算栈顶地址,栈顶 = 栈基址 + (栈深度-1)
        pxTopOfStack = &( pxNewTCB->pxStack[ uxStackDepth - ( configSTACK_DEPTH_TYPE ) 1 ] );
        pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );

        /* Check the alignment of the calculated top of stack is correct. */
        configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0U ) );
    }
    
    /* 设置 TCB 的名字 */
    if( pcName != NULL )
    {
        for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
        {
            pxNewTCB->pcTaskName[ x ] = pcName[ x ];

            /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than
             * configMAX_TASK_NAME_LEN characters just in case the memory after the
             * string is not accessible (extremely unlikely). */
            if( pcName[ x ] == ( char ) 0x00 )
            {
                break;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }

        /* Ensure the name string is terminated in the case that the string length
         * was greater or equal to configMAX_TASK_NAME_LEN. */
        pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1U ] = '\0';
    }

    /* 设置任务优先级 */
    configASSERT( uxPriority < configMAX_PRIORITIES );

    if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
    {
        uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    pxNewTCB->uxPriority = uxPriority;
    #if ( configUSE_MUTEXES == 1 )
    {
        pxNewTCB->uxBasePriority = uxPriority;
    }
    #endif /* configUSE_MUTEXES */

	// 初始化任务成员
    vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
    vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

    /* Set the pxNewTCB as a link back from the ListItem_t.  This is so we can get
     * back to  the containing TCB from a generic item in a list. */
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );

    /* Event lists are always in priority order. */
    listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
    listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );


    /* Initialize the TCB stack to look as if the task was already running,
     * but had been interrupted by the scheduler.  The return address is set
     * to the start of the task function. Once the stack has been initialised
     * the top of stack variable is updated. */
    #if ( portUSING_MPU_WRAPPERS == 1 )
    {
		// 不成立宏,不考虑
    }
    #else /* portUSING_MPU_WRAPPERS */
    {
        /* If the port has capability to detect stack overflow,
         * pass the stack end address to the stack initialization
         * function as well. */
        #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
        {
			// 不成立宏,不考虑
        }
        #else /* portHAS_STACK_OVERFLOW_CHECKING */
        {
            pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
        }
        #endif /* portHAS_STACK_OVERFLOW_CHECKING */
    }
    #endif /* portUSING_MPU_WRAPPERS */

    /* Initialize task state and task attributes. */
    #if ( configNUMBER_OF_CORES > 1 )
    {
        // 不成立宏,不考虑
    }
    #endif /* #if ( configNUMBER_OF_CORES > 1 ) */

    if( pxCreatedTask != NULL )
    {
        // 设置用户传入的指针参数,指向 TCB 句柄
        *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
    }
}

这里函数非常简单,设置了 TCB 的堆栈填充了内容,设置了 TCB 的成员,初始化一系列必要内容。最后调用函数 pxPortInitialiseStack 设置了堆栈的内容之后,设置用户句柄参数就好了。

pxPortInitialiseStack 堆栈初始化函数

函数按照约定设置堆栈从栈底到增长方向的内容之后返回

/*
 * StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters );
 *
 * 按照标准 RISC-V ABI:
 * - pxTopOfStack 通过 a0 寄存器传入(栈顶指针)
 * - pxCode 通过 a1 寄存器传入(任务函数指针)
 * - pvParameters 通过 a2 寄存器传入(任务参数指针)
 * - 新的栈顶指针通过 a0 寄存器返回
 *
 * FreeRTOS 任务的 RISC-V 上下文按以下栈帧结构保存:
 * (全局指针和线程指针假定为常量,因此不保存)
 *
 * 栈结构(从高地址到低地址):
 * xCriticalNesting     - 临界区嵌套计数
 * x31                  - 通用寄存器 x31
 * x30                  - 通用寄存器 x30
 * x29                  - 通用寄存器 x29
 * x28                  - 通用寄存器 x28
 * x27                  - 通用寄存器 x27
 * x26                  - 通用寄存器 x26
 * x25                  - 通用寄存器 x25
 * x24                  - 通用寄存器 x24
 * x23                  - 通用寄存器 x23
 * x22                  - 通用寄存器 x22
 * x21                  - 通用寄存器 x21
 * x20                  - 通用寄存器 x20
 * x19                  - 通用寄存器 x19
 * x18                  - 通用寄存器 x18
 * x17                  - 通用寄存器 x17
 * x16                  - 通用寄存器 x16
 * x15                  - 通用寄存器 x15
 * x14                  - 通用寄存器 x14
 * x13                  - 通用寄存器 x13
 * x12                  - 通用寄存器 x12
 * x11                  - 通用寄存器 x11
 * pvParameters         - 任务参数(存储在 x10/a0 位置)
 * x9                   - 通用寄存器 x9
 * x8                   - 通用寄存器 x8
 * x7                   - 通用寄存器 x7
 * x6                   - 通用寄存器 x6
 * x5                   - 通用寄存器 x5
 * portTASK_RETURN_ADDRESS - 任务返回地址(存储在 x1/ra 位置)
 * [FPU registers]      - 浮点寄存器(如果启用/可用)
 * [VPU registers]      - 向量寄存器(如果启用/可用)
 * [chip specific registers] - 芯片特定寄存器
 * mstatus              - 机器状态寄存器
 * pxCode               - 任务函数入口地址
 */

pxPortInitialiseStack:
    /* === 第一部分:为临界区嵌套计数预留空间 === */
    addi a0, a0, -portWORD_SIZE         /* 栈指针下移一个字长,为临界区嵌套计数预留空间 */
    store_x x0, 0(a0)                   /* 将临界区嵌套计数初始化为0(每个任务开始时都是0) */

    /* === 第二部分:为通用寄存器 x10-x31 预留空间 === */
#ifdef __riscv_32e
    addi a0, a0, -(6 * portWORD_SIZE)   /* RISC-V 32E 变体:为寄存器 x10-x15 预留空间(6个寄存器) */
#else
    addi a0, a0, -(22 * portWORD_SIZE)  /* RISC-V 标准变体:为寄存器 x10-x31 预留空间(22个寄存器) */
#endif
    store_x a2, 0(a0)                   /* 将任务参数 pvParameters(在a2中)存储到栈上x10/a0的位置,任务启动时将作为第一个参数 */

    /* === 第三部分:为寄存器 x5-x9 和返回地址预留空间 === */
    addi a0, a0, -(6 * portWORD_SIZE)   /* 栈指针下移6个字长,为寄存器 x5-x9 + 任务返回地址(x1/ra)预留空间 */
    load_x t0, xTaskReturnAddress       /* 从 xTaskReturnAddress 全局变量加载任务结束时的返回地址到临时寄存器 t0 */
    store_x t0, 0(a0)                   /* 将任务返回地址存储到栈上 x1/ra 的位置,任务结束时会跳转到此地址 */

    /* === 第四部分:处理芯片特定的附加寄存器 === */
    addi t0, x0, portasmADDITIONAL_CONTEXT_SIZE /* 将芯片特定附加寄存器的数量加载到 t0(用作循环计数器) */
chip_specific_stack_frame:              /* 循环标签:为芯片特定寄存器创建栈帧空间 */
    beq t0, x0, 1f                      /* 如果计数器为0,跳转到标签1(没有更多芯片特定寄存器需要保存) */
    addi a0, a0, -portWORD_SIZE         /* 栈指针下移一个字长,为芯片特定寄存器预留空间 */
    store_x x0, 0(a0)                   /* 将芯片特定寄存器初始化为0值 */
    addi t0, t0, -1                     /* 递减剩余芯片特定寄存器计数器 */
    j chip_specific_stack_frame         /* 跳转回循环开始,继续处理下一个芯片特定寄存器 */
1:                                      /* 循环结束标签:所有芯片特定寄存器处理完毕 */

    /* === 第五部分:配置机器状态寄存器 mstatus === */
    csrr t0, mstatus                    /* 读取当前的机器状态寄存器 mstatus 值到 t0 */
    andi t0, t0, ~0x8                   /* 清除 MIE 位(位3),确保在 ISR 中恢复栈时中断被禁用。当调度器启动后创建任务时需要此操作,否则中断本来就是禁用的 */
    addi t1, x0, 0x188                  /* 生成值 0x188,准备设置 MPIE=1(位7)和 MPP=机器模式(位11-12)*/
    slli t1, t1, 4                      /* 将 0x188 左移4位得到 0x1880,对应 mstatus 寄存器中的正确位位置 */
    or t0, t0, t1                       /* 在 mstatus 值中设置 MPIE 和 MPP 位,配置任务执行时的中断和特权模式状态 */

    /* === 第六部分:浮点单元 FPU 状态配置(条件编译) === */
#if( configENABLE_FPU == 1 )
    /* 在 mstatus 值中将 FPU 标记为 clean 状态 */
    li t1, ~MSTATUS_FS_MASK             /* 加载 FS(浮点状态)字段掩码的反值到 t1 */
    and t0, t0, t1                      /* 清除 mstatus 中的 FS 字段位 */
    li t1, MSTATUS_FS_CLEAN             /* 加载 FS_CLEAN 状态值到 t1 */
    or t0, t0, t1                       /* 设置 FS 字段为 CLEAN 状态,表示 FPU 寄存器是干净的(无需保存/恢复) */
#endif

    /* === 第七部分:向量处理单元 VPU 状态配置(条件编译) === */
#if( configENABLE_VPU == 1 )
    /* 在 mstatus 值中将 VPU 标记为 clean 状态 */
    li t1, ~MSTATUS_VS_MASK             /* 加载 VS(向量状态)字段掩码的反值到 t1 */
    and t0, t0, t1                      /* 清除 mstatus 中的 VS 字段位 */
    li t1, MSTATUS_VS_CLEAN             /* 加载 VS_CLEAN 状态值到 t1 */
    or t0, t0, t1                       /* 设置 VS 字段为 CLEAN 状态,表示 VPU 寄存器是干净的(无需保存/恢复) */
#endif

    /* === 第八部分:存储配置好的 mstatus 寄存器 === */
    addi a0, a0, -portWORD_SIZE         /* 栈指针下移一个字长,为 mstatus 寄存器预留空间 */
    store_x t0, 0(a0)                   /* 将配置好的 mstatus 值存储到栈上,任务切换时会恢复此状态 */

    /* === 第九部分:存储任务函数入口地址 === */
    addi a0, a0, -portWORD_SIZE         /* 栈指针下移一个字长,为任务函数地址预留空间 */
    store_x a1, 0(a0)                   /* 将任务函数地址 pxCode(在a1中)存储到栈上,作为 mret 指令的跳转目标 */
    
    /* === 函数返回 === */
    ret                                 /* 返回新的栈顶指针(在 a0 中),此时栈已完全初始化好,可用于任务上下文切换 */

prvAddNewTaskToReadyList 将新任务加入系统就绪列表

更新全局变量 pxCurrentTCB 的值并把任务加入优先级对应的队列,方便后续调度任务指行。pxCurrentTCB 指向优先级最高的任务。

static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
{
    /* 确保在更新任务列表时中断不会访问任务列表,禁用中断 */
    taskENTER_CRITICAL();
    {
        /* 增加当前任务数量计数器 */
        uxCurrentNumberOfTasks = ( UBaseType_t ) ( uxCurrentNumberOfTasks + 1U );
        
        /* 检查当前是否有正在运行的任务 */
        if( pxCurrentTCB == NULL )
        {
            /* 没有其他任务,或者所有其他任务都处于挂起状态 - 
             * 将这个新任务设为当前任务 */
            pxCurrentTCB = pxNewTCB;
            
            /* 检查这是否是系统中的第一个任务 */
            if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
            {
                /* 这是第一个被创建的任务,所以需要进行初步的
                 * 初始化工作。如果此调用失败,我们无法恢复,
                 * 但会报告失败 */
                prvInitialiseTaskLists();
            }
        }
        else
        {
            /* 如果调度器还没有开始运行,在到目前为止创建的任务中,
             * 如果这个新任务是最高优先级的,就将其设为当前任务 */
            if( xSchedulerRunning == pdFALSE )
            {
                /* 比较优先级,数值越大优先级越高 */
                if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
                {
                    /* 新任务优先级更高或相等,更新当前任务指针 */
                    pxCurrentTCB = pxNewTCB;
                }
            }
        }
        
        /* 增加任务编号计数器,用于给每个任务分配唯一编号 */
        uxTaskNumber++;
        
        #if ( configUSE_TRACE_FACILITY == 1 )
        {
            /* 如果启用了跟踪功能,为TCB添加一个计数器,仅用于跟踪 */
            pxNewTCB->uxTCBNumber = uxTaskNumber;
        }
        #endif /* configUSE_TRACE_FACILITY */
        
        /* 将新任务添加到相应优先级的就绪列表中 */
        prvAddTaskToReadyList( pxNewTCB );
        
        /* 执行特定于移植层的TCB设置 */
        portSETUP_TCB( pxNewTCB );
    }
    /* 退出临界区 */
    taskEXIT_CRITICAL();
    
    /* 检查调度器是否正在运行 */
    if( xSchedulerRunning != pdFALSE )
    {
        /* 如果创建的任务优先级高于当前任务,
         * 那么它应该立即运行(抢占式调度) */
        taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxNewTCB );
    }
}

函数作用总结及调用链关系

1. main_blinky
  • 主函数,作为程序入口,负责初始化系统(如打印信息、创建队列)。
  • 核心动作:调用xTaskCreate创建两个不同优先级的任务,最后启动任务调度器(vTaskStartScheduler)。
2. xTaskCreate
  • 任务创建的对外接口函数,负责协调任务创建的全过程。
  • 核心动作:
    • 调用prvCreateTask分配任务栈(StackType_t)和任务控制块(TCB_t)并初始化。
    • prvCreateTask成功,调用prvAddNewTaskToReadyList将新任务加入就绪列表。
    • 返回创建结果(成功/内存分配失败)。
3. prvCreateTask
  • 负责为任务分配内存(栈和TCB),并触发任务初始化。
  • 核心动作:
    • 根据栈增长方向(此处为向下增长,portSTACK_GROWTH < 0),先分配栈空间,再分配TCB空间(避免栈增长覆盖TCB)。
    • 若内存分配成功,调用prvInitialiseNewTask初始化任务(TCB和栈)。
    • 返回初始化后的TCB_t指针(或NULL,表示失败)。
4. prvInitialiseNewTask
  • 真正初始化任务控制块(TCB_t)的核心函数。
  • 核心动作:
    • 设置TCB成员(如任务名pcTaskName、优先级uxPriority、栈基址pxStack)。
    • 初始化任务状态列表项(xStateListItem)和事件列表项(xEventListItem)。
    • 调用pxPortInitialiseStack初始化栈内容,获取栈顶指针并存入TCB(pxTopOfStack)。
    • 设置任务句柄(pxCreatedTask),指向当前TCB。
5. pxPortInitialiseStack
  • 负责按照RISC-V架构的ABI规范初始化任务栈内容,模拟任务“被中断后保存的上下文”,确保任务首次调度时能正确启动。
  • 核心动作:
    • 预留临界区嵌套计数空间,初始化通用寄存器(x10-x31等)的栈帧。
    • 设置任务返回地址(portTASK_RETURN_ADDRESS)、机器状态寄存器(mstatus,配置特权模式和中断状态)。
    • 存储任务函数入口地址(pxTaskCode),作为任务首次执行的起始点。
    • 返回初始化后的栈顶指针,供TCB的pxTopOfStack使用。
6. prvAddNewTaskToReadyList
  • xTaskCreate在任务创建成功后调用,负责将新任务加入系统就绪列表。
  • 核心动作:根据任务优先级,将任务的xStateListItem插入对应优先级的就绪列表,使任务成为可被调度的状态。

调用链箭头图

main_blinky 
    ↓(创建任务)
xTaskCreate 
    ├─→ prvCreateTask (分配栈和TCB,触发初始化)
    │     ↓
    │   prvInitialiseNewTask (初始化TCB成员)
    │     ↓
    │   pxPortInitialiseStack (初始化栈内容)
    │
    └─→ prvAddNewTaskToReadyList (将任务加入就绪列表)

进入临界区时,会禁用中断并做计数。具体 RISCV 实现如下所示:

// 禁用中断宏,数值8对应二进制1000,即第3 bit 位
// 使用 RISC-V 汇编指令 csrc (Clear bits in CSR) 清除 mstatus 寄存器的第3位 (MIE位)
// MIE (Machine Interrupt Enable) 位控制机器模式下的中断使能
// 当 MIE=0 时,禁用所有机器模式中断
#define portDISABLE_INTERRUPTS()                                   __asm volatile ( "csrc mstatus, 8" )
#define portENABLE_INTERRUPTS()                                    __asm volatile ( "csrs mstatus, 8" )

extern size_t xCriticalNesting;
#define portENTER_CRITICAL()      \
    {                             \
        portDISABLE_INTERRUPTS(); \
        xCriticalNesting++;       \
    }

#define portEXIT_CRITICAL()          \
    {                                \
        xCriticalNesting--;          \
        if( xCriticalNesting == 0 )  \
        {                            \
            portENABLE_INTERRUPTS(); \
        }                            \
    }

总结

完结撒花!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值