2

I'm playing around with an Atmel AT91SAM7S microcontroller, and it looks like IRQ handlers are supposed to execute in supervisor mode, while main loop code executes in system mode. Plus, I'm supposed to reserve a certain portion of RAM to be used as stack by supervisor mode code. The startup assembly code I have obtained from a demo program reserves 128 bytes by default.

Why do I have to reserve separate stack space for supervisor mode; why can't it just use the same stack as system mode (main)? What is the benefit of interrupt handling code having a completely different stack than main loop code? I see that the current assembly code for IRQ handling switches from IRQ mode to supervisor mode before jumping to an interrupt handler. Would it be appropriate to execute the interrupt handler in user mode instead? If so, is there anything I need to be careful with?

I'm asking because if the interrupts have their own stack, I need to estimate an upper bound on how much stack space would be used by interrupts in the worst case. If interrupts used the same stack as main, there would be no need to do that, as long as there actually is enough RAM available (which most certainly is, I won't be using that much).

The only thing I can think of is that having a separate stack would be useful if you're implementing an operating system with some kind of memory protection; but since I'm not doing that, does it matter?

P.S. I'm familiar with AVRs and their interrupt handling.

Clarification

It appears interrupt handling begins when the CPU jumps to address 0x18, which contains a branch instruction to AT91F_Irq_Handler, below. From what I can tell, the processor automatically enters interrupt mode; this assembly switches to supervisor mode before branching to a (C) function depending on which interrupt line was triggered. It obtains the branch address from the Advanced Interrupt Controller (AIC).

AT91F_Irq_Handler:
/* Manage Exception Entry               */
/* Adjust and save LR_irq in IRQ stack  */
                sub         lr, lr, #4
                stmfd       sp!, {lr}
/* Save r0 and SPSR (need to be saved for nested interrupt)  */
                mrs         r14, SPSR
                stmfd       sp!, {r0,r14}
/* Write in the IVR to support Protect Mode                 */
/* No effect in Normal Mode                             */
/* De-assert the NIRQ and clear the source in Protect Mode  */
                ldr         r14, =AT91C_BASE_AIC
                ldr         r0 , [r14, #AIC_IVR]
                str         r14, [r14, #AIC_IVR]

/* Enable Interrupt and Switch in Supervisor Mode  */
                msr         CPSR_c, #ARM_MODE_SVC

/* Save scratch/used registers and LR in User Stack  */
                stmfd       sp!, { r1-r3, r12, r14}

/* Branch to the routine pointed by the AIC_IVR  */
                mov         r14, pc
                bx          r0
/* Manage Exception Exit                                  */
/* Restore scratch/used registers and LR from User Stack  */
                ldmia       sp!, { r1-r3, r12, r14}

/* Disable Interrupt and switch back in IRQ mode  */
                msr         CPSR_c, #I_BIT | ARM_MODE_IRQ
/* Mark the End of Interrupt on the AIC  */
                ldr         r14, =AT91C_BASE_AIC
                str         r14, [r14, #AIC_EOICR]
/* Restore SPSR_irq and r0 from IRQ stack  */
                ldmia       sp!, {r0,r14}
                msr         SPSR_cxsf, r14
/* Restore adjusted  LR_irq from IRQ stack directly in the PC  */
                ldmia       sp!, {pc}^
Ambroz Bizjak
  • 7,809
  • 1
  • 38
  • 49
  • A stack overflow is a very nasty fault, the reason for this web site's name. If the supervisor used the same stack then there would be no way to recover from an SO in user mode. Or to deal with an SO generated by an IRQ or exception. – Hans Passant Sep 20 '13 at 19:49
  • @HansPassant There isn't supposed to be a stack overflow anywhere; if you can ensure this, you don't have to care about recovering in the first place. But with (for example) only 128 bytes stack for interrupts, it's easy to accidentally get an overflow (since the interrupt stack will overflow into the user stack, at least in my case). There wouldn't be a problem if the interrupts just continued with the stack where the user stack ends. – Ambroz Bizjak Sep 20 '13 at 19:52
  • I thought we had [a similar question](http://stackoverflow.com/questions/18638711/why-is-cpsr-not-a-banked-register); it is about the `cpsr` which might be interesting to people looking at this question. – artless noise Sep 20 '13 at 20:44

1 Answers1

3

You do not have to use the supervisor r13 as a stack. You can use it as a general purpose register to save something and then transition to system mode and run the rest of the ISR in that mode. It is flexible and you can have a different stack if you wish; you are not hand-cuffed like other architectures. You may stuff the supervisor r13 with some save area pointer or commonly a pointer (or double pointer) to the currently executing task. You need to save the lr, spsr, and any other registers that will be clobbered if you expect the ISR to return. I think that initially, a normal ARM will be in interrupt mode; maybe you have some other handler that already transitions to supervisor mode? Often the system mode has a kernel stack/task control block and it switches in a multi-tasking system. The supervisor mode is a common mode for exception handling.

If you only have a single task and you want to use it's stack you can use the following. After saving context in supervisor, transition to system mode and execute the remainder of the ISR. Just restore the registers including the lr and cpsr to go back to the correct mode, program counter and condition codes.

You do of course have the option to use the supervisor r13 as an exception stack, which many people might like with bounded behavior and faster IRQ latency as the stack is preset and ready to be used by the main ISR body.

artless noise
  • 21,212
  • 6
  • 68
  • 105
  • Most importantly make sure you correct the stack by `pop`ing before you return to the interrupted user code. Your user code might need some options. For instance, sometime 'C' uses space [both above and below the stack pointer](http://stackoverflow.com/questions/15752188/arm-link-register-and-frame-pointer). It is only adjusted on a function call. If you just use the memory, you might be stepping on the interrupted codes local variables. – artless noise Sep 20 '13 at 20:47
  • Yes, I understand the danger, though the same stack method is used in AVR, and supposedly there are no problems with overwriting user data because it was written before the stack pointer was incremented. Do you think ARM compilers (gcc) aren't that careful in that regard? – Ambroz Bizjak Sep 20 '13 at 20:51
  • I would guess that `gcc` has some option to make it work and another option that will break it. It may even depend on the version of `gcc`; You need to look at the manual and probably the generated code. I think that locals are referenced by the `fp` and probably it is safe, but I would (and have) allocate another stack; interrupt handlers are not usually that complex. Just change the 128 bytes to fit your ISR requirements; you need the ISR stack requirements to be high and system memory to be low for this not to make sense. – artless noise Sep 20 '13 at 20:54
  • I haven't found any such option in the gcc man page... But note that having a separate stack for interrupts does not help in this regard if you allow interrupt nesting. – Ambroz Bizjak Sep 22 '13 at 18:17