Hey there !!
I’m trying to investigate a Fault that occurs when trying to start the first task on core 1 in SMP Configuration.
I’ve an UART trace, where I’m supposed to see my four tasks say “Hi I’m <TASK_ID> - Current core : <CORE_ID>”. What I see is tasks 1, 3 and 4 actually printing their trace from core 0. The task 2 seems to never run. My thought is that the core 1 tried to run it, but the fault it meets stop it. As the OS is in cooperative mode (to avoid issues related to the systick in a first time) the core 0 never run it.
The Fault report for core 1 indicates FORCED=1, INVSTATE=1 and PENDSVACT=1 (PendSV is always in my troubles! ) The fault do not occur if I break the core 1 when entering the PendSVHandler : It’s probably the source of my issue.
After some tests I tried, I figured out that the cores are always in privileged mode, even when running the tasks (observed on core 0.) From what I understand of arm datasheet, I’m supposed to switch between Thread mode and Handler mode. Is being always in privileged mode a bad practice ?
The first instruction of my task implementation is - according to the debugger - SUB SP, SP, #4
. Is it possible its causing maybe a race condition-like with PendSVHandler ? It’s the same for core 0 and core 1, but core 0 don’t meet any trouble, so I’m not sure. Is there some specific condition about the privilege levels in an SMP system ??
If it can help, here is my implementation of pendSVHandler :
void xPortPendSVHandler( void )
{
/* This is a naked function. */
__asm volatile
(
" dsb \n"
" isb \n"
" mrs r0, psp \n"
" isb \n"
" \n"
#if configNUMBER_OF_CORES == 1
" ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
" ldr r2, [r3] \n"
#else /* configNUMBER_OF_CORES == 1 */
/* [A */" push {r4} \n" /* save r4 */
/* [B */" push {r0-r3, r12, lr} \n" /* Save only these registers as the other are "callee-saved" */
" bl LLD_V7M_SCB_GetCoreID \n" /* Get the current core ID */
" mov r4, r0 \n" /* the core ID is in r4, so we can restore r0 */
/* B] */" pop{r0-r3, r12, lr} \n" /* Restore the previously saved registers */
" ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
" ldr r2, [r3, r4, lsl #2] \n"
/* A] */" pop {r4} \n"
#endif /* configNUMBER_OF_CORES == 1 */
" \n"
" tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */
" it eq \n"
/* [C */" vstmdbeq r0!, {s16-s31} \n"
" \n"
/* [D */" 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"
/* [E */" stmdb sp!, {r0, r3} \n"
" mov r0, %0 \n"
" msr basepri, r0 \n"
" dsb \n"
" isb \n"
#if configNUMBER_OF_CORES == 1
" bl vTaskSwitchContext \n"
#else /* configNUMBER_OF_CORES == 1 */
/* [A */" push {r4} \n" /* save r4 */
/* [B */" push {r0-r3, r12, lr} \n" /* Save only these registers as the other are "callee-saved" */
" bl LLD_V7M_SCB_GetCoreID \n" /* Get the current core ID */
// " mov r0, r4 \n" /* Place the core ID in r0, the first parameter of a function call */
" bl vTaskSwitchContext \n" /* Actually calling the function */
/* B] */" pop{r0-r3, r12, lr} \n" /* Restore the previously saved registers */
/* A] */" pop {r4} \n"
#endif /* configNUMBER_OF_CORES == 1 */ /* All as it was before after !!!! */
" mov r0, #0 \n"
" msr basepri, r0 \n"
/* E] */" 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"
/* D] */" 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"
/* C] */" vldmiaeq r0!, {s16-s31} \n"
" \n"
" msr psp, r0 \n"
" isb \n"
" \n"
" \n"
" bx r14 \n"
" \n"
" .align 4 \n"
#if configNUMBER_OF_CORES == 1
"pxCurrentTCBConst: .word pxCurrentTCB \n"
#else /* configNUMBER_OF_CORES == 1 */
"pxCurrentTCBConst: .word pxCurrentTCBs \n"
#endif /* configNUMBER_OF_CORES == 1 */
::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
);
}
Thanks for reading