1 /*
2     FreeRTOS V8.2.0 - Copyright (C) 2015 Real Time Engineers Ltd.
3     All rights reserved
4 
5     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
6 
7     This file is part of the FreeRTOS distribution.
8 
9     FreeRTOS is free software; you can redistribute it and/or modify it under
10     the terms of the GNU General Public License (version 2) as published by the
11     Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
12 
13 	***************************************************************************
14     >>!   NOTE: The modification to the GPL is included to allow you to     !<<
15     >>!   distribute a combined work that includes FreeRTOS without being   !<<
16     >>!   obliged to provide the source code for proprietary components     !<<
17     >>!   outside of the FreeRTOS kernel.                                   !<<
18 	***************************************************************************
19 
20     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
21     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22     FOR A PARTICULAR PURPOSE.  Full license text is available on the following
23     link: http://www.freertos.org/a00114.html
24 
25     ***************************************************************************
26      *                                                                       *
27      *    FreeRTOS provides completely free yet professionally developed,    *
28      *    robust, strictly quality controlled, supported, and cross          *
29      *    platform software that is more than just the market leader, it     *
30      *    is the industry's de facto standard.                               *
31      *                                                                       *
32      *    Help yourself get started quickly while simultaneously helping     *
33      *    to support the FreeRTOS project by purchasing a FreeRTOS           *
34      *    tutorial book, reference manual, or both:                          *
35      *    http://www.FreeRTOS.org/Documentation                              *
36      *                                                                       *
37     ***************************************************************************
38 
39     http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
40 	the FAQ page "My application does not run, what could be wrong?".  Have you
41 	defined configASSERT()?
42 
43 	http://www.FreeRTOS.org/support - In return for receiving this top quality
44 	embedded software for free we request you assist our global community by
45 	participating in the support forum.
46 
47 	http://www.FreeRTOS.org/training - Investing in training allows your team to
48 	be as productive as possible as early as possible.  Now you can receive
49 	FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
50 	Ltd, and the world's leading authority on the world's leading RTOS.
51 
52     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
53     including FreeRTOS+Trace - an indispensable productivity tool, a DOS
54     compatible FAT file system, and our tiny thread aware UDP/IP stack.
55 
56     http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
57     Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
58 
59     http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
60     Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS
61     licenses offer ticketed support, indemnification and commercial middleware.
62 
63     http://www.SafeRTOS.com - High Integrity Systems also provide a safety
64     engineered and independently SIL3 certified version for use in safety and
65     mission critical applications that require provable dependability.
66 
67     1 tab == 4 spaces!
68 */
69 
70 /*******************************************************************************
71 // Copyright (c) 2003-2015 Cadence Design Systems, Inc.
72 //
73 // Permission is hereby granted, free of charge, to any person obtaining
74 // a copy of this software and associated documentation files (the
75 // "Software"), to deal in the Software without restriction, including
76 // without limitation the rights to use, copy, modify, merge, publish,
77 // distribute, sublicense, and/or sell copies of the Software, and to
78 // permit persons to whom the Software is furnished to do so, subject to
79 // the following conditions:
80 //
81 // The above copyright notice and this permission notice shall be included
82 // in all copies or substantial portions of the Software.
83 //
84 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
85 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
86 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
87 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
88 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
89 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
90 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
91 --------------------------------------------------------------------------------
92 */
93 #include <stdlib.h>
94 #include <string.h>
95 #include <xtensa/config/core.h>
96 
97 #include "xtensa_rtos.h"
98 
99 #include "soc/cpu.h"
100 
101 #include "FreeRTOS.h"
102 #include "task.h"
103 
104 #include "esp_debug_helpers.h"
105 #include "esp_heap_caps.h"
106 #include "esp_heap_caps_init.h"
107 #include "esp_private/crosscore_int.h"
108 
109 #include "esp_intr_alloc.h"
110 #include "esp_log.h"
111 #include "sdkconfig.h"
112 
113 #include "esp_task_wdt.h"
114 #include "esp_task.h"
115 
116 #include "soc/soc_caps.h"
117 #include "soc/efuse_reg.h"
118 #include "soc/dport_access.h"
119 #include "soc/dport_reg.h"
120 #include "esp_int_wdt.h"
121 
122 
123 #include "sdkconfig.h"
124 
125 #if CONFIG_IDF_TARGET_ESP32
126 #include "esp32/spiram.h"
127 #elif CONFIG_IDF_TARGET_ESP32S2
128 #include "esp32s2/spiram.h"
129 #elif CONFIG_IDF_TARGET_ESP32S3
130 #include "esp32s3/spiram.h"
131 #endif
132 
133 #include "esp_private/startup_internal.h" // [refactor-todo] for g_spiram_ok
134 #include "esp_app_trace.h" // [refactor-todo] for esp_app_trace_init
135 
136 /* Defined in portasm.h */
137 extern void _frxt_tick_timer_init(void);
138 
139 /* Defined in xtensa_context.S */
140 extern void _xt_coproc_init(void);
141 
142 static const char* TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but
143 								// for now maintain the same log output
144 
145 #if CONFIG_FREERTOS_CORETIMER_0
146     #define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER0_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
147 #endif
148 #if CONFIG_FREERTOS_CORETIMER_1
149     #define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER1_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
150 #endif
151 
152 _Static_assert(tskNO_AFFINITY == CONFIG_FREERTOS_NO_AFFINITY, "incorrect tskNO_AFFINITY value");
153 
154 /*-----------------------------------------------------------*/
155 extern volatile int port_xSchedulerRunning[portNUM_PROCESSORS];
156 unsigned port_interruptNesting[portNUM_PROCESSORS] = {0};  // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit
157 BaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0};
158 BaseType_t port_uxOldInterruptState[portNUM_PROCESSORS] = {0};
159 /*-----------------------------------------------------------*/
160 
161 // User exception dispatcher when exiting
162 void _xt_user_exit(void);
163 
164 #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
165 // Wrapper to allow task functions to return (increases stack overhead by 16 bytes)
vPortTaskWrapper(TaskFunction_t pxCode,void * pvParameters)166 static void vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters)
167 {
168 	pxCode(pvParameters);
169 	//FreeRTOS tasks should not return. Log the task name and abort.
170 	char * pcTaskName = pcTaskGetTaskName(NULL);
171 	ESP_LOGE("FreeRTOS", "FreeRTOS Task \"%s\" should not return, Aborting now!", pcTaskName);
172 	abort();
173 }
174 #endif
175 
176 /*
177  * Stack initialization
178  */
179 #if portUSING_MPU_WRAPPERS
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters,BaseType_t xRunPrivileged)180 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged )
181 #else
182 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
183 #endif
184 {
185 	StackType_t *sp, *tp;
186 	XtExcFrame  *frame;
187 	#if XCHAL_CP_NUM > 0
188 	uint32_t *p;
189 	#endif
190 	uint32_t *threadptr;
191 	void *task_thread_local_start;
192 	extern int _thread_local_start, _thread_local_end, _rodata_start;
193 	// TODO: check that TLS area fits the stack
194 	uint32_t thread_local_sz = (uint8_t *)&_thread_local_end - (uint8_t *)&_thread_local_start;
195 
196 	thread_local_sz = ALIGNUP(0x10, thread_local_sz);
197 
198 	/* Initialize task's stack so that we have the following structure at the top:
199 
200 		----LOW ADDRESSES ----------------------------------------HIGH ADDRESSES----------
201 		 task stack | interrupt stack frame | thread local vars | co-processor save area |
202 		----------------------------------------------------------------------------------
203 					|																	 |
204 					SP 																pxTopOfStack
205 
206 		All parts are aligned to 16 byte boundary. */
207 	sp = (StackType_t *) (((UBaseType_t)(pxTopOfStack + 1) - XT_CP_SIZE - thread_local_sz - XT_STK_FRMSZ) & ~0xf);
208 
209 	/* Clear the entire frame (do not use memset() because we don't depend on C library) */
210 	for (tp = sp; tp <= pxTopOfStack; ++tp)
211 		*tp = 0;
212 
213 	frame = (XtExcFrame *) sp;
214 
215 	/* Explicitly initialize certain saved registers */
216 	#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
217 	frame->pc	= (UBaseType_t) vPortTaskWrapper;	/* task wrapper						*/
218 	#else
219 	frame->pc   = (UBaseType_t) pxCode;				/* task entrypoint					*/
220 	#endif
221 	frame->a0	= 0;								/* to terminate GDB backtrace		*/
222 	frame->a1	= (UBaseType_t) sp + XT_STK_FRMSZ;	/* physical top of stack frame		*/
223 	frame->exit = (UBaseType_t) _xt_user_exit;		/* user exception exit dispatcher	*/
224 
225 	/* Set initial PS to int level 0, EXCM disabled ('rfe' will enable), user mode. */
226 	/* Also set entry point argument parameter. */
227 	#ifdef __XTENSA_CALL0_ABI__
228 		#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
229 		frame->a2 = (UBaseType_t) pxCode;
230 		frame->a3 = (UBaseType_t) pvParameters;
231 		#else
232 		frame->a2 = (UBaseType_t) pvParameters;
233 		#endif
234 	frame->ps = PS_UM | PS_EXCM;
235 	#else
236 	/* + for windowed ABI also set WOE and CALLINC (pretend task was 'call4'd). */
237 		#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
238 		frame->a6 = (UBaseType_t) pxCode;
239 		frame->a7 = (UBaseType_t) pvParameters;
240 		#else
241 		frame->a6 = (UBaseType_t) pvParameters;
242 		#endif
243 	frame->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1);
244 	#endif
245 
246 	#ifdef XT_USE_SWPRI
247 	/* Set the initial virtual priority mask value to all 1's. */
248 	frame->vpri = 0xFFFFFFFF;
249 	#endif
250 
251 	/* Init threadptr reg and TLS vars */
252 	task_thread_local_start = (void *)(((uint32_t)pxTopOfStack - XT_CP_SIZE - thread_local_sz) & ~0xf);
253 	memcpy(task_thread_local_start, &_thread_local_start, thread_local_sz);
254 	threadptr = (uint32_t *)(sp + XT_STK_EXTRA);
255 	/* Calculate THREADPTR value:
256 	 * The generated code will add THREADPTR value to a constant value determined at link time,
257 	 * to get the address of the TLS variable.
258 	 * The constant value is calculated by the linker as follows
259 	 * (search for 'tpoff' in elf32-xtensa.c in BFD):
260 	 *    offset = address - tls_section_vma + align_up(TCB_SIZE, tls_section_alignment)
261 	 * where TCB_SIZE is hardcoded to 8. There doesn't seem to be a way to propagate
262 	 * the section alignment value from the ld script into the code, so it is hardcoded
263 	 * in both places.
264 	 */
265 	const uint32_t tls_section_alignment = 0x10;  /* has to be in sync with ALIGN value of .flash.rodata section */
266 	const uint32_t tcb_size = 8; /* Unrelated to FreeRTOS, this is the constant from BFD */
267 	const uint32_t base = (tcb_size + tls_section_alignment - 1) & (~(tls_section_alignment - 1));
268 	*threadptr = (uint32_t)task_thread_local_start - ((uint32_t)&_thread_local_start - (uint32_t)&_rodata_start) - base;
269 
270 	#if XCHAL_CP_NUM > 0
271 	/* Init the coprocessor save area (see xtensa_context.h) */
272 	/* No access to TCB here, so derive indirectly. Stack growth is top to bottom.
273          * //p = (uint32_t *) xMPUSettings->coproc_area;
274 	 */
275 	p = (uint32_t *)(((uint32_t) pxTopOfStack - XT_CP_SIZE) & ~0xf);
276 	p[0] = 0;
277 	p[1] = 0;
278 	p[2] = (((uint32_t) p) + 12 + XCHAL_TOTAL_SA_ALIGN - 1) & -XCHAL_TOTAL_SA_ALIGN;
279 	#endif
280 
281 	return sp;
282 }
283 
284 /*-----------------------------------------------------------*/
285 
vPortEndScheduler(void)286 void vPortEndScheduler( void )
287 {
288 	/* It is unlikely that the Xtensa port will get stopped.  If required simply
289 	disable the tick interrupt here. */
290 	abort();
291 }
292 
293 /*-----------------------------------------------------------*/
294 
xPortStartScheduler(void)295 BaseType_t xPortStartScheduler( void )
296 {
297 	// Interrupts are disabled at this point and stack contains PS with enabled interrupts when task context is restored
298 
299 	#if XCHAL_CP_NUM > 0
300 	/* Initialize co-processor management for tasks. Leave CPENABLE alone. */
301 	_xt_coproc_init();
302 	#endif
303 
304 	/* Init the tick divisor value */
305 	_xt_tick_divisor_init();
306 
307 	/* Setup the hardware to generate the tick. */
308 	_frxt_tick_timer_init();
309 
310 	port_xSchedulerRunning[xPortGetCoreID()] = 1;
311 
312 	// Cannot be directly called from C; never returns
313 	__asm__ volatile ("call0    _frxt_dispatch\n");
314 
315 	/* Should not get here. */
316 	return pdTRUE;
317 }
318 /*-----------------------------------------------------------*/
319 
xPortSysTickHandler(void)320 BaseType_t xPortSysTickHandler( void )
321 {
322 	BaseType_t ret;
323 
324 	portbenchmarkIntLatency();
325 	traceISR_ENTER(SYSTICK_INTR_ID);
326 	ret = xTaskIncrementTick();
327 	if( ret != pdFALSE )
328 	{
329 		portYIELD_FROM_ISR();
330 	} else {
331 		traceISR_EXIT();
332 	}
333 	return ret;
334 }
335 
336 
vPortYieldOtherCore(BaseType_t coreid)337 void vPortYieldOtherCore( BaseType_t coreid ) {
338 	esp_crosscore_int_send_yield( coreid );
339 }
340 
341 /*-----------------------------------------------------------*/
342 
343 /*
344  * Used to set coprocessor area in stack. Current hack is to reuse MPU pointer for coprocessor area.
345  */
346 #if portUSING_MPU_WRAPPERS
vPortStoreTaskMPUSettings(xMPU_SETTINGS * xMPUSettings,const struct xMEMORY_REGION * const xRegions,StackType_t * pxBottomOfStack,uint32_t usStackDepth)347 void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t usStackDepth )
348 {
349 	#if XCHAL_CP_NUM > 0
350 	xMPUSettings->coproc_area = (StackType_t*)((((uint32_t)(pxBottomOfStack + usStackDepth - 1)) - XT_CP_SIZE ) & ~0xf);
351 
352 
353 	/* NOTE: we cannot initialize the coprocessor save area here because FreeRTOS is going to
354          * clear the stack area after we return. This is done in pxPortInitialiseStack().
355 	 */
356 	#endif
357 }
358 
vPortReleaseTaskMPUSettings(xMPU_SETTINGS * xMPUSettings)359 void vPortReleaseTaskMPUSettings( xMPU_SETTINGS *xMPUSettings )
360 {
361 	/* If task has live floating point registers somewhere, release them */
362 	_xt_coproc_release( xMPUSettings->coproc_area );
363 }
364 
365 #endif
366 
367 /*
368  * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs
369  * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway.
370  */
xPortInIsrContext(void)371 BaseType_t xPortInIsrContext(void)
372 {
373 	unsigned int irqStatus;
374 	BaseType_t ret;
375 	irqStatus=portENTER_CRITICAL_NESTED();
376 	ret=(port_interruptNesting[xPortGetCoreID()] != 0);
377 	portEXIT_CRITICAL_NESTED(irqStatus);
378 	return ret;
379 }
380 
381 /*
382  * This function will be called in High prio ISRs. Returns true if the current core was in ISR context
383  * before calling into high prio ISR context.
384  */
xPortInterruptedFromISRContext(void)385 BaseType_t IRAM_ATTR xPortInterruptedFromISRContext(void)
386 {
387 	return (port_interruptNesting[xPortGetCoreID()] != 0);
388 }
389 
vPortEvaluateYieldFromISR(int argc,...)390 void IRAM_ATTR vPortEvaluateYieldFromISR(int argc, ...)
391 {
392     BaseType_t xYield;
393     va_list ap;
394     va_start(ap, argc);
395 
396     if(argc) {
397         xYield = (BaseType_t)va_arg(ap, int);
398         va_end(ap);
399     } else {
400         //it is a empty parameter vPortYieldFromISR macro call:
401         va_end(ap);
402         traceISR_EXIT_TO_SCHEDULER();
403         _frxt_setup_switch();
404         return;
405     }
406 
407     //Yield exists, so need evaluate it first then switch:
408     if(xYield == pdTRUE) {
409         traceISR_EXIT_TO_SCHEDULER();
410         _frxt_setup_switch();
411     }
412 }
413 
vPortAssertIfInISR(void)414 void vPortAssertIfInISR(void)
415 {
416 	configASSERT(xPortInIsrContext());
417 }
418 
419 #define STACK_WATCH_AREA_SIZE 32
420 #define STACK_WATCH_POINT_NUMBER (SOC_CPU_WATCHPOINTS_NUM - 1)
421 
vPortSetStackWatchpoint(void * pxStackStart)422 void vPortSetStackWatchpoint( void* pxStackStart ) {
423 	//Set watchpoint 1 to watch the last 32 bytes of the stack.
424 	//Unfortunately, the Xtensa watchpoints can't set a watchpoint on a random [base - base+n] region because
425 	//the size works by masking off the lowest address bits. For that reason, we futz a bit and watch the lowest 32
426 	//bytes of the stack we can actually watch. In general, this can cause the watchpoint to be triggered at most
427 	//28 bytes early. The value 32 is chosen because it's larger than the stack canary, which in FreeRTOS is 20 bytes.
428 	//This way, we make sure we trigger before/when the stack canary is corrupted, not after.
429 	int addr=(int)pxStackStart;
430 	addr=(addr+31)&(~31);
431 	esp_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char*)addr, 32, ESP_WATCHPOINT_STORE);
432 }
433 
xPortGetTickRateHz(void)434 uint32_t xPortGetTickRateHz(void) {
435 	return (uint32_t)configTICK_RATE_HZ;
436 }
437 
vPortEnterCritical(portMUX_TYPE * mux)438 void __attribute__((optimize("-O3"))) vPortEnterCritical(portMUX_TYPE *mux)
439 {
440 	BaseType_t oldInterruptLevel = portENTER_CRITICAL_NESTED();
441 	/* Interrupts may already be disabled (because we're doing this recursively)
442 	* but we can't get the interrupt level after
443 	* vPortCPUAquireMutex, because it also may mess with interrupts.
444 	* Get it here first, then later figure out if we're nesting
445 	* and save for real there.
446 	*/
447 	vPortCPUAcquireMutex( mux );
448 	BaseType_t coreID = xPortGetCoreID();
449 	BaseType_t newNesting = port_uxCriticalNesting[coreID] + 1;
450 	port_uxCriticalNesting[coreID] = newNesting;
451 
452 	if( newNesting == 1 )
453 	{
454 		//This is the first time we get called. Save original interrupt level.
455 		port_uxOldInterruptState[coreID] = oldInterruptLevel;
456 	}
457 }
458 
vPortExitCritical(portMUX_TYPE * mux)459 void __attribute__((optimize("-O3"))) vPortExitCritical(portMUX_TYPE *mux)
460 {
461 	vPortCPUReleaseMutex( mux );
462 	BaseType_t coreID = xPortGetCoreID();
463 	BaseType_t nesting = port_uxCriticalNesting[coreID];
464 
465 	if(nesting > 0)
466 	{
467 		nesting--;
468 		port_uxCriticalNesting[coreID] = nesting;
469 
470 		if( nesting == 0 )
471 		{
472 			portEXIT_CRITICAL_NESTED(port_uxOldInterruptState[coreID]);
473 		}
474 	}
475 }
476 
vApplicationStackOverflowHook(TaskHandle_t xTask,char * pcTaskName)477 void  __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName )
478 {
479 	#define ERR_STR1 "***ERROR*** A stack overflow in task "
480 	#define ERR_STR2 " has been detected."
481 	const char *str[] = {ERR_STR1, pcTaskName, ERR_STR2};
482 
483 	char buf[sizeof(ERR_STR1) + CONFIG_FREERTOS_MAX_TASK_NAME_LEN + sizeof(ERR_STR2) + 1 /* null char */] = { 0 };
484 
485 	char *dest = buf;
486 	for (size_t i = 0 ; i < sizeof(str)/ sizeof(str[0]); i++) {
487 		dest = strcat(dest, str[i]);
488 	}
489 	esp_system_abort(buf);
490 }
491 
492 #if !CONFIG_FREERTOS_UNICORE
esp_startup_start_app_other_cores(void)493 void esp_startup_start_app_other_cores(void)
494 {
495 	// For now, we only support up to two core: 0 and 1.
496 	if (xPortGetCoreID() >= 2) {
497 		abort();
498 	}
499 
500 	// Wait for FreeRTOS initialization to finish on PRO CPU
501 	while (port_xSchedulerRunning[0] == 0) {
502 		;
503 	}
504 
505 #if CONFIG_APPTRACE_ENABLE
506 	// [refactor-todo] move to esp_system initialization
507 	esp_err_t err = esp_apptrace_init();
508 	assert(err == ESP_OK && "Failed to init apptrace module on APP CPU!");
509 #endif
510 
511 #if CONFIG_ESP_INT_WDT
512 	//Initialize the interrupt watch dog for CPU1.
513 	esp_int_wdt_cpu_init();
514 #endif
515 
516 	esp_crosscore_int_init();
517 #if CONFIG_IDF_TARGET_ESP32
518 	esp_dport_access_int_init();
519 #endif
520 
521 	ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU.");
522 	xPortStartScheduler();
523 	abort(); /* Only get to here if FreeRTOS somehow very broken */
524 }
525 #endif // !CONFIG_FREERTOS_UNICORE
526 
527 extern void esp_startup_start_app_common(void);
528 
esp_startup_start_app(void)529 void esp_startup_start_app(void)
530 {
531 #if !CONFIG_ESP_INT_WDT
532 #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
533 	assert(!soc_has_cache_lock_bug() && "ESP32 Rev 3 + Dual Core + PSRAM requires INT WDT enabled in project config!");
534 #endif
535 #endif
536 
537 	// ESP32 has single core variants. Check that FreeRTOS has been configured properly.
538 #if CONFIG_IDF_TARGET_ESP32 && !CONFIG_FREERTOS_UNICORE
539 	if (REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_DIS_APP_CPU)) {
540 		ESP_EARLY_LOGE(TAG, "Running on single core chip, but FreeRTOS is built with dual core support.");
541 		ESP_EARLY_LOGE(TAG, "Please enable CONFIG_FREERTOS_UNICORE option in menuconfig.");
542 		abort();
543 	}
544 #endif // CONFIG_IDF_TARGET_ESP32 && !CONFIG_FREERTOS_UNICORE
545 
546 	esp_startup_start_app_common();
547 
548 	ESP_LOGI(TAG, "Starting scheduler on PRO CPU.");
549 	vTaskStartScheduler();
550 }
551