alt_exception_entry.S


#include "system.h"

/*
 * This is the exception entry point code, which saves all the caller saved
 * registers and then handles the appropriate exception.  It should be pulled
 * in using a .globl from all the exception handler routines.  This scheme is
 * used so that if an interrupt is never registered, then this code will not
 * appear in the generated executable, thereby improving code footprint.
 */

        /*
         * Explicitly allow the use of r1 (the assembler temporary register)
         * within this code. This register is normally reserved for the use of
         * the assembler.
         */
        .set noat

        /*
         * The top and bottom of the exception stack
         */
#ifdef ALT_EXCEPTION_STACK

        .globl __alt_exception_stack_pointer

#ifdef ALT_STACK_CHECK

        .globl __alt_exception_stack_limit

        /*
         * We need to store the value of the stack limit after interrupt somewhere.
         */
        .globl  alt_exception_old_stack_limit

#endif
#endif

        .section .exceptions.entry.label, "xa"

        .globl alt_exception
        .type alt_exception, @function
alt_exception:

        .section .exceptions.entry, "xa"

#ifdef ALT_EXCEPTION_STACK

#ifdef ALT_STACK_CHECK
        stw   et, %gprel(alt_exception_old_stack_limit)(gp)
#endif

        movhi et, %hiadj(__alt_exception_stack_pointer - 80)
        addi  et, et, %lo(__alt_exception_stack_pointer - 80) 
        stw   sp, 76(et)
        mov   sp, et

#ifdef ALT_STACK_CHECK
        movhi et, %hiadj(__alt_exception_stack_limit)
        addi  et, et, %lo(__alt_exception_stack_limit) 
        stw   et, %gprel(alt_stack_limit_value)(gp)
#endif

#else
        /* 
         * Process an exception.  For all exceptions we must preserve all
         * caller saved registers on the stack (See the Nios2 ABI
         * documentation for details).
         */

        addi  sp, sp, -76

#ifdef ALT_STACK_CHECK

        bltu  sp, et, .Lstack_overflow

#endif

#endif

        stw   ra,  0(sp)

        /*
         * Leave a gap in the stack frame at 4(sp) for the muldiv handler to
         * store zero into.
         */

        stw   r1,   8(sp)
        stw   r2,  12(sp)
        stw   r3,  16(sp)
        stw   r4,  20(sp)
        stw   r5,  24(sp)
        stw   r6,  28(sp)
        stw   r7,  32(sp)

        rdctl r5, estatus

        stw   r8,  36(sp)
        stw   r9,  40(sp)
        stw   r10, 44(sp)
        stw   r11, 48(sp)
        stw   r12, 52(sp)
        stw   r13, 56(sp)
        stw   r14, 60(sp)
        stw   r15, 64(sp)

        stw   r5,  68(sp)
        addi  r15, ea, -4 /* re-issue the interrupted instruction */
        stw   r15,  72(sp)

        /*
         * The interrupt testing code goes here.  If an interrupt is active
         * then it stores ea-4 into 72(sp), handles the interrupt and jumps to
         * .exceptions.exit.  If there is no interrupt then it continues
         */

        .section .exceptions.notirq, "xa"

        stw   ea,  72(sp) /* Return after the instruction which caused the exception */
        ldw   r2, -4(ea)

        /*
         * The other exception handling code goes here.
         */

        .section .exceptions.unknown

        /*
        *  If you get here then one of the following could have happened:
        *
        *  - Your program could have been compiled for a full-featured Nios II
        *    core, but it is running on a smaller core, and instruction emulation
        *    has been disabled by defining ALT_NO_INSTRUCTION_EMULATION.
        *
        *    You can work around the problem by re-enabling instruction emulation,
        *    or you can figure out why your program is being compiled for a system
        *    other than the one that it is running on.
        *
        *  - Your program has executed a trap instruction, but has not implemented
        *    a handler for this instruction.
        *
        *  - Your program has executed an illegal instruction (one which is not
        *    defined in the instruction set).
        *
        *  - Your hardware is broken and is generating spurious interrupts (a
        *    peripheral which deasserts its interrupt output before its interrupt
        *    handler has been executed will cause spurious interrupts).
        *
        */

#ifdef NIOS2_HAS_DEBUG_STUB

        /*
        *  Either tell the user now (if there is a debugger attached) or go into
        *  the debug monitor which will loop until a debugger is attached.
        */
        break

#else

        /*
        *  If there is no debug stub then a BREAK will probably cause a reboot.
        *  An infinate loop will probably be more useful.
        */
0:
        br   0b

#endif

        .section .exceptions.exit, "xa"

        /* 
         * Restore the saved registers, so that all general purpose registers 
         * have been restored to their state at the time the interrupt occured.
         */

        ldw   r5,  68(sp)
        ldw   ea,  72(sp)
        ldw   ra,   0(sp)

        wrctl estatus, r5

        ldw   r1,   8(sp)
        ldw   r2,  12(sp)
        ldw   r3,  16(sp)
        ldw   r4,  20(sp)
        ldw   r5,  24(sp)
        ldw   r6,  28(sp)
        ldw   r7,  32(sp)

#ifdef ALT_EXCEPTION_STACK
#ifdef ALT_STACK_CHECK
        ldw   et, %gprel(alt_exception_old_stack_limit)(gp)
#endif
#endif

        ldw   r8,  36(sp)
        ldw   r9,  40(sp)
        ldw   r10, 44(sp)
        ldw   r11, 48(sp)
        ldw   r12, 52(sp)
        ldw   r13, 56(sp)
        ldw   r14, 60(sp)
        ldw   r15, 64(sp)

#ifdef ALT_EXCEPTION_STACK

#ifdef ALT_STACK_CHECK
        stw   et, %gprel(alt_stack_limit_value)(gp)
        stw   zero, %gprel(alt_exception_old_stack_limit)(gp)
#endif

        ldw   sp,  76(sp)

#else
        addi  sp, sp, 76

#endif

        /*
         * Return to the interrupted instruction.
         */

        eret

#ifdef ALT_STACK_CHECK

.Lstack_overflow:
        break 3

#endif


Maintained by John Loomis, updated Tue Nov 13 2008