ARM Cortex RTOS: Part 2 C Callstacks

If you try out the example OS in the

The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors ” book by J. Yiu, you’ll find that some things might annoy you. These aspects did for me:

  • SVC to start
  • Debugging

SVC Start

Not really sure why many OSes do it this way. As the book mentions, it isn’t strictly necessary. The main idea is that the CPU must switch from MSP to PSP stack. This can be done in Thread mode.


This is an example of what happens in an interrupt when you are running multiple threads. It’s not actually correct because the interrupt does not come from 0x0.


This is the call stack for the running threads. Again, it is correct but it does not show the other state of the threads.


Fix for Interrupts

.type CoOS_InitMainStack,%function
//Reset MSP to init
ldr r0, =0xE000ED08 //r0 = VTOR Use the VTOR offset register to locate the stack.
ldr r0, [r0]		//r0 = *VTOR
ldr r0, [r0]		//r0 = *(e_stack) (stack init)

sub r0, #32 //r0 = r0 - 32
msr msp, r0 //MSP = r0

ldr r1, =_ZN4CoOS16InterruptHandlerEv //r1 = &CoOS::ThreadStart
add r1, #2
str r1, [r0, #0x14] // MSP.LR = &CoOS::ThreadStart
str r1, [r0, #0x18] // MSP.Return Addr = &CoOS::ThreadStart

bx lr

Note the addition of the lines:

//r0 = STACK END (Top of Stack)
sub r0, #32         //r0 = r0 - 32 //obtain start of exception frame
msr msp, r0	    //MSP = r0
ldr r1, =_ZN4CoOS16InterruptHandlerEv //r1 = &CoOS::InterruptHandler
add r1, #2          //Little hack for GDB.
str r1, [r0, #0x14] // MSP.LR = &CoOS::InterruptHandler
str r1, [r0, #0x18] // MSP.Return Addr = &CoOS::InterruptHandler

This initialises the Main Stack Pointer (MSP) such that it’s exception stack LR and PC point to something that we have predefined. In this case it is to point to: CoOS::InterruptHandler.


Fix for threads

When we initalise each Process Exception stack, we can also set the value of the stacked LR value.

   typedef struct {
	uint32_t r4;
	uint32_t r5;
	uint32_t r6;
	uint32_t r7;
	uint32_t r8;
	uint32_t r9;
	uint32_t r10;
	uint32_t r11;
	uint32_t r0;
	uint32_t r1;
	uint32_t r2;
	uint32_t r3;
	uint32_t r12;
	void* lr;
	void* pc;
	uint32_t xpsr;
   } Context;

void ThreadStart() {
   while(1) {}

void Thread::Create(uint32_t start, uint32_t stackSize)
context->pc = (void*)start;
context->lr = (void*)(&ThreadStart + 2); //+2 is required for the GDB debugger to place it FULLY into the function
context->xpsr = 0x01000000; // initial xPSR
m_psp = (uint32_t)m_stack + stackSize - sizeof(Context);

Here we can now see that Task call stacks no longer originate from 0x0, but from something a little nicer to read.



Check out Part 3 where we create a Jlink DLL to help debug our OS.

One thought on “ARM Cortex RTOS: Part 2 C Callstacks

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s