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