Wrong return address in ISR with GCC

Hi!

I've found a strange thing when using interrupts with GCC.

When I compile the code and run it GCC tends to change the code depending in the content of the ISR-routine. When the interrupt occurs the return address + 0x04 is stored in lr register. This is as it should be. The first thing that happens in the ISR is a SUBS lr, lr, #0x04 to get the correct return address. The the ISR will push all used registers on the stack, including lr.

In the end of the ISR it will read from the stack to restore all registers. It's now the strange thing happen.

The compiler can generate two different types of code here: Version 1: Start of ISR:

1ee8: e24ee004 sub lr, lr, #4 ; 0x4 1eec: e92d500f stmdb sp!, {r0, r1, r2, r3, ip, lr} 1ef0: e24dd004 sub sp, sp, #4 ; 0x4 End of ISR: 2004: e28dd004 add sp, sp, #4 ; 0x4 2008: e8bd500f ldmia sp!, {r0, r1, r2, r3, ip, lr} 200c: e25ef004 subs pc, lr, #4 ; 0x4

When popping register from the stack it will restore lr to it's original state. Then PC is loaded with lr-0x04. Because we already did that at the ISR start it will totally removed 8 instead of 4 and this leads to some funny bugs

Version 2: Start of ISR:

1ee8: e24ee004 sub lr, lr, #4 ; 0x4 1eec: e92d500f stmdb sp!, {r0, r1, r2, r3, ip, lr} End of ISR: 1f44: e8fd900f ldmia sp!, {r0, r1, r2, r3, ip, pc}^ The differens here is that it will push all used register including lr. But when it reads them back it will restore lr to PC instead of lr and that will lead to the correct return address.

I've attached my C source code. Am I doing something wrong?

Hope someone can help me...

GCC version: Using built-in specs. Target: arm-elf Configured with: ../gcc-4.1.1/configure --target=arm-elf

--prefix=/g/gnuarm-4.1.1 --enable-interwork --enable-multilib

--with-float=soft --with-newlib

--with-headers=../newlib-1.14.0/newlib/libc/include

--enable-languages=c,c++ Thread model: single gcc version 4.1.1

********************************** Version 1: (C code) ***********************************

__attribute__((interrupt("IRQ"))) void PF_TIM1_Interrupt(void);

