xtos-internal.h 12.6 KB
/*
 * xtos-internal.h  --  internal definitions for single-threaded run-time
 *
 * Copyright (c) 2003-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.
 */

#ifndef XTOS_INTERNAL_H
#define XTOS_INTERNAL_H

#include <xtensa/config/core.h>
#include <xtensa/xtruntime.h>
#include <xtensa/xtruntime-frames.h>
#include <xtensa/xtensa-versions.h>
#ifndef XTOS_PARAMS_H	/* this to allow indirect inclusion of this header from the outside */
#include "xtos-params.h"
#endif

/*  Relative ordering of subpriorities within an interrupt level (or vector):  */
#define XTOS_SPO_ZERO_LO	0	/* lower (eg. zero) numbered interrupts are lower  priority than higher numbered interrupts */
#define XTOS_SPO_ZERO_HI	1	/* lower (eg. zero) numbered interrupts are higher priority than higher numbered interrupts */


/*  Sanity check some parameters from xtos-params.h:  */
#if XTOS_LOCKLEVEL < XCHAL_EXCM_LEVEL || XTOS_LOCKLEVEL > 15
# error Invalid XTOS_LOCKLEVEL value, must be >= EXCM_LEVEL and <= 15, please fix xtos-params.h
#endif

/*  Mask of interrupts locked out at XTOS_LOCKLEVEL:  */
#define XTOS_LOCKOUT_MASK	XCHAL_INTLEVEL_ANDBELOW_MASK(XTOS_LOCKLEVEL)
/*  Mask of interrupts that can still be enabled at XTOS_LOCKLEVEL:  */
#define XTOS_UNLOCKABLE_MASK	(0xFFFFFFFF-XTOS_LOCKOUT_MASK)

/*  Don't set this:  */
#define XTOS_HIGHINT_TRAMP	0	/* mapping high-pri ints to low-pri not auto-supported */
#define XTOS_VIRTUAL_INTERRUPT	XTOS_HIGHINT_TRAMP	/* partially-virtualized INTERRUPT register not currently supported */
#if XTOS_HIGHINT_TRAMP
# error Automatically-generated high-level interrupt trampolines are not presently supported.
#endif

/*
 *  If single interrupt at level-one, sub-prioritization is irrelevant:
 */
#if defined(XCHAL_INTLEVEL1_NUM)
# undef XTOS_SUBPRI
# define XTOS_SUBPRI 0			/* override - only one interrupt */
#endif

/*
 *  In XEA1, the INTENABLE special register must be virtualized to provide
 *  standard XTOS functionality.
 *  In XEA2, this is only needed for software interrupt prioritization.
 */
#if XTOS_SUBPRI || XCHAL_HAVE_XEA1
#define XTOS_VIRTUAL_INTENABLE	1
#else
#define XTOS_VIRTUAL_INTENABLE	0
#endif

/*
 *  If single interrupt per priority, then fairness is irrelevant:
 */
#if (XTOS_SUBPRI && !XTOS_SUBPRI_GROUPS) || defined(XCHAL_INTLEVEL1_NUM)
# undef XTOS_INT_FAIRNESS
# define XTOS_INT_FAIRNESS	0
#endif

/*  Identify special case interrupt handling code in int-lowpri-dispatcher.S:  */
#define XTOS_INT_SPECIALCASE	(XTOS_SUBPRI_ORDER == XTOS_SPO_ZERO_HI && XTOS_INT_FAIRNESS == 0 && XTOS_SUBPRI_GROUPS == 0)

/*
 *  Determine whether to extend the interrupt entry array:
 */
#define XIE_EXTEND		(XTOS_VIRTUAL_INTENABLE && !XTOS_INT_SPECIALCASE)

/*  If we have the NSAU instruction, ordering of interrupts is reversed in _xtos_interrupt_table[]:  */
#if XCHAL_HAVE_NSA
# define MAPINT(n)	((XCHAL_NUM_INTERRUPTS-1)-(n))
# ifdef _ASMLANGUAGE
	.macro	mapint  an
	neg	\an, \an
	addi	\an, \an, XCHAL_NUM_INTERRUPTS-1
	.endm
