| /* |
| * 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 */ |
| |