8
votes

I'm trying to write a barebones round-robin scheduler for the Cortex-M using the CodeSourcery GCC toolchain. My scheduler uses the SysTick to fire an interrupt after the expiry of a time slice and the context switching takes place inside the ISR. To keep things simple, I am using only the main stack pointer (MSP) for everything.

I am stuck in determining how to handle loading the new context on the Cortex-M3. According to the Cortex-M3 Technical Reference Manual (TRM) the process pushes the PC, LR and status registers onto the current stack on the entry to the ISR.

If I push the rest of the registers to save the context of the present task and load a new SP value from the next task's control block how would I go about restoring the rest of its context?

According to what I understand, I need to pop out the registers I push (say {r4-r11}) and the processor will push out the rest (including the return address of the new task (LR) and status registers) automatically when the ISR returns. So I'm assuming I just need to execute a BX after I'm done to switch tasks?

Here is what it says on the TRM:

Exception returns occur when one of the following instructions loads a value of 0xFFFFFFFX into the PC when 1) POP/LDM which includes loading the PC 2) LDR with PC as a destination 3) BX with any register.

How do I go about loading the EXC_RETURN value? Should I just push it on to the stack (as it supposedly does here)? Assuming I've popped out the registers I've pushed via software, how does the Cortex go about popping the registers it has saved? In general, how do I restore a task's context?

I've tried reading the TRM and other ARM references but they seem unclear.

3
the data stored on the stack in the isr represents the state of the prior context yes? so that plus any other registers you need to preserve as the state of the old context, replace those with the state of the new context (including the 0xFFFf... value from the new context when it was last swapped out), then return from the interruptold_timer
For instance, the diagram seems to explain everything? What don't you understand?artless noise
Yes, the diagram was pretty clear but what I don't understand is a) what is the hardware pop mechanism, does it just pop out the last 8 or so words and returns to the LR contained in those words? And where exactly do we write the EXC_RETURN value? The link just writes it on to the stack, apparently.Shrikant Giridhar
@Shrikant Giridhar I've posted the full description on how FreeRTOS makes the context switch on a Cortex-M7. I believe that Cortex-M7 and Cortex-M3 are very similar, except for the floating point unit in the M7 and some extra speed and peripherals. I hope that the description is useful to you. Kind regardsK.Mulier

3 Answers

16
votes

It is indeed quite complicated. I am writing a book about the FreeRTOS operating system running on Cortex-M cores. I have written a chapter about this. From reading your question I believe this chapter will help you:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

2
votes

Reading a bit more and some help on the #ARM IRC channel later I was able to understand the exception return mechanism. Here is what I understand.

As mentioned in the CM3 TRM, the core pushes the registers r0-r3, r12 along with the status, LR and PC on to the current process stack when an exception is registered. Thus, upon exception entry the stack contains 8 words which includes the LR containing the address to return to. The hardware pop mechanism essentially reverses the same action i.e. it pops out the last 8 words during which it loads the LR into the PC to return to the interrupted function.

Thus, the context can be switched simply by moving the stack pointer to an appropriate location such that the current stack frame resembles the stack frame of a task which has just been interrupted i.e. contains the exact words in the same order.

Upon exception entry and after saving the registers on to the stack, the LR is loaded with the EXC_RETURN value. This value contains special status flags to indicate return conditions. This is also used to indicate the end of the interrupt. That is, if the ISR needs to return to a task and switch the stack pointer from the current (MSP) to the PSP it can load the LR with the appropriate EXC_VALUE (indicated in TRM) and simply do a BX LR to switch states.

2
votes

Here is a code fragment that does exactly what you need - https://github.com/DISTORTEC/distortos/blob/master/source/architecture/ARM/ARMv7-M/ARMv7-M-PendSV_Handler.cpp - it works as you described:

  • exception entry automatically stacks some registers,
  • you manually stack remaining registers,
  • you switch SP,
  • you unstack "remaining" registers,
  • exception return unstacks the rest of registers.