Re: arm-elf-gcc building erroneous code for ISR (long posting)

Jun 14, 2004 3 Replies

You can download a newer (actually two: 3.3.1 and 3.4.0) GCC from my webpage. I'm not a GCC expert, but I know they've fixed a serious ARM bugs in their IRQ handling around 3.3.0. You might be hitting it.


-- Regards, Andras Tantos


Hello group,
>
> I'm curently trying to get familiar with the ARM7 by building some
> example-projects for a LPC2106-controller. One such project is the

blinky_irq


example from Keil which should demonstrate the use of interrupts and of

the


LPC2xxx vectored interrupt controller. I'm using gcc-3.2.1 with binutils
3.13.1
and newlib 1.11.0 . So far I had no problems with these tools and I was

able to


build and succesfully run some small demonstrator programs (without

interrupts)


on my LPC2106 board. For the blinky_irq example program I use the linker

script


and startup.s files (with adjustment for the Flash and RAM sizes of the

LPC2106)


that came with the source code from Keil's download area. Since the

program


didn't work I stripped it from all unnecessary functionality (main() is
> essentially an empty loop now) and simply let the timer ISR toggle a LED

on


GPIO24. That worked initially, but after some time (~ 3 minutes) the LED

stopped


toggling. I suspected that somwhere I'm loosing memory such that after

some time


I'm out of RAM. Hence, I took a look at the disassembled code and saw that

the


ISR is messing up the IRQ-mode stack pointer, loosing seven words each

time the


ISR is invoked.
> Since the ISR entry and exit codes are automatically generated by the

compiler I


suspect a bug in gcc. On the other hand, since this would be a very severe
> failure other users should have come across it, too. Does anybody of you

know


that error (i.e. should I change to another gcc version) or am I missing
> something (e.g. gcc options)?
> Below you'll find the build commands used, the sources of the program and

the


disassembled ISR code.
>
> TIA,
> Jens
>
> ------build commands-----------------------------
> $ make blinky_irq.hex
> arm-elf-as -mcpu=arm7tdmi -gstabs Startup.s -o Startup.o
> Startup.s: Assembler messages:
>
> arm-elf-gcc -c -g -I. -mcpu=arm7tdmi Time.c -o Time.o
> arm-elf-gcc -c -g -I. -mcpu=arm7tdmi Blinky.c -o Blinky.o
> arm-elf-gcc -T Flash.ld -nostartfiles -Lgcc -L. -o blinky_irq.elf

Startup.o


Time.o Blinky.o
> arm-elf-objcopy -I elf32-littlearm -O ihex blinky_irq.elf blinky_irq.hex
>
> ------sources------------------------------------
>

/***************************************************************************


***/
/*
*/
/* BLINKY.C: LED Flasher
*/
/*
*/

/***************************************************************************


***/

> #include "LPC210x.h"
> #include "Timer.h"
>
> extern long volatile timeval; /*ISR counter variable*/
>
> void wait (void) { /* wait function, not used

actually */


unsigned long i;
>
> i = timeval;
> while ((i + 10) > timeval){
> }; /* wait 100ms */
> }
>
> int main (void) {
>
> IODIR = 0xFF000000; /* P1.31..24 defined as

Outputs */



> init_timer();
>
> while (1) { /* Loop forever */
> }
> }
>
>

/***************************************************************************


***/
/*
*/
/* TIME.C: Time Functions for 10Hz Clock Tick
*/
/*
*/

/***************************************************************************


***/

> #include "LPC210x.h"
> #include "Timer.h"
>
> long volatile timeval; //counter for wait-routine
>
> void tc0 (void) __attribute__ ((interrupt)); // Generate Interrupt
>
> /* Setup the Timer Counter 0 Interrupt */
> void init_timer (void) {
> VICVectAddr0 = (unsigned long)tc0; // set interrupt vector

in 0


VICVectCntl0 = 0x20 | 4; // use it for Timer 0

Interrupt


VICIntEnable = 0x00000010; // Enable Timer0

Interrupt



> TIMER0_MR0 = 5898239; // 100mSec =
> (0.1*4*14,7456E6)-1 counts
> TIMER0_MCR = 3; // Interrupt and Reset on

MR0


TIMER0_TCR = 1; // Timer0 Enable
> }
>
>
> /* Timer Counter 0 Interrupt executes each 100ms @ 4x14,7456 MHz CPU Clock
*/
void tc0 (void) {
> /* timeval++;*/
> if((IOPIN&0x01000000) == 0x00000000){
> IOSET = 0x01000000;
> }else{
> IOCLR = 0x01000000; //toggle GPIO24
> }
> TIMER0_IR = 1; // Clear interrupt flag
> VICVectAddr = 0; // Acknowledge Interrupt
> }
>
> -------------------disassebled ISR (with some

comments)---------------------


