Undertanding Context switch of ARM v7

amirsadig wrote on Monday, November 07, 2016:

i have beaglebone black board and i am tring to port it… i found some coding about context switch like

#define portRESTORE_CONTEXT()											\
{																		\
extern volatile void * volatile pxCurrentTCB;							\
extern volatile unsigned portLONG ulCriticalNesting;					\
																		\
	/* Set the LR to the task stack. */									\
	__asm volatile (													\
	"LDR		R0, =pxCurrentTCB								\n\t"	\
	"LDR		R0, [R0]										\n\t"	\
	"LDR		LR, [R0]										\n\t"	\
																		\
	/* The critical nesting depth is the first item on the stack. */	\
	/* Load it into the ulCriticalNesting variable. */					\
	"LDR		R0, =ulCriticalNesting							\n\t"	\
	"LDMFD	LR!, {R1}											\n\t"	\
	"STR		R1, [R0]										\n\t"	\
																		\
	/* Get the SPSR from the stack. */									\
	"LDMFD	LR!, {R0}											\n\t"	\
	"MSR		SPSR, R0										\n\t"	\
																		\
	/* Restore all system mode registers for the task. */				\
	"LDMFD	LR, {R0-R14}^										\n\t"	\
	"NOP														\n\t"	\
																		\
	/* Restore the return address. */									\
	"LDR		LR, [LR, #+60]									\n\t"	\
																		\
	/* And return - correcting the offset in the LR to obtain the */	\
	/* correct address. */												\
	"SUBS	PC, LR, #4											\n\t"	\
	);																	\
	( void ) ulCriticalNesting;											\
	( void ) pxCurrentTCB;												\
}

in the following code we have switched to system mode (because the first task has 0x1F as SPSR)

/* Get the SPSR from the stack. */									\
	"LDMFD	LR!, {R0}											\n\t"	\
	"MSR		SPSR, R0										\n\t"	\

but the next instruction read LR still from Supervisor Mode

