Hi everyone, I’m using an STM32F4xx (Cortex-M4, Armv7-M architecture).
While reading the Armv7-M Architecture Reference Manual on page 530, I came across the following section:
Use of SVCall and PendSV to avoid critical code regions
Context switching typically requires the processor to execute a critical region of code with interrupts disabled, to avoid context corruption of key data structures during the switch. This can be a severe constraint on system design and deterministic performance. Armv7-M can support context switching with no critical region, meaning the processor does not have to disable interrupts.
An Armv7-M usage model to avoid critical regions is:
• Configure both SVCall and PendSV with the same, lowest exception priority.
• Use SVCall for Supervisor Calls from threads.
• Use PendSV to handle context-critical work offloaded from the exception handlers, including work that might otherwise be handled by the SVCall handler.Because SVCall and PendSV have the same execution priority they cannot preempt each other, therefore one must process to completion before the other starts. SVCall and PendSV exceptions are always enabled, meaning each executes at some point, when the processor has handled all other exceptions.
In addition, the associated exception handlers do not have to check whether they are returning to a process on exit with this usage model, as the PendSV
exception will occur when returning to a process.
This usage model always sets PendSV to pending to issue a context switch request. However, a system can use both SVCall and PendSV exceptions for context switching because they do not interfere with each other.
From this, I understand that it should be possible to perform thread switching without disabling interrupts.
However, in the FreeRTOS Cortex-M4 port.c
file, I found the following code:
void xPortPendSVHandler( void )
{
......
" mov r0, %0 \n" //%0 is configMAX_SYSCALL_INTERRUPT_PRIORITY
" msr basepri, r0 \n"
" dsb \n"
" isb \n"
" bl vTaskSwitchContext \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
....::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
}
It seems that FreeRTOS disables interrupts that require the use of the xxxxx_FromISR
APIs by setting the BASEPRI
register before calling vTaskSwitchContext
.
I’m wondering if it’s possible to perform task switching with interrupts fully enabled. If so, how can this be achieved while avoiding race conditions or data consistency issues?
Thank you for your help!
---------------------------------------------------------------------------
UPDATE 1:
Hi RAC, richard-damon, and rtel,
Thank you for your replies. From your explanations, I now understand that FreeRTOS needs to disable interrupts that use xxxFromISR
to ensure the integrity of thread-related data structures (which I understand primarily refers to the TCB structure).
However, according to ARM’s description:
“Use PendSV to handle context-critical work offloaded from the exception handlers, including work that might otherwise be handled by the SVCall handler.”
I am wondering if it would be possible for xxxFromISR
to avoid directly accessing these data structures. For instance, when calling vTaskNotifyGiveFromISR
within an interrupt, could it simply submit a “request” into a “queue” instead? All actual work (including accessing and modifying the TCB structure) could then be deferred and handled later in the PendSV handler.
Thank you for your insights!