blob: e41cc846c0e61ce05ec24b30061e77ccd814a5a3 [file] [log] [blame]
// 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 */