0
\$\begingroup\$

I am examining the context switching procedure on a ARM Cortex-M4 based processor running FreeRTOS. In port.c we have:

void xPortPendSVHandler( void )
{
    /* This is a naked function. */

    __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"
    "   tst r14, #0x10                      \n" /* Is the task using the FPU context?  If so, push high vfp registers. */
    "   it eq                               \n"
    "   vstmdbeq r0!, {s16-s31}             \n"
    "                                       \n"
    "   stmdb r0!, {r4-r11, r14}            \n" /* Save the core registers. */
    "   str r0, [r2]                        \n" /* Save the new top of stack into the first member of the TCB. */
    "                                       \n"
    "   stmdb sp!, {r0, r3}                 \n"
    "   mov r0, %0                          \n"
    "   msr basepri, r0                     \n"
    "   dsb                                 \n"
    "   isb                                 \n"
    "   bl vTaskSwitchContext               \n"
    "   mov r0, #0                          \n"
    "   msr basepri, r0                     \n"
    "   ldmia sp!, {r0, r3}                 \n"
    "                                       \n"
    "   ldr r1, [r3]                        \n" /* The first item in pxCurrentTCB is the task top of stack. */
    "   ldr r0, [r1]                        \n"
    "                                       \n"
    "   ldmia r0!, {r4-r11, r14}            \n" /* Pop the core registers. */
    "                                       \n"
    "   tst r14, #0x10                      \n" /* Is the task using the FPU context?  If so, pop the high vfp registers too. */
    "   it eq                               \n"
    "   vldmiaeq r0!, {s16-s31}             \n"
    "                                       \n"
    "   msr psp, r0                         \n"
    "   isb                                 \n"
    "                                       \n"
    #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
        #if WORKAROUND_PMU_CM001 == 1
    "           push { r14 }                \n"
    "           pop { pc }                  \n"
        #endif
    #endif
    "                                       \n"
    "   bx r14                              \n"
    "                                       \n"
    "   .align 4                            \n"
    "pxCurrentTCBConst: .word pxCurrentTCB  \n"
    ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
    );
}

In the first half of the code, the current task status gets pushed to its stack, then vTaskSwitchContext is called. In here, the system decides, if a context switch is needed and gets the TCB of the next task. Then, the context is restored by loading the value from the stack. In my understanding, the LR register should now contain the program counter of the new task. With bx r14, this value is loaded to the actual program counter. When having two tasks A and B, context switching is working. However, when I examine the code by using GDB and jump into xPortPendSVHandler when switching from A to B should occur, the value of R14 (LR) is 0xfffffffd, and not 0x8008700 (PC when task got interrupted). What I am getting wrong?

\$\endgroup\$
3
\$\begingroup\$

R14 (LR) doesn't contain the address to return to when exiting an exception (as you have found) instead it contains information about whether to return to handler or thread mode, which stack pointer to use (msp or psp), and whether it's a floating point context.

This is detailed in the section about Exception entry and exit in the Arm Cortex M4 generic user guide, page 2-28.

The program counter (amongst others) was pushed onto the stack during exception entry, when the processor leaves the exception handler it pops the pc back off the stack and carries on. In this instance the stack pointer used is changed during the exception, which allows the task to switch.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.