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
@@ -0,0 +1,379 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* 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 <stdlib.h>
#include <avr/interrupt.h>
#include "porthardware.h"
#include "FreeRTOS.h"
#include "task.h"
/*-----------------------------------------------------------
* Implementation of functions defined in portable.h for the AVR port.
*----------------------------------------------------------*/
/* Start tasks with interrupts enables. */
#define portFLAGS_INT_ENABLED ( ( StackType_t ) 0x80 )
/*-----------------------------------------------------------*/
/* We require the address of the pxCurrentTCB variable, but don't want to know
* any details of its type. */
typedef void RTOS_TCB_t;
extern volatile RTOS_TCB_t * volatile pxCurrentTCB;
/*-----------------------------------------------------------*/
/*
* Perform hardware setup to enable ticks from timer.
*/
static void prvSetupTimerInterrupt( void );
/*-----------------------------------------------------------*/
/*
* See header file for description.
*/
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
{
uint16_t usAddress;
/*lint -e950 -e611 -e923 Lint doesn't like this much - but nothing I can do about it. */
/* Place a few bytes of known values on the bottom of the stack.
* This is just useful for debugging. Uncomment if needed. */
/* *pxTopOfStack = 0x11; */
/* pxTopOfStack--; */
/* *pxTopOfStack = 0x22; */
/* pxTopOfStack--; */
/* *pxTopOfStack = 0x33; */
/* pxTopOfStack--; */
/* The start of the task code will be popped off the stack last, so place
* it on first. */
usAddress = ( uint16_t ) pxCode;
*pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
pxTopOfStack--;
usAddress >>= 8;
*pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
pxTopOfStack--;
/* Next simulate the stack as if after a call to portSAVE_CONTEXT().
* portSAVE_CONTEXT places the flags on the stack immediately after r0
* to ensure the interrupts get disabled as soon as possible, and so ensuring
* the stack use is minimal should a context switch interrupt occur. */
*pxTopOfStack = ( StackType_t ) 0x00; /* R0 */
pxTopOfStack--;
*pxTopOfStack = portFLAGS_INT_ENABLED;
pxTopOfStack--;
#if defined(__AVR_HAVE_RAMPZ__)
*pxTopOfStack = (StackType_t)0x00; /* RAMPZ */
pxTopOfStack--;
#endif
/* Now the remaining registers. The compiler expects R1 to be 0. */
*pxTopOfStack = ( StackType_t ) 0x00; /* R1 */
/* Leave R2 - R23 untouched */
pxTopOfStack -= 23;
/* Place the parameter on the stack in the expected location. */
usAddress = ( uint16_t ) pvParameters;
*pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
pxTopOfStack--;
usAddress >>= 8;
*pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
/* Leave register R26 - R31 untouched */
pxTopOfStack -= 7;
/*lint +e950 +e611 +e923 */
return pxTopOfStack;
}
/*-----------------------------------------------------------*/
BaseType_t xPortStartScheduler( void )
{
/* Setup the hardware to generate the tick. */
prvSetupTimerInterrupt();
/* Restore the context of the first task that is going to run. */
portRESTORE_CONTEXT();
/* Simulate a function call end as generated by the compiler. We will now
* jump to the start of the task the context of which we have just restored. */
asm volatile ( "ret" );
/* Should not get here. */
return pdTRUE;
}
/*-----------------------------------------------------------*/
void vPortEndScheduler( void )
{
/* vPortEndScheduler is not implemented in this port. */
}
/*-----------------------------------------------------------*/
/*
* Manual context switch. The first thing we do is save the registers so we
* can use a naked attribute.
*/
void vPortYield( void ) __attribute__( ( naked ) );
void vPortYield( void )
{
portSAVE_CONTEXT();
vTaskSwitchContext();
portRESTORE_CONTEXT();
asm volatile ( "ret" );
}
/*-----------------------------------------------------------*/
/*
* Context switch function used by the tick. This must be identical to
* vPortYield() from the call to vTaskSwitchContext() onwards. The only
* difference from vPortYield() is the tick count is incremented as the
* call comes from the tick ISR.
*/
void vPortYieldFromTick( void ) __attribute__( ( naked ) );
void vPortYieldFromTick( void )
{
portSAVE_CONTEXT();
if( xTaskIncrementTick() != pdFALSE )
{
vTaskSwitchContext();
}
portRESTORE_CONTEXT();
asm volatile ( "reti" );
}
/*-----------------------------------------------------------*/
/*
* Setup timer to generate a tick interrupt.
*/
static void prvSetupTimerInterrupt( void )
{
/* Configure low-power timer used in tickless mode */
#if (configUSE_TICKLESS_IDLE == 1)
RTC_INIT();
#endif
TICK_init();
}
/*-----------------------------------------------------------*/
#if (configUSE_PREEMPTION == 1)
/*
* Tick ISR for preemptive scheduler. We can use a naked attribute as
* the context is saved at the start of vPortYieldFromTick(). The tick
* count is incremented after the context is saved.
*/
ISR( TICK_INT_vect, ISR_NAKED )
{
/* Clear tick interrupt flag. */
CLR_INT( INT_FLAGS, INT_MASK );
vPortYieldFromTick();
asm volatile ( "reti" );
}
#else /* if configUSE_PREEMPTION == 1 */
/*
* Tick ISR for the cooperative scheduler. All this does is increment the
* tick count. We don't need to switch context, this can only be done by
* manual calls to taskYIELD();
*/
ISR( TICK_INT_vect )
{
/* Clear tick interrupt flag. */
INT_FLAGS = INT_MASK;
xTaskIncrementTick();
}
#endif /* if configUSE_PREEMPTION == 1 */
#if (configUSE_TICKLESS_IDLE == 1)
volatile uint32_t RTC_OVF_Count = 0;
ISR(RTC_CNT_vect)
{
if (RTC.INTFLAGS & RTC_OVF_bm )
{
RTC_OVF_Count += 0x00010000;
RTC.INTFLAGS = (RTC_OVF_bm);
}
if (RTC.INTFLAGS & RTC_CMP_bm )
{
RTC.INTFLAGS = (RTC_CMP_bm);
//Disable compare interrupt
RTC.INTCTRL &= (0xFF ^ RTC_CMP_bm);
}
}
static uint32_t ulGetExternalTime(void)
{
uint32_t time_rtc;
while (RTC.STATUS & RTC_CNTBUSY_bm)
{
;
}
time_rtc = RTC.CNT;
time_rtc += RTC_OVF_Count;
return time_rtc;
}
static void vSetWakeTimeInterrupt(uint16_t xExpectedIdleTime)
{
uint32_t rtc_cnt_time;
/* compute the required */
rtc_cnt_time = RTC_TICKS_TO_COUNTS(xExpectedIdleTime);
rtc_cnt_time += ulGetExternalTime();
while (RTC.STATUS & RTC_CMPBUSY_bm)
{
;
}
RTC.CMP = (rtc_cnt_time & 0xFFFF);
//Enable compare interrupt
RTC.INTCTRL |= RTC_CMP_bm;
}
/* Define the function that is called by portSUPPRESS_TICKS_AND_SLEEP(). */
__attribute__((weak)) void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)
{
eSleepModeStatus eSleepStatus;
uint32_t ulLowPowerTimeBeforeSleep, ulLowPowerTimeAfterSleep;
/* Read the current time from a time source that will remain operational
while the microcontroller is in a low power state. */
ulLowPowerTimeBeforeSleep = ulGetExternalTime();
/* Stop the timer that is generating the tick interrupt. */
TICK_TMR_STOP();
/* Enter a critical section that will not effect interrupts bringing the MCU
out of sleep mode. */
portDISABLE_INTERRUPTS();
/* Ensure it is still ok to enter the sleep mode. */
eSleepStatus = eTaskConfirmSleepModeStatus();
if (eSleepStatus == eAbortSleep)
{
/* A task has been moved out of the Blocked state since this macro was
* executed, or a context switch is being held pending. Do not enter a
* sleep state. Restart the tick and exit the critical section. */
TICK_TMR_START();
portENABLE_INTERRUPTS();
}
else
{
if (eSleepStatus == eNoTasksWaitingTimeout)
{
/* A user definable macro that allows application code to be inserted
* here. Such application code can be used to minimize power consumption
* further by turning off IO, peripheral clocks, the Flash, etc. */
configPRE_PWR_DOWN_PROCESSING();
/* There are no running state tasks and no tasks that are blocked with a
* time out. Assuming the application does not care if the tick time slips
* with respect to calendar time then enter a deep sleep that can only be
* woken by (in this demo case) the user button being pushed on the
* Curiosity Nano board. If the application does require the tick time
* to keep better track of the calender time then the PIT peripheral can be
* used to make rough adjustments. */
portSET_MODE_AND_SLEEP(SLEEP_MODE_PWR_DOWN);
/* A user definable macro that allows application code to be inserted
* here. Such application code can be used to reverse any actions taken
* by the configPRE_STOP_PROCESSING() */
configPOST_PWR_DOWN_PROCESSING();
}
else
{
/* Configure an interrupt to bring the microcontroller out of its low
* power state at the time the kernel next needs to execute. The
* interrupt must be generated from a source that remains operational
* when the microcontroller is in a low power state. */
vSetWakeTimeInterrupt(xExpectedIdleTime);
/* Allow the application to define some pre-sleep processing. This is
* the standard configPRE_SLEEP_PROCESSING() macro as described on the
* FreeRTOS.org website. */
configPRE_SLEEP_PROCESSING(xExpectedIdleTime);
/* Enter the low power state. */
portSET_MODE_AND_SLEEP(SLEEP_MODE_STANDBY);
/* Determine how long the microcontroller was actually in a low power
* state for, which will be less than xExpectedIdleTime if the
* microcontroller was brought out of low power mode by an interrupt
* other than that configured by the vSetWakeTimeInterrupt() call.
* Note that the scheduler is suspended before
* portSUPPRESS_TICKS_AND_SLEEP() is called, and resumed when
* portSUPPRESS_TICKS_AND_SLEEP() returns. Therefore no other tasks will
* execute until this function completes. */
ulLowPowerTimeAfterSleep = ulGetExternalTime();
/* Allow the application to define some post sleep processing. This is
* the standard configPOST_SLEEP_PROCESSING() macro, as described on the
* FreeRTOS.org website.
* It can be used to reverse the actions of configPRE_SLEEP_PROCESSING(),
* and in so doing, return the microcontroller back to its fully operational state */
configPOST_SLEEP_PROCESSING(xExpectedIdleTime);
/* Correct the kernels tick count to account for the time the
* microcontroller spent in its low power state. */
vTaskStepTick(RTC_COUNTS_TO_TICKS(ulLowPowerTimeAfterSleep - ulLowPowerTimeBeforeSleep));
// vTaskStepTick(xExpectedIdleTime);
}
/* Exit the critical section - it might be possible to do this immediately
* after the SET_MODE_AND_SLEEP calls. */
portENABLE_INTERRUPTS();
/* Restart the timer that is generating the tick interrupt. */
TICK_TMR_START();
}
}
#endif
@@ -0,0 +1,221 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* 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 PORTHARDWARE_H
#define PORTHARDWARE_H
#include "FreeRTOSConfig.h"
/*-----------------------------------------------------------*/
#define CLR_INT( FLAG_REG, FLAG_MASK ) \
asm volatile ( \
"push r16\n\t" \
"ldi r16, %1\n\t" \
"sts %0, r16\n\t" \
"pop r16\n\t" \
: \
: "i" ( _SFR_MEM_ADDR( FLAG_REG ) ), "i" ( ( uint8_t ) ( FLAG_MASK ) ) \
);
#if ( configUSE_TIMER_INSTANCE == 0 )
#define TICK_INT_vect TCB0_INT_vect
#define INT_FLAGS TCB0_INTFLAGS
#define INT_MASK TCB_CAPT_bm
#define TICK_init() \
{ \
TCB0.CCMP = configCPU_CLOCK_HZ / configTICK_RATE_HZ; \
TCB0.INTCTRL = TCB_CAPT_bm; \
TCB0.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_STOP() TCB0.CTRLA = 0x00;
#define TICK_TMR_START() \
{ \
TCB0.INTFLAGS = TCB_CAPT_bm; \
TCB0.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_READ() TCB0.CNT
#define TICK_INT_READY() (TCB0.INTCTRL & TCB_CAPT_bm)
#elif ( configUSE_TIMER_INSTANCE == 1 )
#define TICK_INT_vect TCB1_INT_vect
#define INT_FLAGS TCB1_INTFLAGS
#define INT_MASK TCB_CAPT_bm
#define TICK_init() \
{ \
TCB1.CCMP = configCPU_CLOCK_HZ / configTICK_RATE_HZ; \
TCB1.INTCTRL = TCB_CAPT_bm; \
TCB1.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_STOP() TCB1.CTRLA = 0x00;
#define TICK_TMR_START() \
{ \
TCB1.INTFLAGS = TCB_CAPT_bm; \
TCB1.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_READ() TCB1.CNT
#define TICK_INT_READY() (TCB1.INTCTRL & TCB_CAPT_bm)
#elif ( configUSE_TIMER_INSTANCE == 2 )
#define TICK_INT_vect TCB2_INT_vect
#define INT_FLAGS TCB2_INTFLAGS
#define INT_MASK TCB_CAPT_bm
#define TICK_init() \
{ \
TCB2.CCMP = configCPU_CLOCK_HZ / configTICK_RATE_HZ; \
TCB2.INTCTRL = TCB_CAPT_bm; \
TCB2.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_STOP() TCB2.CTRLA = 0x00;
#define TICK_TMR_START() \
{ \
TCB2.INTFLAGS = TCB_CAPT_bm; \
TCB2.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_READ() TCB2.CNT
#define TICK_INT_READY() (TCB2.INTCTRL & TCB_CAPT_bm)
#elif ( configUSE_TIMER_INSTANCE == 3 )
#define TICK_INT_vect TCB3_INT_vect
#define INT_FLAGS TCB3_INTFLAGS
#define INT_MASK TCB_CAPT_bm
#define TICK_init() \
{ \
TCB3.CCMP = configCPU_CLOCK_HZ / configTICK_RATE_HZ; \
TCB3.INTCTRL = TCB_CAPT_bm; \
TCB3.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_STOP() TCB3.CTRLA = 0x00;
#define TICK_TMR_START() \
{ \
TCB3.INTFLAGS = TCB_CAPT_bm; \
TCB3.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_READ() TCB3.CNT
#define TICK_INT_READY() (TCB3.INTCTRL & TCB_CAPT_bm)
#elif ( configUSE_TIMER_INSTANCE == 4 )
#define TICK_INT_vect TCB4_INT_vect
#define INT_FLAGS TCB4_INTFLAGS
#define INT_MASK TCB_CAPT_bm
#define TICK_init() \
{ \
TCB4.CCMP = configCPU_CLOCK_HZ / configTICK_RATE_HZ; \
TCB4.INTCTRL = TCB_CAPT_bm; \
TCB4.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_STOP() TCB4.CTRLA = 0x00;
#define TICK_TMR_START() \
{ \
TCB4.INTFLAGS = TCB_CAPT_bm; \
TCB4.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_READ() TCB4.CNT
#define TICK_INT_READY() (TCB4.INTCTRL & TCB_CAPT_bm)
#elif ( configUSE_TIMER_INSTANCE == 5 )
#if ( configUSE_TICKLESS_IDLE == 1 )
/* RTC is not supported as tick timer when tickless mode is used */
#error Invalid timer setting.
#else
#define TICK_INT_vect RTC_CNT_vect
#define INT_FLAGS RTC_INTFLAGS
#define INT_MASK RTC_OVF_bm
/* Hertz to period for RTC setup */
#define RTC_PERIOD_HZ( x ) ( 32768 * ( ( 1.0 / x ) ) )
#define TICK_init() \
{ \
while( RTC.STATUS > 0 ) {; } \
RTC.CTRLA = RTC_PRESCALER_DIV1_gc | 1 << RTC_RTCEN_bp; \
RTC.PER = RTC_PERIOD_HZ( configTICK_RATE_HZ ); \
RTC.INTCTRL |= 1 << RTC_OVF_bp; \
}
#undef TICK_TMR_STOP
#undef TICK_TMR_START
#undef TICK_TMR_READ
#undef TICK_INT_READY
#endif
#else /* if ( configUSE_TIMER_INSTANCE == 0 ) */
#undef TICK_INT_vect
#undef INT_FLAGS
#undef INT_MASK
#undef TICK_init
#undef TICK_TMR_STOP
#undef TICK_TMR_START
#undef TICK_TMR_READ
#undef TICK_INT_READY
#error Invalid timer setting.
#endif /* if ( configUSE_TIMER_INSTANCE == 0 ) */
/*-----------------------------------------------------------*/
#if ( configUSE_TICKLESS_IDLE == 1 )
#define LOW_POWER_CLOCK (32768UL)
#define RTC_TICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define RTC_TICKS_TO_COUNTS(tick_cnt) (uint32_t)(((float)(tick_cnt * LOW_POWER_CLOCK)/configTICK_RATE_HZ) - 0.5)
#define RTC_COUNTS_TO_TICKS(counts) (uint32_t)((float)((counts * 1.0) * configTICK_RATE_HZ)/LOW_POWER_CLOCK )
#define RTC_INIT() \
{ \
while( RTC.STATUS > 0 ) {; } \
RTC.PER = 0xFFFF; \
RTC.CMP = 0x3FFF; \
RTC.CNT = 0; \
RTC.INTFLAGS = RTC_OVF_bm | RTC_CMP_bm; \
RTC.CTRLA = RTC_RUNSTDBY_bm | RTC_PRESCALER_DIV1_gc | RTC_RTCEN_bm ; \
RTC.INTCTRL = RTC_OVF_bm | RTC_CMP_bm; \
}
#endif
#endif /* PORTHARDWARE_H */
@@ -0,0 +1,265 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* 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 <avr/sleep.h>
/*-----------------------------------------------------------
* 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 char
#define portFLOAT float
#define portDOUBLE double
#define portLONG long
#define portSHORT int
#define portSTACK_TYPE uint8_t
#define portBASE_TYPE char
#define portPOINTER_SIZE_TYPE uint16_t
typedef portSTACK_TYPE StackType_t;
typedef signed char BaseType_t;
typedef unsigned char UBaseType_t;
#if ( configUSE_16_BIT_TICKS == 1 )
typedef uint16_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif
/*-----------------------------------------------------------*/
/* Critical section management. */
#define portENTER_CRITICAL() \
asm volatile ( "in __tmp_reg__, __SREG__" ); \
asm volatile ( "cli" ); \
asm volatile ( "push __tmp_reg__" )
#define portEXIT_CRITICAL() \
asm volatile ( "pop __tmp_reg__" ); \
asm volatile ( "out __SREG__, __tmp_reg__" )
#define portDISABLE_INTERRUPTS() asm volatile ( "cli" ::);
#define portENABLE_INTERRUPTS() asm volatile ( "sei" ::);
/*-----------------------------------------------------------*/
/* Architecture specifics. */
#define portSTACK_GROWTH ( -1 )
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define portBYTE_ALIGNMENT 1
#define portNOP() asm volatile ( "nop" );
/*-----------------------------------------------------------*/
/* Kernel utilities. */
extern void vPortYield( void ) __attribute__( ( naked ) );
#define portYIELD() vPortYield()
/*-----------------------------------------------------------*/
/* 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 )
/* Macros for tickless idle/low power functionality. */
#ifndef portSUPPRESS_TICKS_AND_SLEEP
extern void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime);
#define portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime) vPortSuppressTicksAndSleep(xExpectedIdleTime)
#endif
#ifndef configPRE_PWR_DOWN_PROCESSING
#define configPRE_PWR_DOWN_PROCESSING()
#endif
#ifndef configPOST_PWR_DOWN_PROCESSING
#define configPOST_PWR_DOWN_PROCESSING()
#endif
/*-----------------------------------------------------------*/
/* Helper macros for portSAVE_CONTEXT/ portRESTORE_CONTEXT - common support for Mega-0 and AVR-Dx families */
#if defined(__AVR_HAVE_RAMPZ__)
#define portSAVE_RAMPZ() \
asm volatile("in r0, __RAMPZ__ \n\t" \
"push r0 \n\t");
#define portRESTORE_RAMPZ() \
asm volatile("pop r0 \n\t" \
"out __RAMPZ__, r0 \n\t");
#else
#define portSAVE_RAMPZ()
#define portRESTORE_RAMPZ()
#endif
/* Macro to save all the general purpose registers, the save the stack pointer
* into the TCB.
* The first thing we do is save the flags then disable interrupts. This is to
* guard our stack against having a context switch interrupt after we have already
* pushed the registers onto the stack - causing the 32 registers to be on the
* stack twice.
* r1 is set to zero as the compiler expects it to be thus, however some
* of the math routines make use of R1.
* The interrupts will have been disabled during the call to portSAVE_CONTEXT()
* so we need not worry about reading/writing to the stack pointer. */
#define portSAVE_CONTEXT() \
{ \
asm volatile("push r0 \n\t" \
"in r0, __SREG__ \n\t" \
"cli \n\t" \
"push r0 \n\t"); \
portSAVE_RAMPZ(); \
asm volatile("push r1 \n\t" \
"clr r1 \n\t" \
"push r2 \n\t" \
"push r3 \n\t" \
"push r4 \n\t" \
"push r5 \n\t" \
"push r6 \n\t" \
"push r7 \n\t" \
"push r8 \n\t" \
"push r9 \n\t" \
"push r10 \n\t" \
"push r11 \n\t" \
"push r12 \n\t" \
"push r13 \n\t" \
"push r14 \n\t" \
"push r15 \n\t" \
"push r16 \n\t" \
"push r17 \n\t" \
"push r18 \n\t" \
"push r19 \n\t" \
"push r20 \n\t" \
"push r21 \n\t" \
"push r22 \n\t" \
"push r23 \n\t" \
"push r24 \n\t" \
"push r25 \n\t" \
"push r26 \n\t" \
"push r27 \n\t" \
"push r28 \n\t" \
"push r29 \n\t" \
"push r30 \n\t" \
"push r31 \n\t" \
"lds r26, pxCurrentTCB \n\t" \
"lds r27, pxCurrentTCB + 1 \n\t" \
"in r0, __SP_L__ \n\t" \
"st x+, r0 \n\t" \
"in r0, __SP_H__ \n\t" \
"st x+, r0 \n\t"); \
}
/* Opposite to portSAVE_CONTEXT(). Interrupts will have been disabled during
* the context save so we can write to the stack pointer. */
#define portRESTORE_CONTEXT() \
{ \
asm volatile("lds r26, pxCurrentTCB \n\t" \
"lds r27, pxCurrentTCB + 1 \n\t" \
"ld r28, x+ \n\t" \
"out __SP_L__, r28 \n\t" \
"ld r29, x+ \n\t" \
"out __SP_H__, r29 \n\t" \
"pop r31 \n\t" \
"pop r30 \n\t" \
"pop r29 \n\t" \
"pop r28 \n\t" \
"pop r27 \n\t" \
"pop r26 \n\t" \
"pop r25 \n\t" \
"pop r24 \n\t" \
"pop r23 \n\t" \
"pop r22 \n\t" \
"pop r21 \n\t" \
"pop r20 \n\t" \
"pop r19 \n\t" \
"pop r18 \n\t" \
"pop r17 \n\t" \
"pop r16 \n\t" \
"pop r15 \n\t" \
"pop r14 \n\t" \
"pop r13 \n\t" \
"pop r12 \n\t" \
"pop r11 \n\t" \
"pop r10 \n\t" \
"pop r9 \n\t" \
"pop r8 \n\t" \
"pop r7 \n\t" \
"pop r6 \n\t" \
"pop r5 \n\t" \
"pop r4 \n\t" \
"pop r3 \n\t" \
"pop r2 \n\t" \
"pop r1 \n\t"); \
portRESTORE_RAMPZ(); \
asm volatile("pop r0 \n\t" \
"out __SREG__, r0 \n\t" \
"pop r0 \n\t"); \
}
/*-----------------------------------------------------------*/
#define portSET_MODE_AND_SLEEP(mode) \
{ \
set_sleep_mode(mode); \
sleep_enable(); \
portENABLE_INTERRUPTS(); \
sleep_cpu(); \
portDISABLE_INTERRUPTS(); \
sleep_disable(); \
}
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* PORTMACRO_H */
@@ -0,0 +1,393 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* 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 <stdlib.h>
#include <avr/interrupt.h>
#include "porthardware.h"
#include "FreeRTOS.h"
#include "task.h"
/*-----------------------------------------------------------
* Implementation of functions defined in portable.h for the AVR port.
*----------------------------------------------------------*/
/* Start tasks with interrupts enables. */
#define portFLAGS_INT_ENABLED ( ( StackType_t ) 0x80 )
/*-----------------------------------------------------------*/
/* We require the address of the pxCurrentTCB variable, but don't want to know
* any details of its type. */
typedef void RTOS_TCB_t;
extern volatile RTOS_TCB_t * volatile pxCurrentTCB;
/*-----------------------------------------------------------*/
/*
* Perform hardware setup to enable ticks from timer.
*/
static void prvSetupTimerInterrupt( void );
/*-----------------------------------------------------------*/
/*
* See header file for description.
*/
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
{
uint16_t usAddress;
/*lint -e950 -e611 -e923 Lint doesn't like this much - but nothing I can do about it. */
/* Place a few bytes of known values on the bottom of the stack.
* This is just useful for debugging. Uncomment if needed. */
/* *pxTopOfStack = 0x11; */
/* pxTopOfStack--; */
/* *pxTopOfStack = 0x22; */
/* pxTopOfStack--; */
/* *pxTopOfStack = 0x33; */
/* pxTopOfStack--; */
/* The start of the task code will be popped off the stack last, so place
* it on first. */
usAddress = ( uint16_t ) pxCode;
*pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
pxTopOfStack--;
usAddress >>= 8;
*pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
pxTopOfStack--;
/* Next simulate the stack as if after a call to portSAVE_CONTEXT().
* portSAVE_CONTEXT places the flags on the stack immediately after r0
* to ensure the interrupts get disabled as soon as possible, and so ensuring
* the stack use is minimal should a context switch interrupt occur. */
*pxTopOfStack = ( StackType_t ) 0x00; /* R0 */
pxTopOfStack--;
*pxTopOfStack = portFLAGS_INT_ENABLED;
pxTopOfStack--;
#if defined(__AVR_HAVE_RAMPZ__)
*pxTopOfStack = (StackType_t)0x00; /* RAMPZ */
pxTopOfStack--;
#endif
/* Now the remaining registers. The compiler expects R1 to be 0. */
*pxTopOfStack = ( StackType_t ) 0x00; /* R1 */
/* Leave R2 - R23 untouched */
pxTopOfStack -= 23;
/* Place the parameter on the stack in the expected location. */
usAddress = ( uint16_t ) pvParameters;
*pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
pxTopOfStack--;
usAddress >>= 8;
*pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
/* Leave register R26 - R31 untouched */
pxTopOfStack -= 7;
/*lint +e950 +e611 +e923 */
return pxTopOfStack;
}
/*-----------------------------------------------------------*/
BaseType_t xPortStartScheduler( void )
{
/* Setup the hardware to generate the tick. */
prvSetupTimerInterrupt();
/* Restore the context of the first task that is going to run. */
portRESTORE_CONTEXT();
/* Simulate a function call end as generated by the compiler. We will now
* jump to the start of the task the context of which we have just restored. */
asm volatile ( "ret" );
/* Should not get here. */
return pdTRUE;
}
/*-----------------------------------------------------------*/
void vPortEndScheduler( void )
{
/* vPortEndScheduler is not implemented in this port. */
}
/*-----------------------------------------------------------*/
/*
* Manual context switch. The first thing we do is save the registers so we
* can use a naked attribute.
*/
void vPortYield( void ) __attribute__( ( naked ) );
void vPortYield( void )
{
portSAVE_CONTEXT();
vTaskSwitchContext();
portRESTORE_CONTEXT();
asm volatile ( "ret" );
}
/*-----------------------------------------------------------*/
/*
* Manual context switch callable from ISRs. The first thing
* we do is save the registers so we can use a naked attribute.
*/
void vPortYieldFromISR( void ) __attribute__( ( naked ) );
void vPortYieldFromISR( void )
{
portSAVE_CONTEXT();
vTaskSwitchContext();
portRESTORE_CONTEXT();
asm volatile ( "reti" );
}
/*-----------------------------------------------------------*/
/*
* Context switch function used by the tick. This must be identical to
* vPortYield() from the call to vTaskSwitchContext() onwards. The only
* difference from vPortYield() is the tick count is incremented as the
* call comes from the tick ISR.
*/
void vPortYieldFromTick( void ) __attribute__( ( naked ) );
void vPortYieldFromTick( void )
{
portSAVE_CONTEXT();
if( xTaskIncrementTick() != pdFALSE )
{
vTaskSwitchContext();
}
portRESTORE_CONTEXT();
asm volatile ( "reti" );
}
/*-----------------------------------------------------------*/
/*
* Setup timer to generate a tick interrupt.
*/
static void prvSetupTimerInterrupt( void )
{
/* Configure low-power timer used in tickless mode */
#if (configUSE_TICKLESS_IDLE == 1)
RTC_INIT();
#endif
TICK_init();
}
/*-----------------------------------------------------------*/
#if (configUSE_PREEMPTION == 1)
/*
* Tick ISR for preemptive scheduler. We can use a naked attribute as
* the context is saved at the start of vPortYieldFromTick(). The tick
* count is incremented after the context is saved.
*/
ISR( TICK_INT_vect, ISR_NAKED )
{
/* Clear tick interrupt flag. */
CLR_INT( INT_FLAGS, INT_MASK );
vPortYieldFromTick();
asm volatile ( "reti" );
}
#else /* if configUSE_PREEMPTION == 1 */
/*
* Tick ISR for the cooperative scheduler. All this does is increment the
* tick count. We don't need to switch context, this can only be done by
* manual calls to taskYIELD();
*/
ISR( TICK_INT_vect )
{
/* Clear tick interrupt flag. */
INT_FLAGS = INT_MASK;
xTaskIncrementTick();
}
#endif /* if configUSE_PREEMPTION == 1 */
#if (configUSE_TICKLESS_IDLE == 1)
volatile uint32_t RTC_OVF_Count = 0;
ISR(RTC_CNT_vect)
{
if (RTC.INTFLAGS & RTC_OVF_bm )
{
RTC_OVF_Count += 0x00010000;
RTC.INTFLAGS = (RTC_OVF_bm);
}
if (RTC.INTFLAGS & RTC_CMP_bm )
{
RTC.INTFLAGS = (RTC_CMP_bm);
//Disable compare interrupt
RTC.INTCTRL &= (0xFF ^ RTC_CMP_bm);
}
}
static uint32_t ulGetExternalTime(void)
{
uint32_t time_rtc;
while (RTC.STATUS & RTC_CNTBUSY_bm)
{
;
}
time_rtc = RTC.CNT;
time_rtc += RTC_OVF_Count;
return time_rtc;
}
static void vSetWakeTimeInterrupt(uint16_t xExpectedIdleTime)
{
uint32_t rtc_cnt_time;
/* compute the required */
rtc_cnt_time = RTC_TICKS_TO_COUNTS(xExpectedIdleTime);
rtc_cnt_time += ulGetExternalTime();
while (RTC.STATUS & RTC_CMPBUSY_bm)
{
;
}
RTC.CMP = (rtc_cnt_time & 0xFFFF);
//Enable compare interrupt
RTC.INTCTRL |= RTC_CMP_bm;
}
/* Define the function that is called by portSUPPRESS_TICKS_AND_SLEEP(). */
__attribute__((weak)) void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)
{
eSleepModeStatus eSleepStatus;
uint32_t ulLowPowerTimeBeforeSleep, ulLowPowerTimeAfterSleep;
/* Read the current time from a time source that will remain operational
while the microcontroller is in a low power state. */
ulLowPowerTimeBeforeSleep = ulGetExternalTime();
/* Stop the timer that is generating the tick interrupt. */
TICK_TMR_STOP();
/* Enter a critical section that will not effect interrupts bringing the MCU
out of sleep mode. */
portDISABLE_INTERRUPTS();
/* Ensure it is still ok to enter the sleep mode. */
eSleepStatus = eTaskConfirmSleepModeStatus();
if (eSleepStatus == eAbortSleep)
{
/* A task has been moved out of the Blocked state since this macro was
* executed, or a context switch is being held pending. Do not enter a
* sleep state. Restart the tick and exit the critical section. */
TICK_TMR_START();
portENABLE_INTERRUPTS();
}
else
{
if (eSleepStatus == eNoTasksWaitingTimeout)
{
/* A user definable macro that allows application code to be inserted
* here. Such application code can be used to minimize power consumption
* further by turning off IO, peripheral clocks, the Flash, etc. */
configPRE_PWR_DOWN_PROCESSING();
/* There are no running state tasks and no tasks that are blocked with a
* time out. Assuming the application does not care if the tick time slips
* with respect to calendar time then enter a deep sleep that can only be
* woken by (in this demo case) the user button being pushed on the
* Curiosity Nano board. If the application does require the tick time
* to keep better track of the calender time then the PIT peripheral can be
* used to make rough adjustments. */
portSET_MODE_AND_SLEEP(SLEEP_MODE_PWR_DOWN);
/* A user definable macro that allows application code to be inserted
* here. Such application code can be used to reverse any actions taken
* by the configPRE_STOP_PROCESSING() */
configPOST_PWR_DOWN_PROCESSING();
}
else
{
/* Configure an interrupt to bring the microcontroller out of its low
* power state at the time the kernel next needs to execute. The
* interrupt must be generated from a source that remains operational
* when the microcontroller is in a low power state. */
vSetWakeTimeInterrupt(xExpectedIdleTime);
/* Allow the application to define some pre-sleep processing. This is
* the standard configPRE_SLEEP_PROCESSING() macro as described on the
* FreeRTOS.org website. */
configPRE_SLEEP_PROCESSING(xExpectedIdleTime);
/* Enter the low power state. */
portSET_MODE_AND_SLEEP(SLEEP_MODE_STANDBY);
/* Determine how long the microcontroller was actually in a low power
* state for, which will be less than xExpectedIdleTime if the
* microcontroller was brought out of low power mode by an interrupt
* other than that configured by the vSetWakeTimeInterrupt() call.
* Note that the scheduler is suspended before
* portSUPPRESS_TICKS_AND_SLEEP() is called, and resumed when
* portSUPPRESS_TICKS_AND_SLEEP() returns. Therefore no other tasks will
* execute until this function completes. */
ulLowPowerTimeAfterSleep = ulGetExternalTime();
/* Allow the application to define some post sleep processing. This is
* the standard configPOST_SLEEP_PROCESSING() macro, as described on the
* FreeRTOS.org website.
* It can be used to reverse the actions of configPRE_SLEEP_PROCESSING(),
* and in so doing, return the microcontroller back to its fully operational state */
configPOST_SLEEP_PROCESSING(xExpectedIdleTime);
/* Correct the kernels tick count to account for the time the
* microcontroller spent in its low power state. */
vTaskStepTick(RTC_COUNTS_TO_TICKS(ulLowPowerTimeAfterSleep - ulLowPowerTimeBeforeSleep));
// vTaskStepTick(xExpectedIdleTime);
}
/* Exit the critical section - it might be possible to do this immediately
* after the SET_MODE_AND_SLEEP calls. */
portENABLE_INTERRUPTS();
/* Restart the timer that is generating the tick interrupt. */
TICK_TMR_START();
}
}
#endif
@@ -0,0 +1,183 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* 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 PORTHARDWARE_H
#define PORTHARDWARE_H
#include "FreeRTOSConfig.h"
/*-----------------------------------------------------------*/
#define CLR_INT( FLAG_REG, FLAG_MASK ) \
asm volatile ( \
"push r16\n\t" \
"ldi r16, %1\n\t" \
"sts %0, r16\n\t" \
"pop r16\n\t" \
: \
: "i" ( _SFR_MEM_ADDR( FLAG_REG ) ), "i" ( ( uint8_t ) ( FLAG_MASK ) ) \
);
#if ( configUSE_TIMER_INSTANCE == 0 )
#define TICK_INT_vect TCB0_INT_vect
#define INT_FLAGS TCB0_INTFLAGS
#define INT_MASK TCB_CAPT_bm
#define TICK_init() \
{ \
TCB0.CCMP = configCPU_CLOCK_HZ / configTICK_RATE_HZ; \
TCB0.INTCTRL = TCB_CAPT_bm; \
TCB0.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_STOP() TCB0.CTRLA = 0x00;
#define TICK_TMR_START() \
{ \
TCB0.INTFLAGS = TCB_CAPT_bm; \
TCB0.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_READ() TCB0.CNT
#define TICK_INT_READY() (TCB0.INTCTRL & TCB_CAPT_bm)
#elif ( configUSE_TIMER_INSTANCE == 1 )
#define TICK_INT_vect TCB1_INT_vect
#define INT_FLAGS TCB1_INTFLAGS
#define INT_MASK TCB_CAPT_bm
#define TICK_init() \
{ \
TCB1.CCMP = configCPU_CLOCK_HZ / configTICK_RATE_HZ; \
TCB1.INTCTRL = TCB_CAPT_bm; \
TCB1.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_STOP() TCB1.CTRLA = 0x00;
#define TICK_TMR_START() \
{ \
TCB1.INTFLAGS = TCB_CAPT_bm; \
TCB1.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_READ() TCB1.CNT
#define TICK_INT_READY() (TCB1.INTCTRL & TCB_CAPT_bm)
#elif ( configUSE_TIMER_INSTANCE == 2 )
#define TICK_INT_vect TCB2_INT_vect
#define INT_FLAGS TCB2_INTFLAGS
#define INT_MASK TCB_CAPT_bm
#define TICK_init() \
{ \
TCB2.CCMP = configCPU_CLOCK_HZ / configTICK_RATE_HZ; \
TCB2.INTCTRL = TCB_CAPT_bm; \
TCB2.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_STOP() TCB2.CTRLA = 0x00;
#define TICK_TMR_START() \
{ \
TCB2.INTFLAGS = TCB_CAPT_bm; \
TCB2.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_READ() TCB2.CNT
#define TICK_INT_READY() (TCB2.INTCTRL & TCB_CAPT_bm)
#elif ( configUSE_TIMER_INSTANCE == 3 )
#define TICK_INT_vect TCB3_INT_vect
#define INT_FLAGS TCB3_INTFLAGS
#define INT_MASK TCB_CAPT_bm
#define TICK_init() \
{ \
TCB3.CCMP = configCPU_CLOCK_HZ / configTICK_RATE_HZ; \
TCB3.INTCTRL = TCB_CAPT_bm; \
TCB3.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_STOP() TCB3.CTRLA = 0x00;
#define TICK_TMR_START() \
{ \
TCB3.INTFLAGS = TCB_CAPT_bm; \
TCB3.CTRLA = TCB_ENABLE_bm; \
}
#define TICK_TMR_READ() TCB3.CNT
#define TICK_INT_READY() (TCB3.INTCTRL & TCB_CAPT_bm)
#elif ( configUSE_TIMER_INSTANCE == 4 )
#define TICK_INT_vect RTC_CNT_vect
#define INT_FLAGS RTC_INTFLAGS
#define INT_MASK RTC_OVF_bm
/* Hertz to period for RTC setup */
#define RTC_PERIOD_HZ( x ) ( 32768 * ( ( 1.0 / x ) ) )
#define TICK_init() \
{ \
while( RTC.STATUS > 0 ) {; } \
RTC.CTRLA = RTC_PRESCALER_DIV1_gc | 1 << RTC_RTCEN_bp; \
RTC.PER = RTC_PERIOD_HZ( configTICK_RATE_HZ ); \
RTC.INTCTRL |= 1 << RTC_OVF_bp; \
}
#else /* if ( configUSE_TIMER_INSTANCE == 0 ) */
#undef TICK_INT_vect
#undef INT_FLAGS
#undef INT_MASK
#undef TICK_init
#error Invalid timer setting.
#endif /* if ( configUSE_TIMER_INSTANCE == 0 ) */
#if ( configUSE_TICKLESS_IDLE == 1 )
#define LOW_POWER_CLOCK (32768UL)
#define RTC_TICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define RTC_TICKS_TO_COUNTS(tick_cnt) (uint32_t)(((float)(tick_cnt * LOW_POWER_CLOCK)/configTICK_RATE_HZ) - 0.5)
#define RTC_COUNTS_TO_TICKS(counts) (uint32_t)((float)((counts * 1.0) * configTICK_RATE_HZ)/LOW_POWER_CLOCK )
#define RTC_INIT() \
{ \
while( RTC.STATUS > 0 ) {; } \
RTC.PER = 0xFFFF; \
RTC.CMP = 0x3FFF; \
RTC.CNT = 0; \
RTC.INTFLAGS = RTC_OVF_bm | RTC_CMP_bm; \
RTC.CTRLA = RTC_RUNSTDBY_bm | RTC_PRESCALER_DIV1_gc | RTC_RTCEN_bm ; \
RTC.INTCTRL = RTC_OVF_bm | RTC_CMP_bm; \
}
#endif
/*-----------------------------------------------------------*/
#endif /* PORTHARDWARE_H */
@@ -0,0 +1,267 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* 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 <avr/sleep.h>
/*-----------------------------------------------------------
* 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 char
#define portFLOAT float
#define portDOUBLE double
#define portLONG long
#define portSHORT int
#define portSTACK_TYPE uint8_t
#define portBASE_TYPE char
#define portPOINTER_SIZE_TYPE uint16_t
typedef portSTACK_TYPE StackType_t;
typedef signed char BaseType_t;
typedef unsigned char UBaseType_t;
#if ( configUSE_16_BIT_TICKS == 1 )
typedef uint16_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif
/*-----------------------------------------------------------*/
/* Critical section management. */
#define portENTER_CRITICAL() \
asm volatile ( "in __tmp_reg__, __SREG__" ); \
asm volatile ( "cli" ); \
asm volatile ( "push __tmp_reg__" )
#define portEXIT_CRITICAL() \
asm volatile ( "pop __tmp_reg__" ); \
asm volatile ( "out __SREG__, __tmp_reg__" )
#define portDISABLE_INTERRUPTS() asm volatile ( "cli" ::);
#define portENABLE_INTERRUPTS() asm volatile ( "sei" ::);
/*-----------------------------------------------------------*/
/* Architecture specifics. */
#define portSTACK_GROWTH ( -1 )
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define portBYTE_ALIGNMENT 1
#define portNOP() asm volatile ( "nop" );
/*-----------------------------------------------------------*/
/* Kernel utilities. */
extern void vPortYield( void ) __attribute__( ( naked ) );
#define portYIELD() vPortYield()
extern void vPortYieldFromISR( void ) __attribute__( ( naked ) );
#define portYIELD_FROM_ISR() vPortYieldFromISR()
/*-----------------------------------------------------------*/
/* 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 )
/* Macros for tickless idle/low power functionality. */
#ifndef portSUPPRESS_TICKS_AND_SLEEP
extern void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime);
#define portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime) vPortSuppressTicksAndSleep(xExpectedIdleTime)
#endif
#ifndef configPRE_PWR_DOWN_PROCESSING
#define configPRE_PWR_DOWN_PROCESSING()
#endif
#ifndef configPOST_PWR_DOWN_PROCESSING
#define configPOST_PWR_DOWN_PROCESSING()
#endif
/*-----------------------------------------------------------*/
/* Helper macros for portSAVE_CONTEXT/ portRESTORE_CONTEXT - common support for Mega-0 and AVR-Dx families */
#if defined(__AVR_HAVE_RAMPZ__)
#define portSAVE_RAMPZ() \
asm volatile("in r0, __RAMPZ__ \n\t" \
"push r0 \n\t");
#define portRESTORE_RAMPZ() \
asm volatile("pop r0 \n\t" \
"out __RAMPZ__, r0 \n\t");
#else
#define portSAVE_RAMPZ()
#define portRESTORE_RAMPZ()
#endif
/* Macro to save all the general purpose registers, the save the stack pointer
* into the TCB.
* The first thing we do is save the flags then disable interrupts. This is to
* guard our stack against having a context switch interrupt after we have already
* pushed the registers onto the stack - causing the 32 registers to be on the
* stack twice.
* r1 is set to zero as the compiler expects it to be thus, however some
* of the math routines make use of R1.
* The interrupts will have been disabled during the call to portSAVE_CONTEXT()
* so we need not worry about reading/writing to the stack pointer. */
#define portSAVE_CONTEXT() \
{ \
asm volatile("push r0 \n\t" \
"in r0, __SREG__ \n\t" \
"cli \n\t" \
"push r0 \n\t"); \
portSAVE_RAMPZ(); \
asm volatile("push r1 \n\t" \
"clr r1 \n\t" \
"push r2 \n\t" \
"push r3 \n\t" \
"push r4 \n\t" \
"push r5 \n\t" \
"push r6 \n\t" \
"push r7 \n\t" \
"push r8 \n\t" \
"push r9 \n\t" \
"push r10 \n\t" \
"push r11 \n\t" \
"push r12 \n\t" \
"push r13 \n\t" \
"push r14 \n\t" \
"push r15 \n\t" \
"push r16 \n\t" \
"push r17 \n\t" \
"push r18 \n\t" \
"push r19 \n\t" \
"push r20 \n\t" \
"push r21 \n\t" \
"push r22 \n\t" \
"push r23 \n\t" \
"push r24 \n\t" \
"push r25 \n\t" \
"push r26 \n\t" \
"push r27 \n\t" \
"push r28 \n\t" \
"push r29 \n\t" \
"push r30 \n\t" \
"push r31 \n\t" \
"lds r26, pxCurrentTCB \n\t" \
"lds r27, pxCurrentTCB + 1 \n\t" \
"in r0, __SP_L__ \n\t" \
"st x+, r0 \n\t" \
"in r0, __SP_H__ \n\t" \
"st x+, r0 \n\t"); \
}
/* Opposite to portSAVE_CONTEXT(). Interrupts will have been disabled during
* the context save so we can write to the stack pointer. */
#define portRESTORE_CONTEXT() \
{ \
asm volatile("lds r26, pxCurrentTCB \n\t" \
"lds r27, pxCurrentTCB + 1 \n\t" \
"ld r28, x+ \n\t" \
"out __SP_L__, r28 \n\t" \
"ld r29, x+ \n\t" \
"out __SP_H__, r29 \n\t" \
"pop r31 \n\t" \
"pop r30 \n\t" \
"pop r29 \n\t" \
"pop r28 \n\t" \
"pop r27 \n\t" \
"pop r26 \n\t" \
"pop r25 \n\t" \
"pop r24 \n\t" \
"pop r23 \n\t" \
"pop r22 \n\t" \
"pop r21 \n\t" \
"pop r20 \n\t" \
"pop r19 \n\t" \
"pop r18 \n\t" \
"pop r17 \n\t" \
"pop r16 \n\t" \
"pop r15 \n\t" \
"pop r14 \n\t" \
"pop r13 \n\t" \
"pop r12 \n\t" \
"pop r11 \n\t" \
"pop r10 \n\t" \
"pop r9 \n\t" \
"pop r8 \n\t" \
"pop r7 \n\t" \
"pop r6 \n\t" \
"pop r5 \n\t" \
"pop r4 \n\t" \
"pop r3 \n\t" \
"pop r2 \n\t" \
"pop r1 \n\t"); \
portRESTORE_RAMPZ(); \
asm volatile("pop r0 \n\t" \
"out __SREG__, r0 \n\t" \
"pop r0 \n\t"); \
}
/*-----------------------------------------------------------*/
#define portSET_MODE_AND_SLEEP(mode) \
{ \
set_sleep_mode(mode); \
sleep_enable(); \
portENABLE_INTERRUPTS(); \
sleep_cpu(); \
portDISABLE_INTERRUPTS(); \
sleep_disable(); \
}
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* PORTMACRO_H */
@@ -0,0 +1 @@
This repository contains multiple directories, each individually licensed. Please see the LICENSE file in each directory.
@@ -0,0 +1,28 @@
## FreeRTOS Partner Supported Ports
This repository contains FreeRTOS ports supported by FreeRTOS partners. For a
partner supported FreeRTOS port:
* The code has not been reviewed by the FreeRTOS team.
* FreeRTOS team has not verified the tests results but tests exist and are
reported to be successful by the partner.
* Customer queries as well as bugs are addressed by the partner.
A new FreeRTOS port can be directly contributed by a partner. Follow the steps
below to contribute a FreeRTOS port to this repository:
1. Write the FreeRTOS port for your Compiler and Architecture.
2. Create a project in the [FreeRTOS Partner Supported Demos Repository](https://github.com/FreeRTOS/FreeRTOS-Partner-Supported-Demos/tree/main)
for your hardware for running tests as mentioned [here](https://github.com/FreeRTOS/FreeRTOS/blob/main/FreeRTOS/Demo/ThirdParty/Template/README.md).
3. Make sure all the tests pass. Add the test results in the Pull Request description.
4. Add a README file with the following information:
1. How to use this port?
2. Link to the test project created in Step 2.
3. Any other relevant information.
5. Raise a PR to merge the FreeRTOS port.
6. Raise another PR to merge the test project in the [FreeRTOS-Partner-Supported-Demos Repository](https://github.com/FreeRTOS/FreeRTOS/tree/main/FreeRTOS/Demo/ThirdParty/Community-Supported).
## License
This repository contains multiple directories, each individually licensed. Please see the LICENSE file in each directory.
@@ -0,0 +1,418 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* 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
*
*/
/*
* Copyright (C) 2018-2022 Texas Instruments Incorporated
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Standard includes. */
#include <stdlib.h>
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include <kernel/dpl/ClockP.h>
#include <drivers/hw_include/cslr.h>
#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
/* Check the configuration. */
#if( configMAX_PRIORITIES > 32 )
#error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
#endif
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
/* A critical section is exited when the critical section nesting count reaches
this value. */
#define portNO_CRITICAL_NESTING ( ( size_t ) 0 )
/* Tasks are not created with a floating point context, but can be given a
floating point context after they have been created. A variable is stored as
part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task
does not have an FPU context, or any other value if the task does have an FPU
context. */
#define portNO_FLOATING_POINT_CONTEXT ( ( StackType_t ) 0 )
/* Constants required to setup the initial task context. */
#define portSP_EL0 ( ( StackType_t ) 0x00 )
#define portEL1 ( ( StackType_t ) 0x04 )
#define portINITIAL_PSTATE ( portEL1 | portSP_EL0 )
/* Masks all bits in the APSR other than the mode bits. */
#define portAPSR_MODE_BITS_MASK ( 0x0C )
/*
* Starts the first task executing. This function is necessarily written in
* assembly code so is implemented in portASM.s.
*/
extern void vPortRestoreTaskContext( void );
/* Saved as part of the task context. If ullPortTaskHasFPUContext is non-zero
then floating point context must be saved and restored for the task. */
volatile uint64_t ullPortTaskHasFPUContext[configNUM_CORES] = {pdFALSE};
/* Set to 1 to pend a context switch from an ISR. */
uint64_t ullPortYieldRequired[configNUM_CORES] = { pdFALSE };
/* Counts the interrupt nesting depth. A context switch is only performed if
if the nesting depth is 0. */
uint64_t ullPortInterruptNesting[configNUM_CORES] = { 0 };
/* flag to control tick ISR handling, this is made true just before schedular start */
volatile uint64_t ullPortSchedularRunning = pdFALSE;
#define portPRIORITY_SHIFT 4
__attribute__(( used )) const uint64_t ullMaxAPIPriorityMask = ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT );
/*
* See header file for description.
*/
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
/* Setup the initial stack of the task. The stack is set exactly as
expected by the portRESTORE_CONTEXT() macro. */
/* First all the general purpose registers. */
pxTopOfStack--;
*pxTopOfStack = 0x0101010101010101ULL; /* R1 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
pxTopOfStack--;
*pxTopOfStack = 0x0303030303030303ULL; /* R3 */
pxTopOfStack--;
*pxTopOfStack = 0x0202020202020202ULL; /* R2 */
pxTopOfStack--;
*pxTopOfStack = 0x0505050505050505ULL; /* R5 */
pxTopOfStack--;
*pxTopOfStack = 0x0404040404040404ULL; /* R4 */
pxTopOfStack--;
*pxTopOfStack = 0x0707070707070707ULL; /* R7 */
pxTopOfStack--;
*pxTopOfStack = 0x0606060606060606ULL; /* R6 */
pxTopOfStack--;
*pxTopOfStack = 0x0909090909090909ULL; /* R9 */
pxTopOfStack--;
*pxTopOfStack = 0x0808080808080808ULL; /* R8 */
pxTopOfStack--;
*pxTopOfStack = 0x1111111111111111ULL; /* R11 */
pxTopOfStack--;
*pxTopOfStack = 0x1010101010101010ULL; /* R10 */
pxTopOfStack--;
*pxTopOfStack = 0x1313131313131313ULL; /* R13 */
pxTopOfStack--;
*pxTopOfStack = 0x1212121212121212ULL; /* R12 */
pxTopOfStack--;
*pxTopOfStack = 0x1515151515151515ULL; /* R15 */
pxTopOfStack--;
*pxTopOfStack = 0x1414141414141414ULL; /* R14 */
pxTopOfStack--;
*pxTopOfStack = 0x1717171717171717ULL; /* R17 */
pxTopOfStack--;
*pxTopOfStack = 0x1616161616161616ULL; /* R16 */
pxTopOfStack--;
*pxTopOfStack = 0x1919191919191919ULL; /* R19 */
pxTopOfStack--;
*pxTopOfStack = 0x1818181818181818ULL; /* R18 */
pxTopOfStack--;
*pxTopOfStack = 0x2121212121212121ULL; /* R21 */
pxTopOfStack--;
*pxTopOfStack = 0x2020202020202020ULL; /* R20 */
pxTopOfStack--;
*pxTopOfStack = 0x2323232323232323ULL; /* R23 */
pxTopOfStack--;
*pxTopOfStack = 0x2222222222222222ULL; /* R22 */
pxTopOfStack--;
*pxTopOfStack = 0x2525252525252525ULL; /* R25 */
pxTopOfStack--;
*pxTopOfStack = 0x2424242424242424ULL; /* R24 */
pxTopOfStack--;
*pxTopOfStack = 0x2727272727272727ULL; /* R27 */
pxTopOfStack--;
*pxTopOfStack = 0x2626262626262626ULL; /* R26 */
pxTopOfStack--;
*pxTopOfStack = 0x2929292929292929ULL; /* R29 */
pxTopOfStack--;
*pxTopOfStack = 0x2828282828282828ULL; /* R28 */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x00; /* XZR - has no effect, used so there are an even number of registers. */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) 0x00; /* R30 - procedure call link register. */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_PSTATE;
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) pxCode; /* Exception return address. */
pxTopOfStack--;
/* The task will start with a critical nesting count of 0 as interrupts are
enabled. */
*pxTopOfStack = portNO_CRITICAL_NESTING;
pxTopOfStack--;
/* The task will start without a floating point context. A task that uses
the floating point hardware must call vPortTaskUsesFPU() before executing
any floating point instructions. */
*pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
return pxTopOfStack;
}
BaseType_t xPortStartScheduler( void )
{
uint32_t ulAPSR;
__asm volatile ( "MRS %0, CurrentEL" : "=r" ( ulAPSR ) );
ulAPSR &= portAPSR_MODE_BITS_MASK;
configASSERT( ulAPSR == portEL1 );
if( ulAPSR == portEL1 )
{
/* Interrupts are turned off in the CPU itself to ensure a tick does
not execute while the scheduler is being started. Interrupts are
automatically turned back on in the CPU when the first task starts
executing. */
portDISABLE_INTERRUPTS();
if (0 == portGET_CORE_ID())
{
ullPortSchedularRunning = pdTRUE;
}
/* Start the first task executing. */
vPortRestoreTaskContext();
}
return 0;
}
void vPortEndScheduler( void )
{
/* Not implemented in ports where there is nothing to return to.
Artificially force an assert. */
configASSERT( 0 );
}
void vPortTimerTickHandler()
{
if( ullPortSchedularRunning == pdTRUE )
{
/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE )
{
ullPortYieldRequired[portGET_CORE_ID()] = pdTRUE;
}
}
}
void vPortTaskUsesFPU( void )
{
/* A task is registering the fact that it needs an FPU context. Set the
FPU flag (which is saved as part of the task context). */
ullPortTaskHasFPUContext[portGET_CORE_ID()] = pdTRUE;
/* Consider initialising the FPSR here - but probably not necessary in
AArch64. */
}
/* configCHECK_FOR_STACK_OVERFLOW is set to 1, so the application must provide an
* implementation of vApplicationStackOverflowHook()
*/
void vApplicationStackOverflowHook( TaskHandle_t xTask,
char * pcTaskName )
{
DebugP_logError("[FreeRTOS] Stack overflow detected for task [%s]", pcTaskName);
DebugP_assertNoLog(0);
}
#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configKERNEL_PROVIDED_STATIC_MEMORY == 0 )
static StaticTask_t xIdleTaskTCB;
static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an
* implementation of vApplicationGetIdleTaskMemory() to provide the memory that is
* used by the Idle task.
*/
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize )
{
/* Pass out a pointer to the StaticTask_t structure in which the Idle task’s
* state will be stored.
*/
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
/* Pass out the array that will be used as the Idle task’s stack. */
*ppxIdleTaskStackBuffer = uxIdleTaskStack;
/* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
* Note that, as the array is necessarily of type StackType_t,
* configMINIMAL_STACK_SIZE is specified in words, not bytes.
*/
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
#if ( configNUMBER_OF_CORES > 1 )
static StaticTask_t xPassiveIdleTaskTCBs[ configNUMBER_OF_CORES - 1 ];
static StackType_t uxPassiveIdleTaskStacks[ configNUMBER_OF_CORES - 1 ][ configMINIMAL_STACK_SIZE ];
void vApplicationGetPassiveIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize,
BaseType_t xPassiveIdleTaskIndex )
{
*ppxIdleTaskTCBBuffer = &( xPassiveIdleTaskTCBs[ xPassiveIdleTaskIndex ] );
*ppxIdleTaskStackBuffer = &( uxPassiveIdleTaskStacks[ xPassiveIdleTaskIndex ][ 0 ] );
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
#endif /* #if ( configNUMBER_OF_CORES > 1 ) */
static StaticTask_t xTimerTaskTCB;
static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];
/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
* application must provide an implementation of vApplicationGetTimerTaskMemory()
* to provide the memory that is used by the Timer service task.
*/
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize )
{
/* Pass out a pointer to the StaticTask_t structure in which the Timer
* task’s state will be stored.
*/
*ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
/* Pass out the array that will be used as the Timer task’s stack. */
*ppxTimerTaskStackBuffer = uxTimerTaskStack;
/* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
* Note that, as the array is necessarily of type StackType_t,
* configTIMER_TASK_STACK_DEPTH is specified in words, not bytes.
*/
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
#endif
/* This function is called when configUSE_IDLE_HOOK is 1 in FreeRTOSConfig.h */
void vApplicationIdleHook( void )
{
void vApplicationLoadHook();
vApplicationLoadHook();
__asm__ volatile ("wfi");
}
/* This function is called when configUSE_MINIMAL_IDLE_HOOK is 1 in FreeRTOSConfig.h */
void vApplicationMinimalIdleHook( void )
{
void vApplicationLoadHook();
vApplicationLoadHook();
__asm__ volatile ("wfi");
}
/* initialize high resolution timer for CPU and task load calculation */
void vPortConfigTimerForRunTimeStats()
{
/* we assume clock is initialized before the schedular is started */
}
/* return current counter value of high speed counter in units of usecs */
uint32_t uiPortGetRunTimeCounterValue()
{
uint64_t timeInUsecs = ClockP_getTimeUsec();
/* note, there is no overflow protection for this 32b value in FreeRTOS
*
* This value will overflow in
* ((0xFFFFFFFF)/(1000000*60)) minutes ~ 71 minutes
*
* We call vApplicationLoadHook() in idle loop to accumlate the task load into a 64b value.
* The implementation of vApplicationLoadHook() is in source\kernel\freertos\dpl\common\TaskP_freertos.c
*/
return (uint32_t)(timeInUsecs);
}
int32_t Signal_coreIntr( CSL_gic500_gicrRegs *pGic500GicrRegs, uint32_t coreId, uint32_t intrNum )
{
if ( coreId < configNUM_CORES )
{
if ( intrNum < HWIP_GICD_SGI_PPI_INTR_ID_MAX )
{
uint32_t mask = 1 << intrNum;
pGic500GicrRegs->CORE[coreId].SGI_PPI.ISPENDR0 = mask;
}
}
__asm("dsb sy");
return SystemP_SUCCESS;
}
/* Read 64b value shared between cores */
uint64_t Get_64(volatile uint64_t* x)
{
__asm("dsb sy");
return *x;
}
/* Write 64b value shared between cores */
void Set_64(volatile uint64_t* x, uint64_t value)
{
*x = value;
__asm("dsb sy");
}
@@ -0,0 +1,602 @@
/*
* FreeRTOS Kernel <DEVELOPMENT BRANCH>
* 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
*
*/
/*
* Copyright (C) 2018-2022 Texas Instruments Incorporated
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
.text
/* Variables and functions. */
.extern vTaskSwitchContext
.extern HwiP_intrHandler
.extern ullPortInterruptNesting
.extern ullPortTaskHasFPUContext
.extern ullPortYieldRequired
.global HwiP_IRQ_Handler
.global HwiP_SVC_Handler
.global vPortRestoreTaskContext
.global HwiP_defaultExcHandler
.global HwiP_abortHandler
.macro PUSH_ALL_CPU_REGS stackPtr
stp x0, x1, [\stackPtr, #-16]!
stp x2, x3, [\stackPtr, #-16]!
stp x4, x5, [\stackPtr, #-16]!
stp x6, x7, [\stackPtr, #-16]!
stp x8, x9, [\stackPtr, #-16]!
stp x10, x11, [\stackPtr, #-16]!
stp x12, x13, [\stackPtr, #-16]!
stp x14, x15, [\stackPtr, #-16]!
stp x16, x17, [\stackPtr, #-16]!
stp x18, x19, [\stackPtr, #-16]!
stp x20, x21, [\stackPtr, #-16]!
stp x22, x23, [\stackPtr, #-16]!
stp x24, x25, [\stackPtr, #-16]!
stp x26, x27, [\stackPtr, #-16]!
stp x28, x29, [\stackPtr, #-16]!
stp x30, xzr, [\stackPtr, #-16]!
.endm
.macro POP_ALL_CPU_REGS stackPtr
LDP X30, XZR, [\stackPtr], #0x10
LDP X28, X29, [\stackPtr], #0x10
LDP X26, X27, [\stackPtr], #0x10
LDP X24, X25, [\stackPtr], #0x10
LDP X22, X23, [\stackPtr], #0x10
LDP X20, X21, [\stackPtr], #0x10
LDP X18, X19, [\stackPtr], #0x10
LDP X16, X17, [\stackPtr], #0x10
LDP X14, X15, [\stackPtr], #0x10
LDP X12, X13, [\stackPtr], #0x10
LDP X10, X11, [\stackPtr], #0x10
LDP X8, X9, [\stackPtr], #0x10
LDP X6, X7, [\stackPtr], #0x10
LDP X4, X5, [\stackPtr], #0x10
LDP X2, X3, [\stackPtr], #0x10
LDP X0, X1, [\stackPtr], #0x10
.endm
.macro PUSH_CALLER_SAVE_CPU_REGS stackPtr
stp x0, x1, [\stackPtr, #-16]!
stp x2, x3, [\stackPtr, #-16]!
stp x4, x5, [\stackPtr, #-16]!
stp x6, x7, [\stackPtr, #-16]!
stp x8, x9, [\stackPtr, #-16]!
stp x10, x11, [\stackPtr, #-16]!
stp x12, x13, [\stackPtr, #-16]!
stp x14, x15, [\stackPtr, #-16]!
stp x16, x17, [\stackPtr, #-16]!
stp x18, x19, [\stackPtr, #-16]!
stp x29, x30, [\stackPtr, #-16]!
.endm
.macro POP_CALLER_SAVE_CPU_REGS stackPtr
ldp x29, x30, [\stackPtr], #16
ldp x18, x19, [\stackPtr], #16
ldp x16, x17, [\stackPtr], #16
ldp x14, x15, [\stackPtr], #16
ldp x12, x13, [\stackPtr], #16
ldp x10, x11, [\stackPtr], #16
ldp x8, x9, [\stackPtr], #16
ldp x6, x7, [\stackPtr], #16
ldp x4, x5, [\stackPtr], #16
ldp x2, x3, [\stackPtr], #16
ldp x0, x1, [\stackPtr], #16
.endm
.macro PUSH_CALLER_SAVE_FPU_REGS stackPtr
stp q0, q1, [\stackPtr, #-32]!
stp q2, q3, [\stackPtr, #-32]!
stp q4, q5, [\stackPtr, #-32]!
stp q6, q7, [\stackPtr, #-32]!
stp q8, q9, [\stackPtr, #-32]!
stp q10, q11, [\stackPtr, #-32]!
stp q12, q13, [\stackPtr, #-32]!
stp q14, q15, [\stackPtr, #-32]!
stp q16, q17, [\stackPtr, #-32]!
stp q18, q19, [\stackPtr, #-32]!
stp q20, q21, [\stackPtr, #-32]!
stp q22, q23, [\stackPtr, #-32]!
stp q24, q25, [\stackPtr, #-32]!
stp q26, q27, [\stackPtr, #-32]!
stp q28, q29, [\stackPtr, #-32]!
stp q30, q31, [\stackPtr, #-32]!
.endm
.macro POP_CALLER_SAVE_FPU_REGS stackPtr
ldp q30, q31, [\stackPtr], #32
ldp q28, q29, [\stackPtr], #32
ldp q26, q27, [\stackPtr], #32
ldp q24, q25, [\stackPtr], #32
ldp q22, q23, [\stackPtr], #32
ldp q20, q21, [\stackPtr], #32
ldp q18, q19, [\stackPtr], #32
ldp q16, q17, [\stackPtr], #32
ldp q14, q15, [\stackPtr], #32
ldp q12, q13, [\stackPtr], #32
ldp q10, q11, [\stackPtr], #32
ldp q8, q9, [\stackPtr], #32
ldp q6, q7, [\stackPtr], #32
ldp q4, q5, [\stackPtr], #32
ldp q2, q3, [\stackPtr], #32
ldp q0, q1, [\stackPtr], #32
.endm
.macro portSAVE_CONTEXT
/* Switch to use the EL0 stack pointer. */
MSR SPSEL, #0
/* Save the entire context. */
PUSH_ALL_CPU_REGS SP
/* Save the SPSR. */
MRS X3, SPSR_EL1
MRS X2, ELR_EL1
STP X2, X3, [SP, #-0x10]!
/* Get coreId to X1 */
MRS X1, MPIDR_EL1
AND X1, X1, #0xff
LDR X0, ullPortTaskHasFPUContextConst
CMP X1, #0
BEQ 3f
ADD X0, X0, #8
3:
/* Save the FPU context indicator. */
LDR X2, [X0]
/* Save the FPU context, if any (32 128-bit registers). */
CMP X2, #0
B.EQ 1f
PUSH_CALLER_SAVE_FPU_REGS SP
1:
/* Store the FPU context indicator. */
STR x2, [SP, #-0x10]!
LDR X0, pxCurrentTCBConst
/* Get coreId and choose the corresponding index for core in pxCurrentTCB */
MRS X1, MPIDR_EL1
AND X1, X1, #0xff
CMP X1, #0
B.EQ 2f
ADD X0, X0, #8
2:
LDR X1, [X0]
MOV X0, SP /* Move SP into X0 for saving. */
STR X0, [X1]
/* Switch to use the ELx stack pointer. */
MSR SPSEL, #1
.endm
; /**********************************************************************/
.macro portRESTORE_CONTEXT
/* Switch to use the EL0 stack pointer. */
MSR SPSEL, #0
/* Set the SP to point to the stack of the task being restored. */
LDR X0, pxCurrentTCBConst
/* Get coreId and choose the corresponding index for core in pxCurrentTCB */
MRS X1, MPIDR_EL1
AND X1, X1, #0xff
CMP X1, #0
B.EQ 2f
ADD X0, X0, #8
2:
LDR X1, [X0]
LDR X0, [X1]
MOV SP, X0
LDR X2, [SP], #0x10 /* FPU context. */
/* Set the PMR register to be correct for the current critical nesting
depth. */
MOV X1, #255 /* X1 holds the unmask value. */
MSR s3_0_c4_c6_0, X1 /* Write the mask value to ICCPMR. s3_0_c4_c6_0 is ICC_PMR_EL1. */
DSB SY /* _RB_Barriers probably not required here. */
ISB SY
/* Get coreId */
MRS X1, MPIDR_EL1
AND X1, X1, #0xff
LDR X0, ullPortTaskHasFPUContextConst
BEQ 3f
ADD X0, X0, #8
3:
/* Restore the FPU context indicator. */
STR X2, [X0]
/* Restore the FPU context, if any. */
CMP X2, #0
B.EQ 1f
POP_CALLER_SAVE_FPU_REGS SP
1:
LDP X2, X3, [SP], #0x10 /* SPSR and ELR. */
/* Restore the SPSR. */
MSR SPSR_EL1, X3
/* Restore the ELR. */
MSR ELR_EL1, X2
POP_ALL_CPU_REGS SP
/* Switch to use the ELx stack pointer. _RB_ Might not be required. */
MSR SPSEL, #1
ERET
.endm
.macro VECTOR_ENTRY name
.align 7
\name:
.endm
.global HwiP_gicv3Vectors
.section .vecs, "ax"
.align 11
HwiP_gicv3Vectors:
/*
*************************************************************************
* Exception from currentEL, using SP0
*************************************************************************
*/
VECTOR_ENTRY el1SyncSP0
B HwiP_SVC_Handler
VECTOR_ENTRY el1IrqSP0
B HwiP_IRQ_Handler
VECTOR_ENTRY el1FiqSP0
b el1FiqSP0
VECTOR_ENTRY el1SErrorSP0
PUSH_ALL_CPU_REGS sp /* save all CPU regs */
mrs x0, sp_el0
mrs x1, elr_el1
mrs x2, spsr_el1
mrs x3, esr_el1
stp x0, x1, [sp, #-16]! /* save sp & elr */
stp x2, x3, [sp, #-16]! /* save spsr and esr */
mov x0, sp
mov x1, #1
ldr x2, =HwiP_defaultExcHandler
br x2
/*
*************************************************************************
* Exception from currentEL, using SPx
*************************************************************************
*/
VECTOR_ENTRY el1SyncSPx
B HwiP_SVC_Handler
VECTOR_ENTRY el1IrqSPx
B HwiP_IRQ_Handler
VECTOR_ENTRY el1FiqSPx
b el1FiqSPx
VECTOR_ENTRY el1SErrorSPx
PUSH_ALL_CPU_REGS sp /* save all CPU regs */
add x0, sp, #256 /* compute original SP */
mrs x1, elr_el1
mrs x2, spsr_el1
mrs x3, esr_el1
stp x0, x1, [sp, #-16]! /* save sp & elr */
stp x2, x3, [sp, #-16]! /* save spsr and esr */
mov x0, sp
mov x1, #1
ldr x2, =HwiP_defaultExcHandler
br x2
/*
*************************************************************************
* Exception from lowerEL, all lowerEL using Aarch64
*************************************************************************
*/
VECTOR_ENTRY el0SyncAarch64
b el0SyncAarch64
VECTOR_ENTRY el0IrqAarch64
b el0IrqAarch64
VECTOR_ENTRY el0FiqAarch64
b el0FiqAarch64
VECTOR_ENTRY el0SErrorAarch64
b el0SErrorAarch64
/*
*************************************************************************
* Exception from lowerEL, all lowerEL using Aarch32
*************************************************************************
*/
VECTOR_ENTRY el0SyncAarch32
b el0SyncAarch32
VECTOR_ENTRY el0IrqAarch32
b el0IrqAarch32
VECTOR_ENTRY el0FiqAarch32
b el0FiqAarch32
VECTOR_ENTRY el0SErrorAarch32
b el0SErrorAarch32
/******************************************************************************
* vPortRestoreTaskContext is used to start the scheduler.
*****************************************************************************/
.align 8
.type vPortRestoreTaskContext, %function
vPortRestoreTaskContext:
DSB SY
ISB SY
/* Start the first task. */
portRESTORE_CONTEXT
/******************************************************************************
* handles SVC entry and exit.
*****************************************************************************/
.align 8
.type HwiP_SVC_Handler, %function
HwiP_SVC_Handler:
/* Save the context of the current task and select a new task to run. */
portSAVE_CONTEXT
MRS X0, ESR_EL1
MRS X2, ELR_EL1
LSR X1, X0, #26
CMP X1, #0x15 /* 0x15 = SVC instruction. */
B.NE HwiP_SVC_Abort
MRS x0, mpidr_el1 /* Get CoreID */
and x0, x0, #0xff
BL vTaskSwitchContext
portRESTORE_CONTEXT
HwiP_SVC_Abort:
/* Full ESR is in X0, exception class code is in X1. */
B .
/******************************************************************************
* handles IRQ entry and exit.
*****************************************************************************/
.align 8
.type HwiP_IRQ_Handler, %function
HwiP_IRQ_Handler:
/* save cpu scratch regs */
PUSH_CALLER_SAVE_CPU_REGS SP
/* Save the SPSR and ELR. */
MRS X3, SPSR_EL1
MRS X2, ELR_EL1
STP X2, X3, [SP, #-0x10]!
/* Increment the interrupt nesting counter. */
LDR X5, ullPortInterruptNestingConst
/* Get coreId and choose the corresponding index for core in ullPortInterruptNesting */
MRS X1, MPIDR_EL1
AND X1, X1, #0xff
CMP X1, #0
B.EQ 2f
ADD X5, X5, #8
2:
LDR X1, [X5] /* Old nesting count in X1. */
ADD X6, X1, #1
STR X6, [X5] /* Address of nesting count variable in X5. */
/* Maintain the interrupt nesting information across the function call. */
STP X1, X5, [SP, #-0x10]!
/* Call the C handler. */
BL HwiP_intrHandler
/* Disable interrupts. */
MSR DAIFSET, #2
DSB SY
ISB SY
/* Restore the critical nesting count. */
LDP X1, X5, [SP], #0x10
STR X1, [X5]
/* Has interrupt nesting unwound? */
CMP X1, #0
B.NE Exit_IRQ_No_Context_Switch
/* Is a context switch required? */
LDR X0, ullPortYieldRequiredConst
/* Get coreId and choose the corresponding index for core in ullPortYieldRequired */
MRS X1, MPIDR_EL1
AND X1, X1, #0xff
CMP X1, #0
B.EQ 2f
ADD X0, X0, #8
2:
LDR X1, [X0]
CMP X1, #0
B.EQ Exit_IRQ_No_Context_Switch
/* Reset ullPortYieldRequired to 0. */
MOV X2, #0
STR X2, [X0]
/* Restore volatile registers. */
LDP X4, X5, [SP], #0x10 /* SPSR and ELR. */
MSR SPSR_EL1, X5
MSR ELR_EL1, X4
DSB SY
ISB SY
POP_CALLER_SAVE_CPU_REGS SP
/* Save the context of the current task and select a new task to run. */
portSAVE_CONTEXT
MRS x0, mpidr_el1 /* Get CoreID */
and x0, x0, #0xff
BL vTaskSwitchContext
portRESTORE_CONTEXT
Exit_IRQ_No_Context_Switch:
/* Restore volatile registers. */
LDP X4, X5, [SP], #0x10 /* SPSR and ELR. */
MSR SPSR_EL1, X5
MSR ELR_EL1, X4
DSB SY
ISB SY
POP_CALLER_SAVE_CPU_REGS SP
ERET
/*
* Void HwiP_defaultExcHandler(uint32_t usingEL0Stack);
*/
.section .text.HwiP_defaultExcHandler, "ax"
.func HwiP_defaultExcHandler
HwiP_defaultExcHandler:
mov x19, x0 /* copy 'usingEL0Stack' argument */
mrs x0, spsr_el1
mrs x1, elr_el1
stp x0, x1, [sp, #-16]! /* save spsr_el1 and elr_el1 */
#if defined(__ARM_FP)
mrs x0, fpcr
mrs x1, fpsr
stp x0, x1, [sp, #-16]! /* save fpcr and fpsr */
#endif
/* call dispatch default handler */
mrs x0, elr_el1 /* pass IRP to dispatchIRQC() */
ldr x1, =HwiP_defaultHandler
blr x1
/* returns with interrupts disabled */
#if defined(__ARM_FP)
ldp x0, x1, [sp], #16
msr fpcr, x0
msr fpsr, x1
#endif
ldp x0, x1, [sp], #16
msr spsr_el1, x0
msr elr_el1, x1
#if defined(__ARM_FP)
POP_CALLER_SAVE_FPU_REGS sp /* restore vfp scratch regs */
#endif
POP_CALLER_SAVE_CPU_REGS sp /* restore cpu scratch regs */
eret
.align 8
/*
* int32_t GateSmp_tryLock(uintptr_t gateWord);
*/
.global GateSmp_tryLock
.type GateSmp_tryLock , %function
GateSmp_tryLock:
ldxr w1, [x0]
cmp w1, #1 /* is locked already? */
beq 1f /* if so, leave with fail */
mov w2, #1 /* locked = 1 */
stxr w1, w2, [x0] /* if so attempt to grab it */
cmp w1, #0 /* did we get it? zero is yes */
/* if not, loop while in contention */
bne GateSmp_tryLock
1:
mov w0, w1
dmb sy
ret
/*
* void GateSmp_unlock(uintptr_t gateWord);
*/
.global GateSmp_unlock
.type GateSmp_unlock , %function
GateSmp_unlock:
dmb sy
mov w1, #0
str w1, [x0]
dsb sy
sev /* let everyone know */
ret
pxCurrentTCBConst: .dword pxCurrentTCBs
ullPortTaskHasFPUContextConst: .dword ullPortTaskHasFPUContext
ullPortInterruptNestingConst: .dword ullPortInterruptNesting
ullPortYieldRequiredConst: .dword ullPortYieldRequired
.end
@@ -0,0 +1,294 @@
/*
* FreeRTOS Kernel V10.4.3
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* 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
*
* 1 tab == 4 spaces!
*/
/*
* Copyright (C) 2018-2022 Texas Instruments Incorporated
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PORTMACRO_H
#define PORTMACRO_H
#ifdef __cplusplus
extern "C" {
#endif
#include <kernel/dpl/HwiP.h>
#include <kernel/a53/HwiP_armv8_gic.h>
#include <kernel/a53/common_armv8.h>
/*-----------------------------------------------------------
* 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 char
#define portFLOAT float
#define portDOUBLE double
#define portLONG long
#define portSHORT short
#define portSTACK_TYPE size_t
#define portBASE_TYPE long
typedef portSTACK_TYPE StackType_t;
typedef portBASE_TYPE BaseType_t;
typedef uint64_t UBaseType_t;
typedef uint32_t TickType_t;
#define portMAX_DELAY ( ( TickType_t ) 0xffffffff )
/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
not need to be guarded with a critical section. */
#define portTICK_TYPE_IS_ATOMIC 1
/* Hardware specifics. */
#define portSTACK_GROWTH ( -1 )
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define portBYTE_ALIGNMENT 16
#define portPOINTER_SIZE_TYPE uint64_t
#define portCRITICAL_NESTING_IN_TCB 1
/* Task utilities. */
extern uint64_t ullPortYieldRequired[];
/* Called at the end of an ISR that can cause a context switch. */
#define portEND_SWITCHING_ISR( xSwitchRequired ) \
{ \
\
if( xSwitchRequired != pdFALSE ) \
{ \
ullPortYieldRequired[portGET_CORE_ID()] = pdTRUE; \
} \
}
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
#define portYIELD() __asm volatile ( "SVC 0" ::: "memory" )
/*-----------------------------------------------------------
* Critical section control
*----------------------------------------------------------*/
extern void vTaskEnterCritical( void );
extern void vTaskExitCritical( void );
#define portDISABLE_INTERRUPTS() HwiP_disable()
#define portENABLE_INTERRUPTS() HwiP_enable()
#define portENTER_CRITICAL() vTaskEnterCritical();
#define portEXIT_CRITICAL() vTaskExitCritical();
#define portSET_INTERRUPT_MASK_FROM_ISR() ({ \
uint64_t x = HwiP_disable(); \
vTaskEnterCritical(); \
x; \
})
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) ({ \
vTaskExitCritical(); \
HwiP_restore(x); \
})
/* Task function macros as described on the FreeRTOS.org WEB site. These are
not required for this port but included in case common demo code that uses these
macros is used. */
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters )
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters )
/* Any task that uses the floating point unit MUST call vPortTaskUsesFPU()
before any floating point instructions are executed. */
void vPortTaskUsesFPU( void );
#define portTASK_USES_FLOATING_POINT() vPortTaskUsesFPU()
/* Architecture specific optimisations. */
#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
#endif
#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
/* Store/clear the ready priorities in a bit map. */
#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __builtin_clz( uxReadyPriorities ) )
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
#define portNOP() __asm volatile( "NOP" )
#define portINLINE __inline
#define portMEMORY_BARRIER() __asm volatile( "" ::: "memory" )
/* port for SMP */
#define portGET_CORE_ID() Armv8_getCoreId()
#define portRESTORE_INTERRUPTS( ulState ) HwiP_restore ( ulState )
#define portCHECK_IF_IN_ISR() HwiP_inISR()
/*-----------------------------------------------------------
* Critical section locks
*----------------------------------------------------------*/
#define ISR_LOCK (0u)
#define TASK_LOCK (1u)
#define portRTOS_LOCK_COUNT 2
#define portMAX_CORE_COUNT 2
#define portRELEASE_ISR_LOCK() vPortRecursiveLock(ISR_LOCK, pdFALSE)
#define portGET_ISR_LOCK() vPortRecursiveLock(ISR_LOCK, pdTRUE)
#define portRELEASE_TASK_LOCK() vPortRecursiveLock(TASK_LOCK, pdFALSE)
#define portGET_TASK_LOCK() vPortRecursiveLock(TASK_LOCK, pdTRUE)
/* Interrupt number to interrupt a core for task yield */
#define YIELD_CORE_INTERRUPT_NO (0U)
extern int32_t Signal_coreIntr( CSL_gic500_gicrRegs *pGic500GicrRegs, uint32_t coreId, uint32_t intrNum );
#define portYIELD_CORE( xCoreID ) do { \
Signal_coreIntr((CSL_gic500_gicrRegs *) ( HWIP_GIC_BASE_ADDR + \
CSL_GIC500_GICR_CORE_CONTROL_CTLR(0U)), xCoreID, YIELD_CORE_INTERRUPT_NO); \
}while(0);
uint64_t Get_64(volatile uint64_t* x);
void Set_64(volatile uint64_t* x, uint64_t value);
/* Which core owns the lock */
volatile uint64_t ucOwnedByCore[ portMAX_CORE_COUNT ];
/* Lock count a core owns */
volatile uint64_t ucRecursionCountByLock[ portRTOS_LOCK_COUNT ];
/* Index 0 is used for ISR lock and Index 1 is used for task lock */
uint32_t GateWord[ portRTOS_LOCK_COUNT ];
int32_t GateSmp_tryLock(uint32_t* gateWord);
void GateSmp_unlock(uint32_t* gateWord);
static inline void vPortRecursiveLock(uint32_t ulLockNum, BaseType_t uxAcquire)
{
uint32_t ulCoreNum = portGET_CORE_ID();
uint32_t ulLockBit = 1u << ulLockNum;
/* Lock acquire */
if (uxAcquire)
{
/* Check if spinlock is available */
/* If spinlock is not available check if the core owns the lock */
/* If the core owns the lock wait increment the lock count by the core */
/* If core does not own the lock wait for the spinlock */
if( GateSmp_tryLock( &GateWord[ulLockNum] ) != 0)
{
/* Check if the core owns the spinlock */
if( Get_64(&ucOwnedByCore[ulCoreNum]) & ulLockBit )
{
configASSERT( Get_64(&ucRecursionCountByLock[ulLockNum]) != 255u);
Set_64(&ucRecursionCountByLock[ulLockNum], (Get_64(&ucRecursionCountByLock[ulLockNum])+1));
return;
}
/* Preload the gate word into the cache */
uint32_t dummy = GateWord[ulLockNum];
dummy++;
/* Wait for spinlock */
while( GateSmp_tryLock(&GateWord[ulLockNum]) != 0);
}
/* Add barrier to ensure lock is taken before we proceed */
__asm__ __volatile__ (
"dmb sy"
::: "memory"
);
/* Assert the lock count is 0 when the spinlock is free and is acquired */
configASSERT(Get_64(&ucRecursionCountByLock[ulLockNum]) == 0);
/* Set lock count as 1 */
Set_64(&ucRecursionCountByLock[ulLockNum], 1);
/* Set ucOwnedByCore */
Set_64(&ucOwnedByCore[ulCoreNum], (Get_64(&ucOwnedByCore[ulCoreNum]) | ulLockBit));
}
/* Lock release */
else
{
/* Assert the lock is not free already */
configASSERT( (Get_64(&ucOwnedByCore[ulCoreNum]) & ulLockBit) != 0 );
configASSERT( Get_64(&ucRecursionCountByLock[ulLockNum]) != 0 );
/* Reduce ucRecursionCountByLock by 1 */
Set_64(&ucRecursionCountByLock[ulLockNum], (Get_64(&ucRecursionCountByLock[ulLockNum]) - 1) );
if( !Get_64(&ucRecursionCountByLock[ulLockNum]) )
{
Set_64(&ucOwnedByCore[ulCoreNum], (Get_64(&ucOwnedByCore[ulCoreNum]) & ~ulLockBit));
GateSmp_unlock(&GateWord[ulLockNum]);
/* Add barrier to ensure lock is taken before we proceed */
__asm__ __volatile__ (
"dmb sy"
::: "memory"
);
}
}
}
#ifdef __cplusplus
} /* extern C */
#endif
#endif /* PORTMACRO_H */
@@ -0,0 +1,460 @@
/*
* Copyright (c) 2023 Infineon Technologies AG. All rights reserved.
*
*
* IMPORTANT NOTICE
*
*
* Use of this file is subject to the terms of use agreed between (i) you or
* the company in which ordinary course of business you are acting and (ii)
* Infineon Technologies AG or its licensees. If and as long as no such
* terms of use are agreed, use of this file is subject to following:
*
* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or
* organization obtaining a copy of the software and accompanying
* documentation covered by this license (the "Software") to use, reproduce,
* display, distribute, execute, and transmit the Software, and to prepare
* derivative works of the Software, and to permit third-parties to whom the
* Software is furnished to do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including
* the above license grant, this restriction and the following disclaimer, must
* be included in all copies of the Software, in whole or in part, and all
* derivative works of the Software, unless such copies or derivative works are
* solely in the form of machine-executable object code generated by a source
* language processor.
*
* 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: BSL-1.0
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
/* Prgoram status word macros */
#define portINITIAL_SYSTEM_PSW \
( 0x000008FFUL ) /* Supervisor Mode, MPU Register Set 0 and Call Depth Counting disabled. */
/* Context save area macros */
#define portCSA_FCX_MASK ( 0x000FFFFFUL )
#define portINITIAL_LOWER_PCXI ( 0x00300000UL ) /* Set UL to upper and PIE to 1 */
#define portINITIAL_UPPER_PCXI ( 0x00200000UL ) /* Set UL to lower and PIE to 1 */
#define portNUM_WORDS_IN_CSA ( 16 )
extern volatile unsigned long * pxCurrentTCB;
/* Tick and context switch config */
#define portTICK_COUNT ( configSTM_CLOCK_HZ / configTICK_RATE_HZ )
/* Register defines */
static volatile uint32_t *const pxStm = configSTM;
static volatile uint32_t *const pxStmSrc = configSTM_SRC;
static volatile uint32_t *const pxContextSrc = configCONTEXT_SRC;
#define portSTM_TIM0 0x10
#define portSTM_CMP0 0x30
#define portSTM_COMCON 0x38
#define portSTM_ICR 0x3C
#define portSTM_ISCR 0x40
#define portSTM_OCS 0xE8
#define portSTM_CMCON_MSTART0_OFF 8
#define portSTM_CMCON_MSIZE0_OFF 0
#define portSTM_ICR_CMP0EN_OFF 0
#define portSTM_ICR_CMP0OS_OFF 2
#define portSTM_ISCR_CMP0IRR_OFF 0
static inline void vPortStartFirstTask( void );
static inline void vPortInitContextSrc( void );
static inline void vPortInitTickTimer( void );
static inline void __attribute__( ( always_inline ) ) vPortLoadContext( unsigned char ucCallDepth );
static inline void __attribute__( ( always_inline ) ) vPortSaveContext( unsigned char ucCallDepth );
static inline uint32_t * __attribute__( ( always_inline ) ) pxPortCsaToAddress( uint32_t xCsa );
static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;
/* FreeRTOS required functions */
BaseType_t xPortStartScheduler( void )
{
vPortInitTickTimer();
vPortInitContextSrc();
vPortStartFirstTask();
return 0;
}
void vPortEndScheduler()
{
pxStmSrc[ 0 ] &= ~( 1 << portSRC_SRCR_SRE_OFF );
pxContextSrc[ 0 ] &= ~( 1 << portSRC_SRCR_SRE_OFF );
}
StackType_t *pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
{
uint32_t xLowerCsa = 0, xUpperCsa = 0;
uint32_t * pxUpperCSA = NULL;
uint32_t * pxLowerCSA = NULL;
/* Have to disable interrupts here because the CSAs are going to be
* manipulated. */
__disable();
{
/* DSync to ensure that buffering is not a problem. */
__dsync();
/* Consume two free CSAs. */
xLowerCsa = __mfcr( portCPU_FCX );
pxLowerCSA = pxPortCsaToAddress( xLowerCsa );
if( pxLowerCSA != NULL )
{
/* The Lower Links to the Upper. */
xUpperCsa = pxLowerCSA[ 0 ];
pxUpperCSA = pxPortCsaToAddress( pxLowerCSA[ 0 ] );
}
/* Check that we have successfully reserved two CSAs. */
if( ( pxLowerCSA != NULL ) && ( pxUpperCSA != NULL ) )
{
/* Remove the two consumed CSAs from the free CSA list. */
__mtcr( portCPU_FCX, pxUpperCSA[ 0 ] );
}
else
{
/* Simply trigger a context list depletion trap. */
__asm( "\tsvlcx" );
}
}
__enable();
/* Upper Context. */
memset( pxUpperCSA, 0, portNUM_WORDS_IN_CSA * sizeof( uint32_t ) );
pxUpperCSA[ 2 ] = ( uint32_t ) pxTopOfStack; /* A10; Stack Return aka Stack Pointer */
pxUpperCSA[ 1 ] = portINITIAL_SYSTEM_PSW; /* PSW */
pxUpperCSA[ 0 ] = portINITIAL_UPPER_PCXI;
/* Lower Context. */
memset( pxLowerCSA, 0, portNUM_WORDS_IN_CSA * sizeof( uint32_t ) );
pxLowerCSA[ 8 ] = ( uint32_t ) pvParameters; /* A4; Address Type Parameter Register */
pxLowerCSA[ 1 ] = ( uint32_t ) pxCode; /* A11; Return Address aka RA */
pxLowerCSA[ 0 ] = portINITIAL_LOWER_PCXI | xUpperCsa; /* PCXI pointing to the Upper context. */
/* Initialize the uxCriticalNesting. */
pxTopOfStack--;
*pxTopOfStack = 0;
/* Save the link to the CSA to the top of stack. */
pxTopOfStack--;
*pxTopOfStack = xLowerCsa;
return pxTopOfStack;
}
void __interrupt( configCONTEXT_INTERRUPT_PRIORITY ) __vector_table( configCPU_NR )
vPortSystemContextHandler()
{
/* Disable interrupts to protect section*/
__disable();
/* Do a save, switch, execute */
vPortSaveContext( 0 );
vTaskSwitchContext();
vPortLoadContext( 0 );
__enable();
}
void __interrupt( configTIMER_INTERRUPT_PRIORITY ) __vector_table( configCPU_NR )
vPortSystemTickHandler()
{
unsigned long ulSavedInterruptMask;
BaseType_t xYieldRequired;
/* Increment compare value by tick count */
pxStm[ portSTM_CMP0 >> 2 ] = pxStm[ portSTM_CMP0 >> 2 ] + portTICK_COUNT;
pxStm[ portSTM_ISCR >> 2 ] |= ( 1 << portSTM_ISCR_CMP0IRR_OFF );
/* Check for possible tick drop.
* If the time is beyond the compare value, the next tick will need a complete
* wrap around. The tick count isn't accruate any more. Increase the tick count
* or adapt to execute xTaskIncrementTick multiple times depending on the
* counts missed. */
#if configCPU_STM_DEBUG != 0
configASSERT( ( pxStm[ portSTM_CMP0 >> 2 ] - pxStm[ portSTM_TIM0 >> 2 ] ) <= portTICK_COUNT );
#endif
/* Kernel API calls require Critical Sections. */
ulSavedInterruptMask = portSET_INTERRUPT_MASK_FROM_ISR();
{
/* Increment the Tick. */
xYieldRequired = xTaskIncrementTick();
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( ulSavedInterruptMask );
portYIELD_FROM_ISR( xYieldRequired );
}
void __attribute__( ( noinline ) ) vPortSyscallYield( void );
int
#if configPROVIDE_SYSCALL_TRAP != 0
__trap( configTIMER_INTERRUPT_PRIORITY ) __vector_table( configCPU_NR )
#endif
vPortSyscallHandler( unsigned char id )
{
switch( id )
{
case 0:
vPortSyscallYield();
break;
default:
break;
}
return 0;
}
void vPortInitTickTimer()
{
pxStm[ portSTM_COMCON >> 2 ] =
( 0 << portSTM_CMCON_MSTART0_OFF ) | ( 31 << portSTM_CMCON_MSIZE0_OFF );
pxStm[ portSTM_ICR >> 2 ] &= ~( 1 << portSTM_ICR_CMP0OS_OFF );
pxStmSrc[ 0 ] = ( ( configCPU_NR > 0 ?
configCPU_NR + 1 : configCPU_NR ) << portSRC_SRCR_TOS_OFF ) |
( ( configTIMER_INTERRUPT_PRIORITY ) << portSRC_SRCR_SRPN_OFF );
pxStmSrc[ 0 ] |= ( 1 << portSRC_SRCR_SRE_OFF );
pxStm[ portSTM_CMP0 >> 2 ] = pxStm[ portSTM_TIM0 >> 2 ];
pxStm[ portSTM_ISCR >> 2 ] |= ( 1 << portSTM_ISCR_CMP0IRR_OFF );
pxStm[ portSTM_ICR >> 2 ] |= ( 1 << portSTM_ICR_CMP0EN_OFF );
pxStm[ portSTM_CMP0 >> 2 ] = pxStm[ portSTM_TIM0 >> 2 ] + portTICK_COUNT;
#if configTICK_STM_DEBUG != 0
pxStm[ portSTM_OCS >> 2 ] = 0x12000000;
#endif
}
void vPortInitContextSrc()
{
pxContextSrc[ 0 ] =
( ( configCPU_NR > 0 ?
configCPU_NR + 1 : configCPU_NR ) << portSRC_SRCR_TOS_OFF ) |
( ( configCONTEXT_INTERRUPT_PRIORITY ) << portSRC_SRCR_SRPN_OFF );
pxContextSrc[ 0 ] |= ( 1 << portSRC_SRCR_SRE_OFF );
}
void vPortStartFirstTask()
{
/* Disable interrupts */
__disable();
vPortLoadContext( 0 );
/* Reset the call stack counting, to avoid trap on rfe */
unsigned long ulPsw = __mfcr( portCPU_PSW );
ulPsw &= ~( portCPU_PSW_CSC_MSK );
__mtcr( portCPU_PSW, ulPsw );
/* Load the lower context and upper context through rfe to enable irqs */
__asm( "\trslcx" );
__asm( "\trfe" );
__nop();
__nop();
__nop();
}
void vPortLoadContext( unsigned char ucCallDepth )
{
uint32_t ** ppxTopOfStack;
uint32_t uxLowerCSA;
/* Dsync is required to complete any memory requests */
__dsync();
/* Load the new CSA id from the stack and update the stack pointer */
ppxTopOfStack = ( uint32_t ** ) pxCurrentTCB;
uxLowerCSA = **ppxTopOfStack;
( *ppxTopOfStack )++;
uxCriticalNesting = **ppxTopOfStack;
( *ppxTopOfStack )++;
/* Store the lower context directly if inside the syscall or interrupt,
* else replace the lower context in the call stack. */
if( !ucCallDepth )
{
/* Update the link register */
__mtcr( portCPU_PCXI, uxLowerCSA );
}
else
{
/* Update previous lower context */
uint32_t * pxCSA = pxPortCsaToAddress( __mfcr( portCPU_PCXI ) );
int i;
for(i = 0; i < ucCallDepth - 1; i++)
{
pxCSA = pxPortCsaToAddress( pxCSA[ 0 ] );
}
pxCSA[ 0 ] = uxLowerCSA;
}
}
void vPortSaveContext( unsigned char ucCallDepth )
{
uint32_t ** ppxTopOfStack;
uint32_t * pxLowerCSA, * pxUpperCSA;
uint32_t uxLowerCSA;
/* Dsync is required for save CSA access */
__dsync();
/* Get the current context information. */
uxLowerCSA = __mfcr( portCPU_PCXI );
/* If this function is used inside a function from the syscall or interrupt,
* load the correct context from the call stack */
if( ucCallDepth )
{
uint32_t * pxCSA = pxPortCsaToAddress( uxLowerCSA );
int i;
for(i = 0; i < ucCallDepth - 1; i++)
{
pxCSA = pxPortCsaToAddress( pxCSA[ 0 ] );
}
uxLowerCSA = pxCSA[ 0 ];
}
pxLowerCSA = pxPortCsaToAddress( uxLowerCSA );
pxUpperCSA = pxPortCsaToAddress( pxLowerCSA[ 0 ] );
/* Load the stack pointer */
ppxTopOfStack = ( uint32_t ** ) pxCurrentTCB;
/* Update the stack info in the TCB */
*ppxTopOfStack = ( uint32_t * ) pxUpperCSA[ 2 ];
/* Place ucNestedContext */
( *ppxTopOfStack )--;
**ppxTopOfStack = uxCriticalNesting;
/* Place the lower CSA id on the stack */
( *ppxTopOfStack )--;
**ppxTopOfStack = uxLowerCSA;
}
void vPortSyscallYield()
{
/* Do a save, switch, execute */
vPortSaveContext( configSYSCALL_CALL_DEPTH );
vTaskSwitchContext();
vPortLoadContext( configSYSCALL_CALL_DEPTH );
}
uint32_t * pxPortCsaToAddress( uint32_t xCsa )
{
uint32_t pxCsa;
pxCsa = ( __extru( xCsa, 16, 4 ) << 28 );
pxCsa = __insert( pxCsa, xCsa, 6, 16 );
return ( uint32_t * ) pxCsa;
}
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++;
/* This is not the interrupt safe version of the enter critical function so
* assert() if it is being called from an interrupt context. Only API
* functions that end in "FromISR" can be used in an interrupt. Only assert if
* the critical nesting count is 1 to protect against recursive calls if the
* assert function also uses a critical section. */
if( uxCriticalNesting == 1 )
{
portASSERT_IF_IN_ISR();
}
}
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
/*
* When a task is deleted, it is yielded permanently until the IDLE task
* has an opportunity to reclaim the memory that that task was using.
* Typically, the memory used by a task is the TCB and Stack but in the
* TriCore this includes the CSAs that were consumed as part of the Call
* Stack. These CSAs can only be returned to the Globally Free Pool when
* they are not part of the current Call Stack, hence, delaying the
* reclamation until the IDLE task is freeing the task's other resources.
* This function uses the head of the linked list of CSAs (from when the
* task yielded for the last time) and finds the tail (the very bottom of
* the call stack) and inserts this list at the head of the Free list,
* attaching the existing Free List to the tail of the reclaimed call stack.
*
* NOTE: In highly loaded systems the release of used CSAs might be delayed,
* since it is executed es part of the calling tasks, if the deleted task is
* different from the calling tasks, or as part of the idle task, if the deleted
* tasks is the same as the calling task.
*/
void vPortReclaimCSA( unsigned long ** pxTCB )
{
uint32_t ulHeadCSA, ulFreeCSA;
uint32_t * pulNextCSA;
/* The lower context (PCXI value) to return to the task is stored as the
* current element on the stack. Mask off everything in the PCXI register
* other than the address. */
ulHeadCSA = ( **pxTCB ) & portCSA_FCX_MASK;
/* Iterate over the CSAs that were consumed as part of the task. */
for(pulNextCSA = pxPortCsaToAddress( ulHeadCSA );
( pulNextCSA[ 0 ] & portCSA_FCX_MASK ) != 0;
pulNextCSA = pxPortCsaToAddress( pulNextCSA[ 0 ] ) )
{
/* Mask off everything in the PCXI value other than the address. */
pulNextCSA[ 0 ] &= portCSA_FCX_MASK;
}
__disable();
{
/* Look up the current free CSA head. */
__dsync();
ulFreeCSA = __mfcr( portCPU_FCX );
/* Join the current free onto the tail of what is being reclaimed. */
pulNextCSA[ 0 ] = ulFreeCSA;
/* Move the head of the reclaimed into the Free. */
__mtcr( portCPU_FCX, ulHeadCSA );
}
__enable();
}
void __attribute__( ( noreturn ) ) vPortLoopForever( void )
{
while( 1 )
{
}
}
@@ -0,0 +1,191 @@
/*
* Copyright (c) 2023 Infineon Technologies AG. All rights reserved.
*
*
* IMPORTANT NOTICE
*
*
* Use of this file is subject to the terms of use agreed between (i) you or
* the company in which ordinary course of business you are acting and (ii)
* Infineon Technologies AG or its licensees. If and as long as no such
* terms of use are agreed, use of this file is subject to following:
*
* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or
* organization obtaining a copy of the software and accompanying
* documentation covered by this license (the "Software") to use, reproduce,
* display, distribute, execute, and transmit the Software, and to prepare
* derivative works of the Software, and to permit third-parties to whom the
* Software is furnished to do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including
* the above license grant, this restriction and the following disclaimer, must
* be included in all copies of the Software, in whole or in part, and all
* derivative works of the Software, unless such copies or derivative works are
* solely in the form of machine-executable object code generated by a source
* language processor.
*
* 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: BSL-1.0
*/
#ifndef PORTMACRO_H
#define PORTMACRO_H
#ifdef __cplusplus
extern "C"
#endif
#include "FreeRTOSConfig.h"
#define portCPU_PSW 0xFE04
#define portCPU_PSW_IS_OFF ( 9 )
#define portCPU_PSW_CSC_MSK ( 0x7F )
#define portCPU_ICR 0xFE2C
#define portCPU_ICR_CCPN_OFF ( 0 )
#define portCPU_ICR_CCPN_MSK ( 0x000000FFUL )
#define portCPU_FCX 0xFE38
#define portCPU_PCXI 0xFE00
#define portCPU_CORE_ID 0xFE1C
/* Register defintions */
#define portSRC_SRCR_SRPN_OFF 0
#define portSRC_SRCR_SRE_OFF 10
#define portSRC_SRCR_TOS_OFF 11
#define portSRC_SRCR_SRR_OFF 24
#define portSRC_SRCR_SETR_OFF 26
/* Type definitions. */
#define portCHAR char
#define portSHORT short
#define portLONG long
#define portFLOAT float
#define portDOUBLE double
#define portSTACK_TYPE uint32_t
#define portBASE_TYPE long
#define portPOINTER_SIZE_TYPE uintptr_t
typedef portSTACK_TYPE StackType_t;
typedef long BaseType_t;
typedef unsigned long UBaseType_t;
#if ( configUSE_16_BIT_TICKS == 1 )
typedef unsigned short TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#else
typedef unsigned int TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif
/* FreeRTOS parameters */
#define portTICK_TYPE_IS_ATOMIC 1
#define portSTACK_GROWTH ( -1 )
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define portBYTE_ALIGNMENT 8
#define portCRITICAL_NESTING_IN_TCB 0
/* Attributes */
#define portDONT_DISCARD __attribute__( ( used ) )
#define portNORETURN __attribute__( ( noreturn ) )
/* Instructions */
#define portNOP() __nop()
#define portMEMORY_BARRIER() __dsync()
/* Critical section management */
extern void vPortEnterCritical( void );
extern void vPortExitCritical( void );
#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()
#define portENABLE_INTERRUPTS() vPortSetCCPN( 0 );
#define portDISABLE_INTERRUPTS() vPortSetCCPN( configMAX_API_CALL_INTERRUPT_PRIORITY )
#define portASSERT_IF_IN_ISR() configASSERT( ( __mfcr( portCPU_PSW ) & ( 1 << portCPU_PSW_IS_OFF ) ) == 0 )
#define portCLEAR_INTERRUPT_MASK_FROM_ISR( ulSavedMaskValue ) vPortSetICR( ulSavedMaskValue )
#define portSET_INTERRUPT_MASK_FROM_ISR() xPortSetCCPN( configMAX_API_CALL_INTERRUPT_PRIORITY )
#ifndef configYIELD_SYSCALL_ID
#define configYIELD_SYSCALL_ID 0
#endif
#define portYIELD() __syscall( configYIELD_SYSCALL_ID )
#define portYIELD_FROM_ISR( xHigherPriorityTaskWoken ) \
{ \
const uint32_t xTrigger = ( ( *configCONTEXT_SRC >> portSRC_SRCR_SRR_OFF ) & 0x1 ) != 1 && ( xHigherPriorityTaskWoken != 0 ); \
*configCONTEXT_SRC |= ( xTrigger << portSRC_SRCR_SETR_OFF ); \
\
/* Wait until write request completes to trigger IRQ */ \
__dsync(); \
__isync(); \
}
#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
#endif
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 )
/* Check the configuration. */
#if ( configMAX_PRIORITIES > 32 )
#error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
#endif
/* Store/clear the ready priorities in a bit map. */
#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( ( uint32_t ) __clz( ( uxReadyPriorities ) ) ) )
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
/* Function prototypes */
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters )
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters )
/* TCB handling */
extern void vPortReclaimCSA( unsigned long ** pxTCB );
#define portCLEAN_UP_TCB( pxTCB ) vPortReclaimCSA( ( unsigned long ** ) ( pxTCB ) )
/* ICR & CCPN modifying functions to enable and disable interrupts.
* Only interrupts with a priority lower than
*/
static void __attribute__( ( used, always_inline ) ) vPortSetCCPN( unsigned char ucCCPN )
{
__disable();
__mtcr( portCPU_ICR, ( __mfcr( portCPU_ICR ) & ~portCPU_ICR_CCPN_MSK ) | ( ucCCPN & portCPU_ICR_CCPN_MSK ) );
__enable();
}
static void __attribute__( ( used, always_inline ) ) vPortSetICR( portBASE_TYPE ulICR )
{
__disable();
__mtcr( portCPU_ICR, ( unsigned int ) ulICR );
__enable();
}
static portBASE_TYPE __attribute__( ( used, always_inline ) ) xPortSetCCPN( unsigned char ucCCPN )
{
uint32_t xICR;
__disable();
xICR = __mfcr( portCPU_ICR );
__mtcr( portCPU_ICR, ( xICR & ~portCPU_ICR_CCPN_MSK ) | ( ucCCPN & portCPU_ICR_CCPN_MSK ) );
__enable();
return ( portBASE_TYPE ) xICR;
}
#ifdef __cplusplus
}
#endif
#endif /* PORTMACRO_H */