Yahoo Groups archive

Lpc2000

Index last updated: 2026-04-28 23:31 UTC

Thread

Save/restore IRQ registers in GNU C

Save/restore IRQ registers in GNU C

2005-10-25 by Guillermo Prandi

Hi, all!

First of all I want to thank everybody in this group. The amount and 
quality of help I'm receiving from you guys is extraordinary. I 
sincerely hope I can contribute helping others in due time.

I am doing some IRQ compilation tests with GCC and the __attribute__ 
((interrupt("IRQ")) syntax. Apparently the code doesn't seem to 
save/restore all the registers I expected. In particular, it doesn't 
seem to save spsr. Also, only general registers r1-r4 are saved 
before calling an external function. I have the following questions:

* Saving spsr is only needed for reentrant IRQs?
* Perhaps saving only r1-r4 is a convention, and functions should 
always save by themselves any other general registers they may use?
* Should I worry or this is perfectly normal and *safe*?

Thanks in advance.

Guille

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

void Uart0Service(void)
{
18f0:  e24ee004  sub    lr, lr, #4 ; 0x4
18f4:  e92d500f  stmdb  sp!, {r0, r1, r2, r3, ip, lr}
unsigned char temp_id;
temp_id = U0IIR;
    18f8:  e3a03903   mov  r3, #49152   ; 0xc000
    18fc:  e283328e   add  r3, r3, #-536870904  ; 0xe0000008
    1900:  e5d32000   ldrb  r2, [r3]
switch(temp_id & IDENT_MASK) {
    1904:  e202200e   and  r2, r2, #14  ; 0xe
case DATA_AVAILABLE:
case DATA_TIMEOUT:
receive();
break;
case TRANSMIT_AVAILABLE:
transmit();
default:
break;
}
    /* case DATA_AVAILABLE */
    1908:  e3520004   cmp  r2, #4       ; 0x4
    190c:  0a000005   beq  1928 <Uart0Service+0x38>

    /* case DATA_TIMEOUT */
    1910:  e352000c   cmp  r2, #12      ; 0xc
    1914:  0a000003   beq  1928 <Uart0Service+0x38>

    /* case [not] TRANSMIT_AVAILABLE */
    1918:  e3520002   cmp  r2, #2       ; 0x2
    191c:  18fd900f   ldmneia  sp!, {r0, r1, r2, r3, ip, pc}^

    1920:  ebfffa69   bl  2cc <transmit>
    1924:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^

    1928:  ebfffa75   bl  304 <receive>
    192c:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^
}

Shouldn't this code also save spsr? Or perhaps this is only needed 
for reentrant IRQs?

Guille

Re: Save/restore IRQ registers in GNU C

2005-10-25 by bdmlpc

Hello Guille,

which registers do you expect to save/restore when calling a
sub-routine? In generell you are only saving registers you need. In
your IRQ all used registers (r0..r3) are saved and restored.
CPSR (current program status register) must not be saved if you are
not using nested interrupts on your IRQ. CPSR is automatically saved
to SPSR_mode (saved program status register of certain mode) when ARM
switches from user/system/supervisor mode to IRQ mode. IRQ is disabled
when switich to IRQ to disable interrupt nesting. SPSR is shadow
register. It is restored when you return from IRQ mode to your
previously used mode. Therfore don't touch CPSR in interrupt services
routines. It is not a x86 architecture! ARM does everything for you.

  Sten

--- In lpc2000@yahoogroups.com, "Guillermo Prandi"
<yahoo.messenger@m...> wrote:
Show quoted textHide quoted text
>
> Hi, all!
> 
> First of all I want to thank everybody in this group. The amount and 
> quality of help I'm receiving from you guys is extraordinary. I 
> sincerely hope I can contribute helping others in due time.
> 
> I am doing some IRQ compilation tests with GCC and the __attribute__ 
> ((interrupt("IRQ")) syntax. Apparently the code doesn't seem to 
> save/restore all the registers I expected. In particular, it doesn't 
> seem to save spsr. Also, only general registers r1-r4 are saved 
> before calling an external function. I have the following questions:
> 
> * Saving spsr is only needed for reentrant IRQs?
> * Perhaps saving only r1-r4 is a convention, and functions should 
> always save by themselves any other general registers they may use?
> * Should I worry or this is perfectly normal and *safe*?
> 
> Thanks in advance.
> 
> Guille
> 
> void Uart0Service(void) __attribute__ ((interrupt("IRQ"));
> 
> void Uart0Service(void)
> {
> 18f0:  e24ee004  sub    lr, lr, #4 ; 0x4
> 18f4:  e92d500f  stmdb  sp!, {r0, r1, r2, r3, ip, lr}
> unsigned char temp_id;
> temp_id = U0IIR;
>     18f8:  e3a03903   mov  r3, #49152   ; 0xc000
>     18fc:  e283328e   add  r3, r3, #-536870904  ; 0xe0000008
>     1900:  e5d32000   ldrb  r2, [r3]
> switch(temp_id & IDENT_MASK) {
>     1904:  e202200e   and  r2, r2, #14  ; 0xe
> case DATA_AVAILABLE:
> case DATA_TIMEOUT:
> receive();
> break;
> case TRANSMIT_AVAILABLE:
> transmit();
> default:
> break;
> }
>     /* case DATA_AVAILABLE */
>     1908:  e3520004   cmp  r2, #4       ; 0x4
>     190c:  0a000005   beq  1928 <Uart0Service+0x38>
> 
>     /* case DATA_TIMEOUT */
>     1910:  e352000c   cmp  r2, #12      ; 0xc
>     1914:  0a000003   beq  1928 <Uart0Service+0x38>
> 
>     /* case [not] TRANSMIT_AVAILABLE */
>     1918:  e3520002   cmp  r2, #2       ; 0x2
>     191c:  18fd900f   ldmneia  sp!, {r0, r1, r2, r3, ip, pc}^
> 
>     1920:  ebfffa69   bl  2cc <transmit>
>     1924:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^
> 
>     1928:  ebfffa75   bl  304 <receive>
>     192c:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^
> }
> 
> Shouldn't this code also save spsr? Or perhaps this is only needed 
> for reentrant IRQs?
> 
> Guille
>

RE: [lpc2000] Save/restore IRQ registers in GNU C

2005-10-25 by David Hawkins

> Hi, all!
> 
> First of all I want to thank everybody in this group. The amount and 
> quality of help I'm receiving from you guys is extraordinary. I 
> sincerely hope I can contribute helping others in due time.
> 
> I am doing some IRQ compilation tests with GCC and the __attribute__ 
> ((interrupt("IRQ")) syntax. Apparently the code doesn't seem to 
> save/restore all the registers I expected. In particular, it doesn't 
> seem to save spsr. Also, only general registers r1-r4 are saved 
> before calling an external function. I have the following questions:
> 
> * Saving spsr is only needed for reentrant IRQs?
> * Perhaps saving only r1-r4 is a convention, and functions should 
> always save by themselves any other general registers they may use?
> * Should I worry or this is perfectly normal and *safe*?
> 
> Thanks in advance.
> 

Hi Guille,

Let me help explain with the following (but incomplete) startup code:

    .global main
    .global _start
	
    /* Symbols defined by the linker script */
    .global _etext
    .global _data
    .global _edata
    .global _bss
    .global _ebss
    
    /* External functions */
    .global fiq_handler
    .global irq_handler

    .text
    .arm

/* ----------------------------------------------------------------
 * Exception vectors
 * ----------------------------------------------------------------
 */
_start:
    b reset  /* reset */
    b loop   /* undefined instruction */
    b loop   /* software interrupt */
    b loop   /* prefetch abort */
    b loop   /* data abort */
    nop      /* reserved for the bootloader checksum */
    ldr pc, irq_addr

    /* FIQ ISR */
fiq_isr:
    sub   lr, lr, #4
    stmfd sp!, {r0-r3, ip, lr}
    bl fiq_handler
    ldmfd sp!, {r0-r3, ip, pc}^

irq_addr: .word irq_isr
irq_isr:
    sub   lr, lr, #4
    stmfd sp!, {r0-r3, ip, lr}
    bl irq_handler
    ldmfd sp!, {r0-r3, ip, pc}^


Ok, above I define two external interrupt handlers written as
C-functions *** without the __interrupt__ attribute ***

The FIQ handler lives at the FIQ vector address, while the IRQ
handler is loaded into the PC, jumping it to irq_isr, which\
in turn saves context and jumps to the irq_handler function.

The ONLY registers you need to save there are the APCS
(ARM Procedure Calling Standard) 'scratch' registers.
Those registers are; r0-r3, and ip. The lr you save cause
you're calling a function.

All that the GNU compiler does is exactly that, and then
it optimizes, i.e., if it doesn't use r0-r3, it'll remove
them from the generated code.

The SPSR register only needs saving when nesting IRQ interrupts.
If an FIQ interrupt occurs while processing an IRQ interrupt,
its ok, as the FIQ mode has its own SPSR.

Cheers
Dave

RE: [lpc2000] Re: Save/restore IRQ registers in GNU C

2005-10-25 by David Hawkins

Hello Guille,

Out of interest, what were the compiler switches used to get
your output below? (I have only started using GCC myself).

Dave
Show quoted textHide quoted text
> > void Uart0Service(void) __attribute__ ((interrupt("IRQ"));
> > 
> > void Uart0Service(void)
> > {
> > 18f0:  e24ee004  sub    lr, lr, #4 ; 0x4
> > 18f4:  e92d500f  stmdb  sp!, {r0, r1, r2, r3, ip, lr}
> > unsigned char temp_id;
> > temp_id = U0IIR;
> >     18f8:  e3a03903   mov  r3, #49152   ; 0xc000
> >     18fc:  e283328e   add  r3, r3, #-536870904  ; 0xe0000008
> >     1900:  e5d32000   ldrb  r2, [r3]
> > switch(temp_id & IDENT_MASK) {
> >     1904:  e202200e   and  r2, r2, #14  ; 0xe
> > case DATA_AVAILABLE:
> > case DATA_TIMEOUT:
> > receive();
> > break;
> > case TRANSMIT_AVAILABLE:
> > transmit();
> > default:
> > break;
> > }
> >     /* case DATA_AVAILABLE */
> >     1908:  e3520004   cmp  r2, #4       ; 0x4
> >     190c:  0a000005   beq  1928 <Uart0Service+0x38>
> > 
> >     /* case DATA_TIMEOUT */
> >     1910:  e352000c   cmp  r2, #12      ; 0xc
> >     1914:  0a000003   beq  1928 <Uart0Service+0x38>
> > 
> >     /* case [not] TRANSMIT_AVAILABLE */
> >     1918:  e3520002   cmp  r2, #2       ; 0x2
> >     191c:  18fd900f   ldmneia  sp!, {r0, r1, r2, r3, ip, pc}^
> > 
> >     1920:  ebfffa69   bl  2cc <transmit>
> >     1924:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^
> > 
> >     1928:  ebfffa75   bl  304 <receive>
> >     192c:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^
> > }
> > 
> > Shouldn't this code also save spsr? Or perhaps this is only needed 
> > for reentrant IRQs?
> > 
> > Guille
> >
> 
> 
> 
> 
> 
> 
> 
>  
> Yahoo! Groups Links
> 
> 
> 
>  
>

Re: Save/restore IRQ registers in GNU C

2005-10-25 by Guillermo Prandi

Hi, Sten. Thanks for your answer. Since this function was calling 
other functions (and could not possibly know what other registers 
might the other functions alter), I was wondering if this code 
shouldn't have saved the r4-r12 registers. I wasn't aware of APCS (r0-
r3 being scratch registers, as David just explained to me), but I 
deeply suspected there was one.

Guille

--- In lpc2000@yahoogroups.com, "bdmlpc" <list@n...> wrote:
>
> Hello Guille,
> 
> which registers do you expect to save/restore when calling a
> sub-routine? In generell you are only saving registers you need. In
> your IRQ all used registers (r0..r3) are saved and restored.
> CPSR (current program status register) must not be saved if you are
> not using nested interrupts on your IRQ. CPSR is automatically saved
> to SPSR_mode (saved program status register of certain mode) when 
ARM
> switches from user/system/supervisor mode to IRQ mode. IRQ is 
disabled
> when switich to IRQ to disable interrupt nesting. SPSR is shadow
> register. It is restored when you return from IRQ mode to your
> previously used mode. Therfore don't touch CPSR in interrupt 
services
> routines. It is not a x86 architecture! ARM does everything for you.
> 
>   Sten
> 
> --- In lpc2000@yahoogroups.com, "Guillermo Prandi"
> <yahoo.messenger@m...> wrote:
> >
> > Hi, all!
> > 
> > First of all I want to thank everybody in this group. The amount 
and 
> > quality of help I'm receiving from you guys is extraordinary. I 
> > sincerely hope I can contribute helping others in due time.
> > 
> > I am doing some IRQ compilation tests with GCC and the 
__attribute__ 
> > ((interrupt("IRQ")) syntax. Apparently the code doesn't seem to 
> > save/restore all the registers I expected. In particular, it 
doesn't 
> > seem to save spsr. Also, only general registers r1-r4 are saved 
> > before calling an external function. I have the following 
questions:
> > 
> > * Saving spsr is only needed for reentrant IRQs?
> > * Perhaps saving only r1-r4 is a convention, and functions should 
> > always save by themselves any other general registers they may 
use?
> > * Should I worry or this is perfectly normal and *safe*?
> > 
> > Thanks in advance.
> > 
> > Guille
> > 
> > void Uart0Service(void) __attribute__ ((interrupt("IRQ"));
> > 
> > void Uart0Service(void)
> > {
> > 18f0:  e24ee004  sub    lr, lr, #4 ; 0x4
> > 18f4:  e92d500f  stmdb  sp!, {r0, r1, r2, r3, ip, lr}
> > unsigned char temp_id;
> > temp_id = U0IIR;
> >     18f8:  e3a03903   mov  r3, #49152   ; 0xc000
> >     18fc:  e283328e   add  r3, r3, #-536870904  ; 0xe0000008
> >     1900:  e5d32000   ldrb  r2, [r3]
> > switch(temp_id & IDENT_MASK) {
> >     1904:  e202200e   and  r2, r2, #14  ; 0xe
> > case DATA_AVAILABLE:
> > case DATA_TIMEOUT:
> > receive();
> > break;
> > case TRANSMIT_AVAILABLE:
> > transmit();
> > default:
> > break;
> > }
> >     /* case DATA_AVAILABLE */
> >     1908:  e3520004   cmp  r2, #4       ; 0x4
> >     190c:  0a000005   beq  1928 <Uart0Service+0x38>
> > 
> >     /* case DATA_TIMEOUT */
> >     1910:  e352000c   cmp  r2, #12      ; 0xc
> >     1914:  0a000003   beq  1928 <Uart0Service+0x38>
> > 
> >     /* case [not] TRANSMIT_AVAILABLE */
> >     1918:  e3520002   cmp  r2, #2       ; 0x2
> >     191c:  18fd900f   ldmneia  sp!, {r0, r1, r2, r3, ip, pc}^
> > 
> >     1920:  ebfffa69   bl  2cc <transmit>
> >     1924:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^
> > 
> >     1928:  ebfffa75   bl  304 <receive>
> >     192c:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^
> > }
> > 
> > Shouldn't this code also save spsr? Or perhaps this is only 
needed 
Show quoted textHide quoted text
> > for reentrant IRQs?
> > 
> > Guille
> >
>

Re: Save/restore IRQ registers in GNU C

2005-10-25 by Guillermo Prandi

Thanks, Dave. This answers my question. I wasn't aware of the APCS, 
but I deeply suspected there was one.

Best regards,

Guille

--- In lpc2000@yahoogroups.com, "David Hawkins" <dwh@o...> wrote:
>
> 
> 
> > Hi, all!
> > 
> > First of all I want to thank everybody in this group. The amount 
and 
> > quality of help I'm receiving from you guys is extraordinary. I 
> > sincerely hope I can contribute helping others in due time.
> > 
> > I am doing some IRQ compilation tests with GCC and the 
__attribute__ 
> > ((interrupt("IRQ")) syntax. Apparently the code doesn't seem to 
> > save/restore all the registers I expected. In particular, it 
doesn't 
> > seem to save spsr. Also, only general registers r1-r4 are saved 
> > before calling an external function. I have the following 
questions:
> > 
> > * Saving spsr is only needed for reentrant IRQs?
> > * Perhaps saving only r1-r4 is a convention, and functions should 
> > always save by themselves any other general registers they may 
use?
> > * Should I worry or this is perfectly normal and *safe*?
> > 
> > Thanks in advance.
> > 
> 
> Hi Guille,
> 
> Let me help explain with the following (but incomplete) startup 
code:
Show quoted textHide quoted text
> 
>     .global main
>     .global _start
> 	
>     /* Symbols defined by the linker script */
>     .global _etext
>     .global _data
>     .global _edata
>     .global _bss
>     .global _ebss
>     
>     /* External functions */
>     .global fiq_handler
>     .global irq_handler
> 
>     .text
>     .arm
> 
> /* ----------------------------------------------------------------
>  * Exception vectors
>  * ----------------------------------------------------------------
>  */
> _start:
>     b reset  /* reset */
>     b loop   /* undefined instruction */
>     b loop   /* software interrupt */
>     b loop   /* prefetch abort */
>     b loop   /* data abort */
>     nop      /* reserved for the bootloader checksum */
>     ldr pc, irq_addr
> 
>     /* FIQ ISR */
> fiq_isr:
>     sub   lr, lr, #4
>     stmfd sp!, {r0-r3, ip, lr}
>     bl fiq_handler
>     ldmfd sp!, {r0-r3, ip, pc}^
> 
> irq_addr: .word irq_isr
> irq_isr:
>     sub   lr, lr, #4
>     stmfd sp!, {r0-r3, ip, lr}
>     bl irq_handler
>     ldmfd sp!, {r0-r3, ip, pc}^
> 
> 
> Ok, above I define two external interrupt handlers written as
> C-functions *** without the __interrupt__ attribute ***
> 
> The FIQ handler lives at the FIQ vector address, while the IRQ
> handler is loaded into the PC, jumping it to irq_isr, which\
> in turn saves context and jumps to the irq_handler function.
> 
> The ONLY registers you need to save there are the APCS
> (ARM Procedure Calling Standard) 'scratch' registers.
> Those registers are; r0-r3, and ip. The lr you save cause
> you're calling a function.
> 
> All that the GNU compiler does is exactly that, and then
> it optimizes, i.e., if it doesn't use r0-r3, it'll remove
> them from the generated code.
> 
> The SPSR register only needs saving when nesting IRQ interrupts.
> If an FIQ interrupt occurs while processing an IRQ interrupt,
> its ok, as the FIQ mode has its own SPSR.
> 
> Cheers
> Dave
>

Re: Save/restore IRQ registers in GNU C

2005-10-25 by Guillermo Prandi

Here's my compile command line:

gcc -c -mcpu=arm7tdmi -I. -gdwarf-2 -DREENTRANT_SYSCALLS_PROVIDED -
DROM_RUN -DGCC_ARM7 -I ./include -O3 -Wall -Wcast-align -Wimplicit -
Wwrite-strings -Wpointer-arith -Wswitch -Wreturn-type -Wunused -
Wshadow -Wa,-adhlns=lib/serial.lst -MD -MP -MF .dep/serial.o.d -
Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes  -
std=gnu99 lib/serial.c -o lib/serial.o

Guille


--- In lpc2000@yahoogroups.com, "David Hawkins" <dwh@o...> wrote:
>
> 
> Hello Guille,
> 
> Out of interest, what were the compiler switches used to get
> your output below? (I have only started using GCC myself).
> 
> Dave
> 
> 
> > > void Uart0Service(void) __attribute__ ((interrupt("IRQ"));
> > > 
> > > void Uart0Service(void)
> > > {
> > > 18f0:  e24ee004  sub    lr, lr, #4 ; 0x4
> > > 18f4:  e92d500f  stmdb  sp!, {r0, r1, r2, r3, ip, lr}
> > > unsigned char temp_id;
> > > temp_id = U0IIR;
> > >     18f8:  e3a03903   mov  r3, #49152   ; 0xc000
> > >     18fc:  e283328e   add  r3, r3, #-536870904  ; 0xe0000008
> > >     1900:  e5d32000   ldrb  r2, [r3]
> > > switch(temp_id & IDENT_MASK) {
> > >     1904:  e202200e   and  r2, r2, #14  ; 0xe
> > > case DATA_AVAILABLE:
> > > case DATA_TIMEOUT:
> > > receive();
> > > break;
> > > case TRANSMIT_AVAILABLE:
> > > transmit();
> > > default:
> > > break;
> > > }
> > >     /* case DATA_AVAILABLE */
> > >     1908:  e3520004   cmp  r2, #4       ; 0x4
> > >     190c:  0a000005   beq  1928 <Uart0Service+0x38>
> > > 
> > >     /* case DATA_TIMEOUT */
> > >     1910:  e352000c   cmp  r2, #12      ; 0xc
> > >     1914:  0a000003   beq  1928 <Uart0Service+0x38>
> > > 
> > >     /* case [not] TRANSMIT_AVAILABLE */
> > >     1918:  e3520002   cmp  r2, #2       ; 0x2
> > >     191c:  18fd900f   ldmneia  sp!, {r0, r1, r2, r3, ip, pc}^
> > > 
> > >     1920:  ebfffa69   bl  2cc <transmit>
> > >     1924:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^
> > > 
> > >     1928:  ebfffa75   bl  304 <receive>
> > >     192c:  e8fd900f   ldmia  sp!, {r0, r1, r2, r3, ip, pc}^
> > > }
> > > 
> > > Shouldn't this code also save spsr? Or perhaps this is only 
needed 
Show quoted textHide quoted text
> > > for reentrant IRQs?
> > > 
> > > Guille
> > >
> > 
> > 
> > 
> > 
> > 
> > 
> > 
> >  
> > Yahoo! Groups Links
> > 
> > 
> > 
> >  
> >
>

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.