# endif
#else /* no NSA */
# define MAPINT(n)	(n)
# ifdef _ASMLANGUAGE
	.macro	mapint  an
	.endm
# endif
#endif


#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)

/***********   Useful macros   ***********/

/*
 *  A useful looping macro:
 *  'iterate' invokes 'what' (an instruction, pseudo-op or other macro)
 *  multiple times, passing it a numbered parameter from 'from' to 'to'
 *  inclusively.  Does not invoke 'what' at all if from > to.
 *  Maximum difference between 'from' and 'to' is 99 minus nesting depth
 *  (GNU 'as' doesn't allow nesting deeper than 100).
 */
	.macro	iterate		from, to, what
	.ifeq	((\to-\from) & ~0xFFF)
	\what	\from
	iterate	"(\from+1)", \to, \what
	.endif
	.endm	// iterate



	//  rsilft
	//
	//  Execute RSIL \ar, \tolevel if \tolevel is different than \fromlevel.
	//  This way the RSIL is avoided if we know at assembly time that
	//  it will not change the level.  Typically, this means the \ar register
	//  is ignored, ie. RSIL is used only to change PS.INTLEVEL.
	//
	.macro	rsilft	ar, fromlevel, tolevel
#if XCHAL_HAVE_INTERRUPTS
	.if \fromlevel - \tolevel
	rsil	\ar, \tolevel
	.endif
#endif
	.endm


	//  Save LOOP and MAC16 registers, if configured, to the exception stack
	//  frame pointed to by address register \esf, using \aa and \ab as temporaries.
	//
	//  This macro essentially saves optional registers that the compiler uses by
	//  default when present.
	//  Note that the acclo/acchi subset of MAC16 may be used even if others
	//  multipliers are present (e.g. mul16, mul32).
	//
	//  Only two temp registers required for this code to be optimal (no interlocks) in both
	//  T10xx (Athens) and Xtensa LX microarchitectures (both 5 and 7 stage pipes):
	//
	.macro	save_loops_mac16	esf, aa, ab
#if XCHAL_HAVE_LOOPS
	rsr	\aa, LCOUNT
	rsr	\ab, LBEG
	s32i	\aa, \esf, UEXC_lcount
	rsr	\aa, LEND
	s32i	\ab, \esf, UEXC_lbeg
	s32i	\aa, \esf, UEXC_lend
#endif
#if XCHAL_HAVE_MAC16
	rsr	\aa, ACCLO
	rsr	\ab, ACCHI
	s32i	\aa, \esf, UEXC_acclo
	s32i	\ab, \esf, UEXC_acchi
# if XTOS_SAVE_ALL_MAC16
	rsr	\aa, M0
	rsr	\ab, M1
	s32i	\aa, \esf, UEXC_mr + 0
	s32i	\ab, \esf, UEXC_mr + 4
	rsr	\aa, M2
	rsr	\ab, M3
	s32i	\aa, \esf, UEXC_mr + 8
	s32i	\ab, \esf, UEXC_mr + 12
# endif
#endif
	.endm

	//  Restore LOOP and MAC16 registers, if configured, from the exception stack
	//  frame pointed to by address register \esf, using \aa, \ab and \ac as temporaries.
	//
	//  Three temp registers are required for this code to be optimal (no interlocks) in
	//  Xtensa LX microarchitectures with 7-stage pipe; otherwise only two
	//  registers would be needed.
	//
	.macro	restore_loops_mac16	esf, aa, ab, ac
#if XCHAL_HAVE_LOOPS
	l32i	\aa, \esf, UEXC_lcount
	l32i	\ab, \esf, UEXC_lbeg
	l32i	\ac, \esf, UEXC_lend
	wsr	\aa, LCOUNT
	wsr	\ab, LBEG
	wsr	\ac, LEND
#endif
#if XCHAL_HAVE_MAC16
	l32i	\aa, \esf, UEXC_acclo
	l32i	\ab, \esf, UEXC_acchi