void PF_TIM1_Interrupt(void) { uint32 i, a, b, c; if(Tim1Counter >= 3) { g_Timer_Expired = TRUE; Tim1Counter = 0; }

Tim1Counter++;

for(i=0;iVAR = 0x0000; //Acknowledge interrupt }

********************************** Version 1: (C and ASM) ***********************************

static void PF_TIM1_Interrupt(void) {

1ee8: e24ee004 sub lr, lr, #4 ; 0x4 1eec: e92d500f stmdb sp!, {r0, r1, r2, r3, ip, lr} 1ef0: e24dd004 sub sp, sp, #4 ; 0x4 static uint32 i, a, b, c; if(Tim1Counter >= 3) 1ef4: e59f3114 ldr r3, [pc, #276] ; 2010 1ef8: e5933000 ldr r3, [r3] 1efc: e3530002 cmp r3, #2 ; 0x2 1f00: 9a000005 bls 1f1c { g_Timer_Expired = TRUE; 1f04: e59f2108 ldr r2, [pc, #264] ; 2014 1f08: e3a03001 mov r3, #1 ; 0x1 1f0c: e5c23000 strb r3, [r2] Tim1Counter = 0; 1f10: e59f20f8 ldr r2, [pc, #248] ; 2010 1f14: e3a03000 mov r3, #0 ; 0x0 1f18: e5823000 str r3, [r2] }

Tim1Counter++;

1f1c: e59f30ec ldr r3, [pc, #236] ; 2010 1f20: e5933000 ldr r3, [r3] 1f24: e2832001 add r2, r3, #1 ; 0x1 1f28: e59f30e0 ldr r3, [pc, #224] ; 2010 1f2c: e5832000 str r2, [r3]

for(i=0;iVAR = 0x0000; //Acknowledge interrupt

1ff8: e59f2034 ldr r2, [pc, #52] ; 2034 1ffc: e3a03000 mov r3, #0 ; 0x0 2000: e5823030 str r3, [r2, #48] } 2004: e28dd004 add sp, sp, #4 ; 0x4 2008: e8bd500f ldmia sp!, {r0, r1, r2, r3, ip, lr} 200c: e25ef004 subs pc, lr, #4 ; 0x4

********************************** Version 2: (C code)

*********************************** __attribute__((interrupt("IRQ"))) void PF_TIM1_Interrupt(void);

void PF_TIM1_Interrupt(void) { //uint32 i, a, b, c; if(Tim1Counter >= 3) { g_Timer_Expired = TRUE; Tim1Counter = 0; }

Tim1Counter++;

/* for(i=0;iVAR = 0x0000; //Acknowledge interrupt }

********************************** Version 2: (C and ASM) *********************************** void PF_TIM1_Interrupt(void) { 1ee8: e24ee004 sub lr, lr, #4 ; 0x4 1eec: e92d500f stmdb sp!, {r0, r1, r2, r3, ip, lr} //uint32 i, a, b, c; if(Tim1Counter >= 3) 1ef0: e59f3050 ldr r3, [pc, #80] ; 1f48 1ef4: e5933000 ldr r3, [r3] 1ef8: e3530002 cmp r3, #2 ; 0x2 1efc: 9a000005 bls 1f18 { g_Timer_Expired = TRUE; 1f00: e59f2044 ldr r2, [pc, #68] ; 1f4c 1f04: e3a03001 mov r3, #1 ; 0x1 1f08: e5c23000 strb r3, [r2] Tim1Counter = 0; 1f0c: e59f2034 ldr r2, [pc, #52] ; 1f48 1f10: e3a03000 mov r3, #0 ; 0x0 1f14: e5823000 str r3, [r2] }

Tim1Counter++;

1f18: e59f3028 ldr r3, [pc, #40] ; 1f48 1f1c: e5933000 ldr r3, [r3] 1f20: e2832001 add r2, r3, #1 ; 0x1 1f24: e59f301c ldr r3, [pc, #28] ; 1f48 1f28: e5832000 str r2, [r3]

/* for(i=0;iVAR = 0x0000; //Acknowledge interrupt

1f38: e59f2014 ldr r2, [pc, #20] ; 1f54 1f3c: e3a03000 mov r3, #0 ; 0x0 1f40: e5823030 str r3, [r2, #48] } 1f44: e8fd900f ldmia sp!, {r0, r1, r2, r3, ip, pc}^
Reply to
Erik Larsson
Loading thread data ...

Using __attribute__((interrupt("IRQ"))), Use an assembly interrupt stub instead. I'm mostly serious here.

I've made it a policy to avoid architecture specific compiler extensions where possible, they tend to be the buggiest part of the compilers (and most of the issues I've run into have been in commercial compilers by the by). Given a) how easy, and b) how educational an interrupt stub is to write it's not worth avoiding the task. Heck the simplest is to borrow one from someone else and verify/modify it to suit.

Robert

Reply to
Robert Adsett

[Code snipped]

Go to

formatting link
There are nice guides for both ARM7 and ARM9 which explains the ARM interrupt structure in detail including example snippets for gcc. I am using the interrupt attribute with gcc 4.2 for arm without any problems.

Regards Anton Erasmus

Reply to
Anton Erasmus

Robert Adsett wrote: ...

...

The same advice from me. Application stared cgeashing after change of the newlib version (?!) After a day of debugging I found problem in the ISR entry/exit code generated by GCC. After switch to assembly all is working ok.

Regards,

--
Artur Lipowski
Reply to
Artur Lipowski

In which version did you find a problem? I have not seen anything suspect in the since older V3.x versions.

--
Regards,
Richard.

+ http://www.FreeRTOS.org
13 official architecture ports, 1000 downloads per week.

+ http://www.SafeRTOS.com
Certified by TÜV as meeting the requirements for safety related systems.
Reply to
FreeRTOS.org

GCC 4.1.2

Regards,

--
Artur Lipowski
Reply to
Artur Lipowski

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.