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}^