1 /*
2 * FreeRTOS Kernel V10.2.1
3 * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * http://www.FreeRTOS.org
23 * http://aws.amazon.com/freertos
24 *
25 * 1 tab == 4 spaces!
26 */
27 #ifndef PORTMACRO_H
28 #define PORTMACRO_H
29
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33
34 #ifndef __ASSEMBLER__
35
36 #include <sdkconfig.h>
37 #include <stdint.h>
38 #include <stdlib.h>
39 #include <stdbool.h>
40 #include <stdarg.h>
41 #include <xtensa/hal.h>
42 #include <xtensa/config/core.h>
43 #include <xtensa/config/system.h> /* required for XSHAL_CLIB */
44 #include <xtensa/xtruntime.h>
45 #include "esp_private/crosscore_int.h"
46 #include "esp_timer.h" /* required for FreeRTOS run time stats */
47 #include "esp_system.h"
48 #include "esp_newlib.h"
49 #include "soc/spinlock.h"
50 #include <esp_heap_caps.h>
51 #include "esp_rom_sys.h"
52 #include "sdkconfig.h"
53 #include "esp_osal/xtensa_api.h"
54 #include "esp_system.h"
55 #include "soc/cpu.h"
56 #include <limits.h>
57
58 #ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS
59 #include "soc/soc_memory_layout.h"
60 #endif
61
62 /*-----------------------------------------------------------
63 * Port specific definitions.
64 *
65 * The settings in this file configure FreeRTOS correctly for the
66 * given hardware and compiler.
67 *
68 * These settings should not be altered.
69 *-----------------------------------------------------------
70 */
71
72 #include "esp_system.h"
73 #include "hal/cpu_hal.h"
74 #include "xt_instr_macros.h"
75
76 /* Type definitions. */
77 #define portCHAR int8_t
78 #define portFLOAT float
79 #define portDOUBLE double
80 #define portLONG int32_t
81 #define portSHORT int16_t
82 #define portSTACK_TYPE uint8_t
83 #define portBASE_TYPE int
84
85 typedef portSTACK_TYPE StackType_t;
86 typedef portBASE_TYPE BaseType_t;
87 typedef unsigned portBASE_TYPE UBaseType_t;
88
89 #if( configUSE_16_BIT_TICKS == 1 )
90 typedef uint16_t TickType_t;
91 #define portMAX_DELAY ( TickType_t ) 0xffff
92 #else
93 typedef uint32_t TickType_t;
94 #define portMAX_DELAY ( TickType_t ) 0xffffffffUL
95 #endif
96 /*-----------------------------------------------------------*/
97
98 // portbenchmark
99 #include "portbenchmark.h"
100
101 #include "sdkconfig.h"
102 #include "esp_attr.h"
103 #include "portmacro_priv.h"
104
105 // Cleaner solution allows nested interrupts disabling and restoring via local registers or stack.
106 // They can be called from interrupts too.
107 // WARNING: Only applies to current CPU. See notes above.
portENTER_CRITICAL_NESTED(void)108 static inline unsigned portENTER_CRITICAL_NESTED(void) {
109 unsigned state = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL);
110 portbenchmarkINTERRUPT_DISABLE();
111 return state;
112 }
113 #define portEXIT_CRITICAL_NESTED(state) do { portbenchmarkINTERRUPT_RESTORE(state); XTOS_RESTORE_JUST_INTLEVEL(state); } while (0)
114
115 /*
116 Modifications to portENTER_CRITICAL.
117
118 For an introduction, see "Critical Sections & Disabling Interrupts" in docs/api-guides/freertos-smp.rst
119
120 The original portENTER_CRITICAL only disabled the ISRs. This is enough for single-CPU operation: by
121 disabling the interrupts, there is no task switch so no other tasks can meddle in the data, and because
122 interrupts are disabled, ISRs can't corrupt data structures either.
123
124 For multiprocessing, things get a bit more hairy. First of all, disabling the interrupts doesn't stop
125 the tasks or ISRs on the other processors meddling with our CPU. For tasks, this is solved by adding
126 a spinlock to the portENTER_CRITICAL macro. A task running on the other CPU accessing the same data will
127 spinlock in the portENTER_CRITICAL code until the first CPU is done.
128
129 For ISRs, we now also need muxes: while portENTER_CRITICAL disabling interrupts will stop ISRs on the same
130 CPU from meddling with the data, it does not stop interrupts on the other cores from interfering with the
131 data. For this, we also use a spinlock in the routines called by the ISR, but these spinlocks
132 do not disable the interrupts (because they already are).
133
134 This all assumes that interrupts are either entirely disabled or enabled. Interrupt priority levels
135 will break this scheme.
136
137 Remark: For the ESP32, portENTER_CRITICAL and portENTER_CRITICAL_ISR both alias vPortEnterCritical, meaning
138 that either function can be called both from ISR as well as task context. This is not standard FreeRTOS
139 behaviour; please keep this in mind if you need any compatibility with other FreeRTOS implementations.
140 */
141 /* "mux" data structure (spinlock) */
142 typedef spinlock_t portMUX_TYPE;
143
144 #define portMUX_FREE_VAL SPINLOCK_FREE
145 #define portMUX_NO_TIMEOUT SPINLOCK_WAIT_FOREVER /* When passed for 'timeout_cycles', spin forever if necessary */
146 #define portMUX_TRY_LOCK SPINLOCK_NO_WAIT /* Try to acquire the spinlock a single time only */
147 #define portMUX_INITIALIZER_UNLOCKED SPINLOCK_INITIALIZER
148
149 #define portCRITICAL_NESTING_IN_TCB 0
150
vPortCPUInitializeMutex(portMUX_TYPE * mux)151 static inline void __attribute__((always_inline)) vPortCPUInitializeMutex(portMUX_TYPE *mux)
152 {
153 spinlock_initialize(mux);
154 }
155
vPortCPUAcquireMutex(portMUX_TYPE * mux)156 static inline void __attribute__((always_inline)) vPortCPUAcquireMutex(portMUX_TYPE *mux)
157 {
158 spinlock_acquire(mux, portMUX_NO_TIMEOUT);
159 }
160
vPortCPUAcquireMutexTimeout(portMUX_TYPE * mux,int timeout)161 static inline bool __attribute__((always_inline)) vPortCPUAcquireMutexTimeout(portMUX_TYPE *mux, int timeout)
162 {
163 return (spinlock_acquire(mux, timeout));
164 }
165
vPortCPUReleaseMutex(portMUX_TYPE * mux)166 static inline void __attribute__((always_inline)) vPortCPUReleaseMutex(portMUX_TYPE *mux)
167 {
168 spinlock_release(mux);
169 }
170
171 void vPortEnterCritical(portMUX_TYPE *mux);
172 void vPortExitCritical(portMUX_TYPE *mux);
173
174 #define portASSERT_IF_IN_ISR() vPortAssertIfInISR()
175 void vPortAssertIfInISR(void);
176
177 /*
178 * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs
179 * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway.
180 */
181 BaseType_t xPortInIsrContext(void);
182
vPortEnterCriticalCompliance(portMUX_TYPE * mux)183 static inline void __attribute__((always_inline)) vPortEnterCriticalCompliance(portMUX_TYPE *mux)
184 {
185 if(!xPortInIsrContext()) {
186 vPortEnterCritical(mux);
187 } else {
188 esp_rom_printf("%s:%d (%s)- port*_CRITICAL called from ISR context!\n",
189 __FILE__, __LINE__, __FUNCTION__);
190 abort();
191 }
192 }
193
vPortExitCriticalCompliance(portMUX_TYPE * mux)194 static inline void __attribute__((always_inline)) vPortExitCriticalCompliance(portMUX_TYPE *mux)
195 {
196 if(!xPortInIsrContext()) {
197 vPortExitCritical(mux);
198 } else {
199 esp_rom_printf("%s:%d (%s)- port*_CRITICAL called from ISR context!\n",
200 __FILE__, __LINE__, __FUNCTION__);
201 abort();
202 }
203 }
204
205 #ifdef CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE
206 /* Calling port*_CRITICAL from ISR context would cause an assert failure.
207 * If the parent function is called from both ISR and Non-ISR context then call port*_CRITICAL_SAFE
208 */
209 #define portENTER_CRITICAL(mux) vPortEnterCriticalCompliance(mux)
210 #define portEXIT_CRITICAL(mux) vPortExitCriticalCompliance(mux)
211 #else
212 #define portENTER_CRITICAL(mux) vPortEnterCritical(mux)
213 #define portEXIT_CRITICAL(mux) vPortExitCritical(mux)
214 #endif
215
216 #define portENTER_CRITICAL_ISR(mux) vPortEnterCritical(mux)
217 #define portEXIT_CRITICAL_ISR(mux) vPortExitCritical(mux)
218
vPortEnterCriticalSafe(portMUX_TYPE * mux)219 static inline void __attribute__((always_inline)) vPortEnterCriticalSafe(portMUX_TYPE *mux)
220 {
221 if (xPortInIsrContext()) {
222 portENTER_CRITICAL_ISR(mux);
223 } else {
224 portENTER_CRITICAL(mux);
225 }
226 }
227
vPortExitCriticalSafe(portMUX_TYPE * mux)228 static inline void __attribute__((always_inline)) vPortExitCriticalSafe(portMUX_TYPE *mux)
229 {
230 if (xPortInIsrContext()) {
231 portEXIT_CRITICAL_ISR(mux);
232 } else {
233 portEXIT_CRITICAL(mux);
234 }
235 }
236
237 #define portENTER_CRITICAL_SAFE(mux) vPortEnterCriticalSafe(mux)
238 #define portEXIT_CRITICAL_SAFE(mux) vPortExitCriticalSafe(mux)
239
240 /*
241 * Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare
242 * *addr to 'compare'. If *addr == compare, *addr is set to *set. *set is updated with the previous
243 * value of *addr (either 'compare' or some other value.)
244 *
245 * Warning: From the ISA docs: in some (unspecified) cases, the s32c1i instruction may return the
246 * *bitwise inverse* of the old mem if the mem wasn't written. This doesn't seem to happen on the
247 * ESP32 (portMUX assertions would fail).
248 */
uxPortCompareSet(volatile uint32_t * addr,uint32_t compare,uint32_t * set)249 static inline void __attribute__((always_inline)) uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) {
250 compare_and_set_native(addr, compare, set);
251 }
252
253 // Critical section management. NW-TODO: replace XTOS_SET_INTLEVEL with more efficient version, if any?
254 // These cannot be nested. They should be used with a lot of care and cannot be called from interrupt level.
255 //
256 // Only applies to one CPU. See notes above & below for reasons not to use these.
257 #define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0)
258 #define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0)
259
260
261 // These FreeRTOS versions are similar to the nested versions above
262 #define portSET_INTERRUPT_MASK_FROM_ISR() portENTER_CRITICAL_NESTED()
263 #define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state)
264
265 //Because the ROM routines don't necessarily handle a stack in external RAM correctly, we force
266 //the stack memory to always be internal.
267 #define portTcbMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
268 #define portStackMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
269 #define pvPortMallocTcbMem(size) heap_caps_malloc(size, portTcbMemoryCaps)
270 #define pvPortMallocStackMem(size) heap_caps_malloc(size, portStackMemoryCaps)
271
272 //xTaskCreateStatic uses these functions to check incoming memory.
273 #define portVALID_TCB_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr))
274 #ifdef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
275 #define portVALID_STACK_MEM(ptr) esp_ptr_byte_accessible(ptr)
276 #else
277 #define portVALID_STACK_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr))
278 #endif
279
280
uxPortCompareSetExtram(volatile uint32_t * addr,uint32_t compare,uint32_t * set)281 static inline void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set)
282 {
283 #ifdef CONFIG_SPIRAM
284 compare_and_set_extram(addr, compare, set);
285 #endif
286 }
287
288
289 /*-----------------------------------------------------------*/
290
291 /* Architecture specifics. */
292 #define portSTACK_GROWTH ( -1 )
293 #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
294 #define portBYTE_ALIGNMENT 4
295 #define portNOP() XT_NOP()
296 /*-----------------------------------------------------------*/
297
298 /* Fine resolution time */
299 #define portGET_RUN_TIME_COUNTER_VALUE() xthal_get_ccount()
300 //ccount or esp_timer are initialized elsewhere
301 #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
302
303 #ifdef CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
304 /* Coarse resolution time (us) */
305 #define portALT_GET_RUN_TIME_COUNTER_VALUE(x) do {x = (uint32_t)esp_timer_get_time();} while(0)
306 #endif
307
308 void vPortYield( void );
309 void vPortEvaluateYieldFromISR(int argc, ...);
310 void _frxt_setup_switch( void );
311 /**
312 * Macro to count number of arguments of a __VA_ARGS__ used to support portYIELD_FROM_ISR with,
313 * or without arguments.
314 */
315 #define portGET_ARGUMENT_COUNT(...) portGET_ARGUMENT_COUNT_INNER(0, ##__VA_ARGS__,1,0)
316 #define portGET_ARGUMENT_COUNT_INNER(zero, one, count, ...) count
317
318 //_Static_assert(portGET_ARGUMENT_COUNT() == 0, "portGET_ARGUMENT_COUNT() result does not match for 0 arguments");
319 //_Static_assert(portGET_ARGUMENT_COUNT(1) == 1, "portGET_ARGUMENT_COUNT() result does not match for 1 argument");
320
321 #define portYIELD() vPortYield()
322
323 /**
324 * @note The macro below could be used when passing a single argument, or without any argument,
325 * it was developed to support both usages of portYIELD inside of an ISR. Any other usage form
326 * might result in undesired behaviour
327 */
328 #define portYIELD_FROM_ISR(...) vPortEvaluateYieldFromISR(portGET_ARGUMENT_COUNT(__VA_ARGS__), ##__VA_ARGS__)
329
330 /* Yielding within an API call (when interrupts are off), means the yield should be delayed
331 until interrupts are re-enabled.
332
333 To do this, we use the "cross-core" interrupt as a trigger to yield on this core when interrupts are re-enabled.This
334 is the same interrupt & code path which is used to trigger a yield between CPUs, although in this case the yield is
335 happening on the same CPU.
336 */
337 // #define portYIELD_WITHIN_API() esp_crosscore_int_send_yield(xPortGetCoreID())
338
339 /*-----------------------------------------------------------*/
340
341 /* Task function macros as described on the FreeRTOS.org WEB site. */
342 #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters )
343 #define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters )
344
345 // When coprocessors are defined, we to maintain a pointer to coprocessors area.
346 // We currently use a hack: redefine field xMPU_SETTINGS in TCB block as a structure that can hold:
347 // MPU wrappers, coprocessor area pointer, trace code structure, and more if needed.
348 // The field is normally used for memory protection. FreeRTOS should create another general purpose field.
349 typedef struct {
350 #if XCHAL_CP_NUM > 0
351 volatile StackType_t* coproc_area; // Pointer to coprocessor save area; MUST BE FIRST
352 #endif
353
354 #if portUSING_MPU_WRAPPERS
355 // Define here mpu_settings, which is port dependent
356 int mpu_setting; // Just a dummy example here; MPU not ported to Xtensa yet
357 #endif
358
359 #if configUSE_TRACE_FACILITY_2
360 struct {
361 // Cf. porttraceStamp()
362 int taskstamp; /* Stamp from inside task to see where we are */
363 int taskstampcount; /* A counter usually incremented when we restart the task's loop */
364 } porttrace;
365 #endif
366 } xMPU_SETTINGS;
367
368 // Main hack to use MPU_wrappers even when no MPU is defined (warning: mpu_setting should not be accessed; otherwise move this above xMPU_SETTINGS)
369 #if (XCHAL_CP_NUM > 0 || configUSE_TRACE_FACILITY_2) && !portUSING_MPU_WRAPPERS // If MPU wrappers not used, we still need to allocate coproc area
370 #undef portUSING_MPU_WRAPPERS
371 #define portUSING_MPU_WRAPPERS 1 // Enable it to allocate coproc area
372 #define MPU_WRAPPERS_H // Override mpu_wrapper.h to disable unwanted code
373 #define PRIVILEGED_FUNCTION
374 #define PRIVILEGED_DATA
375 #endif
376
377 extern void esp_vApplicationIdleHook( void );
378 extern void esp_vApplicationTickHook( void );
379
380 #ifndef CONFIG_FREERTOS_LEGACY_HOOKS
381 #define vApplicationIdleHook esp_vApplicationIdleHook
382 #define vApplicationTickHook esp_vApplicationTickHook
383 #endif /* !CONFIG_FREERTOS_LEGACY_HOOKS */
384
385 void vApplicationSleep( TickType_t xExpectedIdleTime );
386
387 #define portSUPPRESS_TICKS_AND_SLEEP( idleTime ) vApplicationSleep( idleTime )
388
389 void _xt_coproc_release(volatile void * coproc_sa_base);
390
391 /* Architecture specific optimisations. */
392 #if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
393
394 /* Check the configuration. */
395 #if( configMAX_PRIORITIES > 32 )
396 #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 different priorities as tasks that share a priority will time slice.
397 #endif
398
399 /* Store/clear the ready priorities in a bit map. */
400 #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
401 #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
402
403 /*-----------------------------------------------------------*/
404
405 #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __builtin_clz( ( uxReadyPriorities ) ) )
406
407 #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
408
409 /*
410 * Send an interrupt to another core in order to make the task running
411 * on it yield for a higher-priority task.
412 */
413
414 void vPortYieldOtherCore( BaseType_t coreid) ;
415
416 /*
417 Callback to set a watchpoint on the end of the stack. Called every context switch to change the stack
418 watchpoint around.
419 */
420 void vPortSetStackWatchpoint( void* pxStackStart );
421
422 /*
423 * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs
424 * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway.
425 */
426 BaseType_t xPortInIsrContext(void);
427
428 /*
429 * This function will be called in High prio ISRs. Returns true if the current core was in ISR context
430 * before calling into high prio ISR context.
431 */
432 BaseType_t xPortInterruptedFromISRContext(void);
433
434 /*
435 * The structures and methods of manipulating the MPU are contained within the
436 * port layer.
437 *
438 * Fills the xMPUSettings structure with the memory region information
439 * contained in xRegions.
440 */
441 #if( portUSING_MPU_WRAPPERS == 1 )
442 struct xMEMORY_REGION;
443 void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t usStackDepth ) PRIVILEGED_FUNCTION;
444 void vPortReleaseTaskMPUSettings( xMPU_SETTINGS *xMPUSettings );
445 #endif
446
447 /* Multi-core: get current core ID */
xPortGetCoreID(void)448 static inline BaseType_t IRAM_ATTR xPortGetCoreID(void) {
449 return cpu_hal_get_core_id();
450 }
451
452 /* Get tick rate per second */
453 uint32_t xPortGetTickRateHz(void);
454
xPortCanYield(void)455 static inline bool IRAM_ATTR xPortCanYield(void)
456 {
457 uint32_t ps_reg = 0;
458
459 //Get the current value of PS (processor status) register
460 RSR(PS, ps_reg);
461
462 /*
463 * intlevel = (ps_reg & 0xf);
464 * excm = (ps_reg >> 4) & 0x1;
465 * CINTLEVEL is max(excm * EXCMLEVEL, INTLEVEL), where EXCMLEVEL is 3.
466 * However, just return true, only intlevel is zero.
467 */
468
469 return ((ps_reg & PS_INTLEVEL_MASK) == 0);
470 }
471
472 // porttrace
473 #if configUSE_TRACE_FACILITY_2
474 #include "porttrace.h"
475 #endif
476
477 // configASSERT_2 if requested
478 #if configASSERT_2
479 #include <stdio.h>
480 void exit(int);
481 #define configASSERT( x ) if (!(x)) { porttracePrint(-1); printf("\nAssertion failed in %s:%d\n", __FILE__, __LINE__); exit(-1); }
482 #endif
483
484 #endif // __ASSEMBLER__
485
486 #ifdef __cplusplus
487 }
488 #endif
489
490 #endif /* PORTMACRO_H */
491