Merge branch 'backport/shared_stack4.2' into 'release/v4.2'

Backport/shared stack4.2

See merge request espressif/esp-idf!10303
This commit is contained in:
Angus Gratton 2020-09-25 08:02:55 +08:00
commit 6443a96834
8 changed files with 158 additions and 115 deletions

View File

@ -13,6 +13,7 @@
// limitations under the License.
#pragma once
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
@ -23,57 +24,26 @@
extern "C" {
#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 stack Pointer to user alocated stack
* @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
* be called using the current stack.
*/
#define ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression) \
({ \
assert(lock && stack && (stack_size >= CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE)); \
uint32_t backup; \
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); \
StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle(); \
/* pxDummy6 is the stack base of current thread defined in TCB_t */ \
/* place the watchpoint on current task stack after function execution*/ \
vPortSetStackWatchpoint(current->pxDummy6); \
xSemaphoreGive(lock); \
})
void esp_execute_shared_stack_function(SemaphoreHandle_t lock,
void *stack,
size_t stack_size,
shared_stack_function function);
/**
* @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
}
#endif
#endif

View File

@ -8,25 +8,53 @@
#include "test_utils.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)
{
//We can even use Freertos resources inside of this context.
vTaskDelay(100);
printf("Executing this another printf inside a function with external stack");
printf("We can even use FreeRTOS resources... yielding, sp=%p\n", get_sp());
taskYIELD();
shared_stack_sp = (StackType_t *)get_sp();
}
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();
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);
free(shared_stack);
}

View File

@ -15,29 +15,72 @@
#include <esp_expression_with_stack.h>
#include <freertos/xtensa_rtos.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
esp_clear_watchpoint(1);
uint32_t watchpoint_place = ((uint32_t)stack + 32) & 0x1f ;
#endif
StackType_t *top_of_stack = (StackType_t *)&stack[0] +
((stack_size * sizeof(StackType_t)) / sizeof(StackType_t));
uint32_t watchpoint_place = ((uint32_t)stack + 32) & ~0x1f ;
#endif
//We need also to tweak current task stackpointer to avoid erroneous
//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:
top_of_stack = (StackType_t *)(((UBaseType_t)(top_of_stack - 31) -
ALIGNUP(0x10, sizeof(XtSolFrame) )) &
~0xf);
//Fake stack frame to do not break the backtrace
XtSolFrame *frame = (XtSolFrame *)top_of_stack;
frame->a0 = 0;
frame->a1 = (UBaseType_t)top_of_stack;
top_of_stack = (StackType_t *)(((UBaseType_t)(top_of_stack - 16) & ~0xf));
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
esp_set_watchpoint(1, (uint8_t *)watchpoint_place, 32, ESP_WATCHPOINT_STORE);
esp_set_watchpoint(1, (uint8_t *)watchpoint_place, 32, ESP_WATCHPOINT_STORE);
#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

@ -13,44 +13,39 @@
// limitations under the License.
#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
/**
* extern void switch_stack_enter(portSTACK_TYPE *stack, portSTACK_TYPE *backup_stack);
*/
.globl esp_switch_stack_enter
.type esp_switch_stack_enter,@function
/* extern void esp_shared_stack_invoke_function(void) */
.globl esp_shared_stack_invoke_function
.type esp_shared_stack_invoke_function,@function
.align 4
esp_switch_stack_enter:
esp_shared_stack_invoke_function:
#ifndef __XTENSA_CALL0_ABI__
entry sp, 0x10
mov a4, a1
s32i a4, a3, 0 /* on a3 there is a safe place to save the current stack */
l32i a4, a2, 0 /* obtains the user allocated stack buffer */
mov a1, a4 /* sp register now contains caller specified stack */
retw
#else
#error "this code is written for Window ABI"
#endif
/**
* extern void switch_stack_exit(portSTACK_TYPE *backup_stack);
*/
.globl esp_switch_stack_exit
.type esp_switch_stack_exit,@function
.align 4
esp_switch_stack_exit:
#ifndef __XTENSA_CALL0_ABI__
entry sp, 0x10
l32i a4, a2, 0 /* recover the original task stack */
mov a1, a4 /* put it on sp register again */
retw
#else
movi a0, 0 /* must not rotate the window here, */
/* the state of execution for shared stack */
/* functions will be completely destroyed at end */
movi a6, xtensa_shared_stack
l32i sp, a6, 0 /* load shared stack pointer */
movi a12, xtensa_shared_stack_callback
l32i a12, a12, 0
callx4 a12 /* call user function */
movi a6, xtensa_shared_stack_function_done
movi a7, 1
s32i a7, a6, 0 /* hint the function was finished */
movi a6, xtensa_shared_stack_env
movi a7, 0
movi a12, longjmp
callx4 a12 /* jump to last clean state previously saved */
ret
#else
#error "this code is written for Window ABI"
#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
used to save stack space wasted by tasks which call a common function
with intensive stack usage such as `printf`. The given function can
be called inside the macro :cpp:func:`ESP_EXECUTE_EXPRESSION_WITH_STACK`
it will redirect the target function to be executed using the space
allocated by the user.
be called inside the shared stack space which is a callback function
deferred by calling :cpp:func:`esp_execute_shared_stack_function`,
passing that function as parameter
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
the same function shares its allocated stack, a pointer to the top
of stack used to that fuction, and the function itself, note the
function is passed exactly in the same way as do when you call
it on a regular way.
of stack used to that fuction, the size in bytes of stack and, a pointer
to a user function where the shared stack space will reside, after calling
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:
.. 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
//allowing app to reduce its stack size.
void app_main()
@ -39,9 +46,11 @@ The usage may looks like the code below:
assert(printf_lock != NULL);
//Call the desired function using the macro helper:
ESP_EXECUTE_EXPRESSION_WITH_STACK(printf_lock,
shared_stack,
printf("Executing this from external stack! \n"));
esp_execute_shared_stack_function(printf_lock,
shared_stack,
8192,
external_stack_function);
vSemaphoreDelete(printf_lock);
free(shared_stack);
}
@ -51,6 +60,4 @@ The usage may looks like the code below:
API Reference
-------------
.. include-build-file:: inc/esp_expression_with_stack.inc
.. include-build-file:: inc/esp_expression_with_stack.inc

View File

@ -19,7 +19,7 @@ System API
High Resolution Timer <esp_timer>
:esp32: Himem (large external SPI RAM) API <himem>
:esp32: 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>
Logging <log>
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