Merge branch 'backport/shared_stack_4.1' into 'release/v4.1'

Backport/shared stack 4.1

See merge request espressif/esp-idf!10302
This commit is contained in:
Angus Gratton
2020-09-18 05:15:30 +08:00
8 changed files with 155 additions and 105 deletions

View File

@@ -13,8 +13,10 @@
// limitations under the License. // limitations under the License.
#pragma once #pragma once
#include <stdbool.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_debug_helpers.h" #include "esp_debug_helpers.h"
#include "esp_log.h" #include "esp_log.h"
@@ -22,52 +24,25 @@
extern "C" { extern "C" {
#endif #endif
typedef void (*shared_stack_function)(void);
#define ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression) \
esp_execute_shared_stack_function(lock, stack, stack_size, expression)
/** /**
* @brief Executes a 1-line expression with a application alocated stack * @brief Calls user defined shared stack space function
* @param lock Mutex object to protect in case of shared stack * @param lock Mutex object to protect in case of shared stack
* @param stack Pointer to user alocated stack * @param stack Pointer to user alocated stack
* @param stack_size Size of current stack in bytes * @param stack_size Size of current stack in bytes
* @param expression Expression or function to be executed using the stack * @param function pointer to the shared stack function to be executed
* @note if either lock, stack or stack size is invalid, the expression will * @note if either lock, stack or stack size is invalid, the expression will
* be called using the current stack. * be called using the current stack.
*/ */
#define ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression) \ void esp_execute_shared_stack_function(SemaphoreHandle_t lock,
({ \ void *stack,
assert(lock && stack && (stack_size >= CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE)); \ size_t stack_size,
uint32_t backup; \ shared_stack_function function);
xSemaphoreTake(lock, portMAX_DELAY); \
StackType_t *top_of_stack = esp_switch_stack_setup(stack, stack_size); \
esp_switch_stack_enter(top_of_stack, &backup); \
{ \
expression; \
} \
esp_switch_stack_exit(&backup); \
xSemaphoreGive(lock); \
})
/**
* @brief Fill stack frame with CPU-specifics value before use
* @param stack Caller allocated stack pointer
* @param stack_size Size of stack in bytes
* @return New pointer to the top of stack
* @note Application must not call this function directly
*/
StackType_t * esp_switch_stack_setup(StackType_t *stack, size_t stack_size);
/**
* @brief Changes CPU sp-register to use another stack space and save the previous one
* @param stack Caller allocated stack pointer
* @param backup_stack Pointer to a place to save the current stack
* @note Application must not call this function directly
*/
extern void esp_switch_stack_enter(StackType_t *stack, uint32_t *backup_stack);
/**
* @brief Restores the previous CPU sp-register
* @param backup_stack Pointer to the place where stack was saved
* @note Application must not call this function directly
*/
extern void esp_switch_stack_exit(uint32_t *backup_stack);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -8,25 +8,53 @@
#include "test_utils.h" #include "test_utils.h"
#include "esp_expression_with_stack.h" #include "esp_expression_with_stack.h"
//makes sure this is not the task stack... #define SHARED_STACK_SIZE 8192
static StackType_t *shared_stack_sp = NULL;
void external_stack_function(void)
{
printf("Executing this printf from external stack! sp=%p\n", get_sp());
shared_stack_sp = (StackType_t *)get_sp();
}
void another_external_stack_function(void) void another_external_stack_function(void)
{ {
//We can even use Freertos resources inside of this context. //We can even use Freertos resources inside of this context.
vTaskDelay(100); printf("We can even use FreeRTOS resources... yielding, sp=%p\n", get_sp());
printf("Executing this another printf inside a function with external stack"); taskYIELD();
shared_stack_sp = (StackType_t *)get_sp();
} }
TEST_CASE("test printf using shared buffer stack", "[newlib]") TEST_CASE("test printf using shared buffer stack", "[newlib]")
{ {
portSTACK_TYPE *shared_stack = malloc(8192 * sizeof(portSTACK_TYPE)); portSTACK_TYPE *shared_stack = malloc(SHARED_STACK_SIZE);
TEST_ASSERT(shared_stack != NULL); TEST_ASSERT_NOT_NULL(shared_stack);
SemaphoreHandle_t printf_lock = xSemaphoreCreateMutex(); SemaphoreHandle_t printf_lock = xSemaphoreCreateMutex();
TEST_ASSERT_NOT_NULL(printf_lock); TEST_ASSERT_NOT_NULL(printf_lock);
printf("current task sp: %p\n", get_sp());
printf("shared_stack: %p\n", (void *)shared_stack);
printf("shared_stack expected top: %p\n", (void *)(shared_stack + SHARED_STACK_SIZE));
esp_execute_shared_stack_function(printf_lock,
shared_stack,
SHARED_STACK_SIZE,
external_stack_function);
TEST_ASSERT(((shared_stack_sp >= shared_stack) &&
(shared_stack_sp < (shared_stack + SHARED_STACK_SIZE))));
esp_execute_shared_stack_function(printf_lock,
shared_stack,
SHARED_STACK_SIZE,
another_external_stack_function);
TEST_ASSERT(((shared_stack_sp >= shared_stack) &&
(shared_stack_sp < (shared_stack + SHARED_STACK_SIZE))));
ESP_EXECUTE_EXPRESSION_WITH_STACK(printf_lock, shared_stack,8192,printf("Executing this printf from external stack! \n"));
ESP_EXECUTE_EXPRESSION_WITH_STACK(printf_lock, shared_stack,8192,another_external_stack_function());
vSemaphoreDelete(printf_lock); vSemaphoreDelete(printf_lock);
free(shared_stack); free(shared_stack);
} }

View File

@@ -15,14 +15,33 @@
#include <esp_expression_with_stack.h> #include <esp_expression_with_stack.h>
#include <freertos/xtensa_rtos.h> #include <freertos/xtensa_rtos.h>
#include <freertos/xtensa_context.h> #include <freertos/xtensa_context.h>
#include <setjmp.h>
#include <string.h>
StackType_t * esp_switch_stack_setup(StackType_t *stack, size_t stack_size) StackType_t *xtensa_shared_stack;
shared_stack_function xtensa_shared_stack_callback;
jmp_buf xtensa_shared_stack_env;
bool xtensa_shared_stack_function_done = false;
static portMUX_TYPE xtensa_shared_stack_spinlock = portMUX_INITIALIZER_UNLOCKED;
static void *current_task_stack = NULL;
extern void esp_shared_stack_invoke_function(void);
static void esp_switch_stack_setup(StackType_t *stack, size_t stack_size)
{ {
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
int watchpoint_place = (((int)stack + 31) & ~31); int watchpoint_place = (((int)stack + 31) & ~31);
#endif #endif
StackType_t *top_of_stack = (StackType_t *)&stack[0] + //We need also to tweak current task stackpointer to avoid erroneous
((stack_size * sizeof(StackType_t)) / sizeof(StackType_t)); //stack overflow indication, so fills the stack with freertos known pattern:
memset(stack, 0xa5U, stack_size * sizeof(StackType_t));
StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle();
//Then put the fake stack inside of TCB:
current_task_stack = current->pxDummy6;
current->pxDummy6 = (void *)stack;
StackType_t *top_of_stack = stack + stack_size;
//Align stack to a 16byte boundary, as required by CPU specific: //Align stack to a 16byte boundary, as required by CPU specific:
top_of_stack = (StackType_t *)(((UBaseType_t)(top_of_stack - 31) - top_of_stack = (StackType_t *)(((UBaseType_t)(top_of_stack - 31) -
@@ -38,5 +57,36 @@ StackType_t * esp_switch_stack_setup(StackType_t *stack, size_t stack_size)
esp_set_watchpoint(2, (char*)watchpoint_place, 32, ESP_WATCHPOINT_STORE); esp_set_watchpoint(2, (char*)watchpoint_place, 32, ESP_WATCHPOINT_STORE);
#endif #endif
return top_of_stack; xtensa_shared_stack = top_of_stack;
}
void esp_execute_shared_stack_function(SemaphoreHandle_t lock, void *stack, size_t stack_size, shared_stack_function function)
{
assert(lock);
assert(stack);
assert(stack_size > 0 && stack_size >= CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE);
assert(function);
xSemaphoreTake(lock, portMAX_DELAY);
portENTER_CRITICAL(&xtensa_shared_stack_spinlock);
xtensa_shared_stack_function_done = false;
esp_switch_stack_setup(stack, stack_size);
xtensa_shared_stack_callback = function;
portEXIT_CRITICAL(&xtensa_shared_stack_spinlock);
setjmp(xtensa_shared_stack_env);
if(!xtensa_shared_stack_function_done) {
esp_shared_stack_invoke_function();
}
portENTER_CRITICAL(&xtensa_shared_stack_spinlock);
StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle();
//Restore current task stack:
current->pxDummy6 = (StackType_t *)current_task_stack;
vPortSetStackWatchpoint(current->pxDummy6);
portEXIT_CRITICAL(&xtensa_shared_stack_spinlock);
xSemaphoreGive(lock);
} }

View File

@@ -14,48 +14,38 @@
#include <freertos/xtensa_context.h> #include <freertos/xtensa_context.h>
.extern esp_clear_watchpoint .extern xtensa_shared_stack
.extern xtensa_shared_stack_callback
.extern xtensa_shared_stack_function_done
.extern xtensa_shared_stack_env
.extern longjmp
.text .text
/**
* extern void switch_stack_enter(portSTACK_TYPE *stack, portSTACK_TYPE *backup_stack); /* extern void esp_shared_stack_invoke_function(void) */
*/
.globl esp_switch_stack_enter .globl esp_shared_stack_invoke_function
.type esp_switch_stack_enter,@function .type esp_shared_stack_invoke_function,@function
.align 4 .align 4
esp_switch_stack_enter: esp_shared_stack_invoke_function:
#ifndef __XTENSA_CALL0_ABI__ #ifndef __XTENSA_CALL0_ABI__
entry sp, 0x10 movi a0, 0 /* must not rotate the window here, */
mov a4, a1 /* the state of execution for shared stack */
s32i a4, a3, 0 /* on a3 there is a safe place to save the current stack */ /* functions will be completely destroyed at end */
l32i a4, a2, 0 /* obtains the user allocated stack buffer */ movi a6, xtensa_shared_stack
mov a1, a4 /* sp register now contains caller specified stack */ l32i sp, a6, 0 /* load shared stack pointer */
retw movi a12, xtensa_shared_stack_callback
#else l32i a12, a12, 0
#error "this code is written for Window ABI" callx4 a12 /* call user function */
#endif movi a6, xtensa_shared_stack_function_done
movi a7, 1
/** s32i a7, a6, 0 /* hint the function was finished */
* extern void switch_stack_exit(portSTACK_TYPE *backup_stack); movi a6, xtensa_shared_stack_env
*/ movi a7, 0
.globl esp_switch_stack_exit movi a12, longjmp
.type esp_switch_stack_exit,@function callx4 a12 /* jump to last clean state previously saved */
.align 4 ret
esp_switch_stack_exit:
#ifndef __XTENSA_CALL0_ABI__
entry sp, 0x10
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
movi a6, 2
movi a4, esp_clear_watchpoint
callx4 a4 /* clear the watchpoint before releasing stack */
#endif
l32i a4, a2, 0 /* recover the original task stack */
mov a1, a4 /* put it on sp register again */
retw
#else #else
#error "this code is written for Window ABI" #error "this code is written for Window ABI"
#endif #endif

View File

@@ -8,24 +8,31 @@ A given function can be executed with a user allocated stack space
which is independent of current task stack, this mechanism can be which is independent of current task stack, this mechanism can be
used to save stack space wasted by tasks which call a common function used to save stack space wasted by tasks which call a common function
with intensive stack usage such as `printf`. The given function can with intensive stack usage such as `printf`. The given function can
be called inside the macro :cpp:func:`ESP_EXECUTE_EXPRESSION_WITH_STACK` be called inside the shared stack space which is a callback function
it will redirect the target function to be executed using the space deferred by calling :cpp:func:`esp_execute_shared_stack_function`,
allocated by the user. passing that function as parameter
Usage Usage
----- -----
:cpp:func:`ESP_EXECUTE_EXPRESSION_WITH_STACK` takes three arguments, :cpp:func:`esp_execute_shared_stack_function` takes four arguments,
a mutex object allocated by the caller, which is used to protect if a mutex object allocated by the caller, which is used to protect if
the same function shares its allocated stack, a pointer to the top the same function shares its allocated stack, a pointer to the top
of stack used to that fuction, and the function itself, note the of stack used to that fuction, the size in bytes of stack and, a pointer
function is passed exactly in the same way as do when you call to a user function where the shared stack space will reside, after calling
it on a regular way. the function, the user defined function will be deferred as a callback
where functions can be called using the user allocated space without
taking space from current task stack.
The usage may looks like the code below: The usage may looks like the code below:
.. code-block:: c .. code-block:: c
void external_stack_function(void)
{
printf("Executing this printf from external stack! \n");
}
//Let's suppose we wanting to call printf using a separated stack space //Let's suppose we wanting to call printf using a separated stack space
//allowing app to reduce its stack size. //allowing app to reduce its stack size.
void app_main() void app_main()
@@ -39,9 +46,11 @@ The usage may looks like the code below:
assert(printf_lock != NULL); assert(printf_lock != NULL);
//Call the desired function using the macro helper: //Call the desired function using the macro helper:
ESP_EXECUTE_EXPRESSION_WITH_STACK(printf_lock, esp_execute_shared_stack_function(printf_lock,
shared_stack, shared_stack,
printf("Executing this from external stack! \n")); 8192,
external_stack_function);
vSemaphoreDelete(printf_lock); vSemaphoreDelete(printf_lock);
free(shared_stack); free(shared_stack);
} }
@@ -52,5 +61,3 @@ API Reference
------------- -------------
.. include:: /_build/inc/esp_expression_with_stack.inc .. include:: /_build/inc/esp_expression_with_stack.inc

View File

@@ -18,7 +18,7 @@ System API
High Resolution Timer <esp_timer> High Resolution Timer <esp_timer>
Himem (large external SPI RAM) API <himem> Himem (large external SPI RAM) API <himem>
Inter-Processor Call <ipc> Inter-Processor Call <ipc>
Call function with external stack <esp_expression_with_stack> Call function with external stack <esp_function_with_shared_stack>
Interrupt Allocation <intr_alloc> Interrupt Allocation <intr_alloc>
Logging <log> Logging <log>
Miscellaneous System APIs <system> Miscellaneous System APIs <system>

View File

@@ -1 +0,0 @@
.. include:: ../../../en/api-reference/system/esp_expression_with_stack.rst

View File

@@ -0,0 +1 @@
.. include:: ../../../en/api-reference/system/esp_function_with_shared_stack.rst