1 /*
2  * SPDX-FileCopyrightText: 2020 Amazon.com, Inc. or its affiliates
3  * SPDX-FileCopyrightText: 2015-2019 Cadence Design Systems, Inc.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
8  */
9 
10 /*
11  * FreeRTOS Kernel V11.1.0
12  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a copy of
15  * this software and associated documentation files (the "Software"), to deal in
16  * the Software without restriction, including without limitation the rights to
17  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
18  * the Software, and to permit persons to whom the Software is furnished to do so,
19  * subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice shall be included in all
22  * copies or substantial portions of the Software. If you wish to use our Amazon
23  * FreeRTOS name, please do so in a fair use way that does not cause confusion.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
27  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
28  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
29  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31  *
32  * https://www.FreeRTOS.org
33  * https://github.com/FreeRTOS
34  *
35  * 1 tab == 4 spaces!
36  */
37 
38 /*
39  * Copyright (c) 2015-2019 Cadence Design Systems, Inc.
40  *
41  * Permission is hereby granted, free of charge, to any person obtaining
42  * a copy of this software and associated documentation files (the
43  * "Software"), to deal in the Software without restriction, including
44  * without limitation the rights to use, copy, modify, merge, publish,
45  * distribute, sublicense, and/or sell copies of the Software, and to
46  * permit persons to whom the Software is furnished to do so, subject to
47  * the following conditions:
48  *
49  * The above copyright notice and this permission notice shall be included
50  * in all copies or substantial portions of the Software.
51  *
52  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
53  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
54  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
55  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
56  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
57  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
58  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
59  */
60 
61 #include <stdlib.h>
62 #include <string.h>
63 #include <xtensa/config/core.h>
64 
65 #include "xtensa_rtos.h"
66 #include "esp_idf_version.h"
67 
68 #if ( ESP_IDF_VERSION < ESP_IDF_VERSION_VAL( 4, 2, 0 ) )
69     #include "rom/ets_sys.h"
70     #include "esp_panic.h"
71     #include "esp_crosscore_int.h"
72 #else
73     #if CONFIG_IDF_TARGET_ESP32S3
74         #include "esp32s3/rom/ets_sys.h"
75     #elif CONFIG_IDF_TARGET_ESP32S2
76         #include "esp32s2/rom/ets_sys.h"
77     #elif CONFIG_IDF_TARGET_ESP32
78         #include "esp32/rom/ets_sys.h"
79     #endif
80     #include "esp_private/panic_reason.h"
81     #include "esp_debug_helpers.h"
82     #include "esp_private/crosscore_int.h"
83     #include "esp_log.h"
84 #endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0) */
85 #include "soc/cpu.h"
86 
87 #include "FreeRTOS.h"
88 #include "task.h"
89 
90 #include "esp_heap_caps.h"
91 
92 #include "esp_intr_alloc.h"
93 
94 #include "port_systick.h"
95 
96 /* Defined in xtensa_context.S */
97 extern void _xt_coproc_init( void );
98 
99 _Static_assert( tskNO_AFFINITY == CONFIG_FREERTOS_NO_AFFINITY, "incorrect tskNO_AFFINITY value" );
100 
101 /*-----------------------------------------------------------*/
102 
103 extern volatile int port_xSchedulerRunning[ portNUM_PROCESSORS ];
104 unsigned port_interruptNesting[ portNUM_PROCESSORS ] = { 0 }; /* Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit */
105 
106 /*-----------------------------------------------------------*/
107 
108 /* User exception dispatcher when exiting */
109 void _xt_user_exit( void );
110 
111 #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
112 /* Wrapper to allow task functions to return (increases stack overhead by 16 bytes) */
vPortTaskWrapper(TaskFunction_t pxCode,void * pvParameters)113     static void vPortTaskWrapper( TaskFunction_t pxCode,
114                                   void * pvParameters )
115     {
116         pxCode( pvParameters );
117         /*FreeRTOS tasks should not return. Log the task name and abort. */
118         char * pcTaskName = pcTaskGetTaskName( NULL );
119         ESP_LOGE( "FreeRTOS", "FreeRTOS Task \"%s\" should not return, Aborting now!", pcTaskName );
120         abort();
121     }
122 #endif /* if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER */
123 
124 /*
125  * Stack initialization
126  */
127 /* *INDENT-OFF* */
128 #if portUSING_MPU_WRAPPERS
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters,BaseType_t xRunPrivileged)129     StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
130                                          TaskFunction_t pxCode,
131                                          void * pvParameters,
132                                          BaseType_t xRunPrivileged )
133 #else
134     StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
135                                          TaskFunction_t pxCode,
136                                          void * pvParameters )
137 #endif
138 /* *INDENT-ON* */
139 {
140     StackType_t * sp;
141     StackType_t * tp;
142     XtExcFrame * frame;
143 
144     #if XCHAL_CP_NUM > 0
145         uint32_t * p;
146     #endif
147 
148     uint32_t * threadptr;
149     void * task_thread_local_start;
150     extern int _thread_local_start, _thread_local_end, _flash_rodata_start, _flash_rodata_align;
151 
152     /* TODO: check that TLS area fits the stack */
153     uint32_t thread_local_sz = ( uint8_t * ) &_thread_local_end - ( uint8_t * ) &_thread_local_start;
154 
155     thread_local_sz = ALIGNUP( 0x10, thread_local_sz );
156 
157     /* Initialize task's stack so that we have the following structure at the top:
158      *
159      *  ----LOW ADDRESSES ----------------------------------------HIGH ADDRESSES----------
160      *   task stack | interrupt stack frame | thread local vars | co-processor save area |
161      *  ----------------------------------------------------------------------------------
162      |                                                                    |
163      |              SP                                                             pxTopOfStack
164      |
165      |  All parts are aligned to 16 byte boundary.
166      */
167 
168     /* Create interrupt stack frame aligned to 16 byte boundary */
169     sp = ( StackType_t * ) ( ( ( UBaseType_t ) pxTopOfStack - XT_CP_SIZE - thread_local_sz - XT_STK_FRMSZ ) & ~0xf );
170 
171     /* Clear the entire frame (do not use memset() because we don't depend on C library) */
172     for( tp = sp; tp <= pxTopOfStack; ++tp )
173     {
174         *tp = 0;
175     }
176 
177     frame = ( XtExcFrame * ) sp;
178 
179     /* Explicitly initialize certain saved registers */
180     #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
181         frame->pc = ( UBaseType_t ) vPortTaskWrapper; /* task wrapper                       */
182     #else
183         frame->pc = ( UBaseType_t ) pxCode;           /* task entrypoint                    */
184     #endif
185     frame->a0 = 0;                                    /* to terminate GDB backtrace     */
186     frame->a1 = ( UBaseType_t ) sp + XT_STK_FRMSZ;    /* physical top of stack frame        */
187     frame->exit = ( UBaseType_t ) _xt_user_exit;      /* user exception exit dispatcher */
188 
189     /* Set initial PS to int level 0, EXCM disabled ('rfe' will enable), user mode. */
190     /* Also set entry point argument parameter. */
191     #ifdef __XTENSA_CALL0_ABI__
192         #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
193             frame->a2 = ( UBaseType_t ) pxCode;
194             frame->a3 = ( UBaseType_t ) pvParameters;
195         #else
196             frame->a2 = ( UBaseType_t ) pvParameters;
197         #endif
198         frame->ps = PS_UM | PS_EXCM;
199     #else
200         /* + for windowed ABI also set WOE and CALLINC (pretend task was 'call4'd). */
201         #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
202             frame->a6 = ( UBaseType_t ) pxCode;
203             frame->a7 = ( UBaseType_t ) pvParameters;
204         #else
205             frame->a6 = ( UBaseType_t ) pvParameters;
206         #endif
207         frame->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC( 1 );
208     #endif /* ifdef __XTENSA_CALL0_ABI__ */
209 
210     #ifdef XT_USE_SWPRI
211         /* Set the initial virtual priority mask value to all 1's. */
212         frame->vpri = 0xFFFFFFFF;
213     #endif
214 
215     /* Init threadptr register and set up TLS run-time area. */
216     task_thread_local_start = ( void * ) ( ( ( uint32_t ) pxTopOfStack - XT_CP_SIZE - thread_local_sz ) & ~0xf );
217     memcpy( task_thread_local_start, &_thread_local_start, thread_local_sz );
218     threadptr = ( uint32_t * ) ( sp + XT_STK_EXTRA );
219 
220     /* Calculate THREADPTR value.
221      * The generated code will add THREADPTR value to a constant value determined at link time,
222      * to get the address of the TLS variable.
223      * The constant value is calculated by the linker as follows
224      * (search for 'tpoff' in elf32-xtensa.c in BFD):
225      *    offset = address - tls_section_vma + align_up(TCB_SIZE, tls_section_alignment)
226      * where TCB_SIZE is hardcoded to 8.
227      */
228     const uint32_t tls_section_alignment = ( uint32_t ) &_flash_rodata_align; /* ALIGN value of .flash.rodata section */
229     const uint32_t tcb_size = 8;                                              /* Unrelated to FreeRTOS, this is the constant from BFD */
230     const uint32_t base = ( tcb_size + tls_section_alignment - 1 ) & ( ~( tls_section_alignment - 1 ) );
231     *threadptr = ( uint32_t ) task_thread_local_start - ( ( uint32_t ) &_thread_local_start - ( uint32_t ) &_flash_rodata_start ) - base;
232 
233     #if XCHAL_CP_NUM > 0
234         /* Init the coprocessor save area (see xtensa_context.h) */
235 
236         /* No access to TCB here, so derive indirectly. Stack growth is top to bottom.
237          * //p = (uint32_t *) xMPUSettings->coproc_area;
238          */
239         p = ( uint32_t * ) ( ( ( uint32_t ) pxTopOfStack - XT_CP_SIZE ) & ~0xf );
240         configASSERT( ( uint32_t ) p >= frame->a1 );
241         p[ 0 ] = 0;
242         p[ 1 ] = 0;
243         p[ 2 ] = ( ( ( uint32_t ) p ) + 12 + XCHAL_TOTAL_SA_ALIGN - 1 ) & -XCHAL_TOTAL_SA_ALIGN;
244     #endif
245 
246     return sp;
247 }
248 
249 /*-----------------------------------------------------------*/
250 
vPortEndScheduler(void)251 void vPortEndScheduler( void )
252 {
253     /* It is unlikely that the Xtensa port will get stopped.  If required simply
254      * disable the tick interrupt here. */
255     abort();
256 }
257 
258 /*-----------------------------------------------------------*/
259 
xPortStartScheduler(void)260 BaseType_t xPortStartScheduler( void )
261 {
262     portDISABLE_INTERRUPTS();
263     /* Interrupts are disabled at this point and stack contains PS with enabled interrupts when task context is restored */
264 
265     #if XCHAL_CP_NUM > 0
266         /* Initialize co-processor management for tasks. Leave CPENABLE alone. */
267         _xt_coproc_init();
268     #endif
269 
270     /* Setup the hardware to generate the tick */
271     vPortSetupTimer();
272 
273     /* NOTE: For ESP32-S3, vPortSetupTimer allocates an interrupt for the
274      * systimer which is used as the source for FreeRTOS systick.
275      *
276      * The behaviour of portEXIT_CRITICAL is different in FreeRTOS and ESP-IDF -
277      * the former enables the interrupts no matter what the state was at the beginning
278      * of the call while the latter restores the interrupt state to what was at the
279      * beginning of the call.
280      *
281      * This resulted in the interrupts being enabled before the _frxt_dispatch call,
282      * who was unable to switch context to the queued tasks.
283      */
284     portDISABLE_INTERRUPTS();
285 
286     port_xSchedulerRunning[ xPortGetCoreID() ] = 1;
287 
288     /* Cannot be directly called from C; never returns */
289     __asm__ volatile ( "call0    _frxt_dispatch\n" );
290 
291     /* Should not get here. */
292     return pdTRUE;
293 }
294 
295 /*-----------------------------------------------------------*/
296 
vPortYieldOtherCore(BaseType_t coreid)297 void vPortYieldOtherCore( BaseType_t coreid )
298 {
299     esp_crosscore_int_send_yield( coreid );
300 }
301 
302 /*-----------------------------------------------------------*/
303 
304 /*
305  * Used to set coprocessor area in stack. Current hack is to reuse MPU pointer for coprocessor area.
306  */
307 #if portUSING_MPU_WRAPPERS
vPortStoreTaskMPUSettings(xMPU_SETTINGS * xMPUSettings,const struct xMEMORY_REGION * const xRegions,StackType_t * pxBottomOfStack,configSTACK_DEPTH_TYPE uxStackDepth)308     void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
309                                     const struct xMEMORY_REGION * const xRegions,
310                                     StackType_t * pxBottomOfStack,
311                                     configSTACK_DEPTH_TYPE uxStackDepth )
312     {
313         #if XCHAL_CP_NUM > 0
314             xMPUSettings->coproc_area = ( StackType_t * ) ( ( uint32_t ) ( pxBottomOfStack + uxStackDepth - 1 ) );
315             xMPUSettings->coproc_area = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) xMPUSettings->coproc_area ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
316             xMPUSettings->coproc_area = ( StackType_t * ) ( ( ( uint32_t ) xMPUSettings->coproc_area - XT_CP_SIZE ) & ~0xf );
317 
318             /* NOTE: we cannot initialize the coprocessor save area here because FreeRTOS is going to
319              * clear the stack area after we return. This is done in pxPortInitialiseStack().
320              */
321         #endif
322     }
323 
vPortReleaseTaskMPUSettings(xMPU_SETTINGS * xMPUSettings)324     void vPortReleaseTaskMPUSettings( xMPU_SETTINGS * xMPUSettings )
325     {
326         /* If task has live floating point registers somewhere, release them */
327         _xt_coproc_release( xMPUSettings->coproc_area );
328     }
329 
330 #endif /* if portUSING_MPU_WRAPPERS */
331 
332 /*
333  * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs
334  * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway.
335  */
xPortInIsrContext()336 BaseType_t xPortInIsrContext()
337 {
338     unsigned int irqStatus;
339     BaseType_t ret;
340 
341     irqStatus = portSET_INTERRUPT_MASK_FROM_ISR();
342     ret = ( port_interruptNesting[ xPortGetCoreID() ] != 0 );
343     portCLEAR_INTERRUPT_MASK_FROM_ISR( irqStatus );
344     return ret;
345 }
346 
347 /*
348  * This function will be called in High prio ISRs. Returns true if the current core was in ISR context
349  * before calling into high prio ISR context.
350  */
xPortInterruptedFromISRContext()351 BaseType_t IRAM_ATTR xPortInterruptedFromISRContext()
352 {
353     return( port_interruptNesting[ xPortGetCoreID() ] != 0 );
354 }
355 
vPortEvaluateYieldFromISR(int argc,...)356 void IRAM_ATTR vPortEvaluateYieldFromISR( int argc,
357                                           ... )
358 {
359     BaseType_t xYield;
360     va_list ap;
361 
362     va_start( ap, argc );
363 
364     if( argc )
365     {
366         xYield = ( BaseType_t ) va_arg( ap, int );
367         va_end( ap );
368     }
369     else
370     {
371         /*it is a empty parameter vPortYieldFromISR macro call: */
372         va_end( ap );
373         traceISR_EXIT_TO_SCHEDULER();
374         _frxt_setup_switch();
375         return;
376     }
377 
378     /*Yield exists, so need evaluate it first then switch: */
379     if( xYield == pdTRUE )
380     {
381         traceISR_EXIT_TO_SCHEDULER();
382         _frxt_setup_switch();
383     }
384 }
385 
vPortAssertIfInISR()386 void vPortAssertIfInISR()
387 {
388     if( xPortInIsrContext() )
389     {
390         esp_rom_printf( "core=%d port_interruptNesting=%d\n\n", xPortGetCoreID(), port_interruptNesting[ xPortGetCoreID() ] );
391     }
392 
393     configASSERT( !xPortInIsrContext() );
394 }
395 
396 /*
397  * For kernel use: Initialize a per-CPU mux. Mux will be initialized unlocked.
398  */
vPortCPUInitializeMutex(portMUX_TYPE * mux)399 void vPortCPUInitializeMutex( portMUX_TYPE * mux )
400 {
401     #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
402         esp_rom_printf( "Initializing mux %p\n", mux );
403         mux->lastLockedFn = "(never locked)";
404         mux->lastLockedLine = -1;
405     #endif
406     mux->owner = portMUX_FREE_VAL;
407     mux->count = 0;
408 }
409 
410 #include "portmux_impl.h"
411 
412 /*
413  * For kernel use: Acquire a per-CPU mux. Spinlocks, so don't hold on to these muxes for too long.
414  */
415 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
vPortCPUAcquireMutex(portMUX_TYPE * mux,const char * fnName,int line)416     void vPortCPUAcquireMutex( portMUX_TYPE * mux,
417                                const char * fnName,
418                                int line )
419     {
420         unsigned int irqStatus = portSET_INTERRUPT_MASK_FROM_ISR();
421 
422         vPortCPUAcquireMutexIntsDisabled( mux, portMUX_NO_TIMEOUT, fnName, line );
423         portCLEAR_INTERRUPT_MASK_FROM_ISR( irqStatus );
424     }
425 
vPortCPUAcquireMutexTimeout(portMUX_TYPE * mux,int timeout_cycles,const char * fnName,int line)426     bool vPortCPUAcquireMutexTimeout( portMUX_TYPE * mux,
427                                       int timeout_cycles,
428                                       const char * fnName,
429                                       int line )
430     {
431         unsigned int irqStatus = portSET_INTERRUPT_MASK_FROM_ISR();
432         bool result = vPortCPUAcquireMutexIntsDisabled( mux, timeout_cycles, fnName, line );
433 
434         portCLEAR_INTERRUPT_MASK_FROM_ISR( irqStatus );
435         return result;
436     }
437 
438 #else /* ifdef CONFIG_FREERTOS_PORTMUX_DEBUG */
vPortCPUAcquireMutex(portMUX_TYPE * mux)439     void vPortCPUAcquireMutex( portMUX_TYPE * mux )
440     {
441         unsigned int irqStatus = portSET_INTERRUPT_MASK_FROM_ISR();
442 
443         vPortCPUAcquireMutexIntsDisabled( mux, portMUX_NO_TIMEOUT );
444         portCLEAR_INTERRUPT_MASK_FROM_ISR( irqStatus );
445     }
446 
vPortCPUAcquireMutexTimeout(portMUX_TYPE * mux,int timeout_cycles)447     bool vPortCPUAcquireMutexTimeout( portMUX_TYPE * mux,
448                                       int timeout_cycles )
449     {
450         unsigned int irqStatus = portSET_INTERRUPT_MASK_FROM_ISR();
451         bool result = vPortCPUAcquireMutexIntsDisabled( mux, timeout_cycles );
452 
453         portCLEAR_INTERRUPT_MASK_FROM_ISR( irqStatus );
454         return result;
455     }
456 #endif /* ifdef CONFIG_FREERTOS_PORTMUX_DEBUG */
457 
458 
459 /*
460  * For kernel use: Release a per-CPU mux
461  *
462  * Mux must be already locked by this core
463  */
464 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
vPortCPUReleaseMutex(portMUX_TYPE * mux,const char * fnName,int line)465     void vPortCPUReleaseMutex( portMUX_TYPE * mux,
466                                const char * fnName,
467                                int line )
468     {
469         unsigned int irqStatus = portSET_INTERRUPT_MASK_FROM_ISR();
470 
471         vPortCPUReleaseMutexIntsDisabled( mux, fnName, line );
472         portCLEAR_INTERRUPT_MASK_FROM_ISR( irqStatus );
473     }
474 #else
vPortCPUReleaseMutex(portMUX_TYPE * mux)475     void vPortCPUReleaseMutex( portMUX_TYPE * mux )
476     {
477         unsigned int irqStatus = portSET_INTERRUPT_MASK_FROM_ISR();
478 
479         vPortCPUReleaseMutexIntsDisabled( mux );
480         portCLEAR_INTERRUPT_MASK_FROM_ISR( irqStatus );
481     }
482 #endif /* ifdef CONFIG_FREERTOS_PORTMUX_DEBUG */
483 
484 #define STACK_WATCH_AREA_SIZE       ( 32 )
485 #define STACK_WATCH_POINT_NUMBER    ( SOC_CPU_WATCHPOINTS_NUM - 1 )
486 
vPortSetStackWatchpoint(void * pxStackStart)487 void vPortSetStackWatchpoint( void * pxStackStart )
488 {
489     /*Set watchpoint 1 to watch the last 32 bytes of the stack. */
490     /*Unfortunately, the Xtensa watchpoints can't set a watchpoint on a random [base - base+n] region because */
491     /*the size works by masking off the lowest address bits. For that reason, we futz a bit and watch the lowest 32 */
492     /*bytes of the stack we can actually watch. In general, this can cause the watchpoint to be triggered at most */
493     /*28 bytes early. The value 32 is chosen because it's larger than the stack canary, which in FreeRTOS is 20 bytes. */
494     /*This way, we make sure we trigger before/when the stack canary is corrupted, not after. */
495     int addr = ( int ) pxStackStart;
496 
497     addr = ( addr + 31 ) & ( ~31 );
498     esp_cpu_set_watchpoint( STACK_WATCH_POINT_NUMBER, ( char * ) addr, 32, ESP_WATCHPOINT_STORE );
499 }
500 
501 #if ( ESP_IDF_VERSION < ESP_IDF_VERSION_VAL( 4, 2, 0 ) )
502 
503     #if defined( CONFIG_SPIRAM_SUPPORT )
504 
505 /*
506  * Compare & set (S32C1) does not work in external RAM. Instead, this routine uses a mux (in internal memory) to fake it.
507  */
508         static portMUX_TYPE extram_mux = portMUX_INITIALIZER_UNLOCKED;
509 
uxPortCompareSetExtram(volatile uint32_t * addr,uint32_t compare,uint32_t * set)510         void uxPortCompareSetExtram( volatile uint32_t * addr,
511                                      uint32_t compare,
512                                      uint32_t * set )
513         {
514             uint32_t prev;
515 
516             uint32_t oldlevel = portSET_INTERRUPT_MASK_FROM_ISR();
517 
518             #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
519                 vPortCPUAcquireMutexIntsDisabled( &extram_mux, portMUX_NO_TIMEOUT, __FUNCTION__, __LINE__ );
520             #else
521                 vPortCPUAcquireMutexIntsDisabled( &extram_mux, portMUX_NO_TIMEOUT );
522             #endif
523             prev = *addr;
524 
525             if( prev == compare )
526             {
527                 *addr = *set;
528             }
529 
530             *set = prev;
531             #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
532                 vPortCPUReleaseMutexIntsDisabled( &extram_mux, __FUNCTION__, __LINE__ );
533             #else
534                 vPortCPUReleaseMutexIntsDisabled( &extram_mux );
535             #endif
536 
537             portCLEAR_INTERRUPT_MASK_FROM_ISR( oldlevel );
538         }
539     #endif //defined(CONFIG_SPIRAM_SUPPORT)
540 
541 #endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0) */
542 
543 
xPortGetTickRateHz(void)544 uint32_t xPortGetTickRateHz( void )
545 {
546     return ( uint32_t ) configTICK_RATE_HZ;
547 }
548 
549 /* For now, running FreeRTOS on one core and a bare metal on the other (or other OSes) */
550 /* is not supported. For now CONFIG_FREERTOS_UNICORE and CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE */
551 /* should mirror each other's values. */
552 /* */
553 /* And since this should be true, we can just check for CONFIG_FREERTOS_UNICORE. */
554 #if CONFIG_FREERTOS_UNICORE != CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
555     #error "FreeRTOS and system configuration mismatch regarding the use of multiple cores."
556 #endif
557 
558 extern void esp_startup_start_app_common( void );
559 
esp_startup_start_app(void)560 void esp_startup_start_app( void )
561 {
562     #if !CONFIG_ESP_INT_WDT
563         #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
564             assert( !soc_has_cache_lock_bug() && "ESP32 Rev 3 + Dual Core + PSRAM requires INT WDT enabled in project config!" );
565         #endif
566     #endif
567 
568     esp_startup_start_app_common();
569 
570     ESP_LOGI( "cpu_start", "Starting scheduler on PRO CPU." );
571     vTaskStartScheduler();
572 }
573