Moved proj up

This commit is contained in:
Timo Niemann
2026-06-04 10:21:56 +02:00
parent aea013305e
commit 99fb345151
949 changed files with 0 additions and 0 deletions
@@ -0,0 +1,139 @@
### Makefile to build the FreeRTOS library ###
# Build target (options: sim, board)
TARGET = sim
SMALL =
# Tools
CC = xt-clang
AS = xt-clang
AR = xt-ar
ISS = xt-run
XT_CORE = $(patsubst %-params,%,$(notdir $(shell xt-clang --show-config=core)))
CONFIGDIR = $(shell xt-clang --show-config=config)
# For platform-specific commands
include $(CONFIGDIR)/misc/hostenv.mk
# Source code and build locations
# Build with MPU support
ifeq ($(MPU),1)
MPUFLAGS = -DportUSING_MPU_WRAPPERS=1 -DportALIGN_SECTIONS
endif
# Build with overlay support
ifeq ($(OVERLAY),1)
OVLYFLAGS = -DXT_USE_THREAD_SAFE_CLIB=1 -DXT_USE_OVLY=1
endif
SRCROOT = $(subst /,$(S),$(CURDIR))
TSTROOT = $(subst /,$(S),$(abspath $(SRCROOT)$(S)..$(S)..$(S)..$(S)..$(S)..$(S)..$(S)Demo$(S)ThirdParty$(S)Partner-Supported-Demos$(S)Cadence_Xtensa_ISS_xt-clang$(SMALL)))
BLDROOT = $(TSTROOT)$(S)build
BLDAUX ?=
BLDDIR = $(BLDROOT)$(S)$(XT_CORE)$(BLDAUX)
FR_SRCDIR = $(abspath $(SRCROOT)$(S)..$(S)..$(S)..$(S)..$(S)..)
FR_SRCDIR2 = $(FR_SRCDIR)$(S)portable$(S)MemMang
ifeq ($(MPU),1)
FR_SRCDIR3 = $(FR_SRCDIR)$(S)portable$(S)Common
endif
XT_SRCDIR = $(SRCROOT)
vpath %.c $(FR_SRCDIR) $(FR_SRCDIR2) $(FR_SRCDIR3) $(XT_SRCDIR)
vpath %.S $(XT_SRCDIR)
# File lists
# NOTE: Only heap_4.c is included in the default FreeRTOS build.
# If you want to use another heap manager then edit this file
# and specify the one you want.
FR_C_FILES = $(notdir $(wildcard $(FR_SRCDIR)/*.c)) \
$(notdir $(wildcard $(FR_SRCDIR3)/*.c)) \
heap_4.c
XT_C_FILES = $(notdir $(wildcard $(XT_SRCDIR)/*.c))
XT_S_FILES = $(notdir $(wildcard $(XT_SRCDIR)/*.S))
# List of all .o files that will go into the library
LIB_C_O = $(patsubst %.c,%.o,$(filter-out asm-offsets.c,$(XT_C_FILES) $(FR_C_FILES)))
LIB_S_O = $(patsubst %.S,%.o,$(XT_S_FILES))
LIB_O_LIST = $(addprefix $(BLDDIR)/,$(LIB_C_O) $(LIB_S_O))
# Output files
OSLIB = $(BLDDIR)$(S)libfreertos.a
# Build options
ifeq ($(TARGET),sim)
DFLAGS = -DXT_SIMULATOR
endif
ifeq ($(TARGET),board)
DFLAGS = -DXT_BOARD
endif
IFLAGS = \
-I$(FR_SRCDIR)$(S)include -I$(XT_SRCDIR) -I$(TSTROOT)$(S)common$(S)config_files -I$(BLDDIR)
CSTD = -std=c99
ifeq ($(OVERLAY),1)
CFLAGS = -Os -g
else
CFLAGS = -O2 -g
endif
WFLAGS = -Werror -Wall -Wextra
CFGFLAGS ?=
CCFLAGS = $(CSTD) $(CFGFLAGS) $(CFLAGS) $(WFLAGS) -mno-coproc -mlongcalls -ffunction-sections -mno-l32r-flix $(DFLAGS) $(MPUFLAGS) $(OVLYFLAGS)
ASFLAGS = $(CCFLAGS)
# File-specific flags
FLAGS_xtensa_vectors_xea3 = -mtext-section-literals
FLAGS_mpu = -mtext-section-literals
FLAGS_portasm = -mtext-section-literals
FLAGS_xtensa_context = -mtext-section-literals -Wno-error
FLAGS_xtensa_coproc_handler = -mtext-section-literals
# Include dependency rules (generated using -MD)
-include $(wildcard $(BLDDIR)/*.d)
# Targets
all : $(BLDDIR)/.mkdir $(BLDDIR)/reent.h $(OSLIB)
$(BLDDIR)/.mkdir :
@$(MKPATH) $(BLDDIR)
@echo "" > $@
$(BLDDIR)/reent.h : $(BLDDIR)/.mkdir
-$(CP) $(subst /,$(S),$(CONFIGDIR))$(S)xtensa-elf$(S)include$(S)sys$(S)reent.h $(BLDDIR)$(S)reent.h
$(LIB_O_LIST) : $(BLDDIR)/asm-offsets.h
$(OSLIB) : $(LIB_O_LIST)
$(AR) -rs $@ $^
$(BLDDIR)/asm-offsets.h : asm-offsets.c $(BLDDIR)/.mkdir
$(CC) $(CCFLAGS) $(IFLAGS) -MD -MF $(subst .h,.d,$@) -MT $@ -ffunction-sections -fdata-sections -Wl,--gc-sections -o $@.exe $<
$(ISS) $@.exe > $@
$(RM) $(subst /,$(S),$@.exe)
$(BLDDIR)/%.o : %.c $(BLDDIR)/.mkdir
$(CC) $(CCFLAGS) $(IFLAGS) $(FLAGS_$*) -mtext-section-literals -MD -MF $(subst .o,.d,$@) -c -o $@ $<
$(BLDDIR)/%.o : %.S $(BLDDIR)/.mkdir
$(CC) $(ASFLAGS) $(IFLAGS) $(FLAGS_$*) -MD -MF $(subst .o,.d,$@) -c -o $@ $<
clean :
$(RM_R) $(BLDDIR)
clean_all :
$(RM_R) $(BLDROOT)
.PHONY : all clean clean_all
@@ -0,0 +1,24 @@
#include <stddef.h>
#include <stdio.h>
#include "../../../../../tasks.c"
#define DEFINE(sym,val) \
printf("#define %s %d /* %s */\n", #sym, (val), #val)
int main(void)
{
DEFINE(TCB_TOP_OF_STACK_OFF, offsetof(TCB_t, pxTopOfStack));
#if portUSING_MPU_WRAPPERS
DEFINE(TCB_MPU_SETTINGS_OFF, offsetof(TCB_t, xMPUSettings.mpumap));
DEFINE(MPU_ENTRY_SIZE, sizeof(xthal_MPU_entry));
DEFINE(MPU_ENTRY_AS_OFF, offsetof(xthal_MPU_entry, as));
DEFINE(MPU_ENTRY_AT_OFF, offsetof(xthal_MPU_entry, at));
DEFINE(TCB_TASK_NAME_OFF, offsetof(TCB_t, pcTaskName));
#endif
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
DEFINE(TCB_END_OF_STACK_OFF, offsetof(TCB_t, pxEndOfStack));
#endif
#if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 )
DEFINE(TCB_IMPURE_PTR_OFF, offsetof(TCB_t, xTLSBlock));
#endif
}
@@ -0,0 +1,155 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#include "xtensa_rtos.h"
#include "portmacro.h"
#include "asm-offsets.h"
#include <xtensa/hal.h>
/*******************************************************************************
_xt_mpu_restore
!! MUST BE CALLED ONLY BY 'CALL0' INSTRUCTION !!
Restores task's MPU state
Entry Conditions:
A0 = Return address in caller.
*******************************************************************************/
#if portUSING_MPU_WRAPPERS
#if XCHAL_HAVE_XEA3 && (!defined __XTENSA_CALL0_ABI__)
#define A2 a10
#define A3 a11
#define A4 a12
#define A5 a13
#define A6 a14
#else
#define A2 a2
#define A3 a3
#define A4 a4
#define A5 a5
#define A6 a6
#endif
.altmacro
.macro mpu_set_entry entry
/*
* Load both first and last entry for a range to avoid memory access
* when region is partially modified which may result in a TLB multihit.
*/
l32i A3, A2, (TCB_MPU_SETTINGS_OFF + (MPU_ENTRY_SIZE * ((portNUM_MAX_SWAPPED_MPU_PAIRS - \entry) * 2 + 1)) + MPU_ENTRY_AT_OFF)
l32i A4, A2, (TCB_MPU_SETTINGS_OFF + (MPU_ENTRY_SIZE * ((portNUM_MAX_SWAPPED_MPU_PAIRS - \entry) * 2 + 1)) + MPU_ENTRY_AS_OFF)
l32i A5, A2, (TCB_MPU_SETTINGS_OFF + (MPU_ENTRY_SIZE * ((portNUM_MAX_SWAPPED_MPU_PAIRS - \entry) * 2 + 0)) + MPU_ENTRY_AT_OFF)
l32i A6, A2, (TCB_MPU_SETTINGS_OFF + (MPU_ENTRY_SIZE * ((portNUM_MAX_SWAPPED_MPU_PAIRS - \entry) * 2 + 0)) + MPU_ENTRY_AS_OFF)
wptlb A3, A4
wptlb A5, A6
.endm
.section "privileged_functions"
.global _xt_mpu_restore
.type _xt_mpu_restore,@function
.align 4
_xt_mpu_restore:
movi A2, pxCurrentTCB
l32i A2, A2, 0
beqz A2, .Lset
movi A3, g_num_used_mpu_entries
l32i A3, A3, 0
movi A4, .Lset
/*
* A3 = .Lset - 18 * g_num_used_mpu_entries -- is the address of code
* that loads MPU entries of the first g_num_used_mpu_entries regions.
* 18 is the size of piece of code generated by one mpu_set_entry macro
*/
slli A5, A3, 4
addx2 A5, A3, A5
sub A3, A4, A5
jx A3
.begin no-transform
/* Load MPU entries from last to first (not in MPU order,
* in pxCurrentTCB->xMPUSettings order).
*/
.set i, 1
.rept portNUM_MAX_SWAPPED_MPU_PAIRS
mpu_set_entry %i
.set i, i+1
.endr
.Lset:
.end no-transform
isync
ret
#ifdef portALIGN_SECTIONS
.section privileged_functions
.align XCHAL_MPU_ALIGN
.section privileged_data
.align XCHAL_MPU_ALIGN
.section freertos_system_calls
.align XCHAL_MPU_ALIGN
.section .text
.align XCHAL_MPU_ALIGN
.section .data
.align XCHAL_MPU_ALIGN
.section .literal
.align XCHAL_MPU_ALIGN
.section .bss
.align XCHAL_MPU_ALIGN
.section .rodata
.align XCHAL_MPU_ALIGN
.section private_region1
.align XCHAL_MPU_ALIGN
.section private_region2
.align XCHAL_MPU_ALIGN
.section private_region3
.align XCHAL_MPU_ALIGN
.section private_stack
.align XCHAL_MPU_ALIGN
#endif
#endif // portUSING_MPU_WRAPPERS
@@ -0,0 +1,637 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#if portUSING_MPU_WRAPPERS
# define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#endif
#include <stdlib.h>
#include <xtensa/hal.h>
#include <xtensa/config/core.h>
#if XCHAL_HAVE_INTERRUPTS
#include <xtensa/tie/xt_interrupt.h>
#endif
#if XCHAL_HAVE_ISL || XCHAL_HAVE_KSL || XCHAL_HAVE_PSL
#include <xtensa/tie/xt_exception_dispatch.h>
#endif
#include "xtensa_api.h"
#include "xtensa_rtos.h"
#include "FreeRTOS.h"
#include "task.h"
/* Heap area (see heap_4.c). When MPU in use, align it to the MPU
region boundary to avoid overlapping with non-heap data. */
#if portUSING_MPU_WRAPPERS
#define HEAP_SIZE ((configTOTAL_HEAP_SIZE + XCHAL_MPU_ALIGN - 1) & -XCHAL_MPU_ALIGN)
PRIVILEGED_DATA uint8_t ucHeap[ HEAP_SIZE ] __attribute__((aligned(XCHAL_MPU_ALIGN)));
#else
uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif
#if portUSING_MPU_WRAPPERS
/* Configure a number of standard MPU regions that are used by all tasks. */
extern BaseType_t prvSetupMPU( void ) PRIVILEGED_FUNCTION;
/*
* Checks to see if being called from the context of an unprivileged task, and
* if so raises the privilege level and returns false - otherwise does nothing
* other than return true.
*/
BaseType_t xPortRaisePrivilege( void );
#endif
#if XCHAL_CP_NUM > 0
extern void _xt_coproc_init( void );
extern void _xt_coproc_exc(XtExcFrame * fp);
#endif
// Defined in xtensa_vectors.S.
extern void _xt_task_start( void );
#if XCHAL_HAVE_XEA3 && portUSING_MPU_WRAPPERS
extern void _xt_task_start_user( void );
#endif
// Timer tick interval in cycles.
static uint32_t xt_tick_cycles;
TickType_t xMaxSuppressedTicks;
static uint32_t xt_tick_count;
#if ( configUSE_TICKLESS_IDLE != 0 )
// Flag to indicate tick handling should be skipped.
static volatile uint32_t xt_skip_tick;
#endif
#if XCHAL_HAVE_XEA3
int32_t xt_sw_intnum = -1;
#endif
// Duplicate of inaccessible xSchedulerRunning.
uint32_t port_xSchedulerRunning = 0U;
// Interrupt nesting level.
uint32_t port_interruptNesting = 0U;
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
//-----------------------------------------------------------------------------
// Tick timer interrupt handler.
//-----------------------------------------------------------------------------
static void xt_tick_handler( void )
{
int32_t diff;
#if ( configUSE_TICKLESS_IDLE != 0 )
if ( xt_skip_tick )
{
vTaskStepTick( xt_skip_tick );
xt_tick_count += xt_skip_tick;
xt_skip_tick = 0;
}
#endif
do
{
BaseType_t ret;
uint32_t interruptMask;
uint32_t ulOldCCompare = xt_get_ccompare( XT_TIMER_INDEX );
// Set CCOMPARE for next tick.
xt_set_ccompare( XT_TIMER_INDEX, ulOldCCompare + xt_tick_cycles );
portbenchmarkIntLatency();
// Interrupts upto configMAX_SYSCALL_INTERRUPT_PRIORITY must be
// disabled before calling xTaskIncrementTick as it accesses the
// kernel lists.
interruptMask = portSET_INTERRUPT_MASK_FROM_ISR();
{
ret = xTaskIncrementTick();
++xt_tick_count;
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( interruptMask );
portYIELD_FROM_ISR( ret );
// Signed comparison gracefully handles cases where another source
// has called xt_update_clock_frequency(), e.g. for tickless idle
// or variable frequency support, and ccompare was advanced
// farther than expected.
diff = (int32_t)(xt_get_ccount() - ulOldCCompare);
}
while ( diff > (int32_t)xt_tick_cycles );
}
static void update_xt_tick_cycles( void )
{
// Compute the number of cycles per tick.
#ifdef XT_CLOCK_FREQ
xt_tick_cycles = ( XT_CLOCK_FREQ / XT_TICK_PER_SEC );
#elif defined(XT_BOARD)
xt_tick_cycles = xtbsp_clock_freq_hz() / XT_TICK_PER_SEC;
#else
#error "No way to obtain processor clock frequency"
#endif
}
//-----------------------------------------------------------------------------
// Tick timer init. Install interrupt handler, set up first tick, and
// enable timer interrupt.
//-----------------------------------------------------------------------------
static void xt_tick_timer_init( void )
{
update_xt_tick_cycles();
xMaxSuppressedTicks = 0xFFFFFFFFU / xt_tick_cycles;
xt_set_interrupt_handler( XT_TIMER_INTNUM, (xt_handler) xt_tick_handler, 0 );
xt_set_ccompare( XT_TIMER_INDEX, xthal_get_ccount() + xt_tick_cycles );
xt_tick_count = xTaskGetTickCount();
xt_interrupt_enable( XT_TIMER_INTNUM );
}
//-----------------------------------------------------------------------------
// Tick timer stop. Disable timer interrupt and clear ccompare register.
//-----------------------------------------------------------------------------
static void xt_tick_timer_stop( void )
{
xt_interrupt_disable( XT_TIMER_INTNUM );
xt_set_ccompare( XT_TIMER_INDEX, 0 );
}
//-----------------------------------------------------------------------------
// Start the scheduler.
//-----------------------------------------------------------------------------
BaseType_t xPortStartScheduler( void )
{
#if XCHAL_HAVE_XEA3
extern void xt_sched_handler(void * arg);
int32_t i;
#endif
// Interrupts are disabled at this point and stack contains PS with
// enabled interrupts when task context is restored.
#if XCHAL_CP_NUM > 0
// Initialize co-processor management for tasks. Leave CPENABLE alone.
_xt_coproc_init();
#if XCHAL_HAVE_XEA3
// Install the coprocessor exception handler.
xt_set_exception_handler(EXCCAUSE_CP_DISABLED, _xt_coproc_exc);
#endif
#endif
#if XCHAL_HAVE_XEA3
// Select a software interrupt to use for scheduling.
for (i = 0; i < XCHAL_NUM_INTERRUPTS; i++) {
if ((Xthal_inttype[i] == XTHAL_INTTYPE_SOFTWARE) && (Xthal_intlevel[i] == 1)) {
xt_sw_intnum = i;
break;
}
}
if (xt_sw_intnum == -1) {
return pdFALSE;
}
/* Set the interrupt handler and enable the interrupt. */
xt_set_interrupt_handler(xt_sw_intnum, xt_sched_handler, 0);
xt_interrupt_enable(xt_sw_intnum);
#if XCHAL_HAVE_KSL
XT_WSR_KSL(0);
#endif
#if XCHAL_HAVE_ISL
XT_WSR_ISL(0);
#endif
#endif
// Set up and enable timer tick.
xt_tick_timer_init();
#if XT_USE_THREAD_SAFE_CLIB
// Init C library
vPortClibInit();
#endif
#if portUSING_MPU_WRAPPERS
// Setup MPU
if (prvSetupMPU() == pdFALSE)
return pdFALSE;
#endif
port_xSchedulerRunning = 1U;
// Cannot be directly called from C; never returns
__asm__ volatile ("call0 _frxt_dispatch\n");
// Should never get here.
return pdFALSE;
}
//-----------------------------------------------------------------------------
// Stop the scheduler.
//-----------------------------------------------------------------------------
void vPortEndScheduler( void )
{
xt_tick_timer_stop();
port_xSchedulerRunning = 0U;
}
//-----------------------------------------------------------------------------
// Stack initialization.
// Reserve coprocessor save area if needed, construct a dummy stack frame and
// populate it for task startup. Return the pointer to the dummy stack frame.
// (NOTE: the value returned from this function is expected to be stored in
// pxTCB->pxTopOfStack. In the task wrapper code, we will set the value of
// pxTCB->pxEndOfStack, which will then be treated as the coprocessor state
// area pointer.
//-----------------------------------------------------------------------------
#if portUSING_MPU_WRAPPERS
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack,
TaskFunction_t pxCode,
void *pvParameters,
BaseType_t xRunPrivileged,
xMPU_SETTINGS * xMPUSettings )
#else
StackType_t *pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
#endif
{
StackType_t *sp;
StackType_t *tp;
StackType_t *ret;
XtExcFrame *frame;
#if XCHAL_CP_NUM > 0
uint32_t *p;
#endif
#if portUSING_MPU_WRAPPERS
// TODO: Handle this...
(void)xMPUSettings;
#endif
// Allocate enough space for coprocessor state, align base address. This is the
// adjusted top-of-stack and also the start of the coprocessor save area.
sp = (StackType_t *) ((((uint32_t) pxTopOfStack) - (uint32_t) XT_CP_SIZE) & (uint32_t)~0xF);
// Allocate interrupt stack frame. XT_STK_FRMSZ is always a multiple of 16 bytes
// so 16-byte alignment is ensured.
tp = sp - (XT_STK_FRMSZ/sizeof(StackType_t));
// This is the address we will return.
ret = tp;
#if XCHAL_HAVE_XEA3
frame = (XtExcFrame *) (tp + (XT_STK_XTRA_SZ/sizeof(StackType_t)));
#else
frame = (XtExcFrame *) tp;
#endif
// Clear the frame (do not use memset() because we don't depend on C library).
for (; tp < sp; ++tp)
{
*tp = 0;
}
// Explicitly initialize certain saved registers. Note that the entry point
// is set to be the task wrapper, and the address of the coprocessor save
// area (sp) is saved in the dummy frame for the wrapper to use.
#if XCHAL_HAVE_XEA2
frame->pc = (long) pxCode; // task entrypoint
frame->a0 = 0; // to terminate GDB backtrace
frame->a1 = (long) sp; // top of stack - CP save area
frame->exit = (long) _xt_task_start; // task start wrapper
// Set initial PS to int level 0, EXCM disabled ('rfe' will enable), user mode.
// Also set entry point argument parameter.
#ifdef __XTENSA_CALL0_ABI__
frame->a2 = (long) pvParameters;
frame->ps = PS_UM | PS_EXCM;
#else
// + for windowed ABI also set WOE and CALLINC (pretend task was 'call4'd).
frame->a6 = (long) pvParameters;
frame->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1);
#endif
#if portUSING_MPU_WRAPPERS
if(!xRunPrivileged) {
frame->ps |= (1 << PS_RING_SHIFT);
}
#endif
#endif
#if XCHAL_HAVE_XEA3
frame->a8 = (long) pxCode; // task entrypoint
frame->a9 = (long) sp; // top of stack - CP save area
#if portUSING_MPU_WRAPPERS
frame->pc = (!xRunPrivileged) ?
(long) _xt_task_start_user : // PS_RING will be set later
(long) _xt_task_start; // standard task start wrapper
#else
frame->pc = (long) _xt_task_start; // task start wrapper
#endif
frame->ps = PS_STACK_FIRSTKER; // initial PS
frame->atomctl = 0; // initial value
// Set entry point arg.
#ifdef __XTENSA_CALL0_ABI__
frame->a2 = (long) pvParameters;
#else
frame->a10 = (long) pvParameters;
#endif
#endif
#ifdef XT_USE_SWPRI
// Set the initial virtual priority mask value to all 1's.
frame->vpri = 0xFFFFFFFF;
#endif
#if XCHAL_CP_NUM > 0
// Init the coprocessor save area (see xtensa_context.h).
p = (uint32_t *) sp;
p[0] = 0;
p[1] = 0;
p[2] = (((uint32_t) p) + 12 + XCHAL_TOTAL_SA_ALIGN - 1) & -XCHAL_TOTAL_SA_ALIGN;
#endif
return ret;
}
//-----------------------------------------------------------------------------
// Tickless idle support. Suppress N ticks and sleep when directed by kernel.
//-----------------------------------------------------------------------------
#if ( configUSE_TICKLESS_IDLE != 0 )
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{
eSleepModeStatus eSleepStatus;
// Lock out all interrupts. Otherwise reading and using ccount can
// get messy. Shouldn't be a problem here since we are about to go
// to sleep, and the waiti will re-enable interrupts shortly.
// Must call vTaskEnterCritical() in order to increment FreeRTOS
// nesting count, otheriwse the call to vTaskStepTick() inside this
// critical section will inadvertently reenable interrupts.
portENTER_CRITICAL();
eSleepStatus = eTaskConfirmSleepModeStatus();
if ( eSleepStatus == eAbortSleep )
{
// Abort, fall through.
}
else
{
uint32_t num_cycles;
uint32_t first_blocked_tick;
uint32_t ccompare;
uint32_t skip_tick;
uint32_t now;
// Compute number of cycles to sleep for, capped by max limit.
// we use one less than the number of ticks because we are already
// partway through the current tick. This is adjusted later below.
if ( xExpectedIdleTime > xMaxSuppressedTicks )
{
skip_tick = xMaxSuppressedTicks - 1U;
}
else
{
skip_tick = xExpectedIdleTime - 1U;
}
num_cycles = xt_tick_cycles * skip_tick;
first_blocked_tick = xt_get_ccompare( XT_TIMER_INDEX );
xt_skip_tick = skip_tick;
// Set up for timer interrupt and sleep.
ccompare = first_blocked_tick + num_cycles;
xt_set_ccompare( XT_TIMER_INDEX, ccompare );
#if XCHAL_HAVE_XEA3
// Ccompare write will not clear pending interrupt.
xt_interrupt_clear( XT_TIMER_INTNUM );
#endif
// Ensure any clearing of pending interrupt takes effect.
// WAITI instruction will reduce interrupt level and sleep so
// it must be followed by raising the interrupt level to ensure
// the critical section is maintained. The NESTED version is
// used here since it does not increment the nesting count that
// is managed upon entry and exit of this function.
XT_ISYNC();
XT_WAITI( 0 );
portENTER_CRITICAL_NESTED();
skip_tick = xt_skip_tick;
now = xt_get_ccount();
// Awakened by non-timer interrupt, update tick counter here.
if ( skip_tick )
{
// If there's more than a tick period from now to the timer
// deadline try to move deadline to the next possible tick.
// Otherwise update tick count for the passed ticks, but don't
// change the deadline.
// If we get here, we have not handled a timer interrupt, so
// ccompare is still in the future, or in the immediate past
// (crossed after entering the critical section above). In the
// latter case the pending interrupt will be cleared below.
if ( ccompare - now > xt_tick_cycles )
{
uint32_t prev_tick = first_blocked_tick - xt_tick_cycles;
uint32_t actual_cycles = now - prev_tick;
uint32_t ticks = actual_cycles / xt_tick_cycles;
uint32_t diff;
ccompare = first_blocked_tick + ticks * xt_tick_cycles;
do
{
vTaskStepTick( ticks );
xt_tick_count += ticks;
#if XCHAL_HAVE_XEA3
// ccompare may be very close in the xt_set_ccompare call
// below, so we cannot reliably call xt_interrupt_clear
// after it. Make sure that timer IRQ is clear when
// xt_set_ccompare( XT_TIMER_INDEX, ccompare ) is called.
// Set ccompare to current ccount to avoid timer IRQ
// arriving in the gap between xt_interrupt_clear
// and the following xt_set_ccompare.
xt_set_ccompare( XT_TIMER_INDEX, xt_get_ccount() );
// Ccompare write will not clear pending interrupt.
xt_interrupt_clear( XT_TIMER_INTNUM );
#endif
xt_set_ccompare( XT_TIMER_INDEX, ccompare );
diff = xt_get_ccount() - ccompare;
ccompare += xt_tick_cycles;
ticks = 1;
} while ( diff <= INT32_MAX );
}
else
{
vTaskStepTick( skip_tick );
xt_tick_count += skip_tick;
}
xt_skip_tick = 0;
}
}
portEXIT_CRITICAL();
}
#endif
#if portUSING_MPU_WRAPPERS
extern void vPortResetPrivilege(BaseType_t previous);
void vPortEnterCritical( void )
{
// TODO: handle configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS
// For reference, see commit 79704b from 9/16/2022
if( portIS_PRIVILEGED() == pdFALSE )
{
portRAISE_PRIVILEGE();
vTaskEnterCritical();
portRESET_PRIVILEGE();
}
else
{
// TODO: handle port_interruptNesting
vTaskEnterCritical();
}
}
void vPortExitCritical( void )
{
// TODO: handle configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS
// For reference, see commit 79704b from 9/16/2022
if( portIS_PRIVILEGED() == pdFALSE )
{
portRAISE_PRIVILEGE();
vTaskExitCritical();
portRESET_PRIVILEGE();
}
else
{
// TODO: handle port_interruptNesting
vTaskExitCritical();
}
}
#endif
#if ( configUSE_VARIABLE_FREQUENCY != 0 )
static void update_tick_remainder( uint32_t now )
{
uint32_t old_ccompare;
uint32_t old_tick_cycles;
old_ccompare = xt_get_ccompare( XT_TIMER_INDEX );
old_tick_cycles = xt_tick_cycles;
update_xt_tick_cycles();
// If tick deadline has not been reached yet correct number of remaining
// cycles, otherwise timer interrupt must be pending, just service it.
if ( old_ccompare - now < old_tick_cycles )
{
uint32_t new_ccompare = now +
(uint32_t)((uint64_t)(old_ccompare - now) * xt_tick_cycles / old_tick_cycles);
xt_set_ccompare( XT_TIMER_INDEX, new_ccompare );
now = xt_get_ccount();
if ( new_ccompare - now > xt_tick_cycles )
xt_tick_handler();
}
}
#if ( configUSE_TICKLESS_IDLE != 0 )
void xt_update_clock_frequency( void )
{
uint32_t skip_tick;
uint32_t now;
portENTER_CRITICAL();
now = xt_get_ccount();
skip_tick = xt_skip_tick;
if ( skip_tick )
{
uint32_t ccompare = xt_get_ccompare( XT_TIMER_INDEX );
// If there's more than a tick period from now to the timer
// deadline try to move deadline to the next possible tick.
// Otherwise update tick count for the passed ticks, but don't
// change the deadline.
if ( ccompare - now > xt_tick_cycles &&
ccompare - now <= INT32_MAX )
{
uint32_t first_blocked_tick = ccompare - xt_tick_cycles * skip_tick;
uint32_t prev_tick = first_blocked_tick - xt_tick_cycles;
uint32_t actual_cycles = now - prev_tick;
uint32_t ticks = actual_cycles / xt_tick_cycles;
uint32_t diff;
ccompare = first_blocked_tick + ticks * xt_tick_cycles;
do
{
vTaskStepTick( ticks );
xt_tick_count += ticks;
xt_set_ccompare( XT_TIMER_INDEX, ccompare );
diff = xt_get_ccount() - ccompare;
ccompare += xt_tick_cycles;
ticks = 1;
} while ( diff <= INT32_MAX );
}
else
{
vTaskStepTick( skip_tick );
xt_tick_count += skip_tick;
}
xt_skip_tick = 0;
}
update_tick_remainder( now );
portEXIT_CRITICAL();
}
#else
void xt_update_clock_frequency( void )
{
portENTER_CRITICAL();
update_tick_remainder( xt_get_ccount() );
portEXIT_CRITICAL();
}
#endif
#endif
@@ -0,0 +1,702 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#include "xtensa_rtos.h"
#include "asm-offsets.h"
#include "portmacro.h"
.extern pxCurrentTCB
/*
*******************************************************************************
* Interrupt stack. The size of the interrupt stack is determined by the config
* parameter "configISR_STACK_SIZE" in FreeRTOSConfig.h
*******************************************************************************
*/
.data
.align 16
.global port_switch_flag
port_switch_flag:
.word 0
.global port_yield_flag
port_yield_flag:
.word 0
#if portUSING_MPU_WRAPPERS
.section "privileged_functions"
#else
.text
#endif
/*
*******************************************************************************
* _frxt_setup_switch
* void _frxt_setup_switch(void);
*
* Sets an internal flag indicating that a task switch is required on return
* from interrupt handling.
*
*******************************************************************************
*/
.global _frxt_setup_switch
.type _frxt_setup_switch,@function
.align 4
_frxt_setup_switch:
ENTRY(16)
movi a2, port_switch_flag
movi a3, 1
s32i a3, a2, 0
RET(16)
.size _frxt_setup_switch, . - _frxt_setup_switch
#if XCHAL_HAVE_XEA2
/*
*******************************************************************************
* _frxt_int_enter
* void _frxt_int_enter(void)
*
* Implements the Xtensa RTOS porting layer's XT_RTOS_INT_ENTER function for
* freeRTOS. Saves the rest of the interrupt context (not already saved).
* May only be called from assembly code by the 'call0' instruction, with
* interrupts disabled.
* See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
*
*******************************************************************************
*/
.globl _frxt_int_enter
.type _frxt_int_enter,@function
.align 4
_frxt_int_enter:
/* Save a12-13 in the stack frame as required by _xt_context_save. */
s32i a12, a1, XT_STK_A12
s32i a13, a1, XT_STK_A13
/* Save return address in a safe place (free a0). */
mov a12, a0
/* Save the rest of the interrupted context (preserves A12-13). */
call0 _xt_context_save
/*
Save interrupted task's SP in TCB only if not nesting.
Manage nesting directly rather than call the generic IntEnter()
(in windowed ABI we can't call a C function here anyway because PS.EXCM is still set).
*/
movi a2, port_xSchedulerRunning
movi a3, port_interruptNesting
l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */
beqz a2, 1f /* scheduler not running, no tasks */
l32i a2, a3, 0 /* a2 = port_interruptNesting */
addi a2, a2, 1 /* increment nesting count */
s32i a2, a3, 0 /* save nesting count */
bnei a2, 1, .Lnested /* !=0 before incr, so nested */
movi a2, pxCurrentTCB
l32i a2, a2, 0 /* a2 = current TCB */
beqz a2, 1f
s32i a1, a2, TCB_TOP_OF_STACK_OFF /* pxCurrentTCB->pxTopOfStack = SP */
movi a1, xt_interrupt_stack_top /* a1 = top of intr stack */
.Lnested:
1:
mov a0, a12 /* restore return addr and return */
ret
#endif /* XCHAL_HAVE_XEA2 */
#if XCHAL_HAVE_XEA2
/*
*******************************************************************************
* _frxt_int_exit
* void _frxt_int_exit(void)
*
* Implements the Xtensa RTOS porting layer's XT_RTOS_INT_EXIT function for
* FreeRTOS. If required, calls vPortYieldFromInt() to perform task context
* switching, restore the (possibly) new task's context, and return to the
* exit dispatcher saved in the task's stack frame at XT_STK_EXIT.
* May only be called from assembly code by the 'call0' instruction. Does not
* return to caller.
* See the description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
*
*******************************************************************************
*/
.globl _frxt_int_exit
.type _frxt_int_exit,@function
.align 4
_frxt_int_exit:
movi a2, port_xSchedulerRunning
movi a3, port_interruptNesting
rsil a0, XT_IRQ_LOCK_LEVEL /* lock out interrupts */
l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */
beqz a2, .Lnoswitch /* scheduler not running, no tasks */
l32i a2, a3, 0 /* a2 = port_interruptNesting */
addi a2, a2, -1 /* decrement nesting count */
s32i a2, a3, 0 /* save nesting count */
bnez a2, .Lnesting /* !=0 after decr so still nested */
movi a2, pxCurrentTCB
l32i a2, a2, 0 /* a2 = current TCB */
beqz a2, 1f /* no task ? go to dispatcher */
l32i a1, a2, TCB_TOP_OF_STACK_OFF /* SP = pxCurrentTCB->pxTopOfStack */
movi a2, port_switch_flag /* address of switch flag */
l32i a3, a2, 0 /* a3 = port_switch_flag */
beqz a3, .Lnoswitch /* flag = 0 means no switch reqd */
movi a3, 0
s32i a3, a2, 0 /* zero out the flag for next time */
1:
/*
Call0 ABI callee-saved regs a12-15 need to be saved before possible preemption.
However a12-13 were already saved by _frxt_int_enter().
*/
#ifdef __XTENSA_CALL0_ABI__
s32i a14, a1, XT_STK_A14
s32i a15, a1, XT_STK_A15
#endif
#ifdef __XTENSA_CALL0_ABI__
call0 vPortYieldFromInt /* call dispatch inside the function; never returns */
#else
call4 vPortYieldFromInt /* this one returns */
call0 _frxt_dispatch /* tail-call dispatcher */
/* Never returns here. */
#endif
.Lnoswitch:
/*
If we came here then about to resume the interrupted task.
*/
.Lnesting:
/*
We come here only if there was no context switch, that is if this
is a nested interrupt, or the interrupted task was not preempted.
In either case there's no need to load the SP.
*/
#if portUSING_MPU_WRAPPERS
call0 _xt_mpu_restore
#endif
/* Restore full context from interrupt stack frame */
call0 _xt_context_restore
/*
Must return via the exit dispatcher corresponding to the entry point from which
this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt
stack frame is deallocated in the exit dispatcher.
*/
l32i a0, a1, XT_STK_EXIT
ret
#endif /* XCHAL_HAVE_XEA2 */
/*
**********************************************************************************************************
* DISPATCH THE HIGH READY TASK
* void _frxt_dispatch(void)
*
* Switch context to the highest priority ready task, restore its state and dispatch control to it.
*
* This is a common dispatcher that acts as a shared exit path for all the context switch functions
* including vPortYield() and vPortYieldFromInt(), all of which tail-call this dispatcher
* (for windowed ABI vPortYieldFromInt() calls it indirectly via _frxt_int_exit() ).
*
* The Xtensa port uses different stack frames for solicited and unsolicited task suspension (see
* comments on stack frames in xtensa_context.h). This function restores the state accordingly.
* If restoring a task that solicited entry, restores the minimal state and leaves CPENABLE clear.
* If restoring a task that was preempted, restores all state including the task's CPENABLE.
*
* Entry:
* pxCurrentTCB points to the TCB of the task to suspend,
* Because it is tail-called without a true function entrypoint, it needs no 'entry' instruction.
*
* Exit:
* If incoming task called vPortYield() (solicited), this function returns as if from vPortYield().
* If incoming task was preempted by an interrupt, this function jumps to exit dispatcher.
*
**********************************************************************************************************
*/
.globl _frxt_dispatch
.type _frxt_dispatch,@function
.align 4
_frxt_dispatch:
#if XCHAL_HAVE_XEA3
/* Skip "entry" - nothing to save, never returns. */
movi a2, PS_STACK_KERNEL | PS_DI /* Set PS.STACK = Kernel and */
movi a3, PS_STACK_MASK | PS_DI_MASK /* disable interrupts. */
xps a2, a3
movi a2, port_yield_flag
movi a3, 1
s32i a3, a2, 0 /* Indicate thread yield. */
#ifdef __XTENSA_CALL0_ABI__
call0 vTaskSwitchContext // Get next TCB to resume
movi a2, pxCurrentTCB
#else
movi a2, pxCurrentTCB
call8 vTaskSwitchContext // Get next TCB to resume
#endif
l32i a3, a2, 0 /* a3 = pxCurrentTCB */
l32i a1, a3, TCB_TOP_OF_STACK_OFF /* SP = next_TCB->pxTopOfStack */
addi a1, a1, XT_STK_FRMSZ /* Adjust for the dummy frame */
#ifdef __XTENSA_CALL0_ABI__
mov a15, a1 /* Dispatch code expects a15 = old SP */
#endif
movi a0, _xt_dispatch + 3 /* Jump to dispatch code. It will */
ret /* check for ready thread or idle */
/* and handle accordingly. */
ill /* Should never get back here. */
#endif
#if XCHAL_HAVE_XEA2
#ifdef __XTENSA_CALL0_ABI__
call0 vTaskSwitchContext // Get next TCB to resume
movi a2, pxCurrentTCB
#else
movi a2, pxCurrentTCB
call4 vTaskSwitchContext // Get next TCB to resume
#endif
l32i a3, a2, 0
l32i sp, a3, TCB_TOP_OF_STACK_OFF /* SP = next_TCB->pxTopOfStack */
s32i a3, a2, 0
/* Determine the type of stack frame. */
l32i a2, sp, XT_STK_EXIT /* exit dispatcher or solicited flag */
bnez a2, .L_frxt_dispatch_stk
.L_frxt_dispatch_sol:
/* Solicited stack frame. Restore minimal context and return from vPortYield(). */
#if portUSING_MPU_WRAPPERS
movi a2, SYSCALL_raise_priv
syscall
mov a9, a2
call0 _xt_mpu_restore
bnez a9, skip_lowering_priv;
rsr.ps a2
addi a2, a2, PS_RING(1)
wsr.ps a2
skip_lowering_priv:
#endif
l32i a3, sp, XT_STK_PS
#ifdef __XTENSA_CALL0_ABI__
l32i a12, sp, XT_STK_A12
l32i a13, sp, XT_STK_A13
l32i a14, sp, XT_STK_A14
l32i a15, sp, XT_STK_A15
#endif
l32i a0, sp, XT_STK_PC
#if XCHAL_CP_NUM > 0
/* Ensure wsr.CPENABLE is complete (should be, it was cleared on entry). */
rsync
#endif
/* As soons as PS is restored, interrupts can happen. No need to sync PS. */
wsr a3, PS
#ifdef __XTENSA_CALL0_ABI__
addi sp, sp, XT_STK_FRMSZ
ret
#else
retw
#endif
.L_frxt_dispatch_stk:
#if XCHAL_CP_NUM > 0
/* Restore CPENABLE from task's co-processor save area. */
movi a3, pxCurrentTCB
l32i a3, a3, 0
l32i a2, a3, TCB_END_OF_STACK_OFF /* a2 = base of coproc save area */
l16ui a3, a2, XT_CPENABLE /* a3 = saved CPENABLE value */
wsr a3, CPENABLE
#endif
#if portUSING_MPU_WRAPPERS
call0 _xt_mpu_restore
#endif
/* Interrupt stack frame. Restore full context and return to exit dispatcher. */
call0 _xt_context_restore
/* In Call0 ABI, restore callee-saved regs (A12, A13 already restored). */
#ifdef __XTENSA_CALL0_ABI__
l32i a14, sp, XT_STK_A14
l32i a15, sp, XT_STK_A15
#endif
#if XCHAL_CP_NUM > 0
/* Ensure wsr.CPENABLE has completed. */
rsync
#endif
/*
Must return via the exit dispatcher corresponding to the entrypoint from which
this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt
stack frame is deallocated in the exit dispatcher.
*/
l32i a0, sp, XT_STK_EXIT
ret
#endif /* XCHAL_HAVE_XEA2 */
/*
**********************************************************************************************************
* PERFORM A SOLICTED CONTEXT SWITCH (from a task)
* void vPortYield(void)
*
* This function saves the minimal state needed for a solicited task suspension, clears CPENABLE,
* then tail-calls the dispatcher _frxt_dispatch() to perform the actual context switch
*
* At Entry:
* pxCurrentTCB points to the TCB of the task to suspend
* Callable from C (obeys ABI conventions on entry).
*
* Does not return to caller.
*
**********************************************************************************************************
*/
.globl vPortYield
.type vPortYield,@function
.align 4
vPortYield:
#if XCHAL_HAVE_XEA3
#ifdef __XTENSA_CALL0_ABI__
addi sp, sp, -16
s32i a0, sp, 0 /* save return address */
#else
entry sp, 48
s32i a0, sp, 0 /* save return address */
ssai 0
spillw /* spill all registers */
#endif
/*
Save register state into exception frame. This is safe to do with
interrupts enabled, and we will keep the frame allocated when we
call the dispatch function.
*/
movi a0, .Lret
rsr.ps a2
addi sp, sp, -XT_STK_XFRM_SZ
s32i a0, sp, XT_STK_PC /* save return PC */
s32i a2, sp, XT_STK_PS /* save PS */
#ifdef __XTENSA_CALL0_ABI__
s32i a12, sp, XT_STK_A12 /* callee-saved registers */
s32i a13, sp, XT_STK_A13
s32i a14, sp, XT_STK_A14
s32i a15, sp, XT_STK_A15
#endif
movi a2, PS_STACK_KERNEL | PS_DI /* Set PS.STACK = Kernel and */
movi a8, PS_STACK_MASK | PS_DI_MASK /* disable interrupts. */
xps a2, a8
movi a3, pxCurrentTCB
l32i a4, a3, 0 /* a4 = pxCurrentTCB */
#if XCHAL_CP_NUM > 0
/* Save coprocessor callee-saved state (if any). At this point CPENABLE */
/* should still reflect which CPs were in use (enabled). */
call0 _xt_coproc_savecs
/* Clear CPENABLE and give up all co-procs. */
l32i a3, a4, TCB_END_OF_STACK_OFF /* a3 = pxCurrentTCB->cp_state */
movi a5, 0
s16i a5, a3, XT_CPENABLE
wsr a5, CPENABLE /* disable all co-processors */
#endif
addi a5, sp, XT_STK_XFRM_SZ - XT_STK_FRMSZ
s32i a5, a4, TCB_TOP_OF_STACK_OFF /* pxCurrentTCB->pxTopOfStack = SP */
call0 _frxt_dispatch /* Does not return, see below */
/* Execution returns here. Interrupts should be disabled. */
/* NOTE: we expect original SP to have been restored. */
.align 4
.Lret:
addi sp, sp, -XT_STK_XFRM_SZ /* Prepare to restore state */
l32i a2, sp, XT_STK_PS /* Retrieve PS value */
#ifdef __XTENSA_CALL0_ABI__
l32i a12, sp, XT_STK_A12 /* Callee-saved registers */
l32i a13, sp, XT_STK_A13
l32i a14, sp, XT_STK_A14
l32i a15, sp, XT_STK_A15
#endif
addi sp, sp, XT_STK_XFRM_SZ
wsr.ps a2 /* Safe to enable interrupts */
rsync
#ifdef __XTENSA_CALL0_ABI__
l32i a0, sp, 0
addi sp, sp, 16
ret
#else
l32i a0, sp, 0
retw
#endif
#endif /* XCHAL_HAVE_XEA3 */
#if XCHAL_HAVE_XEA2
#ifdef __XTENSA_CALL0_ABI__
addi a2, sp, -XT_STK_FRMSZ /* avoid addi/addmi relaxation that */
mov sp, a2 /* might temporarily move sp up */
#else
entry sp, XT_STK_FRMSZ
#endif
rsr a2, PS
s32i a0, sp, XT_STK_PC
s32i a2, sp, XT_STK_PS
#ifdef __XTENSA_CALL0_ABI__
s32i a12, sp, XT_STK_A12 /* save callee-saved registers */
s32i a13, sp, XT_STK_A13
s32i a14, sp, XT_STK_A14
s32i a15, sp, XT_STK_A15
#else
/* Spill register windows. Calling xthal_window_spill() causes extra */
/* spills and reloads, so we will set things up to call the _nw version */
/* instead to save cycles. */
movi a6, ~(PS_WOE_MASK|PS_INTLEVEL_MASK) /* spills a4-a7 if needed */
and a2, a2, a6 /* clear WOE, INTLEVEL */
addi a2, a2, XT_IRQ_LOCK_LEVEL /* set INTLEVEL */
wsr a2, PS
rsync
call0 xthal_window_spill_nw
l32i a2, sp, XT_STK_PS /* restore PS */
wsr a2, PS
#endif
rsil a2, XT_IRQ_LOCK_LEVEL /* disable low/med interrupts */
#if XCHAL_CP_NUM > 0
/* Save coprocessor callee-saved state (if any). At this point CPENABLE */
/* should still reflect which CPs were in use (enabled). */
call0 _xt_coproc_savecs
#endif
movi a2, pxCurrentTCB
movi a3, 0
l32i a2, a2, 0 /* a2 = pxCurrentTCB */
s32i a3, sp, XT_STK_EXIT /* 0 to flag as solicited frame */
s32i sp, a2, TCB_TOP_OF_STACK_OFF /* pxCurrentTCB->pxTopOfStack = SP */
#if XCHAL_CP_NUM > 0
/* Clear CPENABLE, also in task's co-processor state save area. */
l32i a2, a2, TCB_END_OF_STACK_OFF /* a2 = pxCurrentTCB->cp_state */
movi a3, 0
wsr a3, CPENABLE
beqz a2, 1f
s16i a3, a2, XT_CPENABLE /* clear saved cpenable */
1:
#endif
/* Tail-call dispatcher. */
call0 _frxt_dispatch
/* Never reaches here. */
#endif /* XCHAL_HAVE_XEA2 */
/*
**********************************************************************************************************
* PERFORM AN UNSOLICITED CONTEXT SWITCH (from an interrupt)
* void vPortYieldFromInt(void)
*
* This calls the context switch hook (removed), saves and clears CPENABLE, then tail-calls the dispatcher
* _frxt_dispatch() to perform the actual context switch.
*
* At Entry:
* Interrupted task context has been saved in an interrupt stack frame at pxCurrentTCB->pxTopOfStack.
* pxCurrentTCB points to the TCB of the task to suspend,
* Callable from C (obeys ABI conventions on entry).
*
* At Exit:
* Windowed ABI defers the actual context switch until the stack is unwound to interrupt entry.
* Call0 ABI tail-calls the dispatcher directly (no need to unwind) so does not return to caller.
*
**********************************************************************************************************
*/
.globl vPortYieldFromInt
.type vPortYieldFromInt,@function
.align 4
vPortYieldFromInt:
ENTRY(16)
#if XCHAL_CP_NUM > 0
/* Save CPENABLE in task's co-processor save area, and clear CPENABLE. */
movi a3, pxCurrentTCB /* cp_state = */
l32i a3, a3, 0
l32i a2, a3, TCB_END_OF_STACK_OFF
rsr a3, CPENABLE
s16i a3, a2, XT_CPENABLE /* cp_state->cpenable = CPENABLE; */
movi a3, 0
wsr a3, CPENABLE /* disable all co-processors */
#endif
#ifdef __XTENSA_CALL0_ABI__
/* Tail-call dispatcher. */
call0 _frxt_dispatch
/* Never reaches here. */
#else
RET(16)
#endif
/*
--------------------------------------------------------------------------------
Task entry wrapper. Set up as the resume address in the initial stack frame.
--------------------------------------------------------------------------------
*/
.global _xt_task_start
.type _xt_task_start,@function
.align 4
_xt_task_start:
#if XCHAL_CP_NUM > 0
movi a0, 0
wsr a0, CPENABLE /* disable all coprocessors */
#endif
#if XCHAL_HAVE_XEA3
/* On entry, a8 = task entry point, a9 = top of stack / coproc save area */
movi a0, pxCurrentTCB
l32i a0, a0, 0
s32i a9, a0, TCB_END_OF_STACK_OFF /* TCB->pxEndOfStack = CP save area */
movi a0, 0 /* terminate call stack for gdb */
jx a8
#endif
#if XCHAL_HAVE_XEA2
l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */
wsr a0, PS
l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */
wsr a0, EPC_1
movi a3, pxCurrentTCB
l32i a3, a3, 0
l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */
l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */
s32i sp, a3, TCB_END_OF_STACK_OFF /* TCB->pxEndOfStack = CP save area */
rsync /* ensure PS and EPC written */
rfe /* PS.EXCM is cleared */
#endif
#if XCHAL_HAVE_XEA3
/*
--------------------------------------------------------------------------------
XEA3 User-mode task entry wrapper. Same as _xt_task_start but sets PS_RING
bit after privileged init is complete.
--------------------------------------------------------------------------------
*/
.global _xt_task_start_user
.type _xt_task_start_user,@function
.align 4
_xt_task_start_user:
#if XCHAL_CP_NUM > 0
movi a0, 0
wsr a0, CPENABLE /* disable all coprocessors */
#endif
/* On entry, a8 = task entry point, a9 = top of stack / coproc save area */
movi a0, pxCurrentTCB
l32i a0, a0, 0
s32i a9, a0, TCB_END_OF_STACK_OFF /* TCB->pxEndOfStack = CP save area */
rsr a0, PS
movi a3, (1 << PS_RING_SHIFT)
or a0, a0, a3
wsr a0, PS
movi a0, 0 /* terminate call stack for gdb */
jx a8
#endif
/*
**********************************************************************************************************
* _xt_task_coproc_state
* void _xt_task_coproc_state(void)
*
* Implements the Xtensa RTOS porting layer's XT_RTOS_CP_STATE function for FreeRTOS.
*
* May only be called when a task is running, not within an interrupt handler (returns 0 in that case).
* May only be called from assembly code by the 'call0' instruction. Does NOT obey ABI conventions.
* Returns in a15 a pointer to the base of the co-processor state save area for the current task.
* See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
*
**********************************************************************************************************
*/
#if XCHAL_CP_NUM > 0
.globl _xt_task_coproc_state
.type _xt_task_coproc_state,@function
.align 4
_xt_task_coproc_state:
movi a15, port_xSchedulerRunning /* if (port_xSchedulerRunning */
l32i a15, a15, 0
beqz a15, 2f
movi a15, port_interruptNesting /* && port_interruptNesting == 0 */
l32i a15, a15, 0
bnez a15, 1f
movi a15, pxCurrentTCB
l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */
beqz a15, 2f
l32i a15, a15, TCB_END_OF_STACK_OFF /* a15 = pxCurrentTCB->pxEndOfStack */
ret
1:
movi a15, 0
2:
ret
#endif /* XCHAL_CP_NUM > 0 */
@@ -0,0 +1,51 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* This utility helps benchmarking interrupt latency and context switches.
* In order to enable it, set configBENCHMARK to 1 in FreeRTOSConfig.h.
* You will also need to download the FreeRTOS_trace patch that contains
* portbenchmark.c and the complete version of portbenchmark.h
*/
#ifndef PORTBENCHMARK_H
#define PORTBENCHMARK_H
#if configBENCHMARK
#error "You need to download the FreeRTOS_trace patch that overwrites this file"
#endif
#define portbenchmarkINTERRUPT_DISABLE()
#define portbenchmarkINTERRUPT_RESTORE(newstate)
#define portbenchmarkIntLatency()
#define portbenchmarkIntWait()
#define portbenchmarkReset()
#define portbenchmarkPrint()
#endif /* PORTBENCHMARK */
@@ -0,0 +1,261 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#include "FreeRTOS.h"
#if XT_USE_THREAD_SAFE_CLIB
#if XSHAL_CLIB == XTHAL_CLIB_XCLIB
#include <errno.h>
#include <sys/reent.h>
#include "semphr.h"
typedef SemaphoreHandle_t _Rmtx;
//-----------------------------------------------------------------------------
// Override this and set to nonzero to enable locking.
//-----------------------------------------------------------------------------
int32_t _xclib_use_mt = 1;
//-----------------------------------------------------------------------------
// Init lock.
//-----------------------------------------------------------------------------
void
_Mtxinit(_Rmtx * mtx)
{
// There is a little problem here with recursion. At startup _Initlocks()
// is called (within xclib) which attempts to create all the locks which
// comes here and can result in a call to malloc() which can try to lock
// access to the heap which results in another call to _Initlocks(). This
// infinite recursion must be avoided. What we do here is temporarily set
// the _xclib_use_mt to zero so that recursive calls to _Initlocks() will
// become nops. This allows the xSemaphoreCreateRecursiveMutex() function
// to call malloc() safely.
// Note that we could have used statically allocated mutexes instead but
// that requires configSUPPORT_STATIC_ALLOCATION to be defined which in
// turn requires other data such as idle task TCB/stack, timer task TCB/
// stack to be statically defined.
_xclib_use_mt = 0;
*mtx = xSemaphoreCreateRecursiveMutex();
_xclib_use_mt = 1;
}
//-----------------------------------------------------------------------------
// Destroy lock.
//-----------------------------------------------------------------------------
void
_Mtxdst(_Rmtx * mtx)
{
if ((mtx != NULL) && (*mtx != NULL)) {
vSemaphoreDelete(*mtx);
}
}
//-----------------------------------------------------------------------------
// Lock.
//-----------------------------------------------------------------------------
void
_Mtxlock(_Rmtx * mtx)
{
if ((mtx != NULL) && (*mtx != NULL)) {
xSemaphoreTakeRecursive(*mtx, portMAX_DELAY);
}
}
//-----------------------------------------------------------------------------
// Unlock.
//-----------------------------------------------------------------------------
void
_Mtxunlock(_Rmtx * mtx)
{
if ((mtx != NULL) && (*mtx != NULL)) {
xSemaphoreGiveRecursive(*mtx);
}
}
//-----------------------------------------------------------------------------
// Called by malloc() to allocate blocks of memory from the heap.
//-----------------------------------------------------------------------------
void *
_sbrk_r (struct _reent * reent, int32_t incr)
{
extern char _end[];
extern char _heap_sentry;
static char * _heap_sentry_ptr = &_heap_sentry;
static char * heap_ptr;
char * base;
if (!heap_ptr)
heap_ptr = (char *) &_end[0];
base = heap_ptr;
if (heap_ptr + incr >= _heap_sentry_ptr) {
reent->_errno = ENOMEM;
return (char *) -1;
}
heap_ptr += incr;
return base;
}
//-----------------------------------------------------------------------------
// Global initialization for C library.
//-----------------------------------------------------------------------------
void
vPortClibInit(void)
{
}
//-----------------------------------------------------------------------------
// Per-thread cleanup stub provided for linking, does nothing.
//-----------------------------------------------------------------------------
void
_reclaim_reent(struct _reent * ptr)
{
(void ) ptr; /* Avoid compiler warning */
}
#endif /* XSHAL_CLIB == XTHAL_CLIB_XCLIB */
#if XSHAL_CLIB == XTHAL_CLIB_NEWLIB
#include <errno.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "semphr.h"
static SemaphoreHandle_t xClibMutex;
static uint32_t ulClibInitDone = 0;
//-----------------------------------------------------------------------------
// Get C library lock.
//-----------------------------------------------------------------------------
void
__malloc_lock(struct _reent * ptr)
{
// Suppress compiler warning.
(void) ptr;
if (!ulClibInitDone)
return;
xSemaphoreTakeRecursive(xClibMutex, portMAX_DELAY);
}
//-----------------------------------------------------------------------------
// Release C library lock.
//-----------------------------------------------------------------------------
void
__malloc_unlock(struct _reent * ptr)
{
// Suppress compiler warning.
(void) ptr;
if (!ulClibInitDone)
return;
xSemaphoreGiveRecursive(xClibMutex);
}
//-----------------------------------------------------------------------------
// Lock for environment. Since we have only one global lock we can just call
// the malloc() lock function.
//-----------------------------------------------------------------------------
void
__env_lock(struct _reent * ptr)
{
__malloc_lock(ptr);
}
//-----------------------------------------------------------------------------
// Unlock environment.
//-----------------------------------------------------------------------------
void
__env_unlock(struct _reent * ptr)
{
__malloc_unlock(ptr);
}
//-----------------------------------------------------------------------------
// Called by malloc() to allocate blocks of memory from the heap.
//-----------------------------------------------------------------------------
void *
_sbrk_r (struct _reent * reent, int32_t incr)
{
extern char _end[];
extern char _heap_sentry;
static char * _heap_sentry_ptr = &_heap_sentry;
static char * heap_ptr;
char * base;
if (!heap_ptr)
heap_ptr = (char *) &_end[0];
base = heap_ptr;
if (heap_ptr + incr >= _heap_sentry_ptr) {
reent->_errno = ENOMEM;
return (char *) -1;
}
heap_ptr += incr;
return base;
}
//-----------------------------------------------------------------------------
// Global initialization for C library.
//-----------------------------------------------------------------------------
void
vPortClibInit(void)
{
configASSERT(!ulClibInitDone);
xClibMutex = xSemaphoreCreateRecursiveMutex();
ulClibInitDone = 1;
}
//-----------------------------------------------------------------------------
// Per-thread cleanup stub provided for linking, does nothing.
//-----------------------------------------------------------------------------
void
_reclaim_reent(struct _reent * ptr)
{
(void ) ptr; /* Avoid compiler warning */
}
#endif /* XSHAL_CLIB == XTHAL_CLIB_NEWLIB */
#endif /* XT_USE_THREAD_SAFE_CLIB */
@@ -0,0 +1,447 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#ifndef PORTMACRO_H
#define PORTMACRO_H
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
#include "xtensa_rtos.h"
#ifndef __ASSEMBLER__
#include <stdint.h>
#include <xtensa/tie/xt_core.h>
#include <xtensa/hal.h>
#include <xtensa/config/system.h> /* required for XSHAL_CLIB */
/*-----------------------------------------------------------
* Port specific definitions.
*
* The settings in this file configure FreeRTOS correctly for the
* given hardware and compiler.
*
* These settings should not be altered.
*-----------------------------------------------------------
*/
/* Type definitions. */
#define portCHAR int8_t
#define portFLOAT float
#define portDOUBLE double
#define portLONG int32_t
#define portSHORT int16_t
#define portSTACK_TYPE uint32_t
#define portBASE_TYPE int
typedef portSTACK_TYPE StackType_t;
typedef portBASE_TYPE BaseType_t;
typedef unsigned portBASE_TYPE UBaseType_t;
#if( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS )
typedef uint16_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS )
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#else
#error configTICK_TYPE_WIDTH_IN_BITS set to unsupported tick type width.
#endif
/*-----------------------------------------------------------*/
// portbenchmark
#include "portbenchmark.h"
// Critical section management. These cannot be nested. They should be used
// with a lot of care and cannot be called from interrupt context.
static inline void
portDISABLE_INTERRUPTS(void)
{
#if XCHAL_HAVE_INTERRUPTS
#if XCHAL_HAVE_XEA2
xthal_intlevel_set_min (XT_IRQ_LOCK_LEVEL);
#else
xthal_disable_interrupts ();
#endif
#endif
portbenchmarkINTERRUPT_DISABLE ();
}
static inline void
portENABLE_INTERRUPTS(void)
{
portbenchmarkINTERRUPT_RESTORE (0);
#if XCHAL_HAVE_INTERRUPTS
#if XCHAL_HAVE_XEA2
xthal_intlevel_set (0);
#else
xthal_enable_interrupts ();
#endif
#endif
}
// Nested critical sections. Nesting managed by FreeRTOS.
// This is fine for a single core. TODO: revisit for SMP support.
// NOTE: consider Espressif solution: GCC/Xtensa_ESP32/include/portmacro.h
#define portCRITICAL_NESTING_IN_TCB 1
extern void vTaskEnterCritical(void);
extern void vTaskExitCritical(void);
#if portUSING_MPU_WRAPPERS
extern void vPortEnterCritical(void);
extern void vPortExitCritical(void);
#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()
#define portSTACK_ALIGNMENT XCHAL_MPU_ALIGN
#define portPRIVILEGE_BIT 0x80000000UL
#define portIS_PRIVILEGED() (int) \
({ \
register unsigned code __asm("a2") = SYSCALL_is_priv; \
__asm volatile ("syscall\n" \
: "+a"(code) :: "memory"); \
code; \
})
#define portRAISE_PRIVILEGE() \
do { \
register unsigned code __asm("a2") = SYSCALL_raise_priv; \
__asm volatile ("syscall\n" \
: "+a"(code) :: "memory"); \
} while (0)
#if XCHAL_HAVE_XEA2
#define portRESET_PRIVILEGE() (XT_WSR_PS(XT_RSR_PS() | PS_RING(1)))
#else
#define portRESET_PRIVILEGE() (XT_WSR_PS(XT_RSR_PS() | PS_RING))
#endif
#else
#define portENTER_CRITICAL() vTaskEnterCritical()
#define portEXIT_CRITICAL() vTaskExitCritical()
#define portPRIVILEGE_BIT 0UL
#endif
// These allow nested interrupt disabling and restoring via local registers or stack.
// They can be called from interrupts context.
static inline uint32_t
portENTER_CRITICAL_NESTED(void)
{
uint32_t state;
#if XCHAL_HAVE_INTERRUPTS
#if XCHAL_HAVE_XEA2
state = xthal_intlevel_set_min (XT_IRQ_LOCK_LEVEL);
#else
state = xthal_disable_interrupts ();
#endif
#else
state = 0;
#endif
portbenchmarkINTERRUPT_DISABLE ();
return state;
}
static inline void
portEXIT_CRITICAL_NESTED(uint32_t state)
{
portbenchmarkINTERRUPT_RESTORE (state);
#if XCHAL_HAVE_INTERRUPTS
#if XCHAL_HAVE_XEA2
xthal_intlevel_set (state);
#else
xthal_restore_interrupts (state);
#endif
#endif
}
// These FreeRTOS versions are similar to the nested versions above
#define portSET_INTERRUPT_MASK_FROM_ISR() portENTER_CRITICAL_NESTED()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state)
BaseType_t xPortRaisePrivilege( void );
/*-----------------------------------------------------------*/
/* Architecture specifics. */
#define portSTACK_GROWTH ( -1 )
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#ifdef configBYTE_ALIGNMENT
#define portBYTE_ALIGNMENT configBYTE_ALIGNMENT
#elif XCHAL_DATA_WIDTH < 16
#define portBYTE_ALIGNMENT XCHAL_DATA_WIDTH
#else
#define portBYTE_ALIGNMENT 16
#endif
#define portNOP() XT_NOP()
#define portMEMORY_BARRIER() XT_MEMW()
/*-----------------------------------------------------------*/
/* Fine resolution time */
#define portGET_RUN_TIME_COUNTER_VALUE() xthal_get_ccount()
/* No need to do anything for the ccount timer. */
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() do {} while (0)
/* Kernel utilities. */
void vPortYield( void );
void _frxt_setup_switch( void );
#define portYIELD() vPortYield()
#define portYIELD_FROM_ISR( xHigherPriorityTaskWoken ) \
if ( ( xHigherPriorityTaskWoken ) != 0 ) { \
_frxt_setup_switch(); \
}
/* Tickless idle */
#if ( configUSE_TICKLESS_IDLE != 0 )
extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );
#define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) \
vPortSuppressTicksAndSleep( xExpectedIdleTime )
#endif
#endif //__ASSEMBLER__
/*-----------------------------------------------------------*/
/* Attributes for all the possible regions */
#define portDFLT_MEM_TYPE (XTHAL_MEM_BUFFERABLE | XTHAL_MEM_WRITEBACK)
#define portDFLT_STACK_TYPE portDFLT_MEM_TYPE
#define portDFLT_STACK_ACCESS XTHAL_AR_RWrw
#define portDFLT_USERDEV_TYPE XTHAL_MEM_DEVICE
#define portDFLT_USERDEV_ACCESS XTHAL_AR_RWXrwx
#define portDFLT_KERNDEV_TYPE XTHAL_MEM_DEVICE
#define portDFLT_KERNDEV_ACCESS XTHAL_AR_RWX
#define portDFLT_SHARED_TYPE portDFLT_MEM_TYPE
#define portDFLT_SHARED_ACCESS XTHAL_AR_RWXrwx
#define portDFLT_KERNCODE_TYPE portDFLT_MEM_TYPE
#define portDFLT_KERNCODE_ACCESS XTHAL_AR_RX
#define portDFLT_KERNDATA_TYPE portDFLT_MEM_TYPE
#define portDFLT_KERNDATA_ACCESS XTHAL_AR_RW
#define portDFLT_UNUSED_MEM_TYPE portDFLT_MEM_TYPE
#define portDFLT_UNUSED_MEM_ACCESS XTHAL_AR_RWXrx
#define portNUM_CONFIGURABLE_REGIONS configNUM_CONFIGURABLE_REGIONS
#define portLEGACY_TASK_STACK_START (((uint32_t)_bss_start) & -XCHAL_MPU_ALIGN)
#define portLEGACY_TASK_STACK_END (((uint32_t)_bss_end + XCHAL_MPU_ALIGN - 1) & -XCHAL_MPU_ALIGN)
#ifndef __ASSEMBLER__
/* vPortStoreTaskMPUSettings returns void instead of BaseType_t. Until it's fixed
* errors from that function can be checked using this global variable
*/
extern volatile int xtMPUError;
/* xtMPUError values
*/
typedef enum {
MPU_ERR_SETUP_MPU = 1,
MPU_ERR_REGION_NOT_IN_RANGE,
MPU_ERR_STACK_NOT_IN_RANGE,
MPU_ERR_OVERLAP_OTHER_REGIONS,
MPU_ERR_FIXED_REGION_NOT_ALIGNED,
MPU_ERR_PRIVATE_NOT_ALIGNED,
MPU_ERR_INIT_ENTRY,
MPU_ERR_INIT_ENTRY_NOT_IN_MAP,
MPU_ERR_INIT_MAP_NOT_VALID
} mpu_err_t;
#endif
/*---------- DO NOT EDIT MPU SETTINGS BELOW ----------------*/
#if portUSING_MPU_WRAPPERS
#ifndef XCHAL_HAVE_MPU
# define XCHAL_HAVE_MPU 0
#endif
#if (XCHAL_HAVE_MPU == 0)
# error "MPU Hardware required!"
#endif
#ifndef portNUM_CONFIGURABLE_REGIONS
# define portNUM_CONFIGURABLE_REGIONS 0
#endif
#if defined(configUSER_DEVICE_START) && defined(configUSER_DEVICE_END)
# define portUSE_USER_DEVICE_SPACE 1
# else
# define portUSE_USER_DEVICE_SPACE 0
#endif
#if defined(configSHARED_DATA_START) && defined(configSHARED_DATA_END) && (portUSE_FIXED_MPU_ENTRIES == 1)
# define portUSE_SHARED_DATA 1
# else
# define portUSE_SHARED_DATA 0
#endif
#if defined(configPRIVILEGE_DEVICE_START) && defined(configPRIVILEGE_DEVICE_END)
# define portUSE_PRIVILEGED_DEVICE_SPACE 1
# else
# define portUSE_PRIVILEGED_DEVICE_SPACE 0
#endif
#if defined(configLEGACY_TASK_STACK_START) && defined(configLEGACY_TASK_STACK_END)
# define portLEGACY_UNPRIVILEGED_TASKS 1
# else
# define portLEGACY_UNPRIVILEGED_TASKS 0
#endif
/* Number of MPU entries a restricted thread will use. */
#define portNUM_USED_MPU_ENTRIES (1 /* entry zero*/ + \
2* ( portNUM_CONFIGURABLE_REGIONS + \
portUSE_USER_DEVICE_SPACE + \
portUSE_PRIVILEGED_DEVICE_SPACE + \
portUSE_SHARED_DATA + \
portLEGACY_UNPRIVILEGED_TASKS + \
1 /*stack*/ + \
1 /*FreeRTOS code */ + \
1 /*FreeRTOS data */ \
))
#define portNUM_MAX_SWAPPED_MPU_PAIRS (portNUM_CONFIGURABLE_REGIONS + \
portLEGACY_UNPRIVILEGED_TASKS + 1)
#if XCHAL_MPU_ENTRIES < 16
# error "MPU entries < 16 is not supported"
#endif
#if XCHAL_MPU_ENTRIES < portNUM_USED_MPU_ENTRIES
# error "Require MPU with at least portNUM_USED_MPU_ENTRIES foreground entries"
#endif
#if XCHAL_MPU_ALIGN > 16384
# error "MPU region alignment > 16384 is not supported"
#endif
#ifndef __ASSEMBLER__
extern uint32_t privileged_data_start[];
extern uint32_t privileged_data_end[];
extern uint32_t privileged_functions_start[];
extern uint32_t privileged_functions_end[];
extern uint32_t _bss_start[];
extern uint32_t _bss_end[];
#define portPRIVILEGED_CODE_START ((uint32_t)privileged_functions_start & (uint32_t)-XCHAL_MPU_ALIGN)
#define portPRIVILEGED_CODE_END (((uint32_t)privileged_functions_end + (XCHAL_MPU_ALIGN - 1)) & (uint32_t)-XCHAL_MPU_ALIGN)
#define portPRIVILEGED_DATA_START ((uint32_t)privileged_data_start & (uint32_t)-XCHAL_MPU_ALIGN)
#define portPRIVILEGED_DATA_END (((uint32_t)privileged_data_end + (XCHAL_MPU_ALIGN - 1)) & (uint32_t)-XCHAL_MPU_ALIGN)
typedef struct {
// Define here mpu_settings, which is port dependent
xthal_MPU_entry mpumap[portNUM_MAX_SWAPPED_MPU_PAIRS][2];
} xMPU_SETTINGS;
#endif //ASSEMBLER
#endif //portUSING_MPU_WRAPPERS
/*-----------------------------------------------------------*/
#ifndef __ASSEMBLER__
/* Task function macros as described on the FreeRTOS.org WEB site. */
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters )
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters )
// porttrace
#if configUSE_TRACE_FACILITY_2
#include "porttrace.h"
#endif
// configASSERT_2 if requested
#if configASSERT_2
#include <stdio.h>
void exit(int);
#define configASSERT( x ) if (!(x)) { porttracePrint(-1); printf("\nAssertion failed in %s:%d\n", __FILE__, __LINE__); exit(-1); }
#endif
/* Task delete hook for cleanup. May be an issue if tracing is also needed. */
#define traceTASK_DELETE(pxTCB) portTaskDeleteHook(pxTCB->pxEndOfStack)
static inline void portTaskDeleteHook(void * ptr)
{
#if XCHAL_CP_NUM > 0
/* Release any owned coprocessors. If the owner save area address
matches the address in 'ptr' then we are the owner, set to NULL. */
if (ptr != NULL) {
extern void * _xt_coproc_owner_sa[XCHAL_CP_MAX];
unsigned int i;
for (i = 0; i < XCHAL_CP_MAX; i++) {
if (_xt_coproc_owner_sa[i] == ptr) {
_xt_coproc_owner_sa[i] = NULL;
}
#if (defined XT_DEBUG)
/* Only for testing / debugging */
if (_xt_coproc_owner_sa[i] == (void *)0xf1f1f1f1) {
_xt_coproc_owner_sa[i] = NULL;
}
#endif
}
}
#else
UNUSED(ptr);
#endif
}
/* C library support -- only XCLIB and NEWLIB are supported. */
/* To enable thread-safe C library support, XT_USE_THREAD_SAFE_CLIB must be
defined to be > 0 somewhere above or on the command line. */
#if (XT_USE_THREAD_SAFE_CLIB > 0u) && ((XSHAL_CLIB == XTHAL_CLIB_XCLIB) || (XSHAL_CLIB == XTHAL_CLIB_NEWLIB))
extern void vPortClibInit(void);
#endif // XCLIB || NEWLIB support
#endif // __ASSEMBLER__
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* PORTMACRO_H */
@@ -0,0 +1,618 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#include <stdlib.h>
#include <xtensa/config/core.h>
#include <xtensa/core-macros.h>
#include "asm-offsets.h"
#include "xtensa_rtos.h"
#include "portmacro.h"
#include "FreeRTOS.h"
#include "task.h"
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#include <string.h>
#if portUSING_MPU_WRAPPERS
#ifdef DEBUG
#include <xtensa/xtutil.h>
# define XPRINT(...) \
(xt_printf(__VA_ARGS__))
#else
# define XPRINT(...) \
do { } while (0)
#endif
/* FIXME - this one is needed to spot the error from user threads until
* FreeRTOS is fixed to return BaseType_t from functions here, not void.
*/
volatile int PRIVILEGED_DATA xtMPUError = 0;
/* Actual number of private regions that will be used
*/
volatile int PRIVILEGED_DATA g_num_used_mpu_entries = 0;
/* pdFAIL commented out until the function is fixed to return BaseType_t not void
*/
#define NAME(a) #a
#define FAIL_VOID_RETURN(errnum) \
do { \
XPRINT("*FreeRTOS* Error: %s\n", NAME(errnum)); \
xtMPUError = errnum; \
return; \
} while (0)
typedef struct privateDetails_struct {
uint32_t start;
uint32_t end;
uint32_t index;
} privateDetails_t;
/* Stores initial private mappings allocated by MPUSetup thread. Must be global
* as they'll be used later for old-style threads
*/
static volatile privateDetails_t
g_privateDetails[portNUM_MAX_SWAPPED_MPU_PAIRS] PRIVILEGED_DATA;
#define PRIVATE_LEGACY_STACK_IDX 0
#define PRIVATE_STACK_IDX (portLEGACY_UNPRIVILEGED_TASKS)
#define PRIVATE_CONFIGURABLE_IDX (1 + PRIVATE_STACK_IDX)
#define MPU_ALIGN_DOWN(v) ((uint32_t)(v) & -XCHAL_MPU_ALIGN)
#define MPU_ALIGN_UP(v) (((uint32_t)(v) + XCHAL_MPU_ALIGN - 1) & -XCHAL_MPU_ALIGN)
/*-----------------------------------------------------------*/
/*
* Check if a region is aligned according to MPU requirements
*/
static BaseType_t PRIVILEGED_FUNCTION
region_mpu_aligned(uint32_t start, uint32_t end)
{
if ((start & (XCHAL_MPU_ALIGN - 1)) || (end & (XCHAL_MPU_ALIGN - 1))) {
XPRINT("Region %x/%x not aligned\n", start, end);
return pdFALSE;
}
return pdTRUE;
}
static BaseType_t PRIVILEGED_FUNCTION
region_within_region(uint32_t start, uint32_t end, uint32_t rstart, uint32_t rend)
{
return (start >= rstart) && (end <= rend);
}
static BaseType_t PRIVILEGED_FUNCTION
region_overlaps_region(uint32_t start, uint32_t end, uint32_t rstart, uint32_t rend)
{
return (start < rend) && (end > rstart);
}
/*
* Check if a private region/stack is overlapping any other region
*/
static BaseType_t PRIVILEGED_FUNCTION
private_overlap_other_regions(const struct xMEMORY_REGION * const xRegions,
StackType_t *pxBottomOfStack, uint32_t stackSizeInBytes)
{
int i;
for (i = 0; i < portNUM_CONFIGURABLE_REGIONS + 1; i++) {
uint32_t start, end;
if (!xRegions)
i = portNUM_CONFIGURABLE_REGIONS;
if (i == portNUM_CONFIGURABLE_REGIONS) {
start = (uint32_t)pxBottomOfStack;
end = (uint32_t)pxBottomOfStack + stackSizeInBytes;
} else {
start = (uint32_t)xRegions[i].pvBaseAddress;
end = (uint32_t)(xRegions[i].pvBaseAddress) + xRegions[i].ulLengthInBytes;
}
if (start == 0 && end == 0) {
/* Unused private entries - by design they are placed in MPU so they
* don't affect the mapping. Check might fail for them so just don't do it.
*/
continue;
}
#if (portUSE_SHARED_DATA == 1)
if (region_overlaps_region(start, end,
(uint32_t)configSHARED_DATA_START,
(uint32_t)configSHARED_DATA_END)) {
XPRINT("private region %d overlaps shared data!\n", i);
return pdTRUE;
}
#endif
#if (portUSE_PRIVILEGED_DEVICE_SPACE == 1)
if (region_overlaps_region(start, end,
(uint32_t)configPRIVILEGE_DEVICE_START,
(uint32_t)configPRIVILEGE_DEVICE_END)) {
XPRINT("private region %d overlaps privileged device space!\n", i);
return pdTRUE;
}
#endif
#if (portUSE_USER_DEVICE_SPACE == 1)
if (region_overlaps_region(start, end,
(uint32_t)configUSER_DEVICE_START,
(uint32_t)configUSER_DEVICE_END)) {
XPRINT("private region %d overlaps user device space!\n", i);
return pdTRUE;
}
#endif
#if (portLEGACY_UNPRIVILEGED_TASKS)
if (region_overlaps_region(start, end,
configLEGACY_TASK_STACK_START,
configLEGACY_TASK_STACK_END)) {
XPRINT("private region %d [%08x - %08x[ overlaps legacy tack stacks!\n", i, start, end);
return pdTRUE;
}
#endif
if (region_overlaps_region(start, end,
(uint32_t)portPRIVILEGED_CODE_START,
(uint32_t)portPRIVILEGED_CODE_END)) {
XPRINT("private region %d overlaps FreeRTOS code!\n", i);
return pdTRUE;
}
if (region_overlaps_region(start, end,
(uint32_t)portPRIVILEGED_DATA_START,
(uint32_t)portPRIVILEGED_DATA_END)) {
XPRINT("private region %d overlaps FreeRTOS code!\n", i);
return pdTRUE;
}
}
return pdFALSE;
}
/*
* Check if all fixed regions are aligned according to MPU requirements
*/
static BaseType_t PRIVILEGED_FUNCTION
mpu_fixed_regions_aligned(void)
{
#if (portUSE_SHARED_DATA == 1)
if (!region_mpu_aligned((uint32_t)configSHARED_DATA_START,
(uint32_t)configSHARED_DATA_END)) {
XPRINT("shared region not aligned\n");
return pdFALSE;
}
#endif
#if (portUSE_PRIVILEGED_DEVICE_SPACE == 1)
if (!region_mpu_aligned((uint32_t)configPRIVILEGE_DEVICE_START,
(uint32_t)configPRIVILEGE_DEVICE_END)) {
XPRINT("priv device region not aligned\n");
return pdFALSE;
}
#endif
#if (portUSE_USER_DEVICE_SPACE == 1)
if (!region_mpu_aligned((uint32_t)configUSER_DEVICE_START,
(uint32_t)configUSER_DEVICE_END)) {
XPRINT("user dev region not aligned\n");
return pdFALSE;
}
#endif
#if (portLEGACY_UNPRIVILEGED_TASKS)
if (!region_mpu_aligned(configLEGACY_TASK_STACK_START,
configLEGACY_TASK_STACK_END)) {
XPRINT("legacy stacks region not aligned\n");
return pdFALSE;
}
#endif
if(!region_mpu_aligned((uint32_t)portPRIVILEGED_CODE_START,
(uint32_t)portPRIVILEGED_CODE_END)) {
XPRINT("priv code region not aligned\n");
return pdFALSE;
}
if(!region_mpu_aligned((uint32_t)portPRIVILEGED_DATA_START,
(uint32_t)portPRIVILEGED_DATA_END)) {
XPRINT("priv data region not aligned\n");
return pdFALSE;
}
return pdTRUE;
}
static void PRIVILEGED_FUNCTION
add_mpu_entry_to_TCB(xthal_MPU_entry mpumap[], uint32_t addr, uint32_t attr,
uint32_t type, uint32_t start)
{
int index = 0;
//first check if in order
if (addr & (XCHAL_MPU_ALIGN - 1)) {
XPRINT ("Entry addr:%x aligned to %x!\n", addr, XCHAL_MPU_ALIGN);
addr &= (uint32_t)-XCHAL_MPU_ALIGN;
}
for (index = XCHAL_MPU_ENTRIES - 1; index >= 0; index--) {
/* If already have the same entry check if we are start or end of a segment.
* If start then simply override the matching entry.
* If end, don't add new entry
*/
if ((addr >= (mpumap[index].as & ~0x1U) && start) ||
(addr > (mpumap[index].as & ~0x1U) && !start)) {
//move others down
//FIXME - see not to move everything but only entries added so far
memmove(mpumap, mpumap + 1, sizeof(xthal_MPU_entry) * (uint32_t)index);
break;
}
}
if (index >= 0) {
mpumap[index] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(addr, 1, attr, type);
}
}
static int PRIVILEGED_FUNCTION
find_addr_in_map(xthal_MPU_entry mpumap[], uint32_t addr)
{
int i;
for (i = XCHAL_MPU_ENTRIES - 1; i >= 0; i--) {
if (addr >= (mpumap[i].as & ~0x1U))
return i;
}
return -1;
}
/*---------------------------------------------------------------*/
/* Need to setup MPU so the lower, unused, entries have as=1.
* The function is called from either vPortStoreTaskMPUSettings or prvSetupMPU,
* whichever gets executed first. Former is called when thread is created which
* might happen before the scheduler start (which calls the latter).
*/
static BaseType_t PRIVILEGED_FUNCTION
setupMPU(void)
{
int i;
xthal_MPU_entry mpumap[XCHAL_MPU_ENTRIES];
/* set all MPU entries to as=0x1,privileged */
for (i = 0; i < XCHAL_MPU_ENTRIES; i++) {
mpumap[i] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(0, 1, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE);
}
XPRINT("PRIV FUNC SPACE defined:%x-%x (section:%x-%x)\n", portPRIVILEGED_CODE_START, portPRIVILEGED_CODE_END, (uint32_t)privileged_functions_start, (uint32_t)privileged_functions_end);
XPRINT("PRIV DATA SPACE define:%x-%x (section:%x-%x)\n", portPRIVILEGED_DATA_START, portPRIVILEGED_DATA_END, (uint32_t)privileged_data_start, (uint32_t)privileged_data_end);
add_mpu_entry_to_TCB(mpumap, (uint32_t) portPRIVILEGED_CODE_START, portDFLT_KERNCODE_ACCESS, portDFLT_KERNCODE_TYPE, 1);
add_mpu_entry_to_TCB(mpumap, (uint32_t) portPRIVILEGED_CODE_END, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE, 0);
add_mpu_entry_to_TCB(mpumap, (uint32_t) portPRIVILEGED_DATA_START, portDFLT_KERNDATA_ACCESS, portDFLT_KERNDATA_TYPE, 1);
add_mpu_entry_to_TCB(mpumap, (uint32_t) portPRIVILEGED_DATA_END, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE, 0);
#if (portUSE_PRIVILEGED_DEVICE_SPACE == 1)
XPRINT("PRIV Device Space %x - %x\n", configPRIVILEGE_DEVICE_START, configPRIVILEGE_DEVICE_END);
add_mpu_entry_to_TCB(mpumap, (uint32_t) configPRIVILEGE_DEVICE_START, portDFLT_KERNDEV_ACCESS, portDFLT_KERNDEV_TYPE, 1);
add_mpu_entry_to_TCB(mpumap, (uint32_t) configPRIVILEGE_DEVICE_END, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE, 0);
#endif
#if (portUSE_USER_DEVICE_SPACE == 1)
XPRINT("USER Device Space %x - %x\n", configUSER_DEVICE_START, configUSER_DEVICE_END);
add_mpu_entry_to_TCB(mpumap, (uint32_t) configUSER_DEVICE_START, portDFLT_USERDEV_ACCESS, portDFLT_USERDEV_TYPE, 1);
add_mpu_entry_to_TCB(mpumap, (uint32_t) configUSER_DEVICE_END, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE, 0);
#endif
#if (portUSE_SHARED_DATA == 1)
XPRINT("Shared Data Section: %x - %x\n", configSHARED_DATA_START, configSHARED_DATA_END);
add_mpu_entry_to_TCB(mpumap, (uint32_t) configSHARED_DATA_START, portDFLT_SHARED_ACCESS, portDFLT_SHARED_TYPE, 1); //FIXME - check priv/type
add_mpu_entry_to_TCB(mpumap, (uint32_t) configSHARED_DATA_END, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE, 0);
#endif
for(i = 0; i < g_num_used_mpu_entries; i++) {
XPRINT("Private Data Section %d: %x - %x \n", i, g_privateDetails[i].start , g_privateDetails[i].end);
add_mpu_entry_to_TCB(mpumap, (uint32_t)g_privateDetails[i].start, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE, 1);
add_mpu_entry_to_TCB(mpumap, (uint32_t)g_privateDetails[i].end, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE, 0);
}
for (i = 0; i < XCHAL_MPU_ENTRIES; i++) {
mpumap[i].at &= (uint32_t)-XCHAL_MPU_ENTRIES;
mpumap[i].at |= (uint32_t)i;
}
int map_status = xthal_check_map(mpumap, XCHAL_MPU_ENTRIES);
if (map_status != XTHAL_SUCCESS) {
xtMPUError = MPU_ERR_INIT_MAP_NOT_VALID;
XPRINT("MPU Map Check FAILED: error:%d\n", map_status);
return pdFALSE;
}
xthal_write_map(mpumap, XCHAL_MPU_ENTRIES);
#ifdef DEBUG
xthal_read_map(mpumap);
for (i = 0; i < XCHAL_MPU_ENTRIES; i++) {
XPRINT("ORIG mpumap[%d]: as:%x, at:%x\n", i, mpumap[i].as, mpumap[i].at);
}
#endif
// Find entries for private memories
for(i = 0; i < g_num_used_mpu_entries; i++) {
int index;
XPRINT("Look for addr %x\n", g_privateDetails[i].start);
// If MPU Setup decided not to use a region, it set it to 0-0. Then,
// just swap entries 0,1. In the future, assign a variable telling how
// much to swap and don't even swap those for zero.
if (g_privateDetails[i].start == 0) {
g_privateDetails[i].index = 0;
continue;
}
index = find_addr_in_map(mpumap, g_privateDetails[i].start);
if (index < 0) {
XPRINT("Couldn't find index for addr %x\n", g_privateDetails[i].start);
xtMPUError = MPU_ERR_INIT_ENTRY_NOT_IN_MAP;
return pdFALSE;
}
g_privateDetails[i].index = (uint32_t)index;
}
// Update range if not set by MPU thread, to be from the previous to the next entry
#if DEBUG
for(i = 0; i < g_num_used_mpu_entries; i++) {
XPRINT("Entry %d index: %d, range: %x-%x\n", i, g_privateDetails[i].index, g_privateDetails[i].start, g_privateDetails[i].end);
}
#endif
return pdTRUE;
}
/* Called from xPortStartScheduler. At that time, some threads might have
* been created already, which required them creating the MPU map, as their
* private regions must be checked against it. So, create MPU map is called
* from either parts, but it'll run only once.
* In this function, we also write the MPU with that map.
*/
BaseType_t PRIVILEGED_FUNCTION
prvSetupMPU(void)
{
xthal_dcache_all_writeback_inv();
xthal_icache_all_invalidate();
return pdTRUE;
}
typedef void TCB_t;
extern volatile TCB_t * volatile pxCurrentTCB;
static void PRIVILEGED_FUNCTION
init_private_mpu_regions(const struct xMEMORY_REGION * const xRegions,
StackType_t *pxBottomOfStack, uint32_t stackSizeInBytes)
{
int i;
/* First do checks - if there is overlap or fixed regions not aligned */
if (mpu_fixed_regions_aligned() == pdFALSE) {
FAIL_VOID_RETURN(MPU_ERR_FIXED_REGION_NOT_ALIGNED);
}
#ifdef DEBUG
XPRINT("INITIAL MPU SETTINGS:\n"
"stack: %08x - %08x\n",
(uint32_t)pxBottomOfStack,
(uint32_t)pxBottomOfStack + stackSizeInBytes);
#if portLEGACY_UNPRIVILEGED_TASKS
XPRINT("legacy stack: %08x - %08x\n",
configLEGACY_TASK_STACK_START,
configLEGACY_TASK_STACK_END);
#endif
XPRINT("REGIONS:\n");
for (i = 0; i < portNUM_CONFIGURABLE_REGIONS; i++) {
XPRINT(" %08x - %08x\n",
(uint32_t)xRegions[i].pvBaseAddress,
(uint32_t)(xRegions[i].pvBaseAddress + xRegions[i].ulLengthInBytes));
}
#endif
#if portLEGACY_UNPRIVILEGED_TASKS
g_privateDetails[PRIVATE_LEGACY_STACK_IDX].start = configLEGACY_TASK_STACK_START;
g_privateDetails[PRIVATE_LEGACY_STACK_IDX].end = configLEGACY_TASK_STACK_END;
#endif
if (stackSizeInBytes) {
/* Have addresses ready - update private entries - index comes later */
g_privateDetails[PRIVATE_STACK_IDX].start = (uint32_t)pxBottomOfStack;
g_privateDetails[PRIVATE_STACK_IDX].end = (uint32_t)pxBottomOfStack + stackSizeInBytes;
for (i = 0; i < portNUM_CONFIGURABLE_REGIONS; i++) {
if(xRegions[i].pvBaseAddress == 0 && xRegions[i].ulLengthInBytes == 0) {
break;
}
g_privateDetails[i + PRIVATE_CONFIGURABLE_IDX].start = (uint32_t)(xRegions[i].pvBaseAddress);
g_privateDetails[i + PRIVATE_CONFIGURABLE_IDX].end = (uint32_t)(xRegions[i].pvBaseAddress) + xRegions[i].ulLengthInBytes;
}
g_num_used_mpu_entries = i + PRIVATE_CONFIGURABLE_IDX;
} else {
g_num_used_mpu_entries = portLEGACY_UNPRIVILEGED_TASKS;
}
XPRINT("Will use %d regions out of %d allowed\n",
g_num_used_mpu_entries < PRIVATE_CONFIGURABLE_IDX ?
0 : g_num_used_mpu_entries - PRIVATE_CONFIGURABLE_IDX,
portNUM_CONFIGURABLE_REGIONS);
if (setupMPU() == pdFAIL) {
FAIL_VOID_RETURN(MPU_ERR_SETUP_MPU);
}
return;
}
static void PRIVILEGED_FUNCTION
init_task_mpu(xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions,
StackType_t *pxBottomOfStack, uint32_t stackSizeInBytes)
{
int i;
xthal_MPU_entry (*mpumap)[2] = xMPUSettings->mpumap;
#ifdef DEBUG
int name_offset = TCB_TASK_NAME_OFF - TCB_MPU_SETTINGS_OFF;
XPRINT("\n\nTASK NAME: '%s'\n", (char*)xMPUSettings + name_offset);
#endif
#ifdef DEBUG
XPRINT("PRIVATE: stackBottom:%p, stackSizeBytes:%d, REGIONS (%p):\n",
pxBottomOfStack, stackSizeInBytes, xRegions);
if (xRegions) {
for (i = 0; i < portNUM_CONFIGURABLE_REGIONS; i++)
XPRINT(" %x-%x\n",
(uint32_t)xRegions[i].pvBaseAddress,
(uint32_t)(xRegions[i].pvBaseAddress + xRegions[i].ulLengthInBytes));
}
#endif
if (xRegions) {
/* first check if private regions are aligned or overlap fixed regions. */
if (private_overlap_other_regions(xRegions, pxBottomOfStack, stackSizeInBytes) == pdTRUE) {
FAIL_VOID_RETURN(MPU_ERR_OVERLAP_OTHER_REGIONS);
}
if (region_mpu_aligned((uint32_t)pxBottomOfStack,
(uint32_t)pxBottomOfStack + stackSizeInBytes) == pdFALSE) {
FAIL_VOID_RETURN(MPU_ERR_PRIVATE_NOT_ALIGNED);
}
for (i = 0; i < g_num_used_mpu_entries - PRIVATE_CONFIGURABLE_IDX; i++) {
if (region_mpu_aligned((uint32_t)(xRegions[i].pvBaseAddress),
(uint32_t)(xRegions[i].pvBaseAddress) + xRegions[i].ulLengthInBytes) == pdFALSE) {
FAIL_VOID_RETURN(MPU_ERR_PRIVATE_NOT_ALIGNED);
}
}
/* Set stack MPU mapping */
if (region_within_region((uint32_t)pxBottomOfStack,
(uint32_t)pxBottomOfStack + stackSizeInBytes,
g_privateDetails[PRIVATE_STACK_IDX].start,
g_privateDetails[PRIVATE_STACK_IDX].end) == pdFALSE) {
FAIL_VOID_RETURN(MPU_ERR_STACK_NOT_IN_RANGE);
}
for (i = 0; i < g_num_used_mpu_entries - PRIVATE_CONFIGURABLE_IDX; i++) {
if (region_within_region((uint32_t)xRegions[i].pvBaseAddress,
(uint32_t)xRegions[i].pvBaseAddress + xRegions[i].ulLengthInBytes,
g_privateDetails[i + PRIVATE_CONFIGURABLE_IDX].start,
g_privateDetails[i + PRIVATE_CONFIGURABLE_IDX].end) == pdFALSE)
FAIL_VOID_RETURN(MPU_ERR_REGION_NOT_IN_RANGE);
}
mpumap[PRIVATE_STACK_IDX][0] = (xthal_MPU_entry)XTHAL_MPU_ENTRY((uint32_t)pxBottomOfStack, 1, portDFLT_STACK_ACCESS, portDFLT_STACK_TYPE);
mpumap[PRIVATE_STACK_IDX][1] = (xthal_MPU_entry)XTHAL_MPU_ENTRY((uint32_t)pxBottomOfStack + stackSizeInBytes, 1, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE);
mpumap[PRIVATE_STACK_IDX][0].at |= g_privateDetails[PRIVATE_STACK_IDX].index;
mpumap[PRIVATE_STACK_IDX][1].at |= g_privateDetails[PRIVATE_STACK_IDX].index + 1;
#if portLEGACY_UNPRIVILEGED_TASKS
mpumap[PRIVATE_LEGACY_STACK_IDX][0] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(configLEGACY_TASK_STACK_START, 1, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE);
mpumap[PRIVATE_LEGACY_STACK_IDX][1] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(configLEGACY_TASK_STACK_END , 1, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE);
mpumap[PRIVATE_LEGACY_STACK_IDX][0].at |= g_privateDetails[PRIVATE_LEGACY_STACK_IDX].index;
mpumap[PRIVATE_LEGACY_STACK_IDX][1].at |= g_privateDetails[PRIVATE_LEGACY_STACK_IDX].index + 1;
#endif
/* Set MPU mappings for private memories */
for (i = PRIVATE_CONFIGURABLE_IDX; i < g_num_used_mpu_entries; i++) {
int region_i = i - PRIVATE_CONFIGURABLE_IDX;
uint32_t start = (uint32_t)(xRegions[region_i].pvBaseAddress);
uint32_t end = (uint32_t)(xRegions[region_i].pvBaseAddress) + xRegions[region_i].ulLengthInBytes;
uint32_t privType = xRegions[region_i].ulParameters & 0xFFFFE000; // FIXME - use hal functions for these 2
uint32_t privAccess = xRegions[region_i].ulParameters & 0x00000FFF;
if (start == 0 && end == 0) {
start = g_privateDetails[i].start;
end = g_privateDetails[i].end;
privAccess = portDFLT_UNUSED_MEM_ACCESS;
privType = portDFLT_UNUSED_MEM_TYPE;
}
mpumap[i][0] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(start, 1, privAccess, privType);
mpumap[i][1] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(end, 1, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE);
mpumap[i][0].at |= g_privateDetails[i].index;
mpumap[i][1].at |= g_privateDetails[i].index + 1;
}
} else {
for (i = 0; i < g_num_used_mpu_entries; i++) {
mpumap[i][0] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(g_privateDetails[i].start, 1, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE);
mpumap[i][0].at |= g_privateDetails[i].index;
mpumap[i][1] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(g_privateDetails[i].end, 1, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE);
mpumap[i][1].at |= g_privateDetails[i].index + 1;
}
#if portLEGACY_UNPRIVILEGED_TASKS
mpumap[PRIVATE_LEGACY_STACK_IDX][0] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(MPU_ALIGN_DOWN(pxBottomOfStack), 1, portDFLT_STACK_ACCESS, portDFLT_STACK_TYPE);
mpumap[PRIVATE_LEGACY_STACK_IDX][1] = (xthal_MPU_entry)XTHAL_MPU_ENTRY(MPU_ALIGN_UP((uint32_t)pxBottomOfStack + stackSizeInBytes), 1, portDFLT_UNUSED_MEM_ACCESS, portDFLT_UNUSED_MEM_TYPE);
mpumap[PRIVATE_LEGACY_STACK_IDX][0].at |= g_privateDetails[PRIVATE_LEGACY_STACK_IDX].index;
mpumap[PRIVATE_LEGACY_STACK_IDX][1].at |= g_privateDetails[PRIVATE_LEGACY_STACK_IDX].index + 1;
#endif
}
for (i = 0; i < g_num_used_mpu_entries; i++) {
XPRINT("mpumap[%d]: as:%x, at:%x\n", i * 2 + 0, mpumap[i][0].as, mpumap[i][0].at);
XPRINT("mpumap[%d]: as:%x, at:%x\n", i * 2 + 1, mpumap[i][1].as, mpumap[i][1].at);
}
XPRINT("\n\n");
}
/*
* Used to set coprocessor area in stack. Current hack is to reuse MPU pointer for coprocessor area.
*/
void PRIVILEGED_FUNCTION
vPortStoreTaskMPUSettings(xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions,
StackType_t *pxBottomOfStack, uint32_t ulStackDepth)
{
static int PRIVILEGED_DATA mpu_init_done = 0;
uint32_t stackSizeInBytes = ulStackDepth * sizeof(int);
/* if !xMPUSettings this is initial call to set private region ranges */
if (!mpu_init_done) {
mpu_init_done = 1;
if (!xMPUSettings) {
init_private_mpu_regions(xRegions, pxBottomOfStack, stackSizeInBytes);
} else {
MemoryRegion_t mpuPrivRegions[portNUM_CONFIGURABLE_REGIONS] = {
#if portNUM_CONFIGURABLE_REGIONS > 0
{0, 0, 0},
#endif
};
init_private_mpu_regions(mpuPrivRegions, 0, 0);
}
}
if (xMPUSettings) {
init_task_mpu(xMPUSettings, xRegions, pxBottomOfStack, stackSizeInBytes);
}
}
#endif
@@ -0,0 +1,49 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* This utility helps tracing the entering and exiting from tasks.
* It maintains a circular buffer of tasks in the order they execute,
* and their execution time. To enable it, set configUSE_TRACE_FACILITY_2
* to 1 in FreeRTOSConfig.h. You will also need to download the
* FreeRTOS_trace patch that contains porttrace.c and the complete version
* of porttrace.h.
*/
#ifndef PORTTRACE_H
#define PORTTRACE_H
#if configUSE_TRACE_FACILITY_2
#error "You need to download the FreeRTOS_trace patch that overwrites this file"
#endif
#define porttracePrint(nelements)
#define porttraceStamp(stamp, count_incr)
#endif /* PORTTRACE_H */
@@ -0,0 +1,769 @@
FreeRTOS Port for Xtensa Configurable Processors
================================================
FreeRTOS Kernel Version 11.0.0
Introduction
------------
This document describes the Xtensa port for FreeRTOS multitasking RTOS.
For an introduction to FreeRTOS itself, please refer to FreeRTOS
documentation.
This port currently works with FreeRTOS kernel version 11.0.0.
Xtensa Configuration Requirements and Restrictions
--------------------------------------------------
The Xtensa configurable architecture supports a vast space of processor
features. This port supports all of them, including custom processor
extensions defined in the TIE language, with certain minimum
requirements. You must use Xtensa Tools to compile and link FreeRTOS and
your application for your Xtensa configuration. The port uses the Xtensa
Hardware Abstraction Layer (HAL) to adapt to your Xtensa configuration.
NOTE: It may be possible to build and run this with the open-source
xtensa-linux tools provided you have the correct overlay for your Xtensa
configuration. However, this has not been tested and is currently not
supported by Cadence.
This port includes optional reentrancy support for the 'newlib' and
'xclib' C runtime libraries distributed with Xtensa Tools, providing
thread-safety on a per task basis (for use in tasks only, not interrupt
handlers).
This port also includes a simple example application that may run on
a supported board or the Xtensa instruction set simulator (ISS). There
are also a couple of test programs used in maintaining the port, which
serve as additional examples.
FreeRTOS for Xtensa configurable processors requires the following minimum
processor configuration options:
- Timer interrupt option with at least one timer.
- Interrupt option (implied by the timer interrupt option).
- Exception Architecture Version 2 (XEA2) or Version 3 (XEA3).
- Little-endian byte ordering (XEA2 cores).
Minimal support for certain evaluation boards is provided via a board
independent XTBSP API implemented by a board specific library distributed
with the Xtensa Tools. This provides the board clock frequency and basic
polled drivers for the display and console device. Note that XTBSP
is not a tradtional RTOS "board support package" with RTOS specific
interrupt-driven drivers - it is not specific to any RTOS. Note that
FreeRTOS can run on any Xtensa board without this board support
(a "raw" platform), but you will have to provide the clock frequency
and drivers for any on-board devices you want to use.
Installation
------------
The Xtensa port of FreeRTOS v11 is available at this location:
https://github.com/foss-xtensa/FreeRTOS-Kernel-Partner-Supported-Ports
and is submoduled into the following FreeRTOS repos:
https://github.com/foss-xtensa/FreeRTOS-Kernel
https://github.com/foss-xtensa/FreeRTOS
This download includes the core FreeRTOS source and include files needed
to build the port. You can also download the official releases of FreeRTOS
kernel from:
https://github.com/FreeRTOS/FreeRTOS-Kernel
https://www.freertos.org/
The Xtensa port files are included in the official package under the
"Partner-Supported-Ports" and "Partner-Supported-Demos" submodules, but
may not be the latest versions available.
All source is provided along with a Makefile that works for any host
platform supported by Xtensa Tools (Windows, Linux). These instructions
are written for Windows users, but can easily be understood and adapted
to other host platforms.
First install the FreeRTOS common package in a directory of your choosing.
The structure of that package will look like this:
<install directory>
|-- Demo/ThirdParty/Partner-Supported-Demos/ (submodule)
| `-- Cadence_Xtensa_ISS_xt-clang/
| |-- common
| | |-- application_code
| | | `-- cadence_code
| | `-- config_files
| `-- stress
`-- Source/ (submodule)
`-- portable/ThirdParty/Partner-Supported-Ports/ (submodule)
`-- Cadence
`-- Xtensa
The Xtensa Tools are available from Cadence as part of a processor
license. Be sure you have installed the Xtensa Tools and your processor
configuration.
Building FreeRTOS for Xtensa
----------------------------
To build the FreeRTOS library and the example programs, go into the
directory 'Demo/.../Cadence_Xtensa_ISS_xt-clang' and use the makefile
in that directory. "make all" will build all the examples. There is
another makefile in the 'portable/.../Cadence/Xtensa' directory that
builds just the FreeRTOS library.
By default, you will build for the Xtensa instruction set simulator. If
you have a supported emulation board, you can build to run on that. You
can also build to run on a raw Xtensa core with no board support, a
good starting point for supporting your own target platform. Cadence
recommends doing functional development on the simulator because it
is easier to debug with, then move to a board if/when you need to test
hardware drivers or real-time performance.
The provided makefile simplifies building FreeRTOS and the example
for your Xtensa configuration and platform (ISS, board, etc.). There
are detailed instructions in the comments at the top of the makefile.
The makefiles work on Windows and Linux and support incremental builds.
The build for each Xtensa configuration and target platform is placed in
a subdirectory so several core and platform builds can co-exist even with
incremental rebuilds. You may specify the root of the build area (if tou
want it to be elsewhere than under the source tree) by defining BLDROOT
either in the make command or your shell environment.
Building the FreeRTOS Library
-----------------------------
First, be sure you have installed Xtensa Tools and your processor
configuration, and be sure that Xtensa Tools are in your search path.
You can use xt-make, which comes with the Xtensa Tools, to run the
makefiles.
Change directories to the Xtensa port directory:
> cd portable/ThirdParty/Partner-Supported-Ports/Cadence/Xtensa
Now build the FreeRTOS RTOS as a library (libfreertos.a) as follows:
> xt-make
which by default builds for the simulator (TARGET=sim), or:
> xt-make TARGET=board
which builds for a supported board. Note that the board type does not
need to be specified when building the FreeRTOS library.
If you are building for an Xtensa processor configuration that is not the
default you selected when you installed Xtensa Tools, you need to define the
environment variable XTENSA_CORE. If your configuration is not in the
default registry you selected when you installed Xtensa Tools, you also
need to define the environment variable XTENSA_SYSTEM. See tools manuals.
You can avoid defining these in your environment if you pass the variables
you need to redefine into xt-make as follows:
> xt-make XTENSA_CORE=<your_config_name> XTENSA_SYSTEM=<your_registry> ...
There are more details about build options in the comment in the Makefile.
After the library has been built, you must link your application with this
library in order to use FreeRTOS.
Building the FreeRTOS Examples
------------------------------
The provided examples are designed to run on the Xtensa instruction set
simulator (ISS) or a supported evaluation board programmed with your
Xtensa processor configuration.
To build the examples for the default platform (simulator):
> cd Demo/ThirdParty/Partner-Supported-Demos/Cadence_Xtensa_ISS_xt-clang
> xt-make all
which is the same as
> xt-make all TARGET=sim
The boards currently supported are the Xilinx ML605 and KC705 FPGA
development boards. To target these boards, type
> xt-make all TARGET=ml605
or
> xt-make all TARGET=kc705
To build in a location other than the default, specify the new location
using the BLDROOT variable. Note that this makefile will invoke the
FreeRTOS library build makefile automatically, passing on the relevant
parameters based on what you specified.
You can override the default compilation options by specifying the new
options via CFLAGS. For example:
> xt-make all TARGET=sim CFLAGS="-Os -g"
This compiles the examples and links them with the FreeRTOS library
libfreertos.a and the appropriate linker-support package (LSP) for your
target platform (you can override the LSP by adding LSP=<lsp> to the
xt-make command line). The resulting ELF files can be downloaded and
executed on the target. The example binaries appear in the platform
specific subdirectory described earlier.
To build your application with thread-safe C library support, you
need to make certain modifications to the application to plug in and
invoke the reentrancy support. This allows each task to use the library
without interference with other tasks (it is not safe for interrupt
handlers to call the C library).
First, you must define
XT_USE_THREAD_SAFE_CLIB
to a nonzero value either in xtensa_config.h or on the compiler's command
line. Note that the default xtensa_config.h provided with this port does
define this to 1 if either newlib or xclib is detected.
The space for the per-thread C library context data is allocated within
the FreeRTOS TCB structure.
The MPU example must be built separately since it requires the FreeRTOS
library to be rebuilt with -DportUSING_MPU_WRAPPERS=1 -DportALIGN_SECTIONS
which is handled by the makefile if you do the following:
> xt-make clean
> xt-make MPU=1
The code overlay example must be built separately since it requires the
FreeRTOS library to be rebuilt. XT_USE_OVLY must be defined at build time,
this is handled by the makefile if you do the following:
> xt-make clean
> xt-make overlay
Note that the overlay example generates its own LSP from the 'sim' LSP.
Overlay support works only with the windowed ABI option.
IMPORTANT NOTE
--------------
The header file FreeRTOS.h, which is a part of the core FreeRTOS sources,
includes <reent.h> if thread safety for the C libraries is enabled. For
xclib, this file exists in <sys/reent.h> and so is reported as missing.
To work around this, the makefiles supplied with this port will copy the
reent.h header into the build directory during the build process. If you
use a different build process, then you must make sure to copy this file
to a location that is included in the list of include paths. This can be
the build directory or the directory that contains the Xtensa port source
files.
Running or Debugging an Application
-----------------------------------
To execute the example application on the simulator:
> xt-run [options] example.exe
Consult the Xtensa simulator user manual for more information on options.
To execute the example using the command line debugger:
> xt-gdb example.exe
To execute on the simulator using the Xplorer GUI based debugger:
> xplorer --debug example.exe
To execute on a supported evaluation board, download example.exe per
instructions in the tools manuals. Be sure the board has been programmed
with the correct configuration and is set up to boot from RAM and debug
a downloaded program! Optionally you may connect a terminal or terminal
emulator to the serial port on the board with settings as described in
the board user manual, and see the output of printf on the terminal.
To obtain I/O on a "raw" platform such as an unsupported board, you need
to provide low level I/O drivers (eg. inbyte() and outbyte() for character
I/O if you want to use printf etc.). You can run "raw" executables on
any Xtensa platform, including simulator and any board, but you will not
see any behavior specific to the platform (eg. display, printed output,
stopping simulation at end of program). You can, while debugging, use a
debugger mechanism called GDBIO to obtain basic I/O. To use GDBIO, link
with the gdbio library. Refer to Xtensa tools documentation for details.
Task Stack Sizes
----------------
The application must ensure that every task has enough space for its
stack. Each task needs enough space for its own use, its own interrupt
stack frame (defined in xtensa_context.h) and space to save coprocessor
state, if any. Several factors influence the size of the stack required,
including the compiler optimization level and the use of the C library.
Calls to standard output functions such as printf() can use up a lot of
stack space. The tool xt-stack-usage is helpful in determining safe stack
sizes for your application.
Some macros are provided in xtensa_config.h to help determine the stack
size for tasks that do and do not use the C library. Use these as the
basis for each task's stack size. They are minimum requirements taking
into account your configuration and use of the C library. In particular,
the define
XT_STACK_MIN_SIZE
defines the minimum stack size for any task. Be very careful if you try
to use a stack size smaller than this minimum. Stack overruns can cause
all kinds of hard-to-debug errors. It is recommended that you enable the
FreeRTOS stack checking features during development.
WARNING: The newlib printf() function uses a lot of stack space. Be very
careful in using it. Optionally you can use the 'libxtutil' library for
output - it implements a subset of printf() that has smaller code size
and uses far less stack space. More information about this library is in
the Xtensa Tools documentation.
Interrupt Stack
---------------
Beginning with port version 1.2, the port uses a separate interrupt stack
for handling interrupts. Thus, it is no longer necessary for each task to
reserve space on its stack to handle interrupts. The size of the interrupt
stack is controlled by the parameter "configISR_STACK_SIZE" defined in
FreeRTOSConfig.h. Define this carefully to match your system requirements.
Assembler / Compiler Switches
-----------------------------
The following are compiler switches are used by the provided
Makefile in building the FreeRTOS library and example application.
These can be modified by editing the Makefile or by overriding the
CFLAGS variable in the make command line, for example:
> xt-make CFLAGS="-O2 -DXT_USE_THREAD_SAFE_CLIB"
-g Specifies debug information.
-c Specifies object code generation.
-On Sets compiler optimization level n (default -O0).
-mlongcalls Allows assembler and linker to convert call
instructions to longer indirect call sequences
when target is out of range.
-x assembler-with-cpp Passes .s and .S files through C preprocessor.
-Dmacro Define a preprocessor macro with no value.
-Dmacro=value Define a preprocessor macro with a value.
See the compiler / linker documentation for a full list of switches and
their use.
Many definitions can be provided at compile-time via the -D option
without editing the source code. Here are some of the more useful ones:
XT_USE_THREAD_SAFE_CLIB Enable support for the reentrancy to provide
thread-safety for the newlib and xclib libraries
supplied with Xtensa Tools. Default ON.
Note, the follwing defines are unique to the Xtensa port so have names
beginning with "XT_".
XT_SIMULATOR Set this if building to run on the simulator.
Takes advantage of certain simulator control
and reporting facilities, and adjusts timing
of periodic tick to provide a more acceptable
performance in simulation (see XT_CLOCK_FREQ).
Set by default unless PLATFORM is overridden.
XT_BOARD Set this if building for a supported board.
Be sure to specify the correct LSP for the
board. See the example makefile for usage.
XT_CLOCK_FREQ=freq Specifies the target processor's clock
frequency in Hz. Used primarily to set the
timer that generates the periodic interrupt.
Defaults are provided and may be edited in
xtensa_timer.h (see comments there also).
Default for simulator provides more acceptable
performance, but cannot provide real-time
performance due to variation in simulation
speed per host platform and insufficient
cycles between interrupts to process them.
Supported board platforms by default leave
this undefined and compute the clock frequency
at initialization unless this is explicitly
defined.
XT_TICK_PER_SEC=n Specifies the frequency of the periodic tick.
XT_TIMER_INDEX=n Specifies which timer to use for periodic tick.
Set this if your Xtensa processor configuration
provides more than one suitable timer and you
want to override the default. See xtensa_timer.h .
XT_INTEXC_HOOKS Enables hooks in interrupt vector handlers
to support dynamic installation of exception
and interrupt handlers. Disabled by default.
XT_USE_OVLY Enable code overlay support. It uses a mutex,
hence configUSE_MUTEXES must be enabled.
XT_USE_SWPRI Enable software prioritization of interrupts.
Enabling this will prioritize interrupts with
higher bit numbers over those with lower bit
numbers at the same level. This works only for
low and medium priority interrupts that can be
dispatched to C handlers, and only for XEA2
when 32 or fewer interrupts are configured.
Register Usage and Stack Frames
-------------------------------
The Xtensa architecture specifies two ABIs that determine how the general
purpose registers a0-a15 are used: the standard windowed ABI use with
the Xtensa windowed register file architecture, and the optional and
more conventional Call0 ABI (required for Xtensa configurations without
a windowed register file).
Xtensa processors may have other special registers (including co-processor
registers and other TIE "states") that are independent of this choice
of ABI. See Xtensa documentation for more details.
In the windowed ABI the registers of the current window are used as follows:
a0 = return address
a1 = stack pointer (alias sp)
a2 = first argument and result of call (in simple cases)
a3-7 = second through sixth arguments of call (in simple cases).
Note that complex or large arguments are passed on the
stack. Details are in the Xtensa Tools manuals.
a8-a15 = available for use as temporaries.
There are no callee-save registers. The windowed hardware automatically
saves registers a0-a3 on a call4, a0-a8 on a call8, a0-a12 on a call12,
by rotating the register window. Hardware triggers window overflow and
underflow exceptions as necessary when registers outside the current
window need to be spilled to preallocated space in the stack frame, or
restored. Complete details are in the Xtensa manuals. The entire windowed
register file is saved and restored on interrupt or task context switch.
The Call0 ABI does not make use of register windows, relying instead
on a fixed set of 16 registers without window rotation.
The Call0 ABI is more conventional and uses registers as follows:
a0 = return address
a1 = stack pointer (alias sp)
a2 = first argument and result of call (in simple cases)
a3-7 = second through sixth arguments of call (in simple cases).
Note that complex or large arguments are passed on the
stack. Details are in the Xtensa Tools manuals.
a8-a11 = scratch.
a12-a15 = callee-save (a function must preserve these for its caller).
On a FreeRTOS API call, callee-save registers are saved only when a task
context switch occurs, and other registers are not saved at all (the caller
does not expect them to be preserved). On an interrupt, callee-saved
registers might only be saved and restored when a task context-switch
occurs, but all other registers are always saved and restored.
An Xtensa processor has other special registers independent of the ABI,
depending on the configuration (including co-processor registers and other
TIE state) that are part of the task context. FreeRTOS preserves all such
registers over an unsolicited context-switch triggered by an interrupt.
However it does NOT preserve these over a solicited context-switch during
a FreeRTOS API call. This bears some explanation. These special registers
are either ignored by the compiler or treated as caller-saved, meaning
that if kept "live" over a function call (ie. need to be preserved)
they must be saved and restored by the caller. Since solicited entry to
FreeRTOS is always made by a function call, FreeRTOS assumes the caller
has saved any of these registers that are "live". FreeRTOS avoids a lot
of overhead by not having to save and restore every special register
(there can be many) on every solicited context switch.
As a consequence, the application developer should NOT assume that special
registers are preserved over a FreeRTOS API call such as vTaskDelay().
If multiple tasks use a register, the caller must save and restore it.
The saved context stack frames for context switches that occur as
a result of interrupt handling (interrupt frame) or from task-level
API calls (solicited frame) are described in human readable form in
xtensa_context.h . All suspended tasks have one of these two types
of stack frames. The top of the suspended task's stack is pointed to
by pxCurrentTCB->pxTopOfStack. A special location common to both stack
frames differentiates solicited and interrupt stack frames.
Improving Performance, Footprint, or Ease of Debugging
------------------------------------------------------
To make FreeRTOS run faster you can change the Makefile to enable the
desired optimizations or set a predefined optimization level (-O<level>).
Maximum performance is achieved with -O3 -ipa, but that might increase
the footprint substantially. A good compromise is -O2. See the compiler
manual for details.
Minimal footprint is achieved by optimizing for space with -Os, at the
cost of some performance. See the compiler manual for details.
The Xtensa architecture port-specific assembly files are coded with no
file-scope labels inside functions (all labels inside functions begin with
".L"). This allows a profiler to accurately associate an address with a
function, and also allows the debugger's stack trace to show the correct
function wherever the program counter is within that function. However
there are some tradeoffs in debugging. Local (".L") labels are not
visible to the debugger, so the following limitations may be observed
during debugging:
- You cannot set a breakpoint on a local label inside a function.
- Disassembly will show the entire function, but will get out of sync and
show incorrect opcodes if it crosses any padding before an aligned local
branch target (".L" label, not ".Ln"). Restart disassembly specifying an
address range explicitly between points where there is padding.
Since FreeRTOS is provided in source form, it is not difficult to remove
the ".L" and ".Ln" prefixes from local labels if you want them visible.
They can also be made visible by passing the '-L' option to the assembler
and linker (see the assembler and linker manuals for details).
Interrupt and Exception Handling
--------------------------------
FreeRTOS provides a complete set of efficient exception and first-level
interrupt handlers installed at the appropriate exception and interrupt
vector locations. The Xtensa architecture supports several different
classes of exceptions and interrupts. Being a configurable architecture,
many of these are optional, and the vector locations are determined by
your processor configuration. (Note that Diamond cores are pre-configured
with specific vector locations.) The handlers provided use conditional
compilation to adapt to your processor configuration and include only
the code that is needed.
Xtensa vector locations may reside almost anywhere, including in ROM.
The amount of code space available at each of these locations is
often very small (e.g. due to following vectors). A small stub of
code installed at the vector jumps to the corresponding handler,
usually in RAM. The exception and interrupt handlers are defined in
xtensa_vectors.S. They are not specific to FreeRTOS, but call into
FreeRTOS where appropriate via macros defined in xtensa_rtos.h .
The handlers provided for low and medium priority interrupts are just
dispatchers that save relevant state and call user-definable handlers.
See the files xtensa_vectors.S and xtensa_api.h for more details of how
to create and install application-specific user interrupt handlers.
Similarly, user-defined handlers can be installed for exceptions (other
than a few which are always handled by the OS).
The high priority interrupt handlers provided may be considered templates
into which the application adds code to service specific interrupts.
The places where application handlers should be inserted are tagged with
the comment "USER_EDIT" in xtensa_vectors.S.
This FreeRTOS port supports strict priority-based nesting of interrupts.
An interrupt may only nest on top of one of strictly lower priority.
Equal priority interrupts concurrently pending are handled in an
application-defined sequence before any lower priority interrupts
are handled. During interrupt and exception handling, the processor's
interrupt level (PS.INTLEVEL) is used to control the interrupt priority
level that can be accepted; interrupt sources are not controlled
individually by FreeRTOS (the application is free to access the INTENABLE
register directly to enable/disable individual interrupts, eg. using
Xtensa HAL services). This approach provides the most deterministic
bounds on interrupt latency (for a given priority) and stack depth.
Software prioritization of interrupts at the same priority is controlled
by the definition of XT_USE_SWPRI. See above for a description of this
parameter.
The following subsections describe the handling of each class of exception
and interrupt in more detail. Many have nothing to do with FreeRTOS but
are mentioned because there is code to handle them in xtensa_vectors.S.
Note that the concept of software prioritization and the low/medium/high
priority discussion applies only to XEA2.
User Exception and Interrupt Handler (Low/Medium Priority):
All Xtensa 'general exceptions' come to the user, kernel, or double
exception vector. The exception type is identified by the EXCCAUSE
special register (level 1 interrupts are one particular cause of a
general exception). This port sets up PS to direct all such exceptions
to the user vector. Exceptions taken at the other two vectors usually
indicate a kernel or application bug.
Level 1 interrupts are identified at the beginning of the handler
and are dispatched to a dedicated handler. Then, syscall and alloca
exceptions are identified and dispatched to special handlers described
below. After this, coprocessor exceptions are identified and dispatched
to the coprocessor handler.
Any remaining exceptions are processed as follows:
Having allocated the exception stack frame, the user exception handler
saves the current task state and sets up a C environment and enables
the high-priority class of interrupts (which do not interact with
FreeRTOS), then reads EXCCAUSE and uses the cause (number) to index
into a table of user-specified handlers. The correct handler is then
called. If the handler returns, the context is restored and control is
returned to the code that caused the exception. The user-defined handler
may alter the saved context, or any other system state, that allows the
faulting instruction to be retried.
If the cause is a level 1 (low-priority) or medium-priority interrupt,
the handler enables all interrupts above that priority level after
saving the task context. It then sets up the environment for C code
and then calls the handler (found in the handler table) for the
interrupt number. If the user has not specified a handler, then the
default handler will be called, which will terminate the program.
If the interrupt is for the system timer, it calls a special interrupt
handler for the system timer tick, which handles timer tick processing.
Finally, the handler calls _frxt_int_exit to allow FreeRTOS to perform
any scheduling necessary and return either to the interrupted task
or another.
If software prioritization is enabled, the handler will re-enable all
interrupts at the same level that are numerically higher than the current
one, before calling the user handler. This allows a higher priority
interrupt to pre-empt the lower priority handler.
Medium Priority Interrupt Handlers:
Medium priority interrupts are those at levels 2 up to XCHAL_EXCM_LEVEL,
a configuration-specific maximum interrupt level affected by the global
'exception mode' bit in the processor status word (PS.EXCM).
Interrupt levels above XCHAL_EXCM_LEVEL are of the high-priority class.
The Xtensa hardware documentation considers medium priority interrupts
to be a special case of high-priority interrupts, but from a software
perspective they are very different.
Dispatch of medium-priority interrupts is discussed in the section
above.
High Priority Interrupt Handlers:
High priority interrupts are those strictly above XCHAL_EXCM_LEVEL,
a configuration-specific maximum interrupt level affected by the
global 'exception mode' bit in the processor status word (PS.EXCM).
High priority handlers may not directly interact with FreeRTOS at all,
and are described here only for the sake of completeness. They must
be coded in assembler (may not be coded in C) and are intended to be
used for handling extremely high frequency hardware events that need
to be handled in only a few cycles. A high priority interrupt handler
may trigger a software interrupt at a medium or low priority level to
occasionally signal FreeRTOS. Please see Xtensa documentation.
There is a separate vector and a few special registers for each high
priority interrupt, providing for fast dispatch and efficient nesting
on top of lower priority interrupts. Handlers are templates included
only for the vectors that exist in your Xtensa processor configuration.
These templates are written for only one interrupt per high priority
level to minimize latency servicing very fast time-critical interrupts.
The vector code jumps to the corresponding first-level interrupt handler,
which then executes application-provided assembler code before returning
quickly to the interrupted task or lower priority handler.
Kernel Exception Handler:
Kernel mode is not used in this port of FreeRTOS, and therefore kernel
exceptions should not happen. A stub is provided for the vector that
triggers the debugger (if connected) or calls _xt_panic to freeze the
processor should a kernel exception occur.
Alloca Exception Handler:
Alloca exceptions are generated by the 'movsp' instruction, which
is used only in the windowed ABI. Its purpose is to allocate some
space on top of the stack. Because the window hardware may have
spilled some registers to the 16 byte "base save" area below the
stack pointer, it is necessary to protect those values. The alloca
handler accomplishes this quickly without setting up an interrupt
frame or entering FreeRTOS, by emulating a register underflow and
re-executing 'movsp'.
Syscall Exception Handler:
Syscall exceptions are generated by a 'syscall' instruction.
The windowed ABI specifies that executing this instruction with
a value of zero in register a2 must spill any unsaved registers
in the windowed register file to their pre-determined locations
on the caller's stack. The handler does exactly that, and skips
over the 'syscall' instruction before returning to the caller.
If a2 is non-zero, the handler returns a2 == -1 to the caller.
Co-Processor Exception Handler:
A co-processor exception is generated when a task accesses a
co-processor that it does not "own". Ownership represents which
task's state is currently in the co-processor. Co-processors are
context-switched "lazily" (on demand) only when a non-owning task
uses a co-processor instruction, otherwise a task retains ownership
even when it is preempted from the main processor. The co-processor
exception handler performs the context-switch and manages ownership.
Co-processors may not be used by any code outside the context of a
task. A co-processor exception triggered by code that is not part
of a running task is a fatal error and FreeRTOS for Xtensa will panic.
This restriction is intended to reduce the overhead of saving and
restoring co-processor state (which can be quite large) and in
particular remove that overhead from interrupt handlers.
Debug Exception Handler:
A debug exception is caused as a result of running code, such as by
a 'break' instruction or hardware breakpoints and watchpoints, or
as a result of an external debug interrupt, such as from an OCD based
debugger or multiprocessor debug events ("breakin/breakout"). If the
processor is running in OCD mode under control of an OCD-based debugger,
the trigger event immediately halts the processor and gives control to
the OCD debugger. Otherwise control is transferred to the debug vector.
The debug vector handler calls the simulator if running on the ISS,
which then takes control and interacts with any attached debugger.
If running on hardware and not in OCD mode, debug exceptions are not
expected, so the debug handler calls _xt_panic to freeze the processor.
Double Exception Handler:
A double exception is a general exception that happens while the
processor is in exception mode (PS.EXCM set), and thus indicates a
bug in kernel code. The double exception vector handler triggers
the debugger (if connected) or calls _xt_panic to freeze the
processor.
Window Overflow and Underflow Exception Handlers:
Window overflow and underflow handlers are required for use of the
windowed ABI. Each has its own dedicated vector and highly optimized
code that is independent of OS. See Xtensa documentation for details.
Hooks for Dynamic Installation of Handlers:
Optional hooks are provided in the user exception and low level
interrupt handler and all medium and high priority interrupt handlers,
to dynamically install a handler function (which may be coded in C,
unless in a high-priority interrupt handler). These hooks are enabled
and used by automatic regression tests, they are not part of a normal
FreeRTOS build. However an application is free to take advantage of
them. The interrupt/exception hooks are described in xtensa_rtos.h .
It is recommended that the application not make use of these hooks, but
rather use xt_set_interrupt_handler() and xt_set_exception_handler()
to install application-specific handlers. This method is more convenient
and allows arguments to be passed to the handlers. Software prioritization
of interrupts works only with this method. See xtensa_api.h for details.
Overlay Support
Code overlays are supported as of version 2.30 for Xtensa LX processors
only. This is enabled by defining XT_USE_OVLY when building FreeRTOS and
the application code, and requires liboverlay.a to be specified at link
time. See the overlay example and the Xtensa system SW reference manual
for more details.
-End-
@@ -0,0 +1,170 @@
Notes for Version 3.00
----------------------
- This version supports FreeRTOS version 11.0.0.
- The directory structure and makefiles have been reorganized to conform
to the structure of FreeRTOS version 11.0.0.
Notes for Version 2.40
----------------------
- Now supports Xtensa LX processors with up to 128 interrupts.
Note: XT_USE_SWPRI only supported on LX processors with <= 32 interrupts.
Notes for Version 2.30
----------------------
- Code overlay support enabled (for Xtensa LX only). An example
is provided in the demos/cadence/sim directory. Code overlay
support requires mutexes.
Notes for Version 2.20
----------------------
- FreeRTOS kernel version updated to 10.4.4.
- XAF config defaults brought back into 10.4.4-stable branch.
Variable frequency support is merged into this branch as well.
Notes for Version 2.10
----------------------
- Now supports Xtensa NX processors.
Notes for Version 2.01
----------------------
- Modified debug exception handling to allow user-supplied handler to
be installed. The user handler can be a C function and is installed
like so -
xt_set_exception_handler( EXCCAUSE_DEBUG, dbg_handler );
Notes for version 2.0
---------------------
- Extensive refactor of code to eliminate use of MPU struct for saving
coprocessor state. Coprocessor exception handling has been redone.
- Low power sleep support (tickless idle) added.
- Removed use of XTOS header files and macros. Renamed port macros to
avoid name conflicts with XTOS macros.
- More compiler warnings enabled for build (-Werror -Wextra -std=c99).
- Interrupt and exception API reworked. Timer setup and handling mostly
converted to C code and simplified.
Notes for version 1.7
---------------------
- portSTACK_TYPE changed to uint32_t from uint8_t. Stack size definitions
adjusted accordingly.
- portYIELD_FROM_ISR changed to call _frxt_setup_switch conditionally.
- Tick handler calls xTaskIncrementTick inside critical region to prevent
interrupt nesting issues.
Notes for version 1.6
---------------------
- This version supports FreeRTOS version 10.0.0.
- The directory structure and makefiles have been reorganized to conform
to the structure of FreeRTOS version 10.0.0.
Notes for version 1.5
---------------------
- This version supports FreeRTOS version 9.0.0.
- This version supports thread safe operation with the Xtensa C Library
(xclib) in addition to the Newlib C library (newlib).
The xtensa_config.h file is set up to enable thread safe operation by
default if xclib or newlib is detected. Use -D XT_USE_THREAD_SAFE_CLIB=0
on the command line or in the project definition to disable this.
- Incorrect function prototypes for xt_ints_on/xt_ints_off have been fixed.
Notes for version 1.4.2
-----------------------
- This version implements a bugfix. The bug was that even when the
config parameter "configUSE_TIME_SLICING" was set to zero, time
slicing would still occur due to a problem in the Xtensa interrupt
processing code.
- The "small_test" test has been added to illustrate how FreeRTOS
can be configured to reduce the memory footprint.
Notes for version 1.4
---------------------
- This version supports FreeRTOS version 8.2.0.
- This version adds a new test xt_intr.c for interrupts testing.
- This version separates the trace and benchmark facilities into a
separate downloadable.
Notes for version 1.3
---------------------
- This version implements a separate interrupt stack. The interrupt
dispatch code saves the current task's state on to the task's own
stack and then switches to the interrupt stack for the interrupt
processing. Nested interrupts will continue to use the interrupt
stack. This helps to reduce the sizes of task stacks, since task
stacks no longer have to account for space to accommodate possible
nested interrupts. The size of the interrupt stack is controlled
by a config parameter.
Notes for version 1.2
---------------------
- This version supports FreeRTOS version 8.1.2.
- This version supports both CALL0 and windowed Xtensa configurations.
- This version supports thread safe operation for the Newlib C library.
- This version introduces an API for installing low/medium priority
interrupt handlers and exception handlers, which can be written in C.
See xtensa_api.h for details.
- This version supports software prioritization of interrupts at the
same level. Define XT_USE_SWPRI to use this feature. Higher-numbered
interrupts are handled at a higher priority than lower-numbered ones.
- The FreeRTOS demo application requires a stack size of at least 4 KB.
The standalone tests should be able to run with smaller stack sizes.
The minimum stack size depends on the specific Xtensa configuration.
Adjust the stack size of your application as required.
- The examples are all defined with a heap size of 256 KB. This could
be a problem with Xtensa configurations that have only small amounts
of memory. Adjust the heap size to suit your platform and application.
There is an example of a small-footprint configuration in the directory
Xtensa_XCC_small.
- NOTE: Code overlay support is not yet enabled for this port, even though
the support code is present. This feature is not enabled nor tested.
Do not define XT_USE_OVLY when building.
-End-
@@ -0,0 +1,171 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* Xtensa-specific API for RTOS ports.
*/
#ifndef __XTENSA_API_H__
#define __XTENSA_API_H__
#include <stdint.h>
#include <xtensa/hal.h>
#include "mpu_wrappers.h"
#include "xtensa_context.h"
/* Typedef for C-callable interrupt handler function */
typedef void (*xt_handler)(void *);
/* Typedef for C-callable exception handler function */
typedef void (*xt_exc_handler)(XtExcFrame *);
/*
-------------------------------------------------------------------------------
Call this function to set a handler for the specified exception.
n - Exception number (type)
f - Handler function address, NULL to uninstall handler.
The handler will be passed a pointer to the exception frame, which is created
on the stack of the thread that caused the exception.
If the handler returns, the thread context will be restored and the faulting
instruction will be retried. Any values in the exception frame that are
modified by the handler will be restored as part of the context. For details
of the exception frame structure see xtensa_context.h.
-------------------------------------------------------------------------------
*/
extern xt_exc_handler xt_set_exception_handler( uint32_t n, xt_exc_handler f ) PRIVILEGED_FUNCTION;
/*
-------------------------------------------------------------------------------
Call this function to set a handler for the specified interrupt.
n - Interrupt number.
f - Handler function address, NULL to uninstall handler.
arg - Argument to be passed to handler.
-------------------------------------------------------------------------------
*/
extern xt_handler xt_set_interrupt_handler( uint32_t n, xt_handler f, void * arg ) PRIVILEGED_FUNCTION;
/*
-------------------------------------------------------------------------------
Call this function to enable the specified interrupt.
intnum - Interrupt number to be enabled.
Returns: Nothing.
-------------------------------------------------------------------------------
*/
extern void xt_interrupt_enable( uint32_t intnum ) PRIVILEGED_FUNCTION;
/*
-------------------------------------------------------------------------------
Call this function to disable the specified interrupt.
intnum - Interrupt number to be disabled.
Returns: Nothing.
-------------------------------------------------------------------------------
*/
extern void xt_interrupt_disable( uint32_t intnum ) PRIVILEGED_FUNCTION;
/*
-------------------------------------------------------------------------------
Call this function to check the enable state of the specified interrupt.
intnum - Interrupt number to be checked.
Returns: 1 if the specified interrupt is enabled, zero if the interrupt is
disabled, zero if the interrupt number is invalid.
-------------------------------------------------------------------------------
*/
uint32_t
xt_interrupt_enabled( uint32_t intnum ) PRIVILEGED_FUNCTION;
/*
-------------------------------------------------------------------------------
Call this function to trigger the specified (s/w) interrupt.
intnum - Interrupt number to be triggered.
Returns: Nothing.
-------------------------------------------------------------------------------
*/
extern void xt_interrupt_trigger( uint32_t intnum ) PRIVILEGED_FUNCTION;
/*
-------------------------------------------------------------------------------
Call this function to clear the specified (s/w or edge-triggered)
interrupt.
intnum - Interrupt number to be cleared.
Returns: Nothing.
-------------------------------------------------------------------------------
*/
extern void xt_interrupt_clear( uint32_t intnum ) PRIVILEGED_FUNCTION;
/*
-------------------------------------------------------------------------------
Update CPU core/timer frequency.
This is implemented when configUSE_TICKLESS_IDLE is set to 1.
XT_CLOCK_FREQ or xtbsp_clock_freq_hz must return new clock frequency
when this function is called.
This function should be called as soon as the core frequency has changed.
It is safe to call it from an interrupt service routine.
It will update deadline of the internal tick timer and cache new core
frequency.
Returns: Nothing
-------------------------------------------------------------------------------
*/
extern void xt_update_clock_frequency( void ) PRIVILEGED_FUNCTION;
/*
* These map directly to HAL functions.
*/
#define xt_get_ccount xthal_get_ccount
#define xt_get_ccompare(t) xthal_get_ccompare(t)
#define xt_set_ccompare(t,v) xthal_set_ccompare(t,v)
#endif /* __XTENSA_API_H__ */
@@ -0,0 +1,190 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* Configuration-specific information for Xtensa build. This file must be
* included in FreeRTOSConfig.h to properly set up the config-dependent
* parameters correctly.
*
* NOTE: To enable thread-safe C library support, XT_USE_THREAD_SAFE_CLIB must
* be defined to be > 0 somewhere above or on the command line.
*/
#ifndef XTENSA_CONFIG_H
#define XTENSA_CONFIG_H
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
#include <xtensa/hal.h>
#include <xtensa/config/core.h>
#include <xtensa/config/system.h> /* required for XSHAL_CLIB */
#include "xtensa_context.h"
/*-----------------------------------------------------------------------------
* STACK REQUIREMENTS
*
* This section defines the minimum stack size, and the extra space required to
* be allocated for saving coprocessor state on the stack when needed. The sizes
* are in bytes.
*
* Stack sizes for individual tasks should be derived from these minima based on
* the maximum call depth of the task. A minimum stack size is defined by the
* XT_STACK_MIN_SIZE value. This minimum is based on the requirement for a task
* that calls nothing else but can be interrupted.
*
* If the Xtensa processor configuration includes coprocessors, then space is
* allocated to save the coprocessor state on the stack.
*
* If thread safety is enabled for the C runtime library, (XT_USE_THREAD_SAFE_CLIB
* is defined) then space is allocated to save the C library context in the TCB.
*
* Allocating insufficient stack space is a common source of hard-to-find errors.
* During development, it is best to enable the FreeRTOS stack checking features.
*
* Usage:
*
* XT_USE_THREAD_SAFE_CLIB -- Define this to a nonzero value to enable thread-safe
* use of the C library.
*
* NOTE: The Xtensa toolchain supports multiple C libraries and not all of them
* support thread safety. Check your core configuration to see which C library
* was chosen for your system.
*
* XT_STACK_MIN_SIZE -- The minimum stack size for any task. It is recommended
* that you do not use a stack smaller than this for any
* task. In case you want to use stacks smaller than this
* size, you must verify that the smaller size(s) will work
* under all operating conditions.
*
* XT_STACK_EXTRA -- The amount of extra stack space to be allocated for the
* system overhead (coprocessor state, exception frame etc.).
* Add this to the amount of stack space required by the
* task itself.
*
* XT_SYSTEM_STACK_SIZE -- The size of the system interrupt stack. You will need to
* size this according to your system requirements.
-----------------------------------------------------------------------------*/
/* Extra space required for interrupt/exception hooks. */
#ifdef XT_INTEXC_HOOKS
#ifdef __XTENSA_CALL0_ABI__
#define STK_INTEXC_EXTRA 0x200
#else
#define STK_INTEXC_EXTRA 0x180
#endif
#else
#define STK_INTEXC_EXTRA 0
#endif
/* Check C library thread safety support and compute size of C library save area.
For the supported libraries, we enable thread safety by default, and this can
be overridden from the compiler/make command line. */
#if (XSHAL_CLIB == XTHAL_CLIB_NEWLIB) || (XSHAL_CLIB == XTHAL_CLIB_XCLIB)
#ifndef XT_USE_THREAD_SAFE_CLIB
#define XT_USE_THREAD_SAFE_CLIB 1
#endif
#else
#define XT_USE_THREAD_SAFE_CLIB 0
#endif
#if XT_USE_THREAD_SAFE_CLIB > 0u
#if XSHAL_CLIB == XTHAL_CLIB_XCLIB
#define XT_HAVE_THREAD_SAFE_CLIB 1
#if !defined __ASSEMBLER__
#include <sys/reent.h>
#define XT_CLIB_CONTEXT_AREA_SIZE ((sizeof(struct _reent) + 15) + (-16))
#define XT_CLIB_GLOBAL_PTR _reent_ptr
#define _REENT_INIT_PTR _init_reent
#define _impure_ptr _reent_ptr
void _reclaim_reent(struct _reent * ptr);
#endif
#elif XSHAL_CLIB == XTHAL_CLIB_NEWLIB
#define XT_HAVE_THREAD_SAFE_CLIB 1
#if !defined __ASSEMBLER__
#include <sys/reent.h>
#define XT_CLIB_CONTEXT_AREA_SIZE ((sizeof(struct _reent) + 15) + (-16))
#define XT_CLIB_GLOBAL_PTR _impure_ptr
void _reclaim_reent(struct _reent * ptr);
#endif
#else
#define XT_HAVE_THREAD_SAFE_CLIB 0
#error The selected C runtime library is not thread safe.
#endif
#else
#define XT_CLIB_CONTEXT_AREA_SIZE 0
#endif
/*------------------------------------------------------------------------------
Extra size -- interrupt frame plus coprocessor save area plus hook space.
NOTE: Make sure XT_INTEXC_HOOKS is undefined unless you really need the hooks.
------------------------------------------------------------------------------*/
#ifdef __XTENSA_CALL0_ABI__
#define XT_XTRA_SIZE (XT_STK_FRMSZ + STK_INTEXC_EXTRA + 0x10 + XT_CP_SIZE)
#else
#define XT_XTRA_SIZE (XT_STK_FRMSZ + STK_INTEXC_EXTRA + 0x20 + XT_CP_SIZE)
#endif
/*------------------------------------------------------------------------------
Space allocated for user code -- function calls and local variables.
NOTE: This number can be adjusted to suit your needs. You must verify that the
amount of space you reserve is adequate for the worst-case conditions in your
application.
NOTE: The windowed ABI requires more stack, since space has to be reserved
for spilling register windows.
------------------------------------------------------------------------------*/
#ifdef __XTENSA_CALL0_ABI__
#define XT_USER_SIZE 0x200
#else
#define XT_USER_SIZE 0x400
#endif
/* Minimum recommended stack size. */
#define XT_STACK_MIN_SIZE ((XT_XTRA_SIZE + XT_USER_SIZE) / sizeof(unsigned char))
/* OS overhead. */
#define XT_STACK_EXTRA (XT_XTRA_SIZE)
/* Default system (interrupt) stack size */
#define XT_SYSTEM_STACK_SIZE 0x400
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* XTENSA_CONFIG_H */
@@ -0,0 +1,457 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* XTENSA CONTEXT SAVE AND RESTORE ROUTINES
*
* Low-level Call0 functions for handling generic context save and restore of
* registers not specifically addressed by the interrupt vectors and handlers.
* Those registers (not handled by these functions) are PC, PS, A0, A1 (SP).
* Except for the calls to RTOS functions, this code is generic to Xtensa.
*
* Note that in Call0 ABI, interrupt handlers are expected to preserve the callee-
* save regs (A12-A15), which is always the case if the handlers are coded in C.
* However A12, A13 are made available as scratch registers for interrupt dispatch
* code, so are presumed saved anyway, and are always restored even in Call0 ABI.
* Only A14, A15 are truly handled as callee-save regs.
*
* Because Xtensa is a configurable architecture, this port supports all user
* generated configurations (except restrictions stated in the release notes).
* This is accomplished by conditional compilation using macros and functions
* defined in the Xtensa HAL (hardware adaptation layer) for your configuration.
* Only the processor state included in your configuration is saved and restored,
* including any processor state added by user configuration options or TIE.
*/
/* Warn nicely if this file gets named with a lowercase .s instead of .S: */
#define NOERROR #
NOERROR: .error "C preprocessor needed for this file: make sure its filename\
ends in uppercase .S, or use the compiler's -x assembler-with-cpp option."
#include "xtensa_rtos.h"
#include "portmacro.h"
#include <xtensa/core-macros.h>
#ifdef XT_USE_OVLY
#if (XCHAL_SW_VERSION >= 1504000)
#include <xtensa/overlay.h>
#else
#warning liboverlay bugfixes in RJ.4 tools are recommended
#endif
#include <xtensa/overlay_os_asm.h>
#endif
#if XCHAL_HAVE_XEA2
#if portUSING_MPU_WRAPPERS
.section "privileged_functions"
#else
.text
.literal_position
#endif
/*******************************************************************************
_xt_context_save
!! MUST BE CALLED ONLY BY 'CALL0' INSTRUCTION !!
Saves all Xtensa processor state except PC, PS, A0, A1 (SP), A12, A13, in the
interrupt stack frame defined in xtensa_rtos.h.
Its counterpart is _xt_context_restore (which also restores A12, A13).
Caller is expected to have saved PC, PS, A0, A1 (SP), A12, A13 in the frame.
This function preserves A12 & A13 in order to provide the caller with 2 scratch
regs that need not be saved over the call to this function. The choice of which
2 regs to provide is governed by xthal_window_spill_nw and xthal_save_extra_nw,
to avoid moving data more than necessary. Caller can assign regs accordingly.
Entry Conditions:
A0 = Return address in caller.
A1 = Stack pointer of interrupted thread or handler ("interruptee").
Original A12, A13 have already been saved in the interrupt stack frame.
Other processor state except PC, PS, A0, A1 (SP), A12, A13, is as at the
point of interruption.
If windowed ABI, PS.EXCM = 1 (exceptions disabled).
Exit conditions:
A0 = Return address in caller.
A1 = Stack pointer of interrupted thread or handler ("interruptee").
A12, A13 as at entry (preserved).
If windowed ABI, PS.EXCM = 1 (exceptions disabled).
*******************************************************************************/
.global _xt_context_save
.type _xt_context_save,@function
.align 4
_xt_context_save:
s32i a2, sp, XT_STK_A2
s32i a3, sp, XT_STK_A3
s32i a4, sp, XT_STK_A4
s32i a5, sp, XT_STK_A5
s32i a6, sp, XT_STK_A6
s32i a7, sp, XT_STK_A7
s32i a8, sp, XT_STK_A8
s32i a9, sp, XT_STK_A9
s32i a10, sp, XT_STK_A10
s32i a11, sp, XT_STK_A11
/*
Call0 ABI callee-saved regs a12-15 do not need to be saved here.
a12-13 are the caller's responsibility so it can use them as scratch.
So only need to save a14-a15 here for Windowed ABI (not Call0).
*/
#ifndef __XTENSA_CALL0_ABI__
s32i a14, sp, XT_STK_A14
s32i a15, sp, XT_STK_A15
#endif
rsr a3, SAR
s32i a3, sp, XT_STK_SAR
#if XCHAL_HAVE_LOOPS
rsr a3, LBEG
s32i a3, sp, XT_STK_LBEG
rsr a3, LEND
s32i a3, sp, XT_STK_LEND
rsr a3, LCOUNT
s32i a3, sp, XT_STK_LCOUNT
#endif
#if XCHAL_HAVE_EXCLUSIVE
/* Save and clear state of ATOMCTL */
movi a3, 0
getex a3
s32i a3, sp, XT_STK_ATOMCTL
#endif
#ifdef XT_USE_SWPRI
/* Save virtual priority mask */
movi a3, xt_vpri_mask
l32i a3, a3, 0
s32i a3, sp, XT_STK_VPRI
#endif
#if XCHAL_EXTRA_SA_SIZE > 0 || !defined(__XTENSA_CALL0_ABI__)
mov a9, a0 /* preserve ret addr */
#endif
#ifndef __XTENSA_CALL0_ABI__
/*
To spill the reg windows, temp. need pre-interrupt stack ptr and a4-15.
Need to save a9,12,13 temporarily (in frame temps) and recover originals.
Interrupts need to be disabled below XCHAL_EXCM_LEVEL and window overflow
and underflow exceptions disabled (assured by PS.EXCM == 1).
*/
s32i a12, sp, XT_STK_TMP0 /* temp. save stuff in stack frame */
s32i a13, sp, XT_STK_TMP1
s32i a9, sp, XT_STK_TMP2
/*
Save the overlay state if we are supporting overlays. Since we just saved
three registers, we can conveniently use them here. Note that as of now,
overlays only work for windowed calling ABI.
*/
#ifdef XT_USE_OVLY
l32i a9, sp, XT_STK_PC /* recover saved PC */
_xt_overlay_get_state a9, a12, a13
s32i a9, sp, XT_STK_OVLY /* save overlay state */
#if (defined XT_OV_SCRATCH) /* In newer liboverlay versions */
rsr a9, XT_OV_SCRATCH /* save XT_OV_SCRATCH register */
s32i a9, sp, XT_STK_OVLY_SCRATCH
#endif
#endif
l32i a12, sp, XT_STK_A12 /* recover original a9,12,13 */
l32i a13, sp, XT_STK_A13
l32i a9, sp, XT_STK_A9
addi sp, sp, XT_STK_FRMSZ /* restore the interruptee's SP */
call0 xthal_window_spill_nw /* preserves only a4,5,8,9,12,13 */
addi sp, sp, -XT_STK_FRMSZ
l32i a12, sp, XT_STK_TMP0 /* recover stuff from stack frame */
l32i a13, sp, XT_STK_TMP1
l32i a9, sp, XT_STK_TMP2
#endif
#if XCHAL_EXTRA_SA_SIZE > 0
/*
NOTE: Normally the xthal_save_extra_nw macro only affects address
registers a2-a5. It is theoretically possible for Xtensa processor
designers to write TIE that causes more address registers to be
affected, but it is generally unlikely. If that ever happens,
more registers need to be saved/restored around this macro invocation.
Here we assume a9,12,13 are preserved.
Future Xtensa tools releases might limit the regs that can be affected.
*/
addi a2, sp, XT_STK_EXTRA /* where to save it */
# if XCHAL_EXTRA_SA_ALIGN > 16
movi a3, -XCHAL_EXTRA_SA_ALIGN
and a2, a2, a3 /* align dynamically >16 bytes */
# endif
call0 xthal_save_extra_nw /* destroys a0,2,3,4,5 */
#endif
#if XCHAL_EXTRA_SA_SIZE > 0 || !defined(__XTENSA_CALL0_ABI__)
mov a0, a9 /* retrieve ret addr */
#endif
ret
/*******************************************************************************
_xt_context_restore
!! MUST BE CALLED ONLY BY 'CALL0' INSTRUCTION !!
Restores all Xtensa processor state except PC, PS, A0, A1 (SP) (and in Call0
ABI, A14, A15 which are preserved by all interrupt handlers) from an interrupt
stack frame defined in xtensa_rtos.h .
Its counterpart is _xt_context_save (whose caller saved A12, A13).
Caller is responsible to restore PC, PS, A0, A1 (SP).
Entry Conditions:
A0 = Return address in caller.
A1 = Stack pointer of interrupted thread or handler ("interruptee").
Exit conditions:
A0 = Return address in caller.
A1 = Stack pointer of interrupted thread or handler ("interruptee").
Other processor state except PC, PS, A0, A1 (SP), is as at the point
of interruption.
*******************************************************************************/
.global _xt_context_restore
.type _xt_context_restore,@function
.align 4
_xt_context_restore:
#if XCHAL_EXTRA_SA_SIZE > 0
/*
NOTE: Normally the xthal_restore_extra_nw macro only affects address
registers a2-a5. It is theoretically possible for Xtensa processor
designers to write TIE that causes more address registers to be
affected, but it is generally unlikely. If that ever happens,
more registers need to be saved/restored around this macro invocation.
Here we only assume a13 is preserved.
Future Xtensa tools releases might limit the regs that can be affected.
*/
mov a13, a0 /* preserve ret addr */
addi a2, sp, XT_STK_EXTRA /* where to find it */
# if XCHAL_EXTRA_SA_ALIGN > 16
movi a3, -XCHAL_EXTRA_SA_ALIGN
and a2, a2, a3 /* align dynamically >16 bytes */
# endif
call0 xthal_restore_extra_nw /* destroys a0,2,3,4,5 */
mov a0, a13 /* retrieve ret addr */
#endif
#if XCHAL_HAVE_LOOPS
l32i a2, sp, XT_STK_LBEG
l32i a3, sp, XT_STK_LEND
wsr a2, LBEG
l32i a2, sp, XT_STK_LCOUNT
wsr a3, LEND
wsr a2, LCOUNT
#endif
#if XCHAL_HAVE_EXCLUSIVE
/* Restore state of ATOMCTL */
l32i a2, sp, XT_STK_ATOMCTL
getex a2
#endif
#ifdef XT_USE_OVLY
/*
If we are using overlays, this is a good spot to check if we need
to restore an overlay for the incoming task. Here we have a bunch
of registers to spare. Note that this step is going to use a few
bytes of storage below SP (SP-20 to SP-32) if an overlay is going
to be restored.
*/
l32i a2, sp, XT_STK_PC /* retrieve PC */
l32i a3, sp, XT_STK_PS /* retrieve PS */
l32i a4, sp, XT_STK_OVLY /* retrieve overlay state */
l32i a5, sp, XT_STK_A1 /* retrieve stack ptr */
_xt_overlay_check_map a2, a3, a4, a5, a6
s32i a2, sp, XT_STK_PC /* save updated PC */
s32i a3, sp, XT_STK_PS /* save updated PS */
#if (defined XT_OV_SCRATCH) /* In newer liboverlay versions */
l32i a6, sp, XT_STK_OVLY_SCRATCH
wsr a6, XT_OV_SCRATCH /* restore XT_OV_SCRATCH register */
#endif
#endif
#ifdef XT_USE_SWPRI
/* Restore virtual interrupt priority and interrupt enable */
movi a3, xt_intdata
l32i a4, a3, 0 /* a4 = xt_intenable */
l32i a5, sp, XT_STK_VPRI /* a5 = saved xt_vpri_mask */
and a4, a4, a5
wsr a4, INTENABLE /* update INTENABLE */
s32i a5, a3, 4 /* restore xt_vpri_mask */
#endif
l32i a3, sp, XT_STK_SAR
l32i a2, sp, XT_STK_A2
wsr a3, SAR
l32i a3, sp, XT_STK_A3
l32i a4, sp, XT_STK_A4
l32i a5, sp, XT_STK_A5
l32i a6, sp, XT_STK_A6
l32i a7, sp, XT_STK_A7
l32i a8, sp, XT_STK_A8
l32i a9, sp, XT_STK_A9
l32i a10, sp, XT_STK_A10
l32i a11, sp, XT_STK_A11
/*
Call0 ABI callee-saved regs a12-15 do not need to be restored here.
However a12-13 were saved for scratch before XT_RTOS_INT_ENTER(),
so need to be restored anyway, despite being callee-saved in Call0.
*/
l32i a12, sp, XT_STK_A12
l32i a13, sp, XT_STK_A13
#ifndef __XTENSA_CALL0_ABI__
l32i a14, sp, XT_STK_A14
l32i a15, sp, XT_STK_A15
#endif
ret
#endif /* XCHAL_HAVE_XEA2 */
/*******************************************************************************
_xt_coproc_init
Initializes global co-processor management data, setting all co-processors
to "unowned". Leaves CPENABLE as it found it (does NOT clear it).
Called during initialization of the RTOS, before any threads run.
This may be called from normal Xtensa single-threaded application code which
might use co-processors. The Xtensa run-time initialization enables all
co-processors. They must remain enabled here, else a co-processor exception
might occur outside of a thread, which the exception handler doesn't expect.
Entry Conditions:
Xtensa single-threaded run-time environment is in effect.
No thread is yet running.
Exit conditions:
None.
Obeys ABI conventions per prototype:
void _xt_coproc_init(void)
*******************************************************************************/
#if XCHAL_CP_NUM > 0
.global _xt_coproc_init
.type _xt_coproc_init,@function
.align 4
_xt_coproc_init:
ENTRY0
/* Initialize thread co-processor ownerships to 0 (unowned). */
movi a2, _xt_coproc_owner_sa /* a2 = base of owner array */
addi a3, a2, XCHAL_CP_MAX << 2 /* a3 = top+1 of owner array */
movi a4, 0 /* a4 = 0 (unowned) */
1: s32i a4, a2, 0
addi a2, a2, 4
bltu a2, a3, 1b
RET0
#endif
/*******************************************************************************
_xt_coproc_release
Releases any and all co-processors owned by a given thread. The thread is
identified by it's co-processor state save area defined in xtensa_context.h .
Must be called before a thread's co-proc save area is deleted to avoid
memory corruption when the exception handler tries to save the state.
May be called when a thread terminates or completes but does not delete
the co-proc save area, to avoid the exception handler having to save the
thread's co-proc state before another thread can use it (optimization).
Entry Conditions:
A2 = Pointer to base of co-processor state save area.
Exit conditions:
None.
Obeys ABI conventions per prototype:
void _xt_coproc_release(void * coproc_sa_base)
*******************************************************************************/
#if XCHAL_CP_NUM > 0
.global _xt_coproc_release
.type _xt_coproc_release,@function
.align 4
_xt_coproc_release:
ENTRY0 /* a2 = base of save area */
movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */
addi a4, a3, XCHAL_CP_MAX << 2 /* a4 = top+1 of owner array */
movi a5, 0 /* a5 = 0 (unowned) */
#if XCHAL_HAVE_XEA3
movi a6, PS_DI
xps a6, a6 /* lock interrupts */
#else
rsil a6, XT_IRQ_LOCK_LEVEL /* lock interrupts */
#endif
1: l32i a7, a3, 0 /* a7 = owner at a3 */
bne a2, a7, 2f /* if (coproc_sa_base == owner) */
s32i a5, a3, 0 /* owner = unowned */
2: addi a3, a3, 1<<2 /* a3 = next entry in owner array */
bltu a3, a4, 1b /* repeat until end of array */
3:
wsr a6, PS /* restore interrupts */
rsync
RET0
#endif
@@ -0,0 +1,571 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* XTENSA CONTEXT FRAMES AND MACROS FOR RTOS ASSEMBLER SOURCES
*
* This header contains definitions and macros for use primarily by Xtensa
* RTOS assembly source files. It includes and uses the Xtensa hardware
* abstraction layer (HAL) to deal with config specifics. It may also be
* included in C source files.
*
* NOTE: The Xtensa architecture requires stack pointer alignment to 16 bytes.
*/
#ifndef XTENSA_CONTEXT_H
#define XTENSA_CONTEXT_H
#ifdef __ASSEMBLER__
#include <xtensa/coreasm.h>
#endif
#include <xtensa/config/tie.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
/* Align a value up to nearest n-byte boundary, where n is a power of 2. */
#ifdef __ASSEMBLER__
#define ALIGNUP(n, val) (((val) + (n)-1) & -(n))
#else
#define ALIGNUP(n, val) (((val) + (n)-1) & (unsigned)-(n))
#endif
/* SYSCALL definitions for privilige support */
#define SYSCALL_raise_priv 10
#define SYSCALL_is_priv 12
/*
-------------------------------------------------------------------------------
Macros that help define structures for both C and assembler.
-------------------------------------------------------------------------------
*/
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
#define XSTRUCT_BEGIN .pushsection .text; .struct 0
#define XSTRUCT_FIELD(ctype,size,asname,name) asname: .space size
#define XSTRUCT_AFIELD(ctype,size,asname,name,n) asname: .space (size)*(n)
#define XSTRUCT_END(sname) sname##Size:; .popsection
#else
#define XSTRUCT_BEGIN typedef struct {
#define XSTRUCT_FIELD(ctype,size,asname,name) ctype name;
#define XSTRUCT_AFIELD(ctype,size,asname,name,n) ctype name[n];
#define XSTRUCT_END(sname) } sname;
#endif //_ASMLANGUAGE || __ASSEMBLER__
/*
-------------------------------------------------------------------------------
INTERRUPT/EXCEPTION STACK FRAME FOR A THREAD OR NESTED INTERRUPT
A stack frame of this structure is allocated for any interrupt or exception.
It goes on the current stack. If the RTOS has a system stack for handling
interrupts, every thread stack must allow space for just one interrupt stack
frame, then nested interrupt stack frames go on the system stack.
The frame includes basic registers (explicit) and "extra" registers introduced
by user TIE or the use of the MAC16 option in the user's Xtensa config.
The frame size is minimized by omitting regs not applicable to user's config.
For Windowed ABI, this stack frame includes the interruptee's base save area,
another base save area to manage gcc nested functions, and a little temporary
space to help manage the spilling of the register windows.
-------------------------------------------------------------------------------
*/
#if XCHAL_HAVE_XEA2
XSTRUCT_BEGIN
XSTRUCT_FIELD (long, 4, XT_STK_EXIT, exit) /* exit point for dispatch */
XSTRUCT_FIELD (long, 4, XT_STK_PC, pc) /* return PC */
XSTRUCT_FIELD (long, 4, XT_STK_PS, ps) /* return PS */
XSTRUCT_FIELD (long, 4, XT_STK_A0, a0)
XSTRUCT_FIELD (long, 4, XT_STK_A1, a1) /* stack pointer before interrupt */
XSTRUCT_FIELD (long, 4, XT_STK_A2, a2)
XSTRUCT_FIELD (long, 4, XT_STK_A3, a3)
XSTRUCT_FIELD (long, 4, XT_STK_A4, a4)
XSTRUCT_FIELD (long, 4, XT_STK_A5, a5)
XSTRUCT_FIELD (long, 4, XT_STK_A6, a6)
XSTRUCT_FIELD (long, 4, XT_STK_A7, a7)
XSTRUCT_FIELD (long, 4, XT_STK_A8, a8)
XSTRUCT_FIELD (long, 4, XT_STK_A9, a9)
XSTRUCT_FIELD (long, 4, XT_STK_A10, a10)
XSTRUCT_FIELD (long, 4, XT_STK_A11, a11)
XSTRUCT_FIELD (long, 4, XT_STK_A12, a12)
XSTRUCT_FIELD (long, 4, XT_STK_A13, a13)
XSTRUCT_FIELD (long, 4, XT_STK_A14, a14)
XSTRUCT_FIELD (long, 4, XT_STK_A15, a15)
XSTRUCT_FIELD (long, 4, XT_STK_SAR, sar)
XSTRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause)
XSTRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr)
#if XCHAL_HAVE_LOOPS
XSTRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg)
XSTRUCT_FIELD (long, 4, XT_STK_LEND, lend)
XSTRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount)
#endif
#if XCHAL_HAVE_EXCLUSIVE
XSTRUCT_FIELD (long, 4, XT_STK_ATOMCTL, atomctl)
#endif
#ifndef __XTENSA_CALL0_ABI__
/* Temporary space for saving stuff during window spill */
XSTRUCT_FIELD (long, 4, XT_STK_TMP0, tmp0)
XSTRUCT_FIELD (long, 4, XT_STK_TMP1, tmp1)
XSTRUCT_FIELD (long, 4, XT_STK_TMP2, tmp2)
#endif
#ifdef XT_USE_SWPRI
/* Storage for virtual priority mask */
XSTRUCT_FIELD (long, 4, XT_STK_VPRI, vpri)
#endif
#ifdef XT_USE_OVLY
/* Storage for overlay state */
XSTRUCT_FIELD (long, 4, XT_STK_OVLY, ovly)
XSTRUCT_FIELD (long, 4, XT_STK_OVLY_SCRATCH, ovly_scratch)
#endif
XSTRUCT_END(XtExcFrame)
#endif /* XCHAL_HAVE_XEA2 */
#if XCHAL_HAVE_XEA3
XSTRUCT_BEGIN
#if defined(__XTENSA_WINDOWED_ABI__) && portUSING_MPU_WRAPPERS
/* Temporary space for syscall argument/return value */
XSTRUCT_FIELD (long, 4, XT_STK_A2, a2)
#endif
XSTRUCT_FIELD (long, 4, XT_STK_ATOMCTL, atomctl)
XSTRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount)
XSTRUCT_FIELD (long, 4, XT_STK_LEND, lend)
XSTRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg)
XSTRUCT_FIELD (long, 4, XT_STK_PC, pc)
XSTRUCT_FIELD (long, 4, XT_STK_PS, ps)
XSTRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause)
XSTRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr)
#ifdef __XTENSA_CALL0_ABI__
XSTRUCT_FIELD (long, 4, XT_STK_A0, a0)
XSTRUCT_FIELD (long, 4, XT_STK_A1, a1)
XSTRUCT_FIELD (long, 4, XT_STK_A2, a2)
XSTRUCT_FIELD (long, 4, XT_STK_A3, a3)
XSTRUCT_FIELD (long, 4, XT_STK_A4, a4)
XSTRUCT_FIELD (long, 4, XT_STK_A5, a5)
XSTRUCT_FIELD (long, 4, XT_STK_A6, a6)
XSTRUCT_FIELD (long, 4, XT_STK_A7, a7)
#endif
XSTRUCT_FIELD (long, 4, XT_STK_A8, a8)
XSTRUCT_FIELD (long, 4, XT_STK_A9, a9)
XSTRUCT_FIELD (long, 4, XT_STK_A10, a10)
XSTRUCT_FIELD (long, 4, XT_STK_A11, a11)
XSTRUCT_FIELD (long, 4, XT_STK_A12, a12)
XSTRUCT_FIELD (long, 4, XT_STK_A13, a13)
XSTRUCT_FIELD (long, 4, XT_STK_A14, a14)
XSTRUCT_FIELD (long, 4, XT_STK_A15, a15)
#ifdef __XTENSA_WINDOWED_ABI__
XSTRUCT_AFIELD(long, 4, XT_STK_SPILL, spill, 8)
#endif
XSTRUCT_END(XtExcFrame)
#endif /* XCHAL_HAVE_XEA3 */
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
#define XT_STK_NEXT1 XtExcFrameSize
#else
#define XT_STK_NEXT1 sizeof(XtExcFrame)
#endif
/* Allocate extra storage if needed for non-CP TIE state. Allow for alignment
padding as needed.
*/
#if XCHAL_EXTRA_SA_SIZE != 0
#if XCHAL_EXTRA_SA_ALIGN <= 16
#define XT_STK_EXTRA ALIGNUP(XCHAL_EXTRA_SA_ALIGN, XT_STK_NEXT1)
#else
/* If need more alignment than stack, add space for dynamic alignment */
#define XT_STK_EXTRA (ALIGNUP(XCHAL_EXTRA_SA_ALIGN, XT_STK_NEXT1) + XCHAL_EXTRA_SA_ALIGN)
#endif
#define XT_STK_NEXT2 (XT_STK_EXTRA + XCHAL_EXTRA_SA_SIZE)
#else
/* No extra storage required */
#define XT_STK_NEXT2 XT_STK_NEXT1
#endif
#if XCHAL_HAVE_XEA3
/* Total frame size */
#define XT_STK_FRMSZ (ALIGNUP(0x10, XT_STK_NEXT2))
/* Exception/interrupt frame size */
#define XT_STK_XFRM_SZ XT_STK_NEXT1
/* Extra save area size (including alignment padding) */
#define XT_STK_XTRA_SZ (XT_STK_FRMSZ - XT_STK_XFRM_SZ)
/* Alignment padding area size */
#if XCHAL_EXTRA_SA_SIZE != 0
#define XT_STK_ALIGN_SZ (XT_STK_EXTRA - XT_STK_NEXT1)
#endif
#else
/*
-------------------------------------------------------------------------------
This is the frame size. Add space for 4 registers (interruptee's base save
area) and some space for gcc nested functions if any.
-------------------------------------------------------------------------------
*/
#define XT_STK_FRMSZ (ALIGNUP(0x10, XT_STK_NEXT2) + 0x20)
#endif
/*
-------------------------------------------------------------------------------
CO-PROCESSOR STATE SAVE AREA FOR A THREAD
The RTOS must provide an area per thread to save the state of co-processors
when that thread does not have control. Co-processors are context-switched
lazily (on demand) only when a new thread uses a co-processor instruction,
otherwise a thread retains ownership of the co-processor even when it loses
control of the processor. An Xtensa co-processor exception is triggered when
any co-processor instruction is executed by a thread that is not the owner,
and the context switch of that co-processor is then peformed by the handler.
Ownership represents which thread's state is currently in the co-processor.
Co-processors may not be used by interrupt or exception handlers. If an
co-processor instruction is executed by an interrupt or exception handler,
the co-processor exception handler will trigger a kernel panic and freeze.
This restriction is introduced to reduce the overhead of saving and restoring
co-processor state (which can be quite large) and in particular remove that
overhead from interrupt handlers.
The co-processor state save area may be in any convenient per-thread location
such as in the thread control block or above the thread stack area. It need
not be in the interrupt stack frame since interrupts don't use co-processors.
Along with the save area for each co-processor, two bitmasks with flags per
co-processor (laid out as in the CPENABLE reg) help manage context-switching
co-processors as efficiently as possible:
XT_CPENABLE
The contents of a non-running thread's CPENABLE register.
It represents the co-processors owned (and whose state is still needed)
by the thread. When a thread is preempted, its CPENABLE is saved here.
When a thread solicits a context-swtich, its CPENABLE is cleared - the
compiler has saved the (caller-saved) co-proc state if it needs to.
When a non-running thread loses ownership of a CP, its bit is cleared.
When a thread runs, it's XT_CPENABLE is loaded into the CPENABLE reg.
Avoids co-processor exceptions when no change of ownership is needed.
XT_CPSTORED
A bitmask with the same layout as CPENABLE, a bit per co-processor.
Indicates whether the state of each co-processor is saved in the state
save area. When a thread enters the kernel, only the state of co-procs
still enabled in CPENABLE is saved. When the co-processor exception
handler assigns ownership of a co-processor to a thread, it restores
the saved state only if this bit is set, and clears this bit.
XT_CP_CS_ST
A bitmask with the same layout as CPENABLE, a bit per co-processor.
Indicates whether callee-saved state is saved in the state save area.
Callee-saved state is saved by itself on a solicited context switch,
and restored when needed by the coprocessor exception handler.
Unsolicited switches will cause the entire coprocessor to be saved
when necessary.
XT_CP_ASA
Pointer to the aligned save area. Allows it to be aligned more than
the overall save area (which might only be stack-aligned or TCB-aligned).
Especially relevant for Xtensa cores configured with a very large data
path that requires alignment greater than 16 bytes (ABI stack alignment).
-------------------------------------------------------------------------------
*/
#if XCHAL_CP_NUM > 0
/* Offsets of each coprocessor save area within the 'aligned save area': */
#define XT_CP0_SA 0
#define XT_CP1_SA ALIGNUP(XCHAL_CP1_SA_ALIGN, XT_CP0_SA + XCHAL_CP0_SA_SIZE)
#define XT_CP2_SA ALIGNUP(XCHAL_CP2_SA_ALIGN, XT_CP1_SA + XCHAL_CP1_SA_SIZE)
#define XT_CP3_SA ALIGNUP(XCHAL_CP3_SA_ALIGN, XT_CP2_SA + XCHAL_CP2_SA_SIZE)
#define XT_CP4_SA ALIGNUP(XCHAL_CP4_SA_ALIGN, XT_CP3_SA + XCHAL_CP3_SA_SIZE)
#define XT_CP5_SA ALIGNUP(XCHAL_CP5_SA_ALIGN, XT_CP4_SA + XCHAL_CP4_SA_SIZE)
#define XT_CP6_SA ALIGNUP(XCHAL_CP6_SA_ALIGN, XT_CP5_SA + XCHAL_CP5_SA_SIZE)
#define XT_CP7_SA ALIGNUP(XCHAL_CP7_SA_ALIGN, XT_CP6_SA + XCHAL_CP6_SA_SIZE)
#define XT_CP_SA_SIZE ALIGNUP(16, XT_CP7_SA + XCHAL_CP7_SA_SIZE)
/* Offsets within the overall save area: */
#define XT_CPENABLE 0 /* (2 bytes) coprocessors active for this thread */
#define XT_CPSTORED 2 /* (2 bytes) coprocessors saved for this thread */
#define XT_CP_CS_ST 4 /* (2 bytes) coprocessor callee-saved regs stored for this thread */
#define XT_CP_ASA 8 /* (4 bytes) ptr to aligned save area */
/* Overall size allows for dynamic alignment: */
#define XT_CP_SIZE (12 + XT_CP_SA_SIZE + XCHAL_TOTAL_SA_ALIGN)
#else
#define XT_CP_SIZE 0
#endif
/*
-------------------------------------------------------------------------------
MACROS TO HANDLE ABI SPECIFICS OF FUNCTION ENTRY AND RETURN
Convenient where the frame size requirements are the same for both ABIs.
ENTRY(sz), RET(sz) are for framed functions (have locals or make calls).
ENTRY0, RET0 are for frameless functions (no locals, no calls).
where size = size of stack frame in bytes (must be >0 and aligned to 16).
For framed functions the frame is created and the return address saved at
base of frame (Call0 ABI) or as determined by hardware (Windowed ABI).
For frameless functions, there is no frame and return address remains in a0.
Note: Because CPP macros expand to a single line, macros requiring multi-line
expansions are implemented as assembler macros.
-------------------------------------------------------------------------------
*/
#ifdef __ASSEMBLER__
#ifdef __XTENSA_CALL0_ABI__
/* Call0 */
#define ENTRY(sz) entry1 sz
.macro entry1 size=0x10
addi sp, sp, -\size
s32i a0, sp, 0
.endm
#define ENTRY0
#define RET(sz) ret1 sz
.macro ret1 size=0x10
l32i a0, sp, 0
addi sp, sp, \size
ret
.endm
#define RET0 ret
#else
/* Windowed */
#if XCHAL_HAVE_XEA3
#define ENTRY(sz) entry sp, (sz + 0x20)
#define ENTRY0 entry sp, 0x20
#else
#define ENTRY(sz) entry sp, (sz + 0x10)
#define ENTRY0 entry sp, 0x10
#endif
#define RET(sz) retw
#define RET0 retw
#endif
#endif
/*
-------------------------------------------------------------------------------
This flag is meant for internal use. Have all interrupts be dispatched via a
common wrapper, which takes care of doing some OS-specific stuff common to
all interrupt handlers. Said stuff cannot safely be handled in the RTOS_ENTER
and RTOS_EXIT macros.
-------------------------------------------------------------------------------
*/
#if (defined XT_INTEXC_HOOKS)
#define XT_USE_INT_WRAPPER 1
#else
#define XT_USE_INT_WRAPPER 0
#endif
#if XCHAL_HAVE_XEA2 && (XCHAL_NUM_INTERRUPTS > 32) && (defined XT_USE_SWPRI)
#error "Software prioritization of interrupts (XT_USE_SWPRI) not supported for XEA2 with > 32 interrupts."
#endif
#if XCHAL_HAVE_XEA3
#ifdef XT_USE_SWPRI
//#warning "Software prioritization of interrupts (XT_USE_SWPRI) not supported for XEA3."
#endif
#ifdef __ASSEMBLER__
#include "asm-offsets.h"
// RTOS-specific entry macro. Use only a8, a12-a14.
.macro XT_RTOS_INT_ENTER
.endm
// RTOS-specific exit macro. Use only a8-a14.
// (In call0, a15 holds user SP, must be preserved)
.macro XT_RTOS_INT_EXIT
// Check scheduler state and interrupt nest state.
movi a8, port_xSchedulerRunning
movi a9, port_interruptNesting
l32i a8, a8, 0 // a8 <- port_xSchedulerRunning
beqz a8, .Lnested // scheduler not running, no tasks
l32i a8, a9, 0 // a8 <- port_interruptNesting
bnez a8, .Lnested // != 0 means nested, skip ahead
movi a8, port_yield_flag
l32i a9, a8, 0 // a9 <- port_yield_flag
beqz a9, 2f // no yield
movi a9, 0
s32i a9, a8, 0 // zero out for next time
j .Lyield // no context save needed
2:
movi a8, pxCurrentTCB
l32i a9, a8, 0 // a9 <- pxCurrentTCB
beqz a9, .Lsched // no current, go to scheduler
movi a10, port_switch_flag
l32i a11, a10, 0 // a11 <- port_switch_flag
beqz a11, .Lnested // = 0 means no switch
movi a11, 0
s32i a11, a10, 0 // zero out for next time
// Preemption, save remaining state of current (outgoing) thread
addi a10, a1, -XT_STK_FRMSZ
s32i a10, a9, TCB_TOP_OF_STACK_OFF // pxCurrentTCB->pxTopOfStack <- SP
// Mark as preempted
#if XCHAL_CP_NUM > 0
l32i a10, a9, TCB_END_OF_STACK_OFF
rsr a12, CPENABLE // Save and clear CPENABLE
s16i a12, a10, XT_CPENABLE
movi a12, 0
wsr a12, CPENABLE
#endif
// Save non-CP TIE state if any
#if XCHAL_EXTRA_SA_SIZE > 0
addi a10, a1, -XT_STK_FRMSZ + XT_STK_ALIGN_SZ // where to save
#if XCHAL_EXTRA_SA_ALIGN > 16
movi a12, -XCHAL_EXTRA_SA_ALIGN
and a10, a10, a12 // align dynamically >16 bytes
#endif
xchal_ncp_store a10, a11, a12, a13, a14
#endif
// If windowed ABI, a0-a7 was not saved by dispatch code.
// These don't go into the exception frame, but below the
// exception frame pointer.
#ifdef __XTENSA_WINDOWED_ABI__
addi a10, a1, -XT_STK_FRMSZ -32
s32i a0, a10, 0
s32i a2, a10, 8
s32i a3, a10, 12
s32i a4, a10, 16
s32i a5, a10, 20
s32i a6, a10, 24
s32i a7, a10, 28
#endif
.Lsched:
// When we get here, interrupts are disabled. Also, we have saved
// all registers if needed so all of a0-a15 are available to use.
// Note we are using the existing stack to make the call below.
addi a1, a1, -XT_STK_FRMSZ -32
#ifdef __XTENSA_WINDOWED_ABI__
movi a10, vTaskSwitchContext
callx8 a10
#else
movi a10, vTaskSwitchContext
callx0 a10
#endif
.Lyield:
// Come here directly if the outgoing task yielded. pxCurrentTCB
// has already been updated.
movi a9, pxCurrentTCB
l32i a9, a9, 0 // a9 <- pxCurrentTCB
#if XCHAL_CP_NUM > 0
l32i a10, a9, TCB_END_OF_STACK_OFF
l16ui a10, a10, XT_CPENABLE
wsr a10, CPENABLE // restore CPENABLE
#endif
l32i a1, a9, TCB_TOP_OF_STACK_OFF // a1 <- pxCurrentTCB->pxTopOfStack
addi a10, a1, XT_STK_FRMSZ - XT_STK_XFRM_SZ
j 1f //bbci a10, 31, 1f // Bit 31 = solicited flag
// Solicited restore
addi a1, a1, XT_STK_FRMSZ // restore original SP
addi a2, a1, -XT_STK_XFRM_SZ
l32i a3, a2, XT_STK_PC // a3 = return PC
movi a2, PS_DI
xps a2, a2 // disable interrupts
movi a2, 0
wsr.ms a2 // restore normal DISPST
rsync
jx a3 // return to yield point
1:
// Preempt restore
#if XCHAL_EXTRA_SA_SIZE > 0
addi a10, a1, XT_STK_ALIGN_SZ // where to restore from
#if XCHAL_EXTRA_SA_ALIGN > 16
movi a12, -XCHAL_EXTRA_SA_ALIGN
and a10, a10, a12 // align dynamically >16 bytes
#endif
xchal_ncp_load a10, a11, a12, a13, a14
#endif
#ifdef __XTENSA_WINDOWED_ABI__
// Restore a0-a7 for windowed ABI
addi a8, a1, -32
l32i a0, a8, 0
l32i a2, a8, 8
l32i a3, a8, 12
l32i a4, a8, 16
l32i a5, a8, 20
l32i a6, a8, 24
l32i a7, a8, 28
#else
// For call0, dispatch exit code expects a15 = original SP
addi a15, a1, XT_STK_FRMSZ
#endif
addi a1, a1, XT_STK_FRMSZ // set up SP for dispatch exit
.Lnested:
// Return to the current saved context.
.endm
#endif /* __ASSEMBLER__ */
#endif /* XCHAL_HAVE_XEA3 */
#endif /* XTENSA_CONTEXT_H */
@@ -0,0 +1,543 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
// xtensa_coproc_handler.S - Xtensa coprocessor exception handler.
#include <xtensa/config/specreg.h>
#include <xtensa/coreasm.h>
#include "xtensa_context.h"
#include "xtensa_rtos.h"
#if XCHAL_CP_NUM > 0
//-----------------------------------------------------------------------------
// Coprocessor related state and precomputed values.
//-----------------------------------------------------------------------------
// Table of coprocessor owners, identified by thread's CP save area pointer.
// Zero means coprocessor is not owned.
.data
.global _xt_coproc_owner_sa
.align 16,,XCHAL_CP_MAX << 2 // minimize crossing cache boundaries
_xt_coproc_owner_sa:
.rept XCHAL_CP_MAX
.word 0
.endr
// Bitmask table for CP n's enable bit, indexed by coprocessor number.
.section .rodata, "a"
.global _xt_coproc_mask
.align 16,,8 // try to keep it all in one cache line
.set i, 0
_xt_coproc_mask:
.rept XCHAL_CP_MAX
.long (i<<16) | (1<<i) // upper 16-bits = i, lower = bitmask
.set i, i+1
.endr
// Offset to CP n save area in thread's CP save area.
.global _xt_coproc_sa_offset
.align 16 // minimize crossing cache boundaries
_xt_coproc_sa_offset:
.word XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA
.word XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA
//-----------------------------------------------------------------------------
// _xt_coproc_handler
//
// Handles coprocessor exceptions and manages lazy context switching between
// multiple threads sharing the coprocessor(s).
// Register use:
// a0 - on entry, return address (must have been called via call0).
// a1 - pointing to valid exception stack frame.
// a2 - on entry, must hold coprocessor index. On exit, 0 if OK.
// a3-a15 - may all be used and trashed by this routine.
//-----------------------------------------------------------------------------
.text
.align 4
.global _xt_coproc_handler
_xt_coproc_handler:
mov a7, a0 // a7 = return address
mov a5, a2 // a5 = CP index n
// Get coprocessor state save area of new owner thread
call0 XT_RTOS_CP_STATE // a15 = new owner's save area
beqz a15, .L_xt_coproc_invalid // not in a thread (invalid)
l32i a4, a15, XT_CP_ASA // actual save area address
beqz a4, .L_xt_coproc_invalid // thread has no save area
// Enable the co-processor's bit in CPENABLE
movi a0, _xt_coproc_mask
rsr a4, CPENABLE // a4 = CPENABLE
addx4 a0, a5, a0 // a0 = &_xt_coproc_mask[n]
l32i a0, a0, 0 // a0 = (n << 16) | (1 << n)
movi a3, _xt_coproc_owner_sa
extui a2, a0, 0, 16 // coprocessor bitmask portion
or a4, a4, a2 // a4 = CPENABLE | (1 << n)
wsr a4, CPENABLE
// Get old coprocessor owner thread (save area ptr) and assign new one
addx4 a3, a5, a3 // a3 = &_xt_coproc_owner_sa[n]
l32i a2, a3, 0 // a2 = old owner's save area
s32i a15, a3, 0 // _xt_coproc_owner_sa[n] = new
rsync // ensure wsr.CPENABLE is complete
// Do we need to context-switch this coprocessor ?
beq a15, a2, .L_xt_coproc_done // new owner == old, we're done
// if no old owner then nothing to save
beqz a2, .L_check_new
// If old owner not actively using CP then nothing to save.
l16ui a4, a2, XT_CPENABLE // a4 = old owner's CPENABLE
bnone a4, a0, .L_check_new // old owner not using CP
.L_save_old:
// We need to save old owner's coprocessor state
movi a5, _xt_coproc_sa_offset
// Mark old owner state as no longer active (CPENABLE bit n clear)
xor a4, a4, a0 // clear CP in old owner's CPENABLE
s16i a4, a2, XT_CPENABLE // update old owner's CPENABLE
extui a4, a0, 16, 5 // a4 = CP index = n
addx4 a5, a4, a5 // a5 = &_xt_coproc_sa_offset[n]
// Mark old owner state as saved (CPSTORED bit n set)
l16ui a4, a2, XT_CPSTORED // a4 = old owner's CPSTORED
l32i a5, a5, 0 // a5 = XT_CP[n]_SA offset
or a4, a4, a0 // set CP in old owner's CPSTORED
s16i a4, a2, XT_CPSTORED // update old owner's CPSTORED
l32i a2, a2, XT_CP_ASA // ptr to actual (aligned) save area
extui a3, a0, 16, 5 // a3 = CP index = n
add a2, a2, a5 // a2 = old owner's area for CP n
// The config-specific HAL macro invoked below destroys a2-a6.
// It is theoretically possible for Xtensa processor designers to write TIE
// that causes more address registers to be affected, but it is generally
// unlikely. If that ever happens, more registers needs to be saved/restored
// around this macro invocation, and the value in a15 needs to be recomputed.
xchal_cpi_store_funcbody
.L_check_new:
// Check if any state has to be restored for new owner.
// NOTE: a15 = new owner's save area, cannot be zero when we get here.
l16ui a3, a15, XT_CPSTORED // a3 = new owner's CPSTORED
movi a4, _xt_coproc_sa_offset
bnone a3, a0, .L_check_cs // full CP not saved, check callee-saved
xor a3, a3, a0 // CPSTORED bit is set, clear it
s16i a3, a15, XT_CPSTORED // update new owner's CPSTORED
// Adjust new owner's save area pointers to area for CP n.
extui a3, a0, 16, 5 // a3 = CP index = n
addx4 a4, a3, a4 // a4 = &_xt_coproc_sa_offset[n]
l32i a4, a4, 0 // a4 = XT_CP[n]_SA
l32i a5, a15, XT_CP_ASA // ptr to actual (aligned) save area
add a2, a4, a5 // a2 = new owner's area for CP
// The config-specific HAL macro invoked below destroys a2-a6.
// It is theoretically possible for Xtensa processor designers to write TIE
// that causes more address registers to be affected, but it is generally
// unlikely. If that ever happens, more registers needs to be saved/restored
// around this macro invocation.
xchal_cpi_load_funcbody
.L_xt_coproc_done:
movi a2, 0 // a2 <- 0 == OK
.L_xt_coproc_err:
mov a0, a7 // return address
ret
.L_check_cs:
// a0 = CP mask in low bits, a15 = new owner's save area.
l16ui a2, a15, XT_CP_CS_ST // a2 = mask of CPs saved
bnone a2, a0, .L_xt_coproc_done // if no match then done
and a2, a2, a0 // a2 = which CPs to restore
extui a2, a2, 0, 8 // extract low 8 bits
call0 _xt_coproc_restorecs // restore CP registers
j .L_xt_coproc_done
.L_xt_coproc_invalid:
// Coprocessor exception occurred outside a thread or the thread
// did not allocate space to save coprocessor state. Return error.
movi a2, 1
j .L_xt_coproc_err
//-----------------------------------------------------------------------------
// _xt_coproc_savecs
//
// If there is a current thread and it has a coprocessor state save area, then
// save all callee-saved state into this area. This function is called from the
// solicited context switch handler. It calls a system-specific function to get
// the coprocessor save area base address.
//
// Entry conditions:
// - The thread being switched out is still the current thread.
// - CPENABLE state reflects which coprocessors are active.
// - Registers have been saved/spilled already.
//
// Exit conditions:
// - All necessary CP callee-saved state has been saved.
// - Registers a7-a15 have been trashed.
//
// Must be called from assembly code only, using CALL0.
//-----------------------------------------------------------------------------
.global _xt_coproc_savecs
.type _xt_coproc_savecs,@function
.align 4
_xt_coproc_savecs:
// At entry, CPENABLE should be showing which CPs are enabled.
rsr a11, CPENABLE // a11 = which CPs are enabled
beqz a11, .Ldone // quick exit if none
mov a14, a0 // save return address
call0 XT_RTOS_CP_STATE // get address of CP save area
mov a0, a14 // restore return address
beqz a15, .Ldone // if none then nothing to do
l32i a14, a15, XT_CP_ASA // a14 = base of aligned save area
beqz a14, .Ldone // no save area, nothing to do
s16i a11, a15, XT_CP_CS_ST // save mask of CPs being stored
movi a13, _xt_coproc_sa_offset // array of CP save offsets
l32i a15, a15, XT_CP_ASA // a15 = base of aligned save area
#if XCHAL_CP0_SA_SIZE
bbci.l a11, 0, 2f // CP 0 not enabled
l32i a14, a13, 0 // a14 = _xt_coproc_sa_offset[0]
add a12, a14, a15 // a12 = save area for CP 0
xchal_cp0_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP1_SA_SIZE
bbci.l a11, 1, 2f // CP 1 not enabled
l32i a14, a13, 4 // a14 = _xt_coproc_sa_offset[1]
add a12, a14, a15 // a12 = save area for CP 1
xchal_cp1_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP2_SA_SIZE
bbci.l a11, 2, 2f
l32i a14, a13, 8
add a12, a14, a15
xchal_cp2_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP3_SA_SIZE
bbci.l a11, 3, 2f
l32i a14, a13, 12
add a12, a14, a15
xchal_cp3_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP4_SA_SIZE
bbci.l a11, 4, 2f
l32i a14, a13, 16
add a12, a14, a15
xchal_cp4_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP5_SA_SIZE
bbci.l a11, 5, 2f
l32i a14, a13, 20
add a12, a14, a15
xchal_cp5_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP6_SA_SIZE
bbci.l a11, 6, 2f
l32i a14, a13, 24
add a12, a14, a15
xchal_cp6_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP7_SA_SIZE
bbci.l a11, 7, 2f
l32i a14, a13, 28
add a12, a14, a15
xchal_cp7_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
.Ldone:
ret
//-----------------------------------------------------------------------------
// _xt_coproc_restorecs
//
// Restore any callee-saved coprocessor state for the incoming thread.
// This function is called from coprocessor exception handling, when giving
// ownership to a thread that solicited a context switch earlier. It calls a
// system-specific function to get the coprocessor save area base address.
//
// Entry conditions:
// - The incoming thread is set as the current thread.
// - CPENABLE is set up correctly for all required coprocessors.
// - a2 = mask of coprocessors to be restored.
//
// Exit conditions:
// - All necessary CP callee-saved state has been restored.
// - CPENABLE - unchanged.
// - Registers a2, a8-a15 have been trashed.
//
// Must be called from assembly code only, using CALL0.
//-----------------------------------------------------------------------------
.global _xt_coproc_restorecs
.type _xt_coproc_restorecs,@function
.align 4
_xt_coproc_restorecs:
mov a14, a0 // save return address
call0 XT_RTOS_CP_STATE // get address of CP save area
mov a0, a14 // restore return address
beqz a15, .Ldone2 // if none then nothing to do
l32i a14, a15, XT_CP_ASA // a14 = base of aligned save area
beqz a14, .Ldone2 // no save area, nothing to do
l16ui a13, a15, XT_CP_CS_ST // a13 = which CPs have been saved
xor a13, a13, a2 // clear the ones being restored
s16i a13, a15, XT_CP_CS_ST // update saved CP mask
movi a13, _xt_coproc_sa_offset // array of CP save offsets
l32i a15, a15, XT_CP_ASA // a15 = base of aligned save area
#if XCHAL_CP0_SA_SIZE
bbci.l a2, 0, 2f // CP 0 not enabled
l32i a14, a13, 0 // a14 = _xt_coproc_sa_offset[0]
add a12, a14, a15 // a12 = save area for CP 0
xchal_cp0_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP1_SA_SIZE
bbci.l a2, 1, 2f // CP 1 not enabled
l32i a14, a13, 4 // a14 = _xt_coproc_sa_offset[1]
add a12, a14, a15 // a12 = save area for CP 1
xchal_cp1_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP2_SA_SIZE
bbci.l a2, 2, 2f
l32i a14, a13, 8
add a12, a14, a15
xchal_cp2_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP3_SA_SIZE
bbci.l a2, 3, 2f
l32i a14, a13, 12
add a12, a14, a15
xchal_cp3_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP4_SA_SIZE
bbci.l a2, 4, 2f
l32i a14, a13, 16
add a12, a14, a15
xchal_cp4_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP5_SA_SIZE
bbci.l a2, 5, 2f
l32i a14, a13, 20
add a12, a14, a15
xchal_cp5_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP6_SA_SIZE
bbci.l a2, 6, 2f
l32i a14, a13, 24
add a12, a14, a15
xchal_cp6_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
#if XCHAL_CP7_SA_SIZE
bbci.l a2, 7, 2f
l32i a14, a13, 28
add a12, a14, a15
xchal_cp7_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif
.Ldone2:
ret
#if XCHAL_HAVE_XEA3
//-----------------------------------------------------------------------------
// For XEA3, coprocessor exceptions come here. This is a wrapper function that
// calls _xt_coproc_handler() to do the actual work. We don't want the handler
// to be interrupted because that might cause a round-robin switch and leave
// coprocessor context in a confused state. So interrupts are disabled before
// calling the handler. They will be re-enabled on return from exception.
//-----------------------------------------------------------------------------
.text
.global _xt_coproc_exc
.type _xt_coproc_exc,@function
.align 4
_xt_coproc_exc:
#ifdef __XTENSA_CALL0_ABI__
addi a1, a1, -16 // reserve 16 bytes on stack
s32i a0, a1, 0 // save return address
s32i a15, a1, 8 // must save a15 (see dispatch)
movi a3, PS_DI_MASK
xps a3, a3 // set PS.DI, disable interrupts
l32i a3, a2, XT_STK_EXCCAUSE // a3 <- exccause
extui a2, a3, 8, 4 // a2 <- CP index
call0 _xt_coproc_handler
l32i a0, a1, 0 // restore return address
l32i a15, a1, 8 // restore a15
addi a1, a1, 16
ret
#else
entry a1, 48 // reserve 16 bytes on stack
s32i a0, a1, 0 // save return address
movi a3, PS_DI_MASK
xps a3, a3 // set PS.DI, disable interrupts
l32i a3, a2, XT_STK_EXCCAUSE // a3 <- exccause
extui a2, a3, 8, 4 // a2 <- CP index
call0 _xt_coproc_handler
l32i a0, a1, 0 // restore return address
retw
#endif
#endif // XCHAL_HAVE_XEA3
#if XCHAL_HAVE_XEA2
//-----------------------------------------------------------------------------
// XEA2 coprocessor exception dispatcher. Save enough state to be able to call
// the coprocessor handler, then restore and return.
//-----------------------------------------------------------------------------
#if portUSING_MPU_WRAPPERS
.section "privileged_functions"
#else
.text
#endif
.global _xt_coproc_exc
.type _xt_coproc_exc,@function
.align 4
_xt_coproc_exc:
mov a0, sp // Allocate stack frame
addi sp, sp, -XT_STK_FRMSZ
s32i a0, sp, XT_STK_A1 // save SP
#ifdef __XTENSA_WINDOWED_ABI__
s32e a0, sp, -12 // for debug backtrace
#endif
rsr a0, PS
s32i a0, sp, XT_STK_PS // save PS
rsr a0, EPC_1
s32i a0, sp, XT_STK_PC // save PC
rsr a0, EXCSAVE_1
s32i a0, sp, XT_STK_A0 // retrieve and save a0
#ifdef __XTENSA_WINDOWED_ABI__
s32e a0, sp, -16 // for debug backtrace
#endif
s32i a2, sp, XT_STK_A2
s32i a3, sp, XT_STK_A3
s32i a4, sp, XT_STK_A4
s32i a5, sp, XT_STK_A5
s32i a6, sp, XT_STK_A6
s32i a7, sp, XT_STK_A7
s32i a8, sp, XT_STK_A8
s32i a9, sp, XT_STK_A9
s32i a10, sp, XT_STK_A10
s32i a11, sp, XT_STK_A11
s32i a12, sp, XT_STK_A12
s32i a13, sp, XT_STK_A13
s32i a14, sp, XT_STK_A14
s32i a15, sp, XT_STK_A15
rsr a3, EXCCAUSE // a3 <- exccause
addi a2, a3, -EXCCAUSE_CP0_DISABLED // a2 <- CP index
call0 _xt_coproc_handler
mov a0, a2 // save return value
l32i a2, sp, XT_STK_A2
l32i a3, sp, XT_STK_A3
l32i a4, sp, XT_STK_A4
l32i a5, sp, XT_STK_A5
l32i a6, sp, XT_STK_A6
l32i a7, sp, XT_STK_A7
l32i a8, sp, XT_STK_A8
l32i a9, sp, XT_STK_A9
l32i a10, sp, XT_STK_A10
l32i a11, sp, XT_STK_A11
l32i a12, sp, XT_STK_A12
l32i a13, sp, XT_STK_A13
l32i a14, sp, XT_STK_A14
l32i a15, sp, XT_STK_A15
bnez a0, .Lfail // abort if failure
l32i a0, sp, XT_STK_PC
wsr a0, EPC_1 // restore PC
l32i a0, sp, XT_STK_PS
wsr a0, PS // restore PS
l32i a0, sp, XT_STK_A0
addi a1, a1, XT_STK_FRMSZ // deallocate stack frame
rfe
.Lfail:
call0 _xt_panic
#endif // XCHAL_HAVE_XEA2
#endif // XCHAL_CP_NUM > 0
@@ -0,0 +1,262 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* Xtensa-specific interrupt and exception functions for RTOS ports.
* Also see xtensa_intr_asm.S.
*/
#include <stdlib.h>
#include <xtensa/config/core.h>
#include <xtensa/core-macros.h>
#if XCHAL_HAVE_INTERRUPTS
#include <xtensa/tie/xt_interrupt.h>
#endif
#include "xtensa_api.h"
#if XCHAL_HAVE_EXCEPTIONS
/* Handler table is in xtensa_intr_asm.S */
extern xt_exc_handler xt_exception_table[XCHAL_EXCCAUSE_NUM];
/*
Default handler for unhandled exceptions.
*/
void
xt_unhandled_exception( XtExcFrame * frame )
{
(void) frame;
exit( -1 );
}
/*
This function registers a handler for the specified exception.
The function returns the address of the previous handler.
On error, it returns NULL.
*/
xt_exc_handler
xt_set_exception_handler( uint32_t n, xt_exc_handler f )
{
xt_exc_handler old;
if ( n >= (uint32_t) XCHAL_EXCCAUSE_NUM )
{
// Invalid exception number.
return NULL;
}
old = xt_exception_table[n];
if ( f != NULL )
{
xt_exception_table[n] = f;
}
else
{
xt_exception_table[n] = &xt_unhandled_exception;
}
return old;
}
#endif
#if XCHAL_HAVE_INTERRUPTS
#if XCHAL_HAVE_XEA2 && (XCHAL_NUM_INTERRUPTS <= 32)
/* Defined in xtensa_intr_asm.S */
extern uint32_t xt_intenable;
extern uint32_t xt_vpri_mask;
#endif
/* Handler table is in xtensa_intr_asm.S */
typedef struct xt_handler_table_entry {
xt_handler handler;
void * arg;
} xt_handler_table_entry;
#if (XT_USE_INT_WRAPPER || XCHAL_HAVE_XEA3)
extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS + 1];
#else
extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS];
#endif
/*
Default handler for unhandled interrupts.
*/
void
xt_unhandled_interrupt( void * arg )
{
(void) arg;
exit( -1 );
}
/*
This function registers a handler for the specified interrupt. The "arg"
parameter specifies the argument to be passed to the handler when it is
invoked. The function returns the address of the previous handler.
On error, it returns NULL.
*/
xt_handler
xt_set_interrupt_handler( uint32_t n, xt_handler f, void * arg )
{
xt_handler_table_entry * entry;
xt_handler old;
if ( n >= (uint32_t) XCHAL_NUM_INTERRUPTS )
{
// Invalid interrupt number.
return NULL;
}
#if XCHAL_HAVE_XEA2
if ( Xthal_intlevel[n] > XCHAL_EXCM_LEVEL )
{
// Priority level too high to safely handle in C.
return NULL;
}
#endif
#if (XT_USE_INT_WRAPPER || XCHAL_HAVE_XEA3)
entry = _xt_interrupt_table + n + 1;
#else
entry = _xt_interrupt_table + n;
#endif
old = entry->handler;
if ( f != NULL )
{
entry->handler = f;
entry->arg = arg;
}
else
{
entry->handler = &xt_unhandled_interrupt;
entry->arg = (void*)n;
}
return old;
}
/*
This function enables the interrupt whose number is specified as
the argument.
*/
void
xt_interrupt_enable( uint32_t intnum )
{
#if XCHAL_HAVE_XEA2 && (XCHAL_NUM_INTERRUPTS <= 32)
if ( intnum < (uint32_t) XCHAL_NUM_INTERRUPTS )
{
int ps = XT_RSIL( 15 );
// New INTENABLE = (xt_intenable | mask) & xt_vpri_mask.
xt_intenable |= ( 1U << intnum );
XT_WSR_INTENABLE( xt_intenable & xt_vpri_mask );
XT_WSR_PS( ps );
XT_RSYNC();
}
#else
xthal_interrupt_enable( intnum );
#endif
}
/*
This function disables the interrupt whose number is specified as
the argument.
*/
void
xt_interrupt_disable( uint32_t intnum )
{
#if XCHAL_HAVE_XEA2 && (XCHAL_NUM_INTERRUPTS <= 32)
if ( intnum < (uint32_t) XCHAL_NUM_INTERRUPTS )
{
int ps = XT_RSIL( 15 );
// New INTENABLE = (xt_intenable & ~mask) & xt_vpri_mask.
xt_intenable &= ~( 1U << intnum );
XT_WSR_INTENABLE( xt_intenable & xt_vpri_mask );
XT_WSR_PS( ps );
XT_RSYNC();
}
#else
xthal_interrupt_disable( intnum );
#endif
}
/*
This function returns : 1 if the specified interrupt is enabled, zero
if the interrupt is disabled, zero if the interrupt number is invalid.
*/
uint32_t
xt_interrupt_enabled( uint32_t intnum )
{
#if XCHAL_HAVE_XEA2 && (XCHAL_NUM_INTERRUPTS <= 32)
if ( intnum < (uint32_t) XCHAL_NUM_INTERRUPTS )
{
return ( (xt_intenable & (1U << intnum)) != 0 ) ? 1U : 0;
}
return 0;
#else
return xthal_interrupt_enabled( intnum );
#endif
}
/*
This function triggers the specified interrupt.
*/
void
xt_interrupt_trigger( uint32_t intnum )
{
xthal_interrupt_trigger( intnum );
}
/*
This function clears the specified interrupt.
*/
void
xt_interrupt_clear( uint32_t intnum )
{
xthal_interrupt_clear( intnum );
}
#endif /* XCHAL_HAVE_INTERRUPTS */
@@ -0,0 +1,152 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* Xtensa interrupt handling data and assembly routines.
* Also see xtensa_intr.c and xtensa_vectors.S.
*/
#include <xtensa/hal.h>
#include <xtensa/config/core.h>
#include <FreeRTOSConfig.h>
#include "xtensa_context.h"
#include "xtensa_config.h"
#if XCHAL_HAVE_INTERRUPTS
/*
-------------------------------------------------------------------------------
INTENABLE virtualization information.
-------------------------------------------------------------------------------
*/
#if XCHAL_HAVE_XEA2 && (XCHAL_NUM_INTERRUPTS <= 32)
.data
.global xt_intdata
.align 8
xt_intdata:
.global xt_intenable
.type xt_intenable,@object
.size xt_intenable,4
.global xt_vpri_mask
.type xt_vpri_mask,@object
.size xt_vpri_mask,4
xt_intenable: .word 0 /* Virtual INTENABLE */
xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */
#endif
/*
-------------------------------------------------------------------------------
System interrupt stack.
-------------------------------------------------------------------------------
*/
#if (XCHAL_HAVE_XEA2 || XCHAL_HAVE_ISB)
.data
#else
.section .intr.top, "aw"
#endif
.global xt_interrupt_stack
.global xt_interrupt_stack_top
.align 16
xt_interrupt_stack:
.space configISR_STACK_SIZE
xt_interrupt_stack_top:
/*
-------------------------------------------------------------------------------
Table of C-callable interrupt handlers for each interrupt. For XEA2 configs,
not all slots can be filled, because interrupts at level > EXCM_LEVEL will
not be dispatched to a C handler by default.
-------------------------------------------------------------------------------
*/
#if (XCHAL_HAVE_XEA2 || XCHAL_HAVE_ISB)
.data
#else
.section .intr.data, "aw"
#endif
.global _xt_interrupt_table
.align 16
_xt_interrupt_table:
/*
-------------------------------------------------------------------------------
If using the interrupt wrapper, make the first entry in the interrupt table
point to the wrapper (XEA3) or leave it empty (XEA2).
-------------------------------------------------------------------------------
*/
#if XCHAL_HAVE_XEA3
.word xt_interrupt_wrapper
.word 0
#elif XT_USE_INT_WRAPPER
.word 0
.word 0
#endif
.set i, 0
.rept XCHAL_NUM_INTERRUPTS
.word xt_unhandled_interrupt /* handler address */
.word i /* handler arg (default: intnum) */
.set i, i+1
.endr
#endif /* XCHAL_HAVE_INTERRUPTS */
#if XCHAL_HAVE_EXCEPTIONS
/*
-------------------------------------------------------------------------------
Table of C-callable exception handlers for each exception. Note that not all
slots will be active, because some exceptions (e.g. coprocessor exceptions)
are always handled by the OS and cannot be hooked by user handlers.
-------------------------------------------------------------------------------
*/
.data
.global xt_exception_table
.align 4
xt_exception_table:
.rept XCHAL_EXCCAUSE_NUM
.word xt_unhandled_exception /* handler address */
.endr
#endif
@@ -0,0 +1,99 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#include <xtensa/config/core.h>
#include <xtensa/core-macros.h>
#include "xtensa_rtos.h"
#include "xtensa_api.h"
#include "portmacro.h"
#if (XCHAL_HAVE_XEA3 && XCHAL_HAVE_INTERRUPTS)
/* Handler table is in xtensa_intr_asm.S */
typedef struct xt_handler_table_entry {
void * handler;
void * arg;
} xt_handler_table_entry;
extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS + 1];
extern int32_t xt_sw_intnum;
extern int32_t port_switch_flag;
extern uint32_t port_interruptNesting;
static int32_t xt_wflag;
/**************************************************************************/
/* Wrapper for interrupt handlers. Argument is (intnum << 2). */
/* Execution comes here from the dispatch code if the wrapper is */
/* enabled. */
/**************************************************************************/
void
xt_interrupt_wrapper(void * arg)
{
uint32_t intnum = (uint32_t)(arg) >> 2;
uint32_t state;
xt_handler_table_entry * entry;
xt_handler handler;
state = portENTER_CRITICAL_NESTED();
port_interruptNesting++;
portEXIT_CRITICAL_NESTED(state);
/* Load handler address and argument from table. Note that the
first entry in the table points to this wrapper, so we have
to skip ahead one.
*/
entry = _xt_interrupt_table + intnum + 1;
handler = (xt_handler) entry->handler;
(*handler)(entry->arg);
/* If a context switch is pending, trigger the SW interrupt
to process the switch. Set an internal flag so we don't
trigger the sw interrupt again when handling it.
*/
if (xt_wflag != 0) {
xt_wflag = 0;
}
else if (port_switch_flag) {
xt_wflag = 1;
xt_interrupt_trigger(xt_sw_intnum);
}
state = portENTER_CRITICAL_NESTED();
port_interruptNesting--;
portEXIT_CRITICAL_NESTED(state);
}
#endif /* XCHAL_HAVE_XEA3 && XCHAL_HAVE_INTERRUPTS */
@@ -0,0 +1,92 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* xtensa_overlay_os_hook.c -- Overlay manager OS hooks for FreeRTOS.
*/
#ifdef XT_USE_OVLY
#include <xtensa/overlay.h>
#include "FreeRTOS.h"
#include "semphr.h"
#ifdef __XTENSA_CALL0_ABI__
#error "Windowed ABI is required for overlays"
#elif !XCHAL_HAVE_XEA2
#error "Overlays supported only on Xtensa LX"
#endif
#if configUSE_MUTEXES
/* Mutex object that controls access to the overlay. Currently only one
* overlay region is supported so one mutex suffices.
*/
static SemaphoreHandle_t xt_overlay_mutex;
/* This function should be overridden to provide OS specific init such
* as the creation of a mutex lock that can be used for overlay locking.
* Typically this mutex would be set up with priority inheritance. See
* overlay manager documentation for more details.
*/
void xt_overlay_init_os(void)
{
/* Create the mutex for overlay access. Priority inheritance is
* required.
*/
xt_overlay_mutex = xSemaphoreCreateMutex();
}
/* This function locks access to shared overlay resources, typically
* by acquiring a mutex.
*/
void xt_overlay_lock(void)
{
xSemaphoreTake(xt_overlay_mutex, portMAX_DELAY);
}
/* This function releases access to shared overlay resources, typically
* by unlocking a mutex.
*/
void xt_overlay_unlock(void)
{
xSemaphoreGive(xt_overlay_mutex);
}
#else
#error "Mutex support is required for overlays"
#endif /* configUSE_MUTEXES */
#endif /* XT_USE_OVLY */
@@ -0,0 +1,258 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* RTOS-SPECIFIC INFORMATION FOR XTENSA RTOS ASSEMBLER SOURCES
* (FreeRTOS Port)
*
* This header is the primary glue between generic Xtensa RTOS support
* sources and a specific RTOS port for Xtensa. It contains definitions
* and macros for use primarily by Xtensa assembly coded source files.
*
* Macros in this header map callouts from generic Xtensa files to specific
* RTOS functions. It may also be included in C source files.
*
* Xtensa RTOS ports support all RTOS-compatible configurations of the Xtensa
* architecture, using the Xtensa hardware abstraction layer (HAL) to deal
* with configuration specifics.
*
* Should be included by all Xtensa generic and RTOS port-specific sources.
*/
#ifndef XTENSA_RTOS_H
#define XTENSA_RTOS_H
#ifdef __ASSEMBLER__
#include <xtensa/coreasm.h>
#else
#include <xtensa/config/core.h>
#endif
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
#include <xtensa/simcall.h>
/*
Include any RTOS specific definitions that are needed by this header.
*/
#include <FreeRTOSConfig.h>
/*
Convert FreeRTOSConfig definitions to XTENSA definitions.
However these can still be overridden from the command line.
*/
#ifndef XT_SIMULATOR
#if configXT_SIMULATOR
#define XT_SIMULATOR 1 /* Simulator mode */
#endif
#endif
#ifndef XT_BOARD
#if defined (configXT_BOARD) && (configXT_BOARD != 0)
#define XT_BOARD 1 /* Board mode */
#endif
#endif
#ifndef XT_TIMER_INDEX
#if defined configXT_TIMER_INDEX
#define XT_TIMER_INDEX configXT_TIMER_INDEX /* Index of hardware timer to be used */
#endif
#endif
#ifndef XT_INTEXC_HOOKS
#if configXT_INTEXC_HOOKS
#define XT_INTEXC_HOOKS 1 /* Enables exception hooks */
#endif
#endif
#if XCHAL_HAVE_XEA2 && (XCHAL_NUM_INTERRUPTS > 32) && (defined XT_INTEXC_HOOKS)
#error "FreeRTOS/XTENSA XEA2 interrupt/exception hooks require <= 32 interrupts."
#endif
#if (!XT_SIMULATOR) && (!XT_BOARD)
#error Either XT_SIMULATOR or XT_BOARD must be defined.
#endif
/*
Name of RTOS (for messages).
*/
#define XT_RTOS_NAME FreeRTOS
#define UNUSED(x) ((void)(x))
/*
Check some Xtensa configuration requirements and report error if not met.
Error messages can be customize to the RTOS port.
*/
#if !XCHAL_HAVE_XEA2 && !XCHAL_HAVE_XEA3
#error "FreeRTOS/Xtensa requires XEA2 or XEA3 exception architecture."
#endif
/*******************************************************************************
RTOS CALLOUT MACROS MAPPED TO RTOS PORT-SPECIFIC FUNCTIONS.
Define callout macros used in generic Xtensa code to interact with the RTOS.
The macros are simply the function names for use in calls from assembler code.
Some of these functions may call back to generic functions in xtensa_context.h .
*******************************************************************************/
/*
Inform RTOS of entry into an interrupt handler that will affect it.
Allows RTOS to manage switch to any system stack and count nesting level.
Called after minimal context has been saved, with interrupts disabled.
RTOS port can call0 _xt_context_save to save the rest of the context.
May only be called from assembly code by the 'call0' instruction.
*/
// void XT_RTOS_INT_ENTER(void)
#define XT_RTOS_INT_ENTER _frxt_int_enter
/*
Inform RTOS of completion of an interrupt handler, and give control to
RTOS to perform thread/task scheduling, switch back from any system stack
and restore the context, and return to the exit dispatcher saved in the
stack frame at XT_STK_EXIT. RTOS port can call0 _xt_context_restore
to save the context saved in XT_RTOS_INT_ENTER via _xt_context_save,
leaving only a minimal part of the context to be restored by the exit
dispatcher. This function does not return to the place it was called from.
May only be called from assembly code by the 'call0' instruction.
*/
// void XT_RTOS_INT_EXIT(void)
#define XT_RTOS_INT_EXIT _frxt_int_exit
/*
Inform RTOS of the occurrence of a tick timer interrupt.
If RTOS has no tick timer, leave XT_RTOS_TIMER_INT undefined.
May be coded in or called from C or assembly, per ABI conventions.
RTOS may optionally define XT_TICK_PER_SEC in its own way (eg. macro).
*/
// void XT_RTOS_TIMER_INT(void)
#define XT_RTOS_TIMER_INT _frxt_timer_int
#define XT_TICK_PER_SEC configTICK_RATE_HZ
/*
Return in a15 the base address of the co-processor state save area for the
thread that triggered a co-processor exception, or 0 if no thread was running.
The state save area is structured as defined in xtensa_context.h and has size
XT_CP_SIZE. Co-processor instructions should only be used in thread code, never
in interrupt handlers or the RTOS kernel. May only be called from assembly code
and by the 'call0' instruction. A result of 0 indicates an unrecoverable error.
The implementation may use only a2-4, a15 (all other regs must be preserved).
*/
// void* XT_RTOS_CP_STATE(void)
#define XT_RTOS_CP_STATE _xt_task_coproc_state
/*******************************************************************************
HOOKS TO DYNAMICALLY INSTALL INTERRUPT AND EXCEPTION HANDLERS PER LEVEL.
This Xtensa RTOS port provides hooks for dynamically installing exception
and interrupt handlers to facilitate automated testing where each test
case can install its own handler for user exceptions and each interrupt
priority (level). This consists of an array of function pointers indexed
by interrupt priority, with index 0 being the user exception handler hook.
Each entry in the array is initially 0, and may be replaced by a function
pointer of type XT_INTEXC_HOOK. A handler may be uninstalled by installing 0.
The handler for low and medium priority obeys ABI conventions so may be coded
in C. For the exception handler, the cause is the contents of the EXCCAUSE
reg, and the result is -1 if handled, else the cause (still needs handling).
For interrupt handlers, the cause is a mask of pending enabled interrupts at
that level, and the result is the same mask with the bits for the handled
interrupts cleared (those not cleared still need handling). This allows a test
case to either pre-handle or override the default handling for the exception
or interrupt level (see xtensa_vectors.S).
High priority handlers (including NMI) must be coded in assembly, are always
called by 'call0' regardless of ABI, must preserve all registers except a0,
and must not use or modify the interrupted stack. The hook argument 'cause'
is not passed and the result is ignored, so as not to burden the caller with
saving and restoring a2 (it assumes only one interrupt per level - see the
discussion in high priority interrupts in xtensa_vectors.S). The handler
therefore should be coded to prototype 'void h(void)' even though it plugs
into an array of handlers of prototype 'unsigned h(unsigned)'.
To enable interrupt/exception hooks, compile the RTOS with '-DXT_INTEXC_HOOKS'.
*******************************************************************************/
#define XT_INTEXC_HOOK_NUM (1 + XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI)
#ifndef __ASSEMBLER__
typedef unsigned (*XT_INTEXC_HOOK)(unsigned cause);
extern volatile XT_INTEXC_HOOK _xt_intexc_hooks[XT_INTEXC_HOOK_NUM];
#endif
/*******************************************************************************
Define a debug exception cause for XEA2. This is needed to reserve an entry
in the exception table for a user-defined handler, and also for registering
a user handler. Must use one of the unused/reserved exception causes.
*******************************************************************************/
#ifndef EXCCAUSE_DEBUG
#define EXCCAUSE_DEBUG 21
#endif
/*******************************************************************************
CONVENIENCE INCLUSIONS.
Ensures RTOS specific files need only include this one Xtensa-generic header.
These headers are included last so they can use the RTOS definitions above.
*******************************************************************************/
#include "xtensa_context.h"
#ifdef XT_RTOS_TIMER_INT
#include "xtensa_timer.h"
#endif
/*******************************************************************************
Xtensa Port Version.
*******************************************************************************/
#define XTENSA_PORT_VERSION 3.00
#define XTENSA_PORT_VERSION_STRING "3.00"
#define XT_IRQ_LOCK_LEVEL XCHAL_EXCM_LEVEL
#endif /* XTENSA_RTOS_H */
@@ -0,0 +1,190 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* XTENSA INFORMATION FOR RTOS TICK TIMER AND CLOCK FREQUENCY
*
* This header contains definitions and macros for use primarily by Xtensa
* RTOS assembly coded source files. It includes and uses the Xtensa hardware
* abstraction layer (HAL) to deal with config specifics. It may also be
* included in C source files.
*
* Edit this file to modify timer selection and to specify clock frequency and
* tick duration to match timer interrupt to the real-time tick duration.
*
* If the RTOS has no timer interrupt, then there is no tick timer and the
* clock frequency is irrelevant, so all of these macros are left undefined
* and the Xtensa core configuration need not have a timer.
*/
#ifndef XTENSA_TIMER_H
#define XTENSA_TIMER_H
#ifdef __ASSEMBLER__
#include <xtensa/coreasm.h>
#endif
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
#include <FreeRTOSConfig.h>
/*
Select timer to use for periodic tick, and determine its interrupt number
and priority. User may specify a timer by defining XT_TIMER_INDEX with -D,
in which case its validity is checked (it must exist in this core and must
not be on a high priority interrupt - an error will be reported in invalid).
Otherwise select the highest priority low or medium priority interrupt timer
available.
*/
#if XCHAL_NUM_TIMERS == 0
#error "This Xtensa configuration is unsupported, it has no timers."
#else
#ifndef XT_TIMER_INDEX
#if XCHAL_TIMER3_INTERRUPT != XTHAL_TIMER_UNCONFIGURED
#if XCHAL_INT_LEVEL(XCHAL_TIMER3_INTERRUPT) <= XCHAL_EXCM_LEVEL
#undef XT_TIMER_INDEX
#define XT_TIMER_INDEX 3
#undef XT_TIMER_LEVEL
#define XT_TIMER_LEVEL XCHAL_INT_LEVEL(XCHAL_TIMER3_INTERRUPT)
#endif
#endif
#if XCHAL_TIMER2_INTERRUPT != XTHAL_TIMER_UNCONFIGURED
#if XCHAL_INT_LEVEL(XCHAL_TIMER2_INTERRUPT) <= XCHAL_EXCM_LEVEL && \
(!defined(XT_TIMER_INDEX) || \
XCHAL_INT_LEVEL(XCHAL_TIMER2_INTERRUPT) > XT_TIMER_LEVEL)
#undef XT_TIMER_INDEX
#define XT_TIMER_INDEX 2
#undef XT_TIMER_LEVEL
#define XT_TIMER_LEVEL XCHAL_INT_LEVEL(XCHAL_TIMER2_INTERRUPT)
#endif
#endif
#if XCHAL_TIMER1_INTERRUPT != XTHAL_TIMER_UNCONFIGURED
#if XCHAL_INT_LEVEL(XCHAL_TIMER1_INTERRUPT) <= XCHAL_EXCM_LEVEL && \
(!defined(XT_TIMER_INDEX) || \
XCHAL_INT_LEVEL(XCHAL_TIMER1_INTERRUPT) > XT_TIMER_LEVEL)
#undef XT_TIMER_INDEX
#define XT_TIMER_INDEX 1
#undef XT_TIMER_LEVEL
#define XT_TIMER_LEVEL XCHAL_INT_LEVEL(XCHAL_TIMER1_INTERRUPT)
#endif
#endif
#if XCHAL_TIMER0_INTERRUPT != XTHAL_TIMER_UNCONFIGURED
#if XCHAL_INT_LEVEL(XCHAL_TIMER0_INTERRUPT) <= XCHAL_EXCM_LEVEL && \
(!defined(XT_TIMER_INDEX) || \
XCHAL_INT_LEVEL(XCHAL_TIMER0_INTERRUPT) > XT_TIMER_LEVEL)
#undef XT_TIMER_INDEX
#define XT_TIMER_INDEX 0
#undef XT_TIMER_LEVEL
#define XT_TIMER_LEVEL XCHAL_INT_LEVEL(XCHAL_TIMER0_INTERRUPT)
#endif
#endif
#endif
#ifndef XT_TIMER_INDEX
#error "There is no suitable timer in this Xtensa configuration."
#endif
#define XT_CCOMPARE (CCOMPARE + XT_TIMER_INDEX)
#define XT_TIMER_INTNUM XCHAL_TIMER_INTERRUPT(XT_TIMER_INDEX)
#define XT_TIMER_INTPRI XCHAL_INT_LEVEL(XT_TIMER_INTNUM)
/*
* Xtensa LX8 supports up to 128 interrupts whereas LX7 supports up to 32.
*
* Redefine the legacy XT_TIMER_INTEN (which limited the interrupt number to
* be in the lower 32 or lower 64 bits depending on data type) as two defines
* describing the INTEN bit within a 32-bit component and a 4-block component,
* where each block contains a group of 32 interrupt bits.
*
* Name changes are intentional such that any dependencies are caught at
* compile-time and are found regardless of timer configuration.
*/
#define XT_TIMER_INTEN_BIT (1 << (XT_TIMER_INTNUM & 31U))
#define XT_TIMER_INTEN_BLK (XT_TIMER_INTNUM >> 5)
#if XT_TIMER_INTNUM == XTHAL_TIMER_UNCONFIGURED
#error "The timer selected by XT_TIMER_INDEX does not exist in this core."
#elif XT_TIMER_INTPRI > XCHAL_EXCM_LEVEL
#error "The timer interrupt cannot be high priority (use medium or low)."
#endif
#endif /* XCHAL_NUM_TIMERS */
/*
Set processor clock frequency, used to determine clock divisor for timer tick.
User should BE SURE TO ADJUST THIS for the Xtensa platform being used.
If using a supported board via the board-independent API defined in xtbsp.h,
this may be left undefined and frequency and tick divisor will be computed
and cached during run-time initialization.
NOTE ON SIMULATOR:
Under the Xtensa instruction set simulator, the frequency can only be estimated
because it depends on the speed of the host and the version of the simulator.
Also because it runs much slower than hardware, it is not possible to achieve
real-time performance for most applications under the simulator. A frequency
too low does not allow enough time between timer interrupts, starving threads.
To obtain a more convenient but non-real-time tick duration on the simulator,
compile with the option "-DXT_SIMULATOR".
Adjust this frequency to taste (it's not real-time anyway!).
*/
#if defined(XT_SIMULATOR) && !defined(XT_CLOCK_FREQ)
#define XT_CLOCK_FREQ configCPU_CLOCK_HZ
#endif
#if !defined(XT_CLOCK_FREQ) && !defined(XT_BOARD)
#error "XT_CLOCK_FREQ must be defined for the target platform."
#endif
/*
Default number of timer "ticks" per second (default 100 for 10ms tick).
RTOS may define this in its own way (if applicable) in xtensa_rtos.h.
User may redefine this to an optimal value for the application, either by
editing this here or in xtensa_rtos.h, or compiling with the option
"-DXT_TICK_PER_SEC=<value>" where <value> is a suitable number.
*/
#ifndef XT_TICK_PER_SEC
#define XT_TICK_PER_SEC configTICK_RATE_HZ /* 10 ms tick = 100 ticks per second */
#endif
/*
Derivation of clock divisor for timer tick and interrupt (one per tick).
*/
#ifdef XT_CLOCK_FREQ
#define XT_TICK_DIVISOR (XT_CLOCK_FREQ / XT_TICK_PER_SEC)
#endif
#ifndef __ASSEMBLER__
extern unsigned _xt_tick_divisor;
extern void _xt_tick_divisor_init(void);
#endif
#endif /* XTENSA_TIMER_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,478 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* Copyright (C) 2015-2024 Cadence Design Systems, Inc.
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/**************************************************************************/
/* */
/* DESCRIPTION */
/* */
/* Xtensa exception and interrupt dispatch for XEA3. */
/* */
/* Interrupt handlers and user exception handlers support interaction */
/* with the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT */
/* before and after calling the user's specific interrupt handlers. */
/* */
/* Users can install application-specific interrupt handlers by calling */
/* xt_set_interrupt_handler(). These handlers can be written in C and */
/* must follow the C calling convention. The handler table is indexed by */
/* the interrupt number. Each handler may be provided with an argument. */
/* */
/* Users can install application-specific exception handlers in the */
/* same way, by calling xt_set_exception_handler(). One handler slot is */
/* provided for each exception type. Note that some exceptions are */
/* handled by the porting layer itself, and cannot be taken over by */
/* application code. These are the alloca, syscall, and coprocessor */
/* exceptions. */
/* */
/* Exception handlers can be written in C, and must follow C calling */
/* convention. Each handler is passed a pointer to an exception frame as */
/* its single argument. The exception frame is created on the stack and */
/* holds the saved context of the thread that took the exception. If the */
/* handler returns, the context will be restored and the instruction */
/* that caused the exception will be retried. If the handler makes any */
/* changes to the saved state in the exception frame, the changes will */
/* be applied when restoring the context. */
/* */
/**************************************************************************/
#include <xtensa/config/core.h>
#include <xtensa/coreasm.h>
#if XCHAL_HAVE_XEA3
#include "xtensa_context.h"
#if (XCHAL_HW_VERSION < XTENSA_HWVERSION_RH_2016_2)
#error Xtensa HW earlier than RH_2016.2 not supported.
#endif
#if XT_STK_XTRA_SZ
#define EXCFRAME_XTRA_OFF(field) (XT_STK_XTRA_SZ + (field))
#else
#define EXCFRAME_XTRA_OFF(field) (field)
#endif
//-----------------------------------------------------------------------------
// The entry point vectors are common for call0 and windowed configurations.
//-----------------------------------------------------------------------------
.extern _DoubleExceptionHandler
.extern _xtos_exc_dispatch
.section .DispatchVector.text, "ax"
#if XCHAL_HAVE_VECBASE
.align 64 // 64-byte alignment needed when vecbase
#else // is relocatable
.align 4
#endif
.org 0 // Fixed offset for Reset Vector
.global _DispatchVector
.weak _DispatchVector
_DispatchVector:
j _JumpToResetHandler
.org 3 // Reserved
.local _Reserved1
_Reserved1:
ill
.org 6 // Fixed offset for Double Exception Vector
.global _DoubleExceptionVector
.weak _DoubleExceptionVector
_DoubleExceptionVector:
j _DoubleExceptionHandler
.org 9 // Reserved
.local _Reserved2
_Reserved2:
ill
//-----------------------------------------------------------------------------
// Start of dispatch code.
//-----------------------------------------------------------------------------
.org 12 // Fixed offset for Tailchain entry point
.global _xt_dispatch
_xt_dispatch:
#ifdef __XTENSA_CALL0_ABI__
// NOTE: for call0, a15 is expected to be holding the previous stack pointer
// when we get to the Tailchain segment.
s32si.x4 a2, a15 // Select interrupt, a2 <- (intnum << 2)
movi a0, 0
l32dis.it a0, a0 // a0 <- wrapper addr (handler_table[0])
s32stk a9, a15, 96 // Set new stack pointer
#if XT_STK_XTRA_SZ
addi a1, a1, -XT_STK_XTRA_SZ // Adjust for extra save area
#endif
s32dis.h a0, a0 // Jump to handler if interrupt else fall through
// Note this also clears local exclusive monitor
#else // windowed
s32si.x4 a10, a1 // Select interrupt, a10 <- (intnum << 2)
movi a8, 0
l32dis.it a8, a8 // a8 <- wrapper addr (handler_table[0])
s32stk a9, a1, 96 // Set new stack pointer
#if XT_STK_XTRA_SZ
addi a9, a9, -XT_STK_XTRA_SZ // Adjust for extra save area
#endif
s32dis.h a8, a8 // Jump to handler if interrupt else fall through
// Note this also clears local exclusive monitor
#endif // __XTENSA_CALL0_ABI__
.Lexit:
j _xt_exit
#ifndef __XTENSA_CALL0_ABI__
.org 36 // Fixed offset for Underflow segment
.global _xt_underflow
_xt_underflow:
l32e a8, a1, -64 // a8 <- [a1-32]
l32e a9, a1, -64 // a9 <- [a1-28]
l32e a10, a1, -64 // a10 <- [a1-24]
l32e a11, a1, -64 // a11 <- [a1-20]
l32e a12, a1, -64 // a12 <- [a1-16]
l32e a13, a1, -64 // a13 <- [a1-12]
l32e a14, a1, -64 // a14 <- [a1-8]
l32e a15, a1, -64 // a15 <- [a1-4] ; Return (branch to EPC)
#endif
.org 60 // Fixed offset for Save/Overflow segment
.global _xt_save
_xt_save:
#ifdef __XTENSA_CALL0_ABI__
s32e a0, a1, -64 // [a1-64] <- a0
s32e a2, a1, -48 // [a1-56] <- a2 ; a2 <- EPC
s32e a3, a1, -64 // [a1-52] <- a3
s32e a4, a1, -64 // [a1-48] <- a4
s32e a5, a1, -64 // [a1-44] <- a5
s32e a6, a1, -64 // [a1-40] <- a6
s32e a7, a1, -64 // [a1-36] <- a7
#else
.global _xt_overflow
_xt_overflow:
#endif
s32e a8, a1, -52 // [a1-32] <- a8 ; a8 <- ExcVAddr
s32e a9, a1, -28 // [a1-28] <- a9 ; a9 <- PS/SAR
s32e a10, a1, -48 // [a1-24] <- a10 ; a10 <- EPC
s32e a11, a1, -24 // [a1-20] <- a11 ; a11 <- ExcCause
s32e a12, a1, -44 // [a1-16] <- a12 ; a12 <- LBEG
s32e a13, a1, -40 // [a1-12] <- a13 ; a13 <- LEND
s32e a14, a1, -36 // [a1-8] <- a14 ; a14 <- LCOUNT
s32e a15, a1, -32 // [a1-4] <- a15 ; a15 <- a1
// If Overflow then return (branch to EPC)
_xt_entry:
s32e a8, a1, -4 // [a1-68] <- a8 (ExcVAddr)
s32e a11, a1, -8 // [a1-72] <- a11 (ExcCause)
#if XCHAL_HAVE_LOOPS
s32e a12, a1, -20 // [a1-84] <- a12 (LBEG)
s32e a13, a1, -24 // [a1-88] <- a13 (LEND)
s32e a14, a1, -28 // [a1-92] <- a14 (LCOUNT)
#endif
#if XCHAL_HAVE_EXCLUSIVE
movi a12, 0
getex a12
s32e a12, a1, -32 // [a1-96] <- a12 (ATOMCTL)
#endif
#if (defined __XTENSA_WINDOWED_ABI__) && portUSING_MPU_WRAPPERS
s32e a2, a1, -36 // [a1-100] <- a2 (syscall arg)
#endif
j 1f // make room for literals
.align 4
.literal_position
.Le1:
.word xt_exception_table
1:
// Call OS-specific code for additional work to be done. Stay on interruptee's
// stack in case more saves are required into stack frame.
// NOTE: OS-specific code can use a8, a12-a14, (+a2-a7: call0, a15: windowed).
// ALL other registers must be preserved.
XT_RTOS_INT_ENTER
// This sequence checks the interrupt controller and loads the interrupt
// number if available, and also loads the wrapper handler address.
// If there is an interrupt, execution will branch to the wrapper which
// will then forward to the correct handler.
// All this happens only if there is a pending interrupt. If not, execution
// falls through to exception handling.
#ifdef __XTENSA_CALL0_ABI__
s32si.x4 a2, a1 // [a1-80] <- a2 (EPC) ; a2 <- (intnum << 2)
movi a0, 0
l32dis.it a0, a0 // a0 <- wrapper addr (handler_table[0])
s32stk a9, a1, 96 // [a1-76] <- a9 (PS/SAR) ; a1 = a1-96
#if XT_STK_XTRA_SZ
addi a1, a1, -XT_STK_XTRA_SZ // Adjust for extra save area
#endif
s32dis.h a0, a0 // Jump to handler if interrupt else fall through
#else // windowed
s32si.x4 a10, a1 // [a1-80] <- a10 (EPC) ; a10 <- (intnum << 2)
movi a8, 0
l32dis.it a8, a8 // a8 <- wrapper addr (handler_table[0])
// Exception frame size is 96 bytes for Windowed,
// plus an extra 4 bytes if MPU wrappers are used (for XT_STK_A2)
s32stk a9, a1, 96 // [a1-76] <- a9 (PS/SAR) ; a9 = a1-96
#if portUSING_MPU_WRAPPERS && XT_STK_XTRA_SZ
addi a9, a9, -XT_STK_XTRA_SZ-4 // Adjust for extra save area + XT_STK_A2
#elif XT_STK_XTRA_SZ
addi a9, a9, -XT_STK_XTRA_SZ // Adjust for extra save area
#elif portUSING_MPU_WRAPPERS
addi a9, a9, -4 // Adjust for XT_STK_A2
#endif
s32dis.h a8, a8 // Jump to handler if interrupt else fall through
#endif // __XTENSA_CALL0_ABI__
// At this point we have:
// (note window has rotated for windowed ABI)
// a0 holds return address (Tailchain+3)
// For call0:
// a11 holds ExcCause, also saved in [oldsp - 72]
// a15 holds exception SP, a1 points to exception frame
// For windowed:
// a3 holds ExcCause, also saved in [oldsp - 72]
// a1 holds new SP
// New SP + XT_STK_XTRA_SZ points to exception frame
.global _xt_exception
_xt_exception:
#ifdef __XTENSA_CALL0_ABI__
mov a3, a11 // Copy exception cause to a3
#endif
extui a4, a3, 0, 4 // Extract exception cause
#if portUSING_MPU_WRAPPERS
// Handle "RAISE PRIVILEGE" SYSCALL in assembly;
// others use registered syscall handler
bnei a4, EXCCAUSE_SYSCALL, .L_xt_proc_full_exc
l32i a2, a1, EXCFRAME_XTRA_OFF(XT_STK_A2) // ExcFrame + A2 offset
beqi a2, SYSCALL_raise_priv, do_raise_priv
beqi a2, SYSCALL_is_priv, do_is_priv
.L_xt_proc_full_exc:
#endif /* portUSING_MPU_WRAPPERS */
l32r a2, .Le1 // Load exc table address
addx4 a2, a4, a2 // Index into exc table
l32i a4, a2, 0 // Load handler address
#if XT_STK_XTRA_SZ
addi a2, a1, XT_STK_XTRA_SZ // Argument = Exception frame ptr
#else
mov a2, a1 // Argument = Exception frame ptr
#endif
jx a4 // Return directly from handler
#if portUSING_MPU_WRAPPERS
.align 4
do_is_priv:
l32i a2, a1, EXCFRAME_XTRA_OFF(XT_STK_PS) // ExcFrame + PS offset
movi a4, 1
extui a2, a2, PS_RING_SHIFT, 1
xor a2, a2, a4
j privilege_return
// raise privilege syscall
do_raise_priv:
l32i a4, a1, EXCFRAME_XTRA_OFF(XT_STK_PS) // ExcFrame + PS offset
bbsi a4, PS_RING_SHIFT, raise_privilege
movi a2, 1
privilege_return:
l32i a4, a1, EXCFRAME_XTRA_OFF(XT_STK_PC) // ExcFrame + PC offset
addi a4, a4, 3 // ++PC (3-byte syscall instruction)
s32i a4, a1, EXCFRAME_XTRA_OFF(XT_STK_PC) // ExcFrame + PC offset
s32i a2, a1, EXCFRAME_XTRA_OFF(XT_STK_A2) // Syscall return value
#ifdef __XTENSA_CALL0_ABI__
ret.n // Emulate end of handler
#else
retw.n // Emulate end of handler
#endif
raise_privilege:
movi a2, ~PS_RING_MASK // PS.RING = 0
and a4, a4, a2
s32i a4, a1, EXCFRAME_XTRA_OFF(XT_STK_PS) // ExcFrame + PS offset
movi a2, 0
j privilege_return
#endif /* portUSING_MPU_WRAPPERS */
// Exit/restore sequence
.global _xt_exit
_xt_exit:
#if portUSING_MPU_WRAPPERS
mov a8, a0
call0 _xt_mpu_restore
mov a0, a8
#endif
#ifdef __XTENSA_CALL0_ABI__
mov a1, a15 // Restore stack pointer
#endif
// Run OS-specific code to determine what to restore.
// Interrupts will remain disabled through this sequence.
// WARNING: stack pointer may change within this macro
// so all restores off the stack must happen afterwards.
XT_RTOS_INT_EXIT
.global _xt_restore
_xt_restore:
// Some loads must happen before DISPST = Restore, as these
// will not be accessible via L32E once DISPST = Restore.
#if !(defined __XTENSA_CALL0_ABI__) && portUSING_MPU_WRAPPERS
l32e a2, a1, -36 // a2 <- [a1-100] (syscall return)
#endif
#if XCHAL_HAVE_EXCLUSIVE
l32e a12, a1, -32 // a12 <- [a1-96] (ATOMCTL)
getex a12
#endif
l32e a10, a1, -12 // a10 <- [a1-76] (PS/SAR)
l32e a12, a1, -20 // a12 <- [a1-84] (LBEG)
l32e a13, a1, -24 // a13 <- [a1-88] (LEND)
l32e a14, a1, -28 // a14 <- [a1-92] (LCOUNT)
l32dis.epc a11, a1 // a11 <- [a1-80] (EPC)
// If interrupt goto tailchain else fall through
#ifdef __XTENSA_CALL0_ABI__
l32e a0, a1, -64 // a0 <- [a1-64]
l32e a2, a1, -64 // a2 <- [a1-56]
l32e a3, a1, -64 // a3 <- [a1-52]
l32e a4, a1, -64 // a4 <- [a1-48]
l32e a5, a1, -64 // a5 <- [a1-44]
l32e a6, a1, -64 // a6 <- [a1-40]
l32e a7, a1, -64 // a7 <- [a1-36]
#endif
// Important: the following restrictions must be observed:
// 1) The LCOUNT register must be restored after LBEG/LEND.
// 2) There must be at least 3 instructions between the LCOUNT
// restore and the last L32E (the one that branches).
l32e a12, a1, -44 // LBEG <- a12, a12 <- [a1-16]
l32e a13, a1, -40 // LEND <- a13, a13 <- [a1-12]
l32e a14, a1, -36 // LCOUNT <- a14, a14 <- [a1-8]
l32e a8, a1, -64 // a8 <- [a1-32]
l32e a9, a1, -64 // a9 <- [a1-28]
l32e a10, a1, -60 // PS/SAR <- a10, a10 <- [a1-24]
l32e a11, a1, -48 // EPC <- a11, a11 <- [a1-20]
l32e a15, a1, -64 // a15 <- [a1-4], Branch to EPC if no interrupt
// If interrupt, branch to Tailchain
//-----------------------------------------------------------------------------
// Branch to reset handler code from here. Use CALL0 as a branch, will expand
// to CALLX0 if needed when built with the -mlongcalls option.
//-----------------------------------------------------------------------------
.align 4
.local _JumpToResetHandler
_JumpToResetHandler:
call0 _ResetHandler
//-----------------------------------------------------------------------------
// Scheduler interrupt handler. Triggered by context switch. At this time only
// useful for windowed ABI to spill register windows.
//-----------------------------------------------------------------------------
.align 4
.global xt_sched_handler
xt_sched_handler:
#ifdef __XTENSA_WINDOWED_ABI__
entry a1, 32
ssai 1
spillw
retw
#else
ret
#endif
//-----------------------------------------------------------------------------
// Symbols for the debugger to use in identifying interrupt / exception frames.
//-----------------------------------------------------------------------------
.global _Interrupt
.set _Interrupt, _xt_dispatch - 1
.size _Interrupt, 1
.global _GeneralException
.set _GeneralException, _xt_dispatch
.size _GeneralException, .Lexit - _xt_dispatch
// Size of interrupt frame
.global _InterruptFrameSize
.set _InterruptFrameSize, XT_STK_FRMSZ
// Register save offset in interrupt frame
.global _InterruptRegisterSaveOffset
.set _InterruptRegisterSaveOffset, 0x20
// EPC offset in interrupt frame
.global _InterruptEPCSaveOffset
.set _InterruptEPCSaveOffset, 0x10
// Size of exc frame
.global _GeneralExceptionFrameSize
.set _GeneralExceptionFrameSize, XT_STK_FRMSZ
// Register save offset in exc frame
.global _GeneralExceptionRegisterSaveOffset
.set _GeneralExceptionRegisterSaveOffset, 0x20
// EPC offset in exc frame
.global _GeneralExceptionEPCSaveOffset
.set _GeneralExceptionEPCSaveOffset, 0x10
#endif // XCHAL_HAVE_XEA3