1 /*
2 * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <string.h>
8 #include "FreeRTOS.h"
9 #include "task.h"
10 #include "portmacro.h"
11 #include "esp_system.h"
12 #include "esp_heap_caps_init.h"
13 #include "esp_int_wdt.h"
14 #include "esp_task_wdt.h"
15 #include "esp_task.h"
16 #include "esp_private/crosscore_int.h"
17 #include "esp_private/startup_internal.h" /* Required by g_spiram_ok. [refactor-todo] for g_spiram_ok */
18 #include "esp_log.h"
19 #include "soc/soc_memory_types.h"
20 #include "soc/dport_access.h"
21 #include "sdkconfig.h"
22
23 #if CONFIG_IDF_TARGET_ESP32
24 #include "esp32/spiram.h"
25 #elif CONFIG_IDF_TARGET_ESP32S2
26 #include "esp32s2/spiram.h"
27 #elif CONFIG_IDF_TARGET_ESP32S3
28 #include "esp32s3/spiram.h"
29 #elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
30 // SPIRAM is not supported on ESP32-C3
31 #endif
32
33 #if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL
34 static const char* TAG = "cpu_start";
35 #endif
36
37 /* Architecture-agnostic parts of the FreeRTOS ESP-IDF port layer can go here.
38 *
39 * The actual call flow will be to call esp_startup_start_app() in <ARCH>/port.c,
40 * which will then call esp_startup_start_app_common()
41 */
42
43 // Duplicate of inaccessible xSchedulerRunning; needed at startup to avoid counting nesting
44 volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0};
45
46 // For now, running FreeRTOS on one core and a bare metal on the other (or other OSes)
47 // is not supported. For now CONFIG_FREERTOS_UNICORE and CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
48 // should mirror each other's values.
49 //
50 // And since this should be true, we can just check for CONFIG_FREERTOS_UNICORE.
51 #if CONFIG_FREERTOS_UNICORE != CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
52 #error "FreeRTOS and system configuration mismatch regarding the use of multiple cores."
53 #endif
54
55 static void main_task(void* args);
56
57 #ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
58 void esp_gdbstub_init(void);
59 #endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
60
61 extern void app_main(void);
62
esp_startup_start_app_common(void)63 void esp_startup_start_app_common(void)
64 {
65 #if CONFIG_ESP_INT_WDT
66 esp_int_wdt_init();
67 //Initialize the interrupt watch dog for CPU0.
68 esp_int_wdt_cpu_init();
69 #endif
70
71 esp_crosscore_int_init();
72
73 #ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
74 esp_gdbstub_init();
75 #endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
76
77 portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main",
78 ESP_TASK_MAIN_STACK, NULL,
79 ESP_TASK_MAIN_PRIO, NULL, ESP_TASK_MAIN_CORE);
80 assert(res == pdTRUE);
81 (void)res;
82 }
83
main_task(void * args)84 static void main_task(void* args)
85 {
86 #if !CONFIG_FREERTOS_UNICORE
87 // Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack
88 while (port_xSchedulerRunning[1] == 0) {
89 ;
90 }
91 #endif
92
93 // [refactor-todo] check if there is a way to move the following block to esp_system startup
94 heap_caps_enable_nonos_stack_heaps();
95
96 // Now we have startup stack RAM available for heap, enable any DMA pool memory
97 #if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL
98 if (g_spiram_ok) {
99 esp_err_t r = esp_spiram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL);
100 if (r != ESP_OK) {
101 ESP_EARLY_LOGE(TAG, "Could not reserve internal/DMA pool (error 0x%x)", r);
102 abort();
103 }
104 }
105 #endif
106
107 //Initialize task wdt if configured to do so
108 #ifdef CONFIG_ESP_TASK_WDT_PANIC
109 ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, true));
110 #elif CONFIG_ESP_TASK_WDT
111 ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false));
112 #endif
113
114 //Add IDLE 0 to task wdt
115 #ifdef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
116 TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
117 if(idle_0 != NULL){
118 ESP_ERROR_CHECK(esp_task_wdt_add(idle_0));
119 }
120 #endif
121 //Add IDLE 1 to task wdt
122 #ifdef CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
123 TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
124 if(idle_1 != NULL){
125 ESP_ERROR_CHECK(esp_task_wdt_add(idle_1));
126 }
127 #endif
128
129 app_main();
130 vTaskDelete(NULL);
131 }
132
133 // -------------------- Heap Related -----------------------
134
xPortCheckValidTCBMem(const void * ptr)135 bool xPortCheckValidTCBMem(const void *ptr)
136 {
137 return esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr);
138 }
139
xPortcheckValidStackMem(const void * ptr)140 bool xPortcheckValidStackMem(const void *ptr)
141 {
142 #ifdef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
143 return esp_ptr_byte_accessible(ptr);
144 #else
145 return esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr);
146 #endif
147 }
148
149 // ------------- FreeRTOS Static Allocation ----------------
150
151 /*
152 This function is required by FreeRTOS when configSUPPORT_STATIC_ALLOCATION is
153 enabled and is used by FreeRTOS to obtain memory for its IDLE tasks.
154
155 Like the pvPortMallocTcbMem() and pvPortMallocStackMem() macros, TCB and stack
156 memory MUST be placed in internal RAM.
157 */
vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize)158 void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
159 StackType_t **ppxIdleTaskStackBuffer,
160 uint32_t *pulIdleTaskStackSize )
161 {
162 StaticTask_t *pxTCBBufferTemp;
163 StackType_t *pxStackBufferTemp;
164 //Allocate TCB and stack buffer in internal memory
165 pxTCBBufferTemp = pvPortMallocTcbMem(sizeof(StaticTask_t));
166 pxStackBufferTemp = pvPortMallocStackMem(configIDLE_TASK_STACK_SIZE);
167 assert(pxTCBBufferTemp != NULL);
168 assert(pxStackBufferTemp != NULL);
169 //Write back pointers
170 *ppxIdleTaskTCBBuffer = pxTCBBufferTemp;
171 *ppxIdleTaskStackBuffer = pxStackBufferTemp;
172 *pulIdleTaskStackSize = configIDLE_TASK_STACK_SIZE;
173 }
174
175 /*
176 This function is required by FreeRTOS when configSUPPORT_STATIC_ALLOCATION is
177 enabled and is used by the FreeRTOS Timer to obtain memory for its daemone task.
178
179
180 Like the pvPortMallocTcbMem() and pvPortMallocStackMem() macros, TCB and stack
181 memory MUST be placed in internal RAM.
182 */
vApplicationGetTimerTaskMemory(StaticTask_t ** ppxTimerTaskTCBBuffer,StackType_t ** ppxTimerTaskStackBuffer,uint32_t * pulTimerTaskStackSize)183 void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
184 StackType_t **ppxTimerTaskStackBuffer,
185 uint32_t *pulTimerTaskStackSize )
186 {
187 StaticTask_t *pxTCBBufferTemp;
188 StackType_t *pxStackBufferTemp;
189 //Allocate TCB and stack buffer in internal memory
190 pxTCBBufferTemp = pvPortMallocTcbMem(sizeof(StaticTask_t));
191 pxStackBufferTemp = pvPortMallocStackMem(configTIMER_TASK_STACK_DEPTH);
192 assert(pxTCBBufferTemp != NULL);
193 assert(pxStackBufferTemp != NULL);
194 //Write back pointers
195 *ppxTimerTaskTCBBuffer = pxTCBBufferTemp;
196 *ppxTimerTaskStackBuffer = pxStackBufferTemp;
197 *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
198 }
199