Skip to content

Commit 70ab924

Browse files
committed
Especially when internal memory fills up, some FreeRTOS structures (queues etc) get allocated in psram. These structures also contain a spinlock, which needs an atomic-compare-swap operation to work. The psram hardware, however, does not support this operation. As a workaround, this patch detects these spinlocks and will, instead of S32C1I, use equivalent C-code to simulate the behaviour, with an (internal) mux for atomicity.
1 parent 8ef7434 commit 70ab924

File tree

8 files changed

+263
-115
lines changed

8 files changed

+263
-115
lines changed

components/freertos/include/freertos/portable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,5 +219,7 @@ uint32_t xPortGetTickRateHz(void);
219219
}
220220
#endif
221221

222+
void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set);
223+
222224
#endif /* PORTABLE_H */
223225

components/freertos/port.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,10 +306,6 @@ void vPortAssertIfInISR()
306306
* For kernel use: Initialize a per-CPU mux. Mux will be initialized unlocked.
307307
*/
308308
void vPortCPUInitializeMutex(portMUX_TYPE *mux) {
309-
#if defined(CONFIG_SPIRAM_SUPPORT)
310-
// Check if mux belongs to internal memory (DRAM), prerequisite for atomic operations
311-
configASSERT(esp_ptr_internal((const void *) mux));
312-
#endif
313309

314310
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
315311
ets_printf("Initializing mux %p\n", mux);
@@ -386,6 +382,34 @@ void vPortSetStackWatchpoint( void* pxStackStart ) {
386382
esp_set_watchpoint(1, (char*)addr, 32, ESP_WATCHPOINT_STORE);
387383
}
388384

385+
#if defined(CONFIG_SPIRAM_SUPPORT)
386+
/*
387+
* Compare & set (S32C1) does not work in external RAM. Instead, this routine uses a mux (in internal memory) to fake it.
388+
*/
389+
static portMUX_TYPE extram_mux = portMUX_INITIALIZER_UNLOCKED;
390+
391+
void uxPortCompareSetExtram(volatile uint32_t *addr, uint32_t compare, uint32_t *set) {
392+
uint32_t prev;
393+
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
394+
vPortCPUAcquireMutexIntsDisabled(&extram_mux, portMUX_NO_TIMEOUT, __FUNCTION__, __LINE__);
395+
#else
396+
vPortCPUAcquireMutexIntsDisabled(&extram_mux, portMUX_NO_TIMEOUT);
397+
#endif
398+
prev=*addr;
399+
if (prev==compare) {
400+
*addr=*set;
401+
}
402+
*set=prev;
403+
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
404+
vPortCPUReleaseMutexIntsDisabled(&extram_mux, __FUNCTION__, __LINE__);
405+
#else
406+
vPortCPUReleaseMutexIntsDisabled(&extram_mux);
407+
#endif
408+
}
409+
#endif //defined(CONFIG_SPIRAM_SUPPORT)
410+
411+
412+
389413
uint32_t xPortGetTickRateHz(void) {
390414
return (uint32_t)configTICK_RATE_HZ;
391415
}

components/freertos/portmux_impl.h

Lines changed: 52 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -32,138 +32,80 @@
3232
deal by FreeRTOS internals.
3333
3434
It should be #included by freertos port.c or tasks.c, in esp-idf.
35+
36+
The way it works is that it essentially uses portmux_impl.inc.h as a
37+
generator template of sorts. When no external memory is used, this
38+
template is only used to generate the vPortCPUAcquireMutexIntsDisabledInternal
39+
and vPortCPUReleaseMutexIntsDisabledInternal functions, which use S32C1 to
40+
do an atomic compare & swap. When external memory is used the functions
41+
vPortCPUAcquireMutexIntsDisabledExtram and vPortCPUReleaseMutexIntsDisabledExtram
42+
are also generated, which use uxPortCompareSetExtram to fake the S32C1 instruction.
43+
The wrapper functions vPortCPUAcquireMutexIntsDisabled and
44+
vPortCPUReleaseMutexIntsDisabled will then use the appropriate function to do the
45+
actual lock/unlock.
3546
*/
3647
#include "soc/cpu.h"
48+
#include "portable.h"
3749

3850
/* XOR one core ID with this value to get the other core ID */
3951
#define CORE_ID_XOR_SWAP (CORE_ID_PRO ^ CORE_ID_APP)
4052

41-
static inline bool __attribute__((always_inline))
42-
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
43-
vPortCPUAcquireMutexIntsDisabled(portMUX_TYPE *mux, int timeout_cycles, const char *fnName, int line) {
44-
#else
45-
vPortCPUAcquireMutexIntsDisabled(portMUX_TYPE *mux, int timeout_cycles) {
46-
#endif
47-
#if !CONFIG_FREERTOS_UNICORE
48-
uint32_t res;
49-
portBASE_TYPE coreID, otherCoreID;
50-
uint32_t ccount_start;
51-
bool set_timeout = timeout_cycles > portMUX_NO_TIMEOUT;
52-
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
53-
if (!set_timeout) {
54-
timeout_cycles = 10000; // Always set a timeout in debug mode
55-
set_timeout = true;
56-
}
57-
#endif
58-
if (set_timeout) { // Timeout
59-
RSR(CCOUNT, ccount_start);
60-
}
6153

62-
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
63-
uint32_t owner = mux->owner;
64-
if (owner != portMUX_FREE_VAL && owner != CORE_ID_PRO && owner != CORE_ID_APP) {
65-
ets_printf("ERROR: vPortCPUAcquireMutex: mux %p is uninitialized (0x%X)! Called from %s line %d.\n", mux, owner, fnName, line);
66-
mux->owner=portMUX_FREE_VAL;
67-
}
68-
#endif
6954

70-
/* Spin until we own the core */
71-
72-
RSR(PRID, coreID);
73-
/* Note: coreID is the full 32 bit core ID (CORE_ID_PRO/CORE_ID_APP),
74-
not the 0/1 value returned by xPortGetCoreID()
75-
*/
76-
otherCoreID = CORE_ID_XOR_SWAP ^ coreID;
77-
do {
78-
/* mux->owner should be one of portMUX_FREE_VAL, CORE_ID_PRO,
79-
CORE_ID_APP:
80-
81-
- If portMUX_FREE_VAL, we want to atomically set to 'coreID'.
82-
- If "our" coreID, we can drop through immediately.
83-
- If "otherCoreID", we spin here.
84-
*/
85-
res = coreID;
86-
uxPortCompareSet(&mux->owner, portMUX_FREE_VAL, &res);
87-
88-
if (res != otherCoreID) {
89-
break; // mux->owner is "our" coreID
90-
}
91-
92-
if (set_timeout) {
93-
uint32_t ccount_now;
94-
RSR(CCOUNT, ccount_now);
95-
if (ccount_now - ccount_start > (unsigned)timeout_cycles) {
96-
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
97-
ets_printf("Timeout on mux! last non-recursive lock %s line %d, curr %s line %d\n", mux->lastLockedFn, mux->lastLockedLine, fnName, line);
98-
ets_printf("Owner 0x%x count %d\n", mux->owner, mux->count);
99-
#endif
100-
return false;
101-
}
102-
}
103-
} while (1);
10455

105-
assert(res == coreID || res == portMUX_FREE_VAL); /* any other value implies memory corruption or uninitialized mux */
106-
assert((res == portMUX_FREE_VAL) == (mux->count == 0)); /* we're first to lock iff count is zero */
107-
assert(mux->count < 0xFF); /* Bad count value implies memory corruption */
56+
//Define the mux routines for use with muxes in internal RAM
57+
#define PORTMUX_AQUIRE_MUX_FN_NAME vPortCPUAcquireMutexIntsDisabledInternal
58+
#define PORTMUX_RELEASE_MUX_FN_NAME vPortCPUReleaseMutexIntsDisabledInternal
59+
#define PORTMUX_COMPARE_SET_FN_NAME uxPortCompareSet
60+
#include "portmux_impl.inc.h"
61+
#undef PORTMUX_AQUIRE_MUX_FN_NAME
62+
#undef PORTMUX_RELEASE_MUX_FN_NAME
63+
#undef PORTMUX_COMPARE_SET_FN_NAME
10864

109-
/* now we own it, we can increment the refcount */
110-
mux->count++;
11165

66+
#if defined(CONFIG_SPIRAM_SUPPORT)
11267

113-
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
114-
if (res==portMUX_FREE_VAL) { //initial lock
115-
mux->lastLockedFn=fnName;
116-
mux->lastLockedLine=line;
117-
} else {
118-
ets_printf("Recursive lock: count=%d last non-recursive lock %s line %d, curr %s line %d\n", mux->count-1,
119-
mux->lastLockedFn, mux->lastLockedLine, fnName, line);
120-
}
121-
#endif /* CONFIG_FREERTOS_PORTMUX_DEBUG */
122-
#endif /* CONFIG_FREERTOS_UNICORE */
123-
return true;
124-
}
68+
#define PORTMUX_AQUIRE_MUX_FN_NAME vPortCPUAcquireMutexIntsDisabledExtram
69+
#define PORTMUX_RELEASE_MUX_FN_NAME vPortCPUReleaseMutexIntsDisabledExtram
70+
#define PORTMUX_COMPARE_SET_FN_NAME uxPortCompareSetExtram
71+
#include "portmux_impl.inc.h"
72+
#undef PORTMUX_AQUIRE_MUX_FN_NAME
73+
#undef PORTMUX_RELEASE_MUX_FN_NAME
74+
#undef PORTMUX_COMPARE_SET_FN_NAME
12575

126-
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
127-
static inline void vPortCPUReleaseMutexIntsDisabled(portMUX_TYPE *mux, const char *fnName, int line) {
128-
#else
129-
static inline void vPortCPUReleaseMutexIntsDisabled(portMUX_TYPE *mux) {
13076
#endif
131-
#if !CONFIG_FREERTOS_UNICORE
132-
portBASE_TYPE coreID;
77+
78+
13379
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
134-
const char *lastLockedFn=mux->lastLockedFn;
135-
int lastLockedLine=mux->lastLockedLine;
136-
mux->lastLockedFn=fnName;
137-
mux->lastLockedLine=line;
138-
uint32_t owner = mux->owner;
139-
if (owner != portMUX_FREE_VAL && owner != CORE_ID_PRO && owner != CORE_ID_APP) {
140-
ets_printf("ERROR: vPortCPUReleaseMutex: mux %p is invalid (0x%x)!\n", mux, mux->owner);
141-
}
80+
#define PORTMUX_AQUIRE_MUX_FN_ARGS portMUX_TYPE *mux, int timeout_cycles, const char *fnName, int line
81+
#define PORTMUX_RELEASE_MUX_FN_ARGS portMUX_TYPE *mux, const char *fnName, int line
82+
#define PORTMUX_AQUIRE_MUX_FN_CALL_ARGS(x) x, timeout_cycles, fnName, line
83+
#define PORTMUX_RELEASE_MUX_FN_CALL_ARGS(x) x, fnName, line
84+
#else
85+
#define PORTMUX_AQUIRE_MUX_FN_ARGS portMUX_TYPE *mux, int timeout_cycles
86+
#define PORTMUX_RELEASE_MUX_FN_ARGS portMUX_TYPE *mux
87+
#define PORTMUX_AQUIRE_MUX_FN_CALL_ARGS(x) x, timeout_cycles
88+
#define PORTMUX_RELEASE_MUX_FN_CALL_ARGS(x) x
14289
#endif
14390

144-
#if CONFIG_FREERTOS_PORTMUX_DEBUG || !defined(NDEBUG)
145-
RSR(PRID, coreID);
146-
#endif
14791

148-
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG
149-
if (coreID != mux->owner) {
150-
ets_printf("ERROR: vPortCPUReleaseMutex: mux %p was already unlocked!\n", mux);
151-
ets_printf("Last non-recursive unlock %s line %d, curr unlock %s line %d\n", lastLockedFn, lastLockedLine, fnName, line);
92+
static inline bool __attribute__((always_inline)) vPortCPUAcquireMutexIntsDisabled(PORTMUX_AQUIRE_MUX_FN_ARGS) {
93+
#if defined(CONFIG_SPIRAM_SUPPORT)
94+
if (esp_ptr_external_ram(mux)) {
95+
return vPortCPUAcquireMutexIntsDisabledExtram(PORTMUX_AQUIRE_MUX_FN_CALL_ARGS(mux));
15296
}
15397
#endif
98+
return vPortCPUAcquireMutexIntsDisabledInternal(PORTMUX_AQUIRE_MUX_FN_CALL_ARGS(mux));
99+
}
154100

155-
assert(coreID == mux->owner); // This is a mutex we didn't lock, or it's corrupt
156-
assert(mux->count > 0); // Indicates memory corruption
157-
assert(mux->count < 0x100); // Indicates memory corruption
158101

159-
mux->count--;
160-
if(mux->count == 0) {
161-
mux->owner = portMUX_FREE_VAL;
162-
}
163-
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG_RECURSIVE
164-
else {
165-
ets_printf("Recursive unlock: count=%d last locked %s line %d, curr %s line %d\n", mux->count, lastLockedFn, lastLockedLine, fnName, line);
102+
static inline void vPortCPUReleaseMutexIntsDisabled(PORTMUX_RELEASE_MUX_FN_ARGS) {
103+
#if defined(CONFIG_SPIRAM_SUPPORT)
104+
if (esp_ptr_external_ram(mux)) {
105+
vPortCPUReleaseMutexIntsDisabledExtram(PORTMUX_RELEASE_MUX_FN_CALL_ARGS(mux));
106+
return;
166107
}
167108
#endif
168-
#endif //!CONFIG_FREERTOS_UNICORE
109+
vPortCPUReleaseMutexIntsDisabledInternal(PORTMUX_RELEASE_MUX_FN_CALL_ARGS(mux));
169110
}
111+

0 commit comments

Comments
 (0)