Ambroz Bizjak
Ambroz Bizjak

Reputation: 8095

Why does ARM supervisor mode have its own stack?

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

Upvotes: 2

Views: 2524

Answers (1)

artless-noise-bye-due2AI
artless-noise-bye-due2AI

Reputation: 22420

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.

Upvotes: 3

Related Questions