2013年12月27日 星期五

How to do thread context switch in RTOS

git clone --recursive  https://github.com/FreeRTOS/FreeRTOS

//vector table for a Cortex M3 interrupt
~/FreeRTOS/Demo/CORTEX_STM32F103_Keil/STM32F10x.s
//found
; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
__Vectors       DCD     __initial_sp              ; Top of Stack
                DCD     Reset_Handler             ; Reset Handler
                DCD     NMI_Handler               ; NMI Handler
                DCD     HardFault_Handler         ; Hard Fault Handler
                DCD     MemManage_Handler         ; MPU Fault Handler
                DCD     BusFault_Handler          ; Bus Fault Handler
                DCD     UsageFault_Handler        ; Usage Fault Handler
                DCD     0                         ; Reserved
                DCD     0                         ; Reserved
                DCD     0                         ; Reserved
                DCD     0                         ; Reserved
                DCD     vPortSVCHandler           ; SVCall Handler
                DCD     DebugMon_Handler          ; Debug Monitor Handler
                DCD     0                         ; Reserved
                DCD     xPortPendSVHandler        ; PendSV Handler
                DCD     xPortSysTickHandler       ; SysTick Handler


//
~/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
void xPortSysTickHandler( void )
{
    portDISABLE_INTERRUPTS();
    {
        /* Increment the RTOS tick. */
        if( xTaskIncrementTick() != pdFALSE )
        {
            /* A context switch is required.  Context switching is performed in
             * the PendSV interrupt.  Pend the PendSV interrupt. */
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }
    portENABLE_INTERRUPTS();
}
Explain:
when set portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT will  interrupt to xPortPendSVHandler

//
~/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
void xPortPendSVHandler( void )
{
    __asm volatile
    (
        "    mrs r0, psp                            \n"
        "    isb                                    \n"
        "                                        \n"
        "    ldr    r3, pxCurrentTCBConst            \n"/* Get the location of the current TCB. */
        "    ldr    r2, [r3]                        \n"
        "                                        \n"
        "    stmdb r0!, {r4-r11}                    \n"/* Save the remaining registers. */
        "    str r0, [r2]                        \n"/* Save the new top of stack. */
        "                                        \n"
        "    stmdb sp!, {r3, r14}                \n"
        "    mov r0, %0                            \n"
        "    msr basepri, r0                        \n"
        "    bl vTaskSwitchContext                \n"
        "    mov r0, #0                            \n"
        "    msr basepri, r0                        \n"
        "    ldmia sp!, {r3, r14}                \n"
        "                                        \n"/* Restore the context. */
        "    ldr r1, [r3]                        \n"
        "    ldr r0, [r1]                        \n"/* The first item in pxCurrentTCB is the task top of stack. */
        "    ldmia r0!, {r4-r11}                    \n"/* Pop the registers. */
        "    msr psp, r0                            \n"
        "    isb                                    \n"
        "    bx r14                                \n"
        "                                        \n"
        "    .align 4                            \n"
        "pxCurrentTCBConst: .word pxCurrentTCB    \n"
        ::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
    );
}
Explain:
(a.)
pxCurrentTCBConst is a current task
(b.)
bl vTaskSwitchContext  //will jump to vTaskSwitchContext
(c.)
mov r0, %0
msr basepri, r0
%0 = configMAX_SYSCALL_INTERRUPT_PRIORITY
after configMAX_SYSCALL_INTERRUPT_PRIORITY will disable interrupt function
in ~/FreeRTOS/Demo/CORTEX_STM32F103_Keil/FreeRTOSConfig.h
#define configMAX_SYSCALL_INTERRUPT_PRIORITY     191
(d.)
mov r0, #0
msr basepri, r0 //enable all interrupt function

//
~/FreeRTOS/Source/tasks.c
void vTaskSwitchContext( void )
{
    if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
    {
        /* The scheduler is currently suspended - do not allow a context
         * switch. */
        xYieldPending = pdTRUE;
    }
    else
    {
        xYieldPending = pdFALSE;
        traceTASK_SWITCHED_OUT();

        #if ( configGENERATE_RUN_TIME_STATS == 1 )
            {
                #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
                    portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
                #else
                    ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
                #endif
 
                if( ulTotalRunTime > ulTaskSwitchedInTime )
                {
                    pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime);
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                ulTaskSwitchedInTime = ulTotalRunTime;
            }
        #endif /* configGENERATE_RUN_TIME_STATS */

        /* Check for stack overflow, if configured. */
        taskCHECK_FOR_STACK_OVERFLOW();

        /* Before the currently running task is switched out, save its errno. */
        #if ( configUSE_POSIX_ERRNO == 1 )
            {
                pxCurrentTCB->iTaskErrno = FreeRTOS_errno;
            }
        #endif

        /* Select a new task to run using either the generic C or port
         * optimised asm code. */
        taskSELECT_HIGHEST_PRIORITY_TASK();
        traceTASK_SWITCHED_IN();

        /* After the new task is switched in, update the global errno. */
        #if ( configUSE_POSIX_ERRNO == 1 )
            {
                FreeRTOS_errno = pxCurrentTCB->iTaskErrno;
            }
        #endif

        #if ( configUSE_NEWLIB_REENTRANT == 1 )
            {
                /* Switch Newlib's _impure_ptr variable to point to the _reent
                 * structure specific to this task.
                 * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
                 * for additional information. */
                _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
            }
        #endif /* configUSE_NEWLIB_REENTRANT */
    }
}
Explain:
taskSELECT_HIGHEST_PRIORITY_TASK();       
get to new highest priority task to pxCurrentTCB
when leave vTaskSwitchContext
xPortPendSVHandler will run pxCurrentTCB

沒有留言:

張貼留言