# if XTOS_SAVE_ALL_MAC16
	l32i	\ac, \esf, UEXC_mr + 0
	wsr	\aa, ACCLO
	wsr	\ab, ACCHI
	wsr	\ac, M0
	l32i	\aa, \esf, UEXC_mr + 4
	l32i	\ab, \esf, UEXC_mr + 8
	l32i	\ac, \esf, UEXC_mr + 12
	wsr	\aa, M1
	wsr	\ab, M2
	wsr	\ac, M3
# else
	wsr	\aa, ACCLO
	wsr	\ab, ACCHI
# endif
#endif
	.endm


/*  Offsets from _xtos_intstruct structure:  */
	.struct 0
#if XTOS_VIRTUAL_INTENABLE
XTOS_ENABLED_OFS:	.space	4	/* _xtos_enabled variable */
XTOS_VPRI_ENABLED_OFS:	.space	4	/* _xtos_vpri_enabled variable */
#endif
#if XTOS_VIRTUAL_INTERRUPT
XTOS_PENDING_OFS:	.space	4	/* _xtos_pending variable */
#endif
	.text


#if XTOS_VIRTUAL_INTENABLE
	// Update INTENABLE register, computing it as follows:
	//	INTENABLE = _xtos_enabled & _xtos_vpri_enabled
	// 			[ & ~_xtos_pending ]
	//
	// Entry:
	//	register ax = &_xtos_intstruct
	//	register ay, az undefined (temporaries)
	//	PS.INTLEVEL set to XTOS_LOCKLEVEL or higher (eg. via xtos_lock)
	//	window overflows prevented (PS.WOE=0, PS.EXCM=1, or overflows
	//		already done for registers ax, ay, az)
	//
	// Exit:
	//	registers ax, ay, az clobbered
	//	PS unchanged
	//	caller needs to SYNC (?) for INTENABLE changes to take effect
	//
	// Note: in other software prioritization schemes/implementations,
	// the term <_xtos_vpri_enabled> in the above expression is often
	// replaced with another expression that computes the set of
	// interrupts allowed to be enabled at the current software virtualized
	// interrupt priority.
	//
	// For example, a simple alternative implementation of software
	// prioritization for XTOS might have been the following:
	//	INTENABLE = _xtos_enabled & (vpri_enabled | UNLOCKABLE_MASK)
	// which removes the need for the interrupt dispatcher to 'or' the
	// UNLOCKABLE_MASK bits into _xtos_vpri_enabled, and lets other code
	// disable all lockout level interrupts by just clearing _xtos_vpri_enabled
	// rather than setting it to UNLOCKABLE_MASK.
	// Other implementations sometimes use a table, eg:
	//	INTENABLE = _xtos_enabled & enable_table[current_vpri]
	// The HAL (used by some 3rd party OSes) uses essentially a table-driven
	// version, with other tables enabling run-time changing of priorities.
	//
	.macro	xtos_update_intenable	ax, ay, az
	//movi	\ax, _xtos_intstruct
	l32i	\ay, \ax, XTOS_VPRI_ENABLED_OFS		// ay = _xtos_vpri_enabled
	l32i	\az, \ax, XTOS_ENABLED_OFS		// az = _xtos_enabled
	//interlock
	and	\az, \az, \ay		// az = _xtos_enabled & _xtos_vpri_enabled
# if XTOS_VIRTUAL_INTERRUPT
	l32i	\ay, \ax, XTOS_PENDING_OFS		// ay = _xtos_pending
	movi	\ax, -1
	xor	\ay, \ay, \ax		// ay = ~_xtos_pending
	and	\az, \az, \ay		// az &= ~_xtos_pending
# endif
	wsr	\az, INTENABLE
	.endm
#endif /* VIRTUAL_INTENABLE */

	.macro	xtos_lock	ax
	rsil    \ax, XTOS_LOCKLEVEL	// lockout
	.endm

	.macro	xtos_unlock	ax
	wsr     \ax, PS			// unlock
	rsync
	.endm

/*  Offsets to XtosIntHandlerEntry structure fields (see below):  */
# define XIE_HANDLER	0
# define XIE_ARG	4
# define XIE_SIZE	8
# if XIE_EXTEND
#  define XIE_VPRIMASK	(XIE_SIZE*XCHAL_NUM_INTERRUPTS+0)	/* if VIRTUAL_INTENABLE [SUBPRI||XEA1] && !SPECIALCASE */
#  define XIE_LEVELMASK	(XIE_SIZE*XCHAL_NUM_INTERRUPTS+4)	/* [fairness preloop]  if FAIRNESS && SUBPRI [&& SUBPRI_GROUPS] */
# endif

