Skip to content

Commit 6798ab3

Browse files
committed
Merge branch 'bugfix/shared_stack_not_switching_correctly' into 'master'
bugfix/shared_stack: Fix task stack not being replaced by shared stack correctly See merge request espressif/esp-idf!7956
2 parents f83a61e + 11f6add commit 6798ab3

File tree

9 files changed

+151
-106
lines changed

9 files changed

+151
-106
lines changed

components/esp_common/include/esp_expression_with_stack.h

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414
#pragma once
1515

16+
#include <stdbool.h>
1617
#include "freertos/FreeRTOS.h"
1718
#include "freertos/semphr.h"
1819
#include "freertos/task.h"
@@ -23,56 +24,25 @@
2324
extern "C" {
2425
#endif
2526

27+
typedef void (*shared_stack_function)(void);
28+
29+
#define ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression) \
30+
esp_execute_shared_stack_function(lock, stack, stack_size, expression)
31+
2632
/**
27-
* @brief Executes a 1-line expression with a application alocated stack
33+
* @brief Calls user defined shared stack space function
2834
* @param lock Mutex object to protect in case of shared stack
2935
* @param stack Pointer to user alocated stack
3036
* @param stack_size Size of current stack in bytes
31-
* @param expression Expression or function to be executed using the stack
37+
* @param function pointer to the shared stack function to be executed
3238
* @note if either lock, stack or stack size is invalid, the expression will
3339
* be called using the current stack.
3440
*/
35-
#define ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression) \
36-
({ \
37-
assert(lock && stack && (stack_size >= CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE)); \
38-
uint32_t backup; \
39-
xSemaphoreTake(lock, portMAX_DELAY); \
40-
StackType_t *top_of_stack = esp_switch_stack_setup(stack, stack_size); \
41-
esp_switch_stack_enter(top_of_stack, &backup); \
42-
{ \
43-
expression; \
44-
} \
45-
esp_switch_stack_exit(&backup); \
46-
StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle(); \
47-
/* pxDummy6 is the stack base of current thread defined in TCB_t */ \
48-
/* place the watchpoint on current task stack after function execution*/ \
49-
vPortSetStackWatchpoint(current->pxDummy6); \
50-
xSemaphoreGive(lock); \
51-
})
52-
53-
/**
54-
* @brief Fill stack frame with CPU-specifics value before use
55-
* @param stack Caller allocated stack pointer
56-
* @param stack_size Size of stack in bytes
57-
* @return New pointer to the top of stack
58-
* @note Application must not call this function directly
59-
*/
60-
StackType_t * esp_switch_stack_setup(StackType_t *stack, size_t stack_size);
41+
void esp_execute_shared_stack_function(SemaphoreHandle_t lock,
42+
void *stack,
43+
size_t stack_size,
44+
shared_stack_function function);
6145

62-
/**
63-
* @brief Changes CPU sp-register to use another stack space and save the previous one
64-
* @param stack Caller allocated stack pointer
65-
* @param backup_stack Pointer to a place to save the current stack
66-
* @note Application must not call this function directly
67-
*/
68-
extern void esp_switch_stack_enter(StackType_t *stack, uint32_t *backup_stack);
69-
70-
/**
71-
* @brief Restores the previous CPU sp-register
72-
* @param backup_stack Pointer to the place where stack was saved
73-
* @note Application must not call this function directly
74-
*/
75-
extern void esp_switch_stack_exit(uint32_t *backup_stack);
7646

7747
#ifdef __cplusplus
7848
}

components/newlib/test/test_shared_stack_printf.c

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,53 @@
88
#include "test_utils.h"
99
#include "esp_expression_with_stack.h"
1010

11-
//makes sure this is not the task stack...
11+
#define SHARED_STACK_SIZE 8192
12+
13+
static StackType_t *shared_stack_sp = NULL;
14+
15+
void external_stack_function(void)
16+
{
17+
printf("Executing this printf from external stack! sp=%p\n", get_sp());
18+
shared_stack_sp = (StackType_t *)get_sp();
19+
}
20+
1221
void another_external_stack_function(void)
1322
{
1423
//We can even use Freertos resources inside of this context.
15-
vTaskDelay(100);
16-
printf("Executing this another printf inside a function with external stack");
24+
printf("We can even use FreeRTOS resources... yielding, sp=%p\n", get_sp());
25+
taskYIELD();
26+
shared_stack_sp = (StackType_t *)get_sp();
1727
}
1828

