Select Page

Atomthread Cortex-m Port

The Cortex-M port is different from the other architectures insofar as it makes use of two particular features. First, it uses separate stacks for thread and exception context. When the core enters exception mode, it first pushes xPSR, PC, LR, r0-r3 and r12 on the currently active stack (probably the thread stack in PSP) and then switches to the main stack stored in MSP. It also stores a special EXC_RETURN code in LR which, when loaded into the PC, will determine if on return program execution continues to use the MSP or switches over to the PSP and, on cores with an FPU, whether FPU registers need to be restored. The Cortex-M also implements a nested vectored interrupt controller (NVIC), which means that a running ISR may be pre-empted by an exception of higher priority.

The use of separate stacks for thread and exception context has the nice implication that you do not have to reserve space on every task’s stack for possible use by ISRs. But it also means that it breaks atomthreads concept of simply swapping task stacks, regardless of if atomSched() was called from thread or interrupt context. We would have to implement different archContextSwitch() functions called from thread or exception context and also do messy stack manipulations depending on whether the task to be scheduled in was scheduled out in in the same context or not. Yuck! And don’t get me started on nested exceptions calling atomIntExit()…

This is where the second feature comes handy, the PendSV mechanism. PendSV is an asynchronous exception with the lowest possible priority, which means that, when triggered, it will be called by the NVIC if there are no other exceptions pending or running. We use it by having archContextSwitch() set up a pointer to the TCB that should be scheduled in and then trigger the PendSv exception. As soon as program flow leaves the critical section or performs the outermost exception return, the pend_sv_handler() will be called and the thread context switch takes place.