Questions on the ARM ports context switches

julesrapanga wrote on Monday, October 27, 2008:

    I’m trying to port FreeRTOS to the Nintendo DS and have been having some problems with starting and switching tasks. The STORE_CONTEXT and RESTORE_CONTEXT procedures that seems to be identical on most of the other ARM7/ARM9 ports don’t work for me, and although I’m a novice at all of this I’m not sure how they’re meant to work in the first place.

The first thing that I’d like to know is which processor mode the ARM is meant to be in when it starts the first task, as there is a line in it that writes the stacks value for the PSR to the SPSR. When I check it at runtime I seem to be in System mode, but since this has no SPSR I think this instruction causes problems.

I also don’t understand the last few lines of the RESTORE:

    LDMFD    LR, {R0-R14}^
    LDR    LR, [LR, #+60]
    SUBS    PC, LR, #4

Specifically the second last line, as the value in LR is set in initialiseStack to 0xAAAAAAAA, I don’t see how this should provide the PC with the start of the first task.

Hopefully someone can help me understand this as my degree projet is resting on it.

Thanks in advance,
     Gordon Parke

jorick23 wrote on Tuesday, October 28, 2008:

The processor needs to be in Supervisor mode when it starts up.  Tasks run in System mode, but FreeRTOS must be in Supervisor mode when it starts so that it has the necessary privileges when controlling tasks.  After setting up the interrupt and abort stacks, the last lines of your stack setup code in the startup file should set up the supervisor stack and leave it in supervisor mode.

The first line of the restore, LDMFD LR, {R0-R14}^ restores the user mode registers from the task stack which the link register is pointing to.  The ^ specifies user registers rather than the registers of the current mode.

The NOP is required because the processor cannot access banked registers (the supervisor mode registers) in the instruction following the LDMFD instruction.  The next instruction (LDR LR, …) would be accessing the link register which is a banked register.  Page A4-33 of the ARM Architecture Reference Manual mentions this (although not in much detail).

The LDMFD instruction restored the user link register since r14 was mentioned in the list.  Now LDR LR, [LR, #+60] restores the program counter (return address) from the task stack.  The program counter is stored 60 bytes away from where the link register is pointing because r0 to r14 were stored in that area.

The last line, SUBS PC, LR, #4 subtracts 4 from the return address because the instruction pipeline mechanism causes the PC to be advanced by 4 by the time it gets stored on the stack.  Using the SUBS version of the subtract instruction instead of SUB causes the SPSR of the current mode to be copied to the CPSR (referenced in page A4-98 of the ARM Architecture Reference Manual).

julesrapanga wrote on Tuesday, October 28, 2008:

OK, lets see. After reading your reply I added a few lines to switch the mode to Supervisor before running the Restore context code, which worked and the first task start. I tried edited the startup file but this didn’t work, I’m using a toolchain that I’m not very familiar with so it’s very possible that I’m just editing the wrong file. Also, do FreeRTOS tasks have to be run in system mode? My mentor thought that they should be user mode.

I’m having a hard time working out which registers are the user mode ones and which ones are the supervisors. in LDMFD LR, {R0-R14}^ I get that r0-r14 are the user mode registers, but is LR supervisor or user? If you just say r13 does that always refer to user/system mode, or is it the mode you’re in. Does LR have a different meaning that R14, implying anything different?

Even when I do get the first task loaded I get a strange bug in that after my timer interrupt goes off, the name of the current task gets corrupted, the first 4 characters get replaced with random symbols (the last two seem to be smiley faces, one inverted…) I suspect that this means the rest of the task is corrupted too… Doubt anyone can help with that at the minute though.

richard_damon wrote on Wednesday, October 29, 2008:

FreeRTOS tasks need to run in system mode as that is needed for its implementation of critical sections (user mode tasks can’t disable interrupts, to run tasks with critical sections in user mode, you would need to make critical section use a SWI to create critical sections).

jorick23 wrote on Wednesday, October 29, 2008:

In LDMFD LR, {R0-R14}^, the LR is a supervisor register.  When working in a privileged mode with user registers, everything to the left of the comma is a banked register in the mode you are in, and everything to the right of the comma is a user register (assuming that you have the ^).

Check to make sure you have enough IRQ stack space.  When memory gets corrupted after an IRQ fires off, that’s the first thing I look for.  The smiley faces are caused by bytes with values 0x00 and 0x01.  So you’re probably looking at 0x0001xxxx as the first four characters which looks like a program return address which is making me think that your IRQ stack is too small.

julesrapanga wrote on Wednesday, October 29, 2008:

I wonder if anyone knows how much stack space is required for the basic context switch?

I’ve looked at a few example demos and they all allocate 0x400 stack space, but the loader for the NDS only allocates 0x100, with this memory (DTCM) being quite limited. Others have tried relocating the stacks to main memory but this has a substansial performance hit and some nds library functions get messed up.

davedoors wrote on Wednesday, October 29, 2008:

0x400 is way more than is needed by the context switch. But this is the Supervisor more stack so also has to be big enough for main().