1929
TEST_CASE("test printf using shared buffer stack", "[newlib]")
2030
{
21-
portSTACK_TYPE *shared_stack = malloc(8192 * sizeof(portSTACK_TYPE));
31+
portSTACK_TYPE *shared_stack = malloc(SHARED_STACK_SIZE);
2232

23-
TEST_ASSERT(shared_stack != NULL);
33+
TEST_ASSERT_NOT_NULL(shared_stack);
2434

2535
SemaphoreHandle_t printf_lock = xSemaphoreCreateMutex();
2636
TEST_ASSERT_NOT_NULL(printf_lock);
37+
printf("current task sp: %p\n", get_sp());
38+
printf("shared_stack: %p\n", (void *)shared_stack);
39+
printf("shared_stack expected top: %p\n", (void *)(shared_stack + SHARED_STACK_SIZE));
40+
41+
42+
esp_execute_shared_stack_function(printf_lock,
43+
shared_stack,
44+
SHARED_STACK_SIZE,
45+
external_stack_function);
46+
47+
TEST_ASSERT(((shared_stack_sp >= shared_stack) &&
48+
(shared_stack_sp < (shared_stack + SHARED_STACK_SIZE))));
49+
50+
esp_execute_shared_stack_function(printf_lock,
51+
shared_stack,
52+
SHARED_STACK_SIZE,
53+
another_external_stack_function);
54+
55+
TEST_ASSERT(((shared_stack_sp >= shared_stack) &&
56+
(shared_stack_sp < (shared_stack + SHARED_STACK_SIZE))));
2757

28-
ESP_EXECUTE_EXPRESSION_WITH_STACK(printf_lock, shared_stack,8192,printf("Executing this printf from external stack! \n"));
29-
ESP_EXECUTE_EXPRESSION_WITH_STACK(printf_lock, shared_stack,8192,another_external_stack_function());
3058
vSemaphoreDelete(printf_lock);
3159
free(shared_stack);
3260
}

