|========================================================================== | | vectors.S | | ColdFire exception vectors | |========================================================================== | ####ECOSGPLCOPYRIGHTBEGIN#### | ------------------------------------------- | This file is part of eCos, the Embedded Configurable Operating System. | Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006 Free Software Foundation, Inc. | | eCos is free software; you can redistribute it and/or modify it under | the terms of the GNU General Public License as published by the Free | Software Foundation; either version 2 or (at your option) any later | version. | | eCos is distributed in the hope that it will be useful, but WITHOUT | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | for more details. | | You should have received a copy of the GNU General Public License | along with eCos; if not, write to the Free Software Foundation, Inc., | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | | As a special exception, if other files instantiate templates or use | macros or inline functions from this file, or you compile this file | and link it with other works to produce a work based on this file, | this file does not by itself cause the resulting work to be covered by | the GNU General Public License. However the source code for this file | must still be made available in accordance with section (3) of the GNU | General Public License v2. | | This exception does not invalidate any other reasons why a work based | on this file might be covered by the GNU General Public License. | ------------------------------------------- | ####ECOSGPLCOPYRIGHTEND#### |============================================================================= |#####DESCRIPTIONBEGIN#### | | Author(s): Enrico Piria | Contributors: Wade Jensen | Date: 2005-25-06 | Purpose: ColdFire exception vectors | Description: This file contains the first level default VSRs | that save and restore state for both exceptions and | interrupts. | |####DESCRIPTIONEND#### |========================================================================== #include #include #include #include #include #ifdef CYGPKG_KERNEL #include #endif | ---------------------------------------------------------------------------- | Hardware reset vector .section ".boot","x" .balign 4 .globl cyg_hal_reset_vsr cyg_hal_reset_vsr: | Define the entry point for the linker. .globl _start _start: | Make sure that all interrupts are masked. hal_cpu_int_disable | Initial setup. Just do the minimum to be able to perform | initialization in C. | Initialize CPU variant hal_cpu_init | Platform specific hardware initialization. | This may include memory controller initialization. hal_hardware_init | Setup boot stack hal_boot_stack_init | Set up the initial frame pointer. lea 0,%fp link %fp,#0 | Call the C routine to complete the reset process. .extern hal_reset jsr hal_reset | If we return, stop. 9: stop #0x2000 bra 9b | ---------------------------------------------------------------------------- | Default exception vector handler | | The default handler for all machine exceptions. We save the | machine state and call the default C exception handler. This routine passes a | pointer to the saved state to the C exception handler. The stack pointer in | the saved state points to the the sp before the exception. | The format/vector word in the exception stack contains the vector | number. | void hal_exception_handler(CYG_WORD vector, HAL_SavedRegisters *regs); .text .balign 4 .globl cyg_hal_default_exception_vsr cyg_hal_default_exception_vsr: | Disable all interrupts hal_cpu_int_disable | Preserve the entire state. | Allocate space for all registers (including the stack pointer). | Write all registers to the stack space. lea.l -CYGARC_CF_EXCEPTION_DECREMENT(%sp),%sp movem.l %d0-%d7,CYGARC_CFREG_DREGS(%sp) movem.l %a0-%a6,CYGARC_CFREG_AREGS(%sp) #ifdef CYGHWR_HAL_COLDFIRE_MAC save_mac_registers %d0 #endif | Write the original stack pointer value to the stack. | The format/vector word, sr, and pc are already on the stack. find_original_sp %d0 move.l %d0,CYGARC_CFREG_SP(%sp) | Calculate the vector number. The format/vector word on the stack | contains the vector number. move.w CYGARC_CF_FMTVECWORD(%sp),%d0 and.l #0x000003fc,%d0 lsr.l #2,%d0 | Pass a pointer to the saved state to the exception handler. pea.l (%sp) | Push the vector number parameter. move.l %d0,-(%sp) | Call the default exception handler. This routine may modify | the exception context. .extern hal_exception_handler jsr hal_exception_handler | Remove the vector number and the state pointer from the stack. addq.l #2*4,%sp | Get a pointer to the location following the exception context. find_original_sp %d0 | Restore all of the registers that we do not need in the following | code. We will copy all registers that are not restored here | to the new stack before restoring them. #ifdef CYGHWR_HAL_COLDFIRE_MAC restore_mac_registers %d0 #endif movem.l CYGARC_CFREG_D2(%sp),%d2-%d7 movem.l CYGARC_CFREG_A1(%sp),%a1-%a6 | Load the address of the new SP. move.l CYGARC_CFREG_SP(%sp),%d1 | We now have: | d0.l : original stack pointer | d1.l : final stack pointer | ColdFire programmer's manual doesn't tell if rte instruction expects | the stack frame to be aligned at 32-bit boundaries. | So, we align the new stack value, and adjust the format field | accordingly. At the end of rte instruction the stack will thus point | to the desired location. | Compare the new stack address to the end of the exception context. | This will tell us the order that we need to copy the exception | stack and the remaining registers from the old exception context to | the new one. The order is important because the stack frames might | overlap. cmp.l %d0,%d1 | If the new SP and the old one coincide. beq 2f | If the new SP is at a higher address than the old one. bgt 1f | The new SP is at a lower address than the old one. Copy from the | lowest address to the highest address. | Align stack at longword boundary move.l %d1,%d0 and.l #0xfffffffc,%d0 move.l %d0,%a0 | Allocate new frame sub.l #CYGARC_CF_CONTEXT_SIZE,%a0 | Copy D0, D1, A0, FVW, SR, and PC from the old stack to the new stack. | Note that we copy in ascending order. | Copy D0, D1, A0 move.l CYGARC_CFREG_D0(%sp),CYGARC_CFREG_D0(%a0) move.l CYGARC_CFREG_D1(%sp),CYGARC_CFREG_D1(%a0) move.l CYGARC_CFREG_A0(%sp),CYGARC_CFREG_A0(%a0) | Based on target SP address, construct new format field and.l #0x00000003,%d1 or.l #0x4,%d1 lsl.l #8,%d1 lsl.l #4,%d1 | Load old format field move.w CYGARC_CF_FMTVECWORD(%sp),%d0 | Clear old format field and.l #0x0fff,%d0 | Write the new one or.l %d1,%d0 move.w %d0,CYGARC_CF_FMTVECWORD(%a0) | Copy SR and PC move.w CYGARC_CF_SR(%sp),CYGARC_CF_SR(%a0) move.l CYGARC_CFREG_PC(%sp),CYGARC_CFREG_PC(%a0) | A0 points to the top of the new stack move.l %a0,%sp | Restore remaining registers and exit jmp 2f 1: | The new SP is at a higher address than the old one. Copy from the | highest address to the lowest address. | Align stack at longword boundary move.l %d1,%d0 and.l #0xfffffffc,%d0 move.l %d0,%a0 | Allocate new frame sub.l #CYGARC_CF_CONTEXT_SIZE,%a0 | Copy D0, D1, A0, FVW, SR, and PC from the old stack to the new stack. | Note that we copy in descending order. | Copy PC and SR move.l CYGARC_CFREG_PC(%sp),CYGARC_CFREG_PC(%a0) move.w CYGARC_CF_SR(%sp),CYGARC_CF_SR(%a0) | Based on target SP address, construct new format field and.l #0x00000003,%d1 or.l #0x4,%d1 lsl.l #8,%d1 lsl.l #4,%d1 | Load old format field move.w CYGARC_CF_FMTVECWORD(%sp),%d0 | Clear old format field and.l #0x0fff,%d0 | Write the new one or.l %d1,%d0 move.w %d0,CYGARC_CF_FMTVECWORD(%a0) | Copy A0, D1, D0 move.l CYGARC_CFREG_A0(%sp),CYGARC_CFREG_A0(%a0) move.l CYGARC_CFREG_D1(%sp),CYGARC_CFREG_D1(%a0) move.l CYGARC_CFREG_D0(%sp),CYGARC_CFREG_D0(%a0) | A0 points to the top of the new stack move.l %a0,%sp 2: | Restore remaining registers move.l CYGARC_CFREG_D0(%sp),%d0 move.l CYGARC_CFREG_D1(%sp),%d1 move.l CYGARC_CFREG_A0(%sp),%a0 add.l #CYGARC_CF_EXCEPTION_DECREMENT,%sp | Return from exception rte | ---------------------------------------------------------------------------- | Spurious interrupt vector handler | | Used for spurious and uninitialized interrupts. | It is unknown at which priority spurious interrupts are generated. So, the | safest thing to do is to disable all interrupts while processing spurious | ones. .text .balign 4 .globl cyg_hal_default_spurious_vsr cyg_hal_default_spurious_vsr: #ifndef CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS | Disable all interrupts. On the first instruction, interrupt sampling | is always disabled. hal_cpu_int_disable | Preserve all registers that this handler needs to preserve. | The C code will preserve all other registers. int_pres_regs | Pass a pointer to the saved state to the interrupt handler. pea.l (%sp) | Call spurious interrupt handler .extern hal_spurious_interrupt jsr hal_spurious_interrupt | Remove the arguments from the stack. addq.l #4,%sp | Restore the preserved registers for the current thread. int_rest_regs #endif /* ifndef CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS */ | Just return from interrupt. rte | ---------------------------------------------------------------------------- | User interrupt vector handler | | Control is transferred here from a user interrupt vector (#64-255). | Before branching to common code, load a value to translate the | vector table offset to the ISR table offset. .text .balign 4 .globl cyg_hal_default_interrupt_vsr cyg_hal_default_interrupt_vsr: | Disable all interrupts. On the first instruction, interrupt sampling | is always disabled. hal_cpu_int_disable | Preserve all registers that this handler needs to preserve. | The C code will preserve all other registers. int_pres_regs | It is safe to use breakpoints below this point. .globl _cyg_hal_default_interrupt_vsr_bp_safe _cyg_hal_default_interrupt_vsr_bp_safe: | Adding this value to the vector table offset will result in the | corresponding offset into the ISR table. move.l #(-CYGNUM_HAL_ISR_MIN)*4,%d2 | d2.l: Contains a value to translate the vector table offset to | the ISR table offset. | Calculate the vector offset. The format/vector word on the stack | contains the vector number. Mask off all unused bits. The bit | position of the vector number field makes it automatically multiplied | by four. move.w CYGARC_CF_FMTVECWORD(%sp),%d1 and.l #0x000003fc,%d1 | Calculate the ISR table offset. Add the vector table offset to the | translation value. add.l %d1,%d2 | Calculate the vector number using the vector table offset. asr.l #2,%d1 | d2.l: Contains the offset into the ISR table. | d1.l: Contains the vector number. #if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) \ || defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT) | If we are supporting Ctrl-C interrupts from GDB, we must squirrel | away a pointer to the saved interrupt state here so that we can | plant a breakpoint at some later time. .extern hal_saved_interrupt_state move.l %sp,(hal_saved_interrupt_state) #endif #ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT | Lock the scheduler if we are using the kernel. .extern cyg_scheduler_sched_lock addq.l #1,cyg_scheduler_sched_lock #endif /* CYGFUN_HAL_COMMON_KERNEL_SUPPORT */ #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK | a0 = sp. We'll need it later move.l %sp,%a0 cmp.l #__interrupt_stack_base,%sp | If sp < base : not on istack blt 1f cmp.l #__interrupt_stack,%sp | If sp <= top : already on istack ble 2f 1: | Switch to istack lea __interrupt_stack,%sp 2: | Save old SP on istack pea (%a0) #endif #if defined(CYGPKG_KERNEL_INSTRUMENT) && defined(CYGDBG_KERNEL_INSTRUMENT_INTR) .extern cyg_instrument | Save d1 move.l %d1,-(%sp) | arg2 = 0 move.l #0,-(%sp) | arg1 = vector number move.l %d1,-(%sp) | type = INTR,RAISE move.l #0x0301,-(%sp) | Call instrumentation jsr cyg_instrument | Remove args from stack add.l #12,%sp | Restore %d1 move.l (%sp)+,%d1 #endif #ifdef CYGSEM_HAL_COMMON_INTERRUPTS_ALLOW_NESTING | If interrupt nesting is enabled, we have to determine the IPL of the | current interrupt. We inline the following macro, which is defined | by ColdFire variants. The vector number of the current interrupt | is passed in d0, and the return value is in d0. | Registers a0-a1/d0-d1 are for use by the macro, other registers | must be saved explicitly before being used. | Save %d1 move.l %d1,-(%sp) | Pass d1 as argument to macro move.l %d1,%d0 | Retrieve IPL, which will be contained in d0 hal_variant_retrieve_ipl | Shift IPL up to the same position occupied in sr lsl.l #8,%d0 | Transform d0 in a mask to be applied to sr or.l #0xfffff0ff,%d0 | Update sr. Use d1 as working register move.w %sr,%d1 and.l %d0,%d1 move.w %d1,%sr | Restore d1 move.l (%sp)+,%d1 #endif | We need to call the following routines. The isr address, data, and | intr are all from the ISR table. The interrupt_end routine is | only called if we are using the kernel. regs points to the saved | registers on the stack. isr_ret is the return value from the ISR. | vector is the vector number. | static cyg_uint32 isr(CYG_ADDRWORD vector, CYG_ADDRWORD data) | externC void interrupt_end(cyg_uint32 isr_ret, Cyg_Interrupt *intr, | HAL_SavedRegisters *regs) | Push the data value from the table. .extern cyg_hal_interrupt_data lea cyg_hal_interrupt_data,%a0 move.l (%a0,%d2.l),-(%sp) | Get the address of the ISR from the table. .extern cyg_hal_interrupt_handlers lea cyg_hal_interrupt_handlers,%a0 move.l (%a0,%d2.l),%a0 | Push the vector number parameter. move.l %d1,-(%sp) | Call the ISR. jsr (%a0) | Remove the isr parameters from the stack. addq.l #4*2,%sp | d0.l now contains the return value from the ISR. #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK | If we are returning from the last nested interrupt, move back | to the thread stack. interrupt_end() must be called on the | thread stack since it potentially causes a context switch. | Since we have arranged for the top of stack location to | contain the sp we need to go back to here, just pop it off | and put it in SP. move.l (%sp),%sp | sp = *sp #endif #ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT | We only need to call interrupt_end() when there is a kernel | present to do any tidying up. To keep the following code simple, | we enable all interrupts before calling DSRs only if a common | interrupt stack is in use. | Push the regs pointer. pea (%sp) | Push the intr object pointer from the table. .extern cyg_hal_interrupt_objects lea cyg_hal_interrupt_objects,%a0 move.l (%a0,%d2.l),-(%sp) | Push ISR return value move.l %d0,-(%sp) | Even when this is not the last nested interrupt, we must call | interrupt_end() to post the DSR and decrement the scheduler | lock. | Call the interrupt_end C routine. .extern interrupt_end jsr interrupt_end | Remove the isr_ret, intr, and regs parameters from the stack. lea (4*3)(%sp),%sp #endif /* ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT */ | Restore the preserved registers for the current thread. int_rest_regs | Restore the SR and PC. rte | ---------------------------------------------------------------------------- | Execute pending DSRs on the interrupt stack with interrupts enabled. | Note: this can only be called from code running on a thread stack #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK .extern cyg_interrupt_call_pending_DSRs FUNC_START(hal_interrupt_stack_call_pending_DSRs) | Change to interrupt stack, save state and set up stack for | calls to C code. | By virtue of GNU C calling conventions, we are free to use registers | %d0-%d1 and %a0-%a1 without saving them. | a0 = sp move.l %sp, %a0 | Switch to istack lea __interrupt_stack,%sp | Save old SP on istack pea (%a0) | Save sr move.w %sr,%d0 move.l %d0,-(%sp) | Enable interrupts hal_cpu_int_enable %d0 | Call into kernel which will execute DSRs jsr cyg_interrupt_call_pending_DSRs move.l (%sp)+,%d0 | Restore previous interrupt state hal_cpu_int_merge %d0,%d1 | Restore sp move.l (%sp),%sp | return to caller rts #endif | ---------------------------------------------------------------------------- | Interrupt and reset stack | | WARNING: Do not put this in any memory section that gets initialized. | Doing so may cause the C code to initialize its own stack. .section ".uninvar","aw",@nobits .balign 16 .global cyg_interrupt_stack_base cyg_interrupt_stack_base: __interrupt_stack_base: .skip CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE .balign 16 .global cyg_interrupt_stack cyg_interrupt_stack: __interrupt_stack: .skip 0x10