/*  To simplify code:  */
# if XCHAL_HAVE_NSA
#  define IFNSA(a,b)	a
# else
#  define IFNSA(a,b)	b
# endif

#else /* !_ASMLANGUAGE && !__ASSEMBLER__ */

/*
 *  Interrupt handler table entry.
 *  Unregistered entries have 'handler' point to _xtos_unhandled_interrupt().
 */
typedef struct XtosIntHandlerEntry {
    _xtos_handler	handler;
    void *		arg;
} XtosIntHandlerEntry;
# if XIE_EXTEND
typedef struct XtosIntMaskEntry {
    unsigned		vpri_mask;	/* mask of interrupts enabled when this interrupt is taken */
    unsigned		level_mask;	/* mask of interrupts at this interrupt's level */
} XtosIntMaskEntry;
# endif

#endif /* !_ASMLANGUAGE && !__ASSEMBLER__ */

/*
 *  Notes...
 *
 *  XEA1 and interrupt-SUBPRIoritization both imply virtualization of INTENABLE.
 *  Synchronous trampoloines imply partial virtualization of the INTERRUPT
 *  register, which in turn also implies virtualization of INTENABLE register.
 *  High-level interrupts manipulating the set of enabled interrupts implies
 *  at least a high XTOS_LOCK_LEVEL, although not necessarily INTENABLE virtualization.
 *
 *  With INTENABLE register virtualization, at all times the INTENABLE
 *  register reflects the expression:
 *	(set of interrupts enabled) & (set of interrupts enabled by current
 *					virtual priority)
 *
 *  Unrelated (DBREAK semantics):
 *
 *	A[31-6] = DBA[3-6]
 *	---------------------
 *	A[5-0] & DBC[5-C] & szmask
 *
 *	= DBA[5-0] & szmask
 *			^___  ???
 */


/*  Report whether the XSR instruction is available (conservative):  */
#define HAVE_XSR	(XCHAL_HAVE_XEA2 || !XCHAL_HAVE_EXCEPTIONS)
/*
 *  This is more accurate, but not a reliable test in software releases prior to 6.0
 *  (where the targeted hardware parameter was not explicit in the XPG):
 *
 *#define HAVE_XSR	(XCHAL_HW_MIN_VERSION >= XTENSA_HWVERSION_T1040_0)
 */



/* Macros for supporting hi-level and medium-level interrupt handling. */

#if XCHAL_NUM_INTLEVELS > 6
#error Template files (*-template.S) limit support to interrupt levels <= 6
#endif

#if  defined(__XTENSA_WINDOWED_ABI__) && XCHAL_HAVE_CALL4AND12 == 0
#error CALL8-only is not supported!
#endif

#define INTERRUPT_IS_HI(level)  \
	( XCHAL_HAVE_INTERRUPTS && \
	 (XCHAL_EXCM_LEVEL < level) && \
	 (XCHAL_NUM_INTLEVELS >= level) && \
	 (XCHAL_HAVE_DEBUG ? XCHAL_DEBUGLEVEL != level : 1))

#define INTERRUPT_IS_MED(level) \
	(XCHAL_HAVE_INTERRUPTS && (XCHAL_EXCM_LEVEL >= level))


#define _JOIN(x,y)	x ## y
#define JOIN(x,y)	_JOIN(x,y)

#define _JOIN3(a,b,c)	a ## b ## c
#define JOIN3(a,b,c)	_JOIN3(a,b,c)

#define LABEL(x,y)		JOIN3(x,_INTERRUPT_LEVEL,y)
#define EXCSAVE_LEVEL		JOIN(EXCSAVE_,_INTERRUPT_LEVEL)
#define INTLEVEL_VSIZE		JOIN3(XSHAL_INTLEVEL,_INTERRUPT_LEVEL,_VECTOR_SIZE)



#endif /* XTOS_INTERNAL_H */