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