components/xtensa/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ else()
77
set(priv_requires soc freertos)
88
set(srcs "debug_helpers.c"
99
"debug_helpers_asm.S"
10-
"expression_with_stack_xtensa_asm.S"
1110
"expression_with_stack_xtensa.c"
11+
"expression_with_stack_xtensa_asm.S"
1212
"eri.c"
1313
"trax.c"
1414
"${target}/trax_init.c"

components/xtensa/expression_with_stack_xtensa.c

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,72 @@
1515
#include <esp_expression_with_stack.h>
1616
#include <freertos/xtensa_rtos.h>
1717
#include <freertos/xtensa_context.h>
18+
#include <setjmp.h>
19+
#include <string.h>
1820

19-
StackType_t * esp_switch_stack_setup(StackType_t *stack, size_t stack_size)
21+
StackType_t *xtensa_shared_stack;
22+
shared_stack_function xtensa_shared_stack_callback;
23+
jmp_buf xtensa_shared_stack_env;
24+
bool xtensa_shared_stack_function_done = false;
25+
static portMUX_TYPE xtensa_shared_stack_spinlock = portMUX_INITIALIZER_UNLOCKED;
26+
static void *current_task_stack = NULL;
27+
28+
extern void esp_shared_stack_invoke_function(void);
29+
30+
static void esp_switch_stack_setup(StackType_t *stack, size_t stack_size)
2031
{
2132
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
2233
esp_clear_watchpoint(1);
23-
uint32_t watchpoint_place = ((uint32_t)stack + 32) & 0x1f ;
34+
uint32_t watchpoint_place = ((uint32_t)stack + 32) & ~0x1f ;
2435
#endif
25-
StackType_t *top_of_stack = (StackType_t *)&stack[0] +
26-
((stack_size * sizeof(StackType_t)) / sizeof(StackType_t));
36+
//We need also to tweak current task stackpointer to avoid erroneous
37+
//stack overflow indication, so fills the stack with freertos known pattern:
38+
memset(stack, 0xa5U, stack_size * sizeof(StackType_t));
2739

28-
//Align stack to a 16byte boundary, as required by CPU specific:
29-
top_of_stack = (StackType_t *)(((UBaseType_t)(top_of_stack - 31) -
30-
ALIGNUP(0x10, sizeof(XtSolFrame) )) &
31-
~0xf);
40+
StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle();
41+
//Then put the fake stack inside of TCB:
42+
current_task_stack = current->pxDummy6;
43+
current->pxDummy6 = (void *)stack;
3244

33-
//Fake stack frame to do not break the backtrace
34-
XtSolFrame *frame = (XtSolFrame *)top_of_stack;
35-
frame->a0 = 0;
36-
frame->a1 = (UBaseType_t)top_of_stack;
45+
StackType_t *top_of_stack = stack + stack_size;
46+
47+
//Align stack to a 16byte boundary, as required by CPU specific:
48+
top_of_stack = (StackType_t *)(((UBaseType_t)(top_of_stack - 16) & ~0xf));
3749

3850
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
3951
esp_set_watchpoint(1, (uint8_t *)watchpoint_place, 32, ESP_WATCHPOINT_STORE);
4052
#endif
4153

42-
return top_of_stack;
54+
xtensa_shared_stack = top_of_stack;
55+
}
56+
57+
58+
void esp_execute_shared_stack_function(SemaphoreHandle_t lock, void *stack, size_t stack_size, shared_stack_function function)
59+
{
60+
assert(lock);
61+
assert(stack);
62+
assert(stack_size > 0 && stack_size >= CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE);
63+
assert(function);
64+
65+
xSemaphoreTake(lock, portMAX_DELAY);
66+
portENTER_CRITICAL(&xtensa_shared_stack_spinlock);
67+
xtensa_shared_stack_function_done = false;
68+
esp_switch_stack_setup(stack, stack_size);
69+
xtensa_shared_stack_callback = function;
70+
portEXIT_CRITICAL(&xtensa_shared_stack_spinlock);
71+
72+
setjmp(xtensa_shared_stack_env);
73+
if(!xtensa_shared_stack_function_done) {
74+
esp_shared_stack_invoke_function();
75+
}
76+
77+
portENTER_CRITICAL(&xtensa_shared_stack_spinlock);
78+
StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle();
79+
80+
//Restore current task stack:
81+
current->pxDummy6 = (StackType_t *)current_task_stack;
82+
vPortSetStackWatchpoint(current->pxDummy6);
83+
portEXIT_CRITICAL(&xtensa_shared_stack_spinlock);
84+
85+
xSemaphoreGive(lock);
4386
}

components/xtensa/expression_with_stack_xtensa_asm.S

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,43 +14,38 @@
1414

1515
#include <freertos/xtensa_context.h>
1616

17-
.extern esp_clear_watchpoint
17+
.extern xtensa_shared_stack
18+
.extern xtensa_shared_stack_callback
19+
.extern xtensa_shared_stack_function_done
20+
.extern xtensa_shared_stack_env
21+
.extern longjmp
1822
.text
1923

20-
/**
21-
* extern void switch_stack_enter(portSTACK_TYPE *stack, portSTACK_TYPE *backup_stack);
22-
*/
23-
.globl esp_switch_stack_enter
24-
.type esp_switch_stack_enter,@function
25-
.align 4
26-
esp_switch_stack_enter:
2724

28-
#ifndef __XTENSA_CALL0_ABI__
29-
entry sp, 0x10
30-
mov a4, a1
31-
s32i a4, a3, 0 /* on a3 there is a safe place to save the current stack */
32-
l32i a4, a2, 0 /* obtains the user allocated stack buffer */
33-
mov a1, a4 /* sp register now contains caller specified stack */
34-
retw
35-
#else
36-
#error "this code is written for Window ABI"
37-
#endif
25+
/* extern void esp_shared_stack_invoke_function(void) */
3826

39-
/**
40-
* extern void switch_stack_exit(portSTACK_TYPE *backup_stack);
41-
*/
42-
.globl esp_switch_stack_exit
43-
.type esp_switch_stack_exit,@function
44-
.align 4
45-
esp_switch_stack_exit:
27+
.globl esp_shared_stack_invoke_function
28+
.type esp_shared_stack_invoke_function,@function
29+
.align 4
30+
esp_shared_stack_invoke_function:
4631

4732
#ifndef __XTENSA_CALL0_ABI__
48-
entry sp, 0x10
49-
50-
l32i a4, a2, 0 /* recover the original task stack */
51-
mov a1, a4 /* put it on sp register again */
52-
retw
53-
33+
movi a0, 0 /* must not rotate the window here, */
34+
/* the state of execution for shared stack */
35+
/* functions will be completely destroyed at end */
36+
movi a6, xtensa_shared_stack
37+
l32i sp, a6, 0 /* load shared stack pointer */
38+
movi a12, xtensa_shared_stack_callback
39+
l32i a12, a12, 0
40+
callx4 a12 /* call user function */
41+
movi a6, xtensa_shared_stack_function_done
42+
movi a7, 1
43+
s32i a7, a6, 0 /* hint the function was finished */
44+
movi a6, xtensa_shared_stack_env
45+
movi a7, 0
46+
movi a12, longjmp
47+
callx4 a12 /* jump to last clean state previously saved */
48+
ret
5449
#else
5550
#error "this code is written for Window ABI"
5651
#endif

docs/en/api-reference/system/esp_expression_with_stack.rst renamed to docs/en/api-reference/system/esp_function_with_shared_stack.rst

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,31 @@ A given function can be executed with a user allocated stack space
88
which is independent of current task stack, this mechanism can be
99
used to save stack space wasted by tasks which call a common function
1010
with intensive stack usage such as `printf`. The given function can
11-
be called inside the macro :cpp:func:`ESP_EXECUTE_EXPRESSION_WITH_STACK`
12-
it will redirect the target function to be executed using the space
13-
allocated by the user.
11+
be called inside the shared stack space which is a callback function
12+
deferred by calling :cpp:func:`esp_execute_shared_stack_function`,
13+
passing that function as parameter
1414

1515
Usage
1616
-----
1717

18-
:cpp:func:`ESP_EXECUTE_EXPRESSION_WITH_STACK` takes three arguments,
18+
:cpp:func:`esp_execute_shared_stack_function` takes four arguments,
1919
a mutex object allocated by the caller, which is used to protect if
2020
the same function shares its allocated stack, a pointer to the top
21-
of stack used to that fuction, and the function itself, note the
22-
function is passed exactly in the same way as do when you call
23-
it on a regular way.
21+
of stack used to that fuction, the size in bytes of stack and, a pointer
22+
to a user function where the shared stack space will reside, after calling
23+
the function, the user defined function will be deferred as a callback
24+
where functions can be called using the user allocated space without
25+
taking space from current task stack.
2426

2527
The usage may looks like the code below:
2628

2729
.. code-block:: c
2830
31+
void external_stack_function(void)
32+
{
33+
printf("Executing this printf from external stack! \n");
34+
}
35+
2936
//Let's suppose we wanting to call printf using a separated stack space
3037
//allowing app to reduce its stack size.
3138
void app_main()
@@ -39,9 +46,11 @@ The usage may looks like the code below:
3946
assert(printf_lock != NULL);
4047
4148
//Call the desired function using the macro helper:
42-
ESP_EXECUTE_EXPRESSION_WITH_STACK(printf_lock,
43-
shared_stack,
44-
printf("Executing this from external stack! \n"));
49+
esp_execute_shared_stack_function(printf_lock,
50+
shared_stack,
51+
8192,
52+
external_stack_function);
53+
4554
vSemaphoreDelete(printf_lock);
4655
free(shared_stack);
4756
}

docs/en/api-reference/system/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ System API
1919
High Resolution Timer <esp_timer>
2020
:esp32: Himem (large external SPI RAM) API <himem>
2121
:esp32: Inter-Processor Call <ipc>
22-
Call function with external stack <esp_expression_with_stack>
22+
Call function with external stack <esp_function_with_shared_stack>
2323
Interrupt Allocation <intr_alloc>
2424
Logging <log>
2525
Miscellaneous System APIs <system>

docs/zh_CN/api-reference/system/esp_expression_with_stack.rst

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.. include:: ../../../en/api-reference/system/esp_function_with_shared_stack.rst

0 commit comments

Comments
 (0)