LDR		LR, [LR, #+60]

can someone tell how this happend. i expected after setting SPSR to 0x1F that LR point to banked LR of System mode and not of Supervisor Mode.
the LR in system mode has a dummy value like 0x0a0a0a0a.

davedoors wrote on Monday, November 07, 2016:

ARMv7 what? Not M so probably A. Is the problem on the first context switch? What mode was the Cortex in when vTaskSwitchContext() is called?

amirsadig wrote on Tuesday, November 08, 2016:

beaglebone black come with AM335x which is ARMv7 Cortex-A 8.
there is no problem with the code … it works for the first context switch.

i have made some research and understanfs now what LDMFD LR, {R0-R14}^ exactly what ‘^’ means.
in ARM Manual :

^

is an optional suffix. You must not use it in User mode or System mode. It has two purposes:

    If op is LDM and reglist contains the pc (r15), in addition to the normal multiple register transfer, the SPSR is copied into the CPSR. This is for returning from exception handlers. Use this only from exception modes.

    Otherwise, data is transferred into or out of the User mode registers instead of the current mode registers.

but what i am still not understand the code was running in supervisor mode and the code

"LDMFD  LR!, {R0}                                           \n\t"   \
"MSR        SPSR, R0                                        \n\t"   \

only store the SPSR but not change the execution mode. after changing PC to call the first task code the execution mode changed to system mode … this what i still not understands

rs9562 wrote on Thursday, December 22, 2016:

Using a LPC2132 board with working:
LCD driver
LED port driver (8 segments)
Keypad driver (interrupt driven using vector irq or firq)

I try to initialize minimal hardware (which makes no difference) and make a call to xPortStartScheduler().

In portRESTORE_CONTEXT() macro I am getting a DATA_ABORT exception:

LR = 0x0aaaaaaaa
SPSR = 0x200000df
Addr = the following line in portRESTORE_CONTEXT()
/* Restore the return address. */
“LDR LR, [LR, #+60]”

The LR has a value of 0xaaaaaaaa which is why the exception occurs.
That address is in the reserved area on chip.
The 0xaaaaaaaa is written to a stack area in pxPortInitialiseStack() 

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )

{
StackType_t *pxOriginalTOS;

    pxOriginalTOS = pxTopOfStack;

    /* To ensure asserts in tasks.c don't fail, although in this case the assert
    is not really required. */
    pxTopOfStack--;

    /* Setup the initial stack of the task.  The stack is set exactly as
    expected by the portRESTORE_CONTEXT() macro. */

    /* First on the stack is the return address - which in this case is the
    start of the task.  The offset is added to make the return address appear
    as it would within an IRQ ISR. */
    *pxTopOfStack = ( StackType_t ) pxCode + portINSTRUCTION_SIZE;
    pxTopOfStack--;

    *pxTopOfStack = ( StackType_t ) 0xaaaaaaaa;     /* R14 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) pxOriginalTOS; /* Stack used when task starts goes in R13. */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x12121212;     /* R12 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x11111111;     /* R11 */
    pxTopOfStack--;

The call stack is:

xPortStartScheduler():
  vPortISRStartFirstTask();
    portRESTORE_CONTEXT();

I am using FreeRTOSv9.0.0/FreeRTOS/Source/portable/GCC/ARM7_LPC2000/port.c
Any idea what to change?

rtel wrote on Thursday, December 22, 2016:

Its been a while since I used an ARM7 port, but from memory I would say your problem is almost certainly due to having the MCU in the wrong mode when xPortStartScheduler() is called. Make sure the MCU is in Supervisor mode, and definitely not in User mode. The mode is normally set in the start up code before main() is called.

rs9562 wrote on Friday, December 23, 2016:

I see that in section 6.3 (Troubleshooting). I made the change to supervisor mode and still get a data abort with spsr = 0x200000d3. It has been a while since I used an ARM7 too, last
projects were a LPC1769 (Cortex M3) and a STM32f7Discovery (Cortex M7).

SPSR = 0x200000d3
C flag
I flag
F flag
Mode = 0x13 (SVC)

rtel wrote on Friday, December 23, 2016:

Please attach your start up file (the file that sets up the stacks for
each CPU mode).

rs9562 wrote on Friday, December 23, 2016:

Here is the relevant section. I can attach the entire contents of the startup file if necessary.

.equ USR_MODE,   0x10
.equ FIQ_MODE,   0x11
.equ IRQ_MODE,   0x12
.equ SVC_MODE,   0x13
.equ ABT_MODE,   0x17
.equ UND_MODE,   0x1B
.equ SYS_MODE,   0x1F

.equ UND_STACK_SIZE,  0x00000010
.equ ABT_STACK_SIZE,  0x00000010
.equ FIQ_STACK_SIZE,  0x00000080
.equ IRQ_STACK_SIZE,  0x00000080
.equ SVC_STACK_SIZE,  0x00000400
.equ SYS_STACK_SIZE,  0x00000400

.equ IRQ_DISABLE,  (1 << 7)
.equ FIQ_DISABLE,  (1 << 6)

ldr r0, STACK_START

/* Undefined mode stack */
msr CPSR_c, #UND_MODE | IRQ_DISABLE | FIQ_DISABLE
mov sp, r0
sub r0, r0, #UND_STACK_SIZE

/* Abort mode stack */
msr CPSR_c, #ABT_MODE | IRQ_DISABLE | FIQ_DISABLE
mov sp, r0
sub r0, r0, #ABT_STACK_SIZE

/* FIQ mode stack */
msr CPSR_c, #FIQ_MODE | IRQ_DISABLE | FIQ_DISABLE
mov sp, r0
sub r0, r0, #FIQ_STACK_SIZE

/* IRQ mode stack */
msr CPSR_c, #IRQ_MODE | IRQ_DISABLE | FIQ_DISABLE
mov sp, r0
sub r0, r0, #IRQ_STACK_SIZE

/* Supervisor mode stack */
msr CPSR_c, #SVC_MODE | IRQ_DISABLE | FIQ_DISABLE
mov sp, r0
sub r0, r0, #SVC_STACK_SIZE

/* System mode stack */
msr CPSR_c, #SYS_MODE | IRQ_DISABLE | FIQ_DISABLE
mov sp, r0

/* Start in supervisor mode */
msr CPSR_c, #SVC_MODE | IRQ_DISABLE | FIQ_DISABLE
bl  main
b    .

STACK_START:.word   __stack_start__

__stack_start__ equates to __ram_end__ which is 0x40004000

/*
 * Linker script for LPC2132 ARM7 MCU
 *
 *
 * __ram_end__       +--------------+ __stack_start__
 *                   |              |
 *                   |     Stack    |
 *                   |              |
 *                   +--------------+ __stack_end__
 *                   |              |
 *                   |     heap     |
 *                   |              |
 *                   +--------------+ __bss_end__
 *                   |              |
 *                   |     .bss     |
 *                   |              |
 *                   +--------------+ __bss_start__
 *                   |    .data     | __data_end__
 *                   |Virtual Memory|
 *                   | Address (VMA)|
 * __ram_start__     +--------------+ __data_start__
 *
 * __flash_end__     +--------------+
 *                   |              |
 *                   |     empty    |
 *                   |              |
 *                   +--------------+
 *                   |    .data     |
 *                   | Load Memory  |
 *                   | Address (LMA)|
 *                   +--------------+ __text_end__
 *                   |              |
 *                   |   .rodata    |
 *                   |              |
 *                   +--------------+
 *                   |              |
 *                   |    .text     |
 *                   |              |
 *                   +--------------+
 *                   |   .startup   |
 *  __flash_start__  +--------------+ __text_start__
 */

rs9562 wrote on Friday, December 23, 2016:

I found a typing fix for this file:

FreeRTOSv9.0.0/FreeRTOS/Source/portable/GCC/ARM7_LPC2000/portmacro.h" line 120 of 269

/*

  • portRESTORE_CONTEXT, portRESTORE_CONTEXT, portENTER_SWITCHING_ISR
  • and portEXIT_SWITCHING_ISR can only be called from ARM mode, but
  • are included here for efficiency. An attempt to call one from
  • THUMB mode code will result in a compile time error.
    */

Edit the second “portRESTORE_CONTEXT” to read as “portSAVE_CONTEXT”.

rs9562 wrote on Friday, December 23, 2016:

After removing the “^” from the line in portRESTORE_CONTEXT() to see what difference it made:

LDMFD LR, {R0-R14}^

I had not restored it. Then adjusted the CSPR to SVC mode before calling main. Still got a data abort. After I restored the “^” things worked. Thanks for your time.

rtel wrote on Saturday, December 24, 2016:

Refering to your start up file: The IRQ mode stack is much too small, and there is no need to set up a System mode stack as it will nevery be used.

rs9562 wrote on Saturday, December 24, 2016:

I was going to tune the stack sizes later, but since you brought it up I really appreciate
what you suggest and I will make the changes. The previous code I borrowed this from was
a lpc2148 project I worked on about 8 years ago and the code started in SYS_MODE.

rtel wrote on Saturday, December 24, 2016:

Look at the examples in the download, and copy them.



rs9562 wrote on Wednesday, December 28, 2016:

I have a few tasks running:

  1. RTC timer values (seconds, minutes. etc) are read from RTC registers.
  2. LED bank displays seconds and tenths of seconds.
  3. 16 key keypad detects entry interrupt and decodes a key.
  4. A LCD is printing the elapsed time, key entry, led count.

It can run for a few hours with no hang up. A few times I have noticed a data abort within portISR.c at portRESTORE_CONTEXT.

void vPortYieldProcessor( void )
{
   /* Within an IRQ ISR the link register has an offset from the true return
address, but an SWI ISR does not.  Add the offset manually so the same
ISR return code can be used in both cases. */
__asm volatile ( "ADD       LR, LR, #4" );

/* Perform the context switch.  First save the context of the current task. */
portSAVE_CONTEXT();

/* Find the highest priority task that is ready to run. */
__asm volatile ( "bl vTaskSwitchContext" );

/* Restore the context of the new task. */
portRESTORE_CONTEXT();

}

I was thinking that I might have to have some synchronization between reading the RTC registers and servicing the rtc interrupt. As the problems become fewer the complexity increases. As it is I am only using a 4 line with 20 characters per line LCD display. I just print out R13, R14, SPSR at the abort.

rtel wrote on Wednesday, December 28, 2016:

Did you increase the size of the IRQ stack, as per the comment a few
posts above?

rs9562 wrote on Wednesday, December 28, 2016:

This happened twice in a row, same exact registers:
Data Abort Exception:

FreeRTOSv9.0.0/FreeRTOS/Source/portable/GCC/ARM7_LPC2000/portISR.c
r0 = 0x40000024 (ulCriticalNesting)
r1 = 0x40000060 (pxCurrentTCB + 4)
r2 = 0x00000001
r3 = 0x4000013c (pxCurrentTCB + e0)
r4 = 0x0000000e
r5 = 0x05050505
r6 = 0x06060606
r7 = 0x400004f0 (in the heap)
r8 = 0x08080808
r9 = 0x09090909
r10=0x10101010
r11=0x40003fec (Abort stack area , start = 0x40003ff0 length = 0x10)
r12=0x0000320d
r13=0x40003fcc (IRQ stack area, start = 0x40003fd0 length = 0x800)
r14=0x00004a34
spsr=0x20000093

    /* Restore the context of the new task. */
    portRESTORE_CONTEXT();
4a20:       e59f01fc        ldr     r0, [pc, #508]  ; 4c24 <vPortExitCritical+0x64>
4a24:       e5900000        ldr     r0, [r0]
4a28:       e590e000        ldr     lr, [r0]
4a2c:       e59f01f4        ldr     r0, [pc, #500]  ; 4c28 <vPortExitCritical+0x68>
4a30:       e8be0002        ldm     lr!, {r1}
4a34:       e5801000        str     r1, [r0]
4a38:       e8be0001        ldm     lr!, {r0}
4a3c:       e169f000        msr     SPSR_fc, r0
4a40:       e8de7fff        ldm     lr, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, sp, lr}^
4a44:       e1a00000        nop                     ; (mov r0, r0)
4a48:       e59ee03c        ldr     lr, [lr, #60]   ; 0x3c
4a4c:       e25ef004        subs    pc, lr, #4
4a50:       e59f300c        ldr     r3, [pc, #12]   ; 4a64 <swi_handler+0xa8>
4a54:       e5933000        ldr     r3, [r3]
4a58:       e59f3008        ldr     r3, [pc, #8]    ; 4a68 <swi_handler+0xac>
4a5c:       e5933000        ldr     r3, [r3]
}
4a60:       e1a00000        nop                     ; (mov r0, r0)
4a64:       40000024        .word   0x40000024
4a68:       4000005c        .word   0x4000005c

After adding code to display exception registers and use the the stack checking feature I found that I had to adjust comfigMINIMAL_STACK_SIZE. Since I’m only working with 16K sram I have to careful with memory use. I’m planning to make a few modifications so that a certain key press will display the stack high water marks for the running tasks. Thank you to Real Time Engineers for your comments.