guillep2k wrote on Tuesday, January 24, 2006:
Hi… I am a newbie with ARM assembler, so I am not fully sure about why this happens. In the LPC2000 port, the portSAVE_CONTEXT macro reads like this:
#define portSAVE_CONTEXT()
{
extern volatile void * volatile pxCurrentTCB;
extern volatile unsigned portLONG ulCriticalNesting;
/* Push R0 as we are going to use the register. */
asm volatile ( "STMDB SP!, {R0}" );
/* Set R0 to point to the task stack pointer. */
asm volatile ( "STMDB SP,{SP}^" );
asm volatile ( "SUB SP, SP, #4" );
asm volatile ( "LDMIA SP!,{R0}" );
/* Push the return address onto the stack. */
asm volatile ( "STMDB R0!, {LR}" );
/* Now we have saved LR we can use it instead of R0. */
asm volatile ( "MOV LR, R0" );
/* Pop R0 so we can save it onto the system mode stack. */
asm volatile ( "LDMIA SP!, {R0}" );
/* Push all the system mode registers onto the task stack. */
asm volatile ( "STMDB LR,{R0-LR}^");
asm volatile ( "SUB LR, LR, #60" );
/* Push the SPSR onto the task stack. */
asm volatile ( "MRS R0, SPSR" );
asm volatile ( "STMDB LR!, {R0}" );
asm volatile ( "LDR R0, =ulCriticalNesting " );
asm volatile ( "LDR R0, [R0]" );
asm volatile ( "STMDB LR!, {R0}" );
/* Store the new top of stack for the task. */
asm volatile ( "LDR R0, %0" : : "m" (pxCurrentTCB) );
asm volatile ( "STR LR, [R0]" );
( void ) ulCriticalNesting;
}
However, once compiled the code looked like this in one of my ISR functions:
12b8: e92d0001 stmdb sp!, {r0}
12bc: e94d2000 stmdb sp, {sp}^
12c0: e24dd004 sub sp, sp, #4 ; 0x4
12c4: e8bd0001 ldmia sp!, {r0}
12c8: e9204000 stmdb r0!, {lr}
12cc: e1a0e000 mov lr, r0
12d0: e8bd0001 ldmia sp!, {r0}
12d4: e94e7fff stmdb lr, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, sp, lr}^
12d8: e24ee03c sub lr, lr, #60 ; 0x3c
12dc: e14f0000 mrs r0, SPSR
12e0: e92e0001 stmdb lr!, {r0}
12e4: e59f0b18 ldr r0, [pc, #2840] ; 1e04 <.text+0x1e04>
12e8: e5900000 ldr r0, [r0]
12ec: e92e0001 stmdb lr!, {r0}
12f0: e59fe0d8 ldr lr, [pc, #216] ; 13d0 <.text+0x13d0>
12f4: e59e0000 ldr r0, [lr]
12f8: e580e000 str lr, [r0]
12fc: e59f60d0 ldr r6, [pc, #208] ; 13d4 <.text+0x13d4>
1300: e5963000 ldr r3, [r6]
1304: e24eb004 sub fp, lr, #4 ; 0x4
…
13d0: 400009a4 andmi r0, r0, r4, lsr #19 ; pxCurrentTCB
13d4: 400000e8 andmi r0, r0, r8, ror #1 ; ulCriticalNesting
…
1e04: 400000e8 andmi r0, r0, r8, ror #1 ; ulCriticalNesting
You can see at 12f0 that LR gets scratched!!! So, when 12f8 wants to save LR into [R0], it is saving a different value than expected, which is the position of the top of the task stack. What is actually saved into [R0] (i.e., pxCurrentTCB->pxTopOfStack) is the address of… pxCurrentTCB->pxTopOfStack!!!
I temporarily changed the macro into:
#define portSAVE_CONTEXT()
{
extern volatile void * volatile pxCurrentTCB;
extern volatile unsigned portLONG ulCriticalNesting;
/* Push R0 as we are going to use the register. */
asm volatile ( "STMDB SP!, {R0}" );
/* Set R0 to point to the task stack pointer. */
asm volatile ( "STMDB SP,{SP}^" );
asm volatile ( "SUB SP, SP, #4" );
asm volatile ( "LDMIA SP!,{R0}" );
/* Push the return address onto the stack. */
asm volatile ( "STMDB R0!, {LR}" );
/* Now we have saved LR we can use it instead of R0. */
asm volatile ( "MOV LR, R0" );
/* Pop R0 so we can save it onto the system mode stack. */
asm volatile ( "LDMIA SP!, {R0}" );
/* Push all the system mode registers onto the task stack. */
asm volatile ( "STMDB LR,{R0-LR}^");
asm volatile ( "SUB LR, LR, #60" );
/* Push the SPSR onto the task stack. */
asm volatile ( "MRS R0, SPSR" );
asm volatile ( "STMDB LR!, {R0}" );
asm volatile ( "LDR R0, =ulCriticalNesting " );
asm volatile ( "LDR R0, [R0]" );
asm volatile ( "STMDB LR!, {R0}" );
asm volatile ( "MOV R4, LR" ); /*GAP*/
/* Store the new top of stack for the task. */
asm volatile ( "LDR R0, %0" : : "m" (pxCurrentTCB) );
asm volatile ( "STR R4, [R0]" ); /*GAP*/
/* asm volatile ( "STR LR, [R0]" ); GAP*/
( void ) ulCriticalNesting;
}
Notice the usage of R4 to save the contents of LR. Having done this, my program started working. However I am a bit worried about this problem… Is it perhaps a bug of GCC assembler ("gcc (GCC) 4.0.1 (WinARM)")? A bad use of the %0/"m" syntax? Do I have the wrong GCC options? Check my options here:
Compiling:
gcc -c -mcpu=arm7tdmi -I. -gdwarf-2 -DREENTRANT_SYSCALLS_PROVIDED -DROM_RUN -DGCC_ARM7 -I ./include -O2 -Wall -Wcast-align -Wimplicit -Wwrite-strings -Wpointer-arith -Wswitch -Wreturn-type -Wunused -Wshadow -Wa,-adhlns=lib/µp.lst -MD -MP -MF .de
p/µp.o.d -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 lib/µp.c -o lib/µp.o
Linking:
gcc -mcpu=arm7tdmi -I. -gdwarf-2 -DREENTRANT_SYSCALLS_PROVIDED -DROM_RUN -DGCC_ARM7 -I ./include -O2 -Wall -Wcast-align -Wimplicit -Wwrite-strings -Wpointer-arith -Wswitch -Wreturn-type -Wunused -Wshadow -Wa,-adhlns=build/crt0.lst -MD -MP -MF .d
ep/h02.elf.d build/crt0.o h02.o lib/error.o lib/heap_3.o lib/list.o lib/mballoc.o lib/port.o lib/portISR.o lib/printf.o lib/queue.o lib/serial.o lib/spi.o lib/tasks.o lib/uart.o lib/util.o lib/µp.o --output h02.elf -nostartfiles -Wl,-Map=h02.m
ap,–cref -lc -lm -lc -lgcc -Tbuild/LPC2138-ROM.ld
Any thoughts will be greately appreciated.
Guille