...
> 00000278 :
> ; --ip is stored on stack but not restored at ISR exit (see below)!!
> 278: e52dc004 str ip, [sp, -#4]!
> 27c: e1a0c00d mov ip, sp
> 280: e24ee004 sub lr, lr, #4 ; 0x4
> ; --write-back further decrements sp. This is not corrected on ISR exit!!
> ; --btw: why is pc stored on the stack anyway and why is ip with it's new
> ; --contents pushed onto the stack, too??
> 284: e92dd80c stmdb sp!, {r2, r3, fp, ip, lr, pc}
> 288: e24cb004 sub fp, ip, #4 ; 0x4
> 28c: e3a0320e mov r3, #-536870912 ; 0xe0000000
> 290: e283390a add r3, r3, #163840 ; 0x28000
> 294: e5933000 ldr r3, [r3]
> 298: e2033401 and r3, r3, #16777216 ; 0x1000000
> 29c: e3530000 cmp r3, #0 ; 0x0
> 2a0: 1a000004 bne 2b8
> 2a4: e3a0324e mov r3, #-536870908 ; 0xe0000004
> 2a8: e283390a add r3, r3, #163840 ; 0x28000
> 2ac: e3a02401 mov r2, #16777216 ; 0x1000000
> 2b0: e5832000 str r2, [r3]
> 2b4: ea000003 b 2c8
> 2b8: e3a032ce mov r3, #-536870900 ; 0xe000000c
> 2bc: e283390a add r3, r3, #163840 ; 0x28000
> 2c0: e3a02401 mov r2, #16777216 ; 0x1000000
> 2c4: e5832000 str r2, [r3]
> 2c8: e3a0320e mov r3, #-536870912 ; 0xe0000000
> 2cc: e2833901 add r3, r3, #16384 ; 0x4000
> 2d0: e3a02001 mov r2, #1 ; 0x1
> 2d4: e5832000 str r2, [r3]
> 2d8: e3e03d3f mvn r3, #4032 ; 0xfc0
> 2dc: e243300f sub r3, r3, #15 ; 0xf
> 2e0: e3a02000 mov r2, #0 ; 0x0
> 2e4: e5832000 str r2, [r3]
> ; --registers are restored, pc is loaded with (lr-4) and cpsr is restored
> ; --so this causes a return to the user code. But ip is not restored to

it's


; --initial value but to the value that was assigned to ip after it was

saved


; --on the stack. And, sp is not restored to it's initial value!!
> 2e8: e95b980c ldmdb fp, {r2, r3, fp, ip, pc}^ ;
>
>
>

You can download a newer (actually two: 3.3.1 and 3.4.0) GCC from my webpage. I know they've fixed a serious ARM bugs in their IRQ handling around 3.3.0. You might be hitting it.

-- Regards, Andras Tantos

blinky_irq

the

3.13.1

able to

interrupts)

script

LPC2106)

program

on

stopped

some time

the

time the

compiler I

know

the

Hi Andras,

I already suspected such a bug but wanted to make sure that a new gcc version would solve my problem before downloading and installing the new tools. BTW: the link to your page gets me a "403: Forbidden" error. Anyway, thanks for the reply, Jens

blinky_irq

the

3.13.1

able to

interrupts)

script

LPC2106)

program

on

stopped

some time

the

time the

compiler I

know

the

[snip]

using arm-elf-gcc 3.3.1 I don't have this problem. Not that the interrupt attribute needs to specify the type of interrupt ("IRQ", "FIQ","SWI" etc).

void __attribute__((interrupt("IRQ"))) SYS_10ms_irq_handler(void) { TIMER2->status = 4; // clear timer int VIC->VectAddr = 0; // clear VIC tick_count++; }

arm-elf-gcc -S -O -DUVDAS -mcpu=arm7tdmi sys_irq.c

SYS_10ms_irq_handler: @ Interrupt Service Routine. @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 @ link register save eliminated. stmfd sp!, {r2, r3} @ lr needed for prologue mvn r3, #244736 sub r3, r3, #940 sub r3, r3, #3 mov r2, #4 str r2, [r3, #8] mov r3, #-2147483648 mov r3, r3, asr #19 mov r2, #0 str r2, [r3, #48] ldr r2, .L3 ldr r3, [r2, #0] add r3, r3, #1 str r3, [r2, #0] ldmfd sp!, {r2, r3} subs pc, lr, #4 .L4: .align 2 .L3: .word tick_count

this include the correct subs pc,lr,#4 that performs a return from interrupt (swapping back to old stack).

regards Peter

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required