| // High-Priority Interrupt Handler Template |
| // $Id: //depot/rel/Foxhill/dot.8/Xtensa/OS/xtos/int-highpri-template.S#1 $ |
| |
| // Copyright (c) 2004-2010 Tensilica Inc. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining |
| // a copy of this software and associated documentation files (the |
| // "Software"), to deal in the Software without restriction, including |
| // without limitation the rights to use, copy, modify, merge, publish, |
| // distribute, sublicense, and/or sell copies of the Software, and to |
| // permit persons to whom the Software is furnished to do so, subject to |
| // the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included |
| // in all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| // |
| // This file provides skeleton code for writing high-priority interrupt |
| // handlers in assembler for performance. |
| // |
| // By default, this file is included by inth-template.S . |
| // The default Makefile defines _INTERRUPT_LEVEL when assembling |
| // inth-template.S for each medium and high priority interrupt level. |
| // |
| // To use this template file, define a macro called _INTERRUPT_LEVEL |
| // to be the interrupt priority level of the vector, then include this file. |
| |
| |
| #include <xtensa/coreasm.h> |
| #include "xtos-internal.h" |
| |
| |
| #if XCHAL_HAVE_INTERRUPTS |
| |
| #define INTERRUPT_MASK XCHAL_INTLEVEL_MASK(_INTERRUPT_LEVEL) |
| #define SINGLE_INTERRUPT (INTERRUPT_MASK & (INTERRUPT_MASK - 1) == 0) |
| #define SINGLE_INT_NUM XCHAL_INTLEVEL_NUM(_INTERRUPT_LEVEL) |
| |
| |
| // NOTE: It is strongly recommended that high-priority |
| // interrupt handlers be written in assembly. |
| // |
| // High-priority interrupt handlers can be written in C, |
| // but only at the cost of an unreasonable amount of state |
| // save and restore (including the entire physical address |
| // register file and others, see int-highpri-dispatcher.S) |
| // that makes high-priority interrupt dispatching much slower |
| // than for low and medium priority interrupts. |
| // (Low and medium priority interrupts are masked by atomic |
| // register window operations, so they take advantage of a |
| // coherent window state for fast entry. High priority |
| // interrupts are not masked by window operations so they |
| // can interrupt them, leading to a potentially incoherent |
| // window state at the time of the interrupt. Given that |
| // high priority handlers must save and restore everything |
| // they touch, they end up needing to save and restore the |
| // entire window state [physical address register file etc.] |
| // and all exception state which they can also interrupt.) |
| // See also the Microprocessor Programmer's Guide. |
| |
| // High-priority interrupts are designed to be very fast and with |
| // very low latency. |
| // Typical high-priority interrupt service routines are kept |
| // relatively small and fast. Either there is little to do, |
| // or the routine handles only the necessary high priority |
| // activities related to a device and leaves the rest |
| // (other more complex and time-consuming activities) |
| // to be scheduled later, eg. by triggering a level-one |
| // (low-priority) or medium-priority software interrupt whose |
| // handler can be written in C for the more extensive processing. |
| |
| // NOTE: The following handler is just skeleton example |
| // code. It is NOT a functional handler. For software, edge- |
| // triggered and write-error interrupts, it simply does nothing |
| // and return. For other types (timer and level-triggered), |
| // this code does not clear the source(s) of interrupt, |
| // hence if any interrupt at this priority level are both enabled |
| // and triggered, the processor repeatedly takes the interrupt |
| // in a loop. This is all okay as a default, because |
| // XTOS (and other operating systems) clears the INTENABLE |
| // register at startup, requiring the application to |
| // enable specific interrupts before they can be taken. |
| // So as long as you don't enable any interrupt of this |
| // priority level, this example handler will never execute. |
| |
| // Exports |
| .global LABEL(_Level,FromVector) |
| |
| .data |
| .align 4 |
| LABEL(int,save): |
| .space 4 // save area |
| |
| .text |
| .align 4 |
| LABEL(_Level,FromVector): |
| // The vectoring code has already saved a2 in EXCSAVEn. |
| // Save any other registers we'll use: |
| movi a2, LABEL(int,save) |
| s32i a1, a2, 0 |
| // ... add more as needed (increase save area accordingly) ... |
| |
| // WRITE YOUR INTERRUPT HANDLING CODE HERE... |
| |
| // If multiple interrupts are mapped to this priority level, |
| // you'll probably need to distinguish which interrupt(s) |
| // occurred by reading the INTERRUPT (INTREAD) and |
| // INTENABLE registers, and'ing them together, and |
| // looking at what bits are set in both. |
| // If any of the interrupts are level-triggered, be ready |
| // to handle the case where no interrupts are to be handled |
| // -- this is called a spurious interrupt, and can happen |
| // when the level-triggered interrupt line goes inactive |
| // after the interrupt is taken but before the INTERRUPT |
| // register is read. |
| |
| // You'll also normally want to clear the source of |
| // the interrupt before returning, to avoid getting |
| // the same interrupt again immediately. For illustration, |
| // this code clears all software, edge-triggered, and |
| // write-error interrupts at this priority level (if any). |
| // NOTE: Timer interrupts must be cleared by writing to |
| // the corresponding CCOMPAREn register; and level-sensitive |
| // interrupts can only be cleared externally, usually by |
| // requesting the associated device to do so (in a |
| // device-specific manner). |
| // |
| movi a1, INTERRUPT_MASK |
| wsr.intclear a1 |
| |
| // Restore registers: |
| l32i a1, a2, 0 |
| #if HAVE_XSR |
| movi a2, LABEL(_Level,FromVector) // restore handler address |
| xchgsr excsave _INTERRUPT_LEVEL a2 |
| #else |
| readsr excsave _INTERRUPT_LEVEL a2 |
| #endif |
| // ... add more if more are saved above ... |
| |
| #if XCHAL_HAVE_EXCLUSIVE |
| // If your code used L32EX/S32EX, then clear any active excl monitors. |
| // Uncomment the line below. |
| // clrex |
| #endif |
| |
| // Return: |
| rfi _INTERRUPT_LEVEL |
| |
| .size LABEL(_Level,FromVector), . - LABEL(_Level,FromVector) |
| |
| #endif /* XCHAL_HAVE_INTERRUPTS */ |