diff --git a/components/esp_common/include/esp_expression_with_stack.h b/components/esp_common/include/esp_expression_with_stack.h new file mode 100644 index 000000000..3aa12a253 --- /dev/null +++ b/components/esp_common/include/esp_expression_with_stack.h @@ -0,0 +1,69 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_EXPRESSION_WITH_STACK_H +#define __ESP_EXPRESSION_WITH_STACK_H + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_debug_helpers.h" + + +/** + * @brief Executes a 1-line expression with a application alocated stack + * @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 + */ +#define ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression) \ +({ \ + if (lock && stack && 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); \ + 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); + +#endif \ No newline at end of file diff --git a/components/newlib/test/test_shared_stack_printf.c b/components/newlib/test/test_shared_stack_printf.c new file mode 100644 index 000000000..9b020f581 --- /dev/null +++ b/components/newlib/test/test_shared_stack_printf.c @@ -0,0 +1,31 @@ +#include +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "sdkconfig.h" +#include "test_utils.h" +#include "esp_expression_with_stack.h" + +//makes sure this is not the task stack... +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"); +} + +TEST_CASE("test printf using shared buffer stack", "[newlib]") +{ + portSTACK_TYPE *shared_stack = malloc(8192 * sizeof(portSTACK_TYPE)); + + TEST_ASSERT(shared_stack != NULL); + + SemaphoreHandle_t printf_lock = xSemaphoreCreateMutex(); + TEST_ASSERT_NOT_NULL(printf_lock); + + 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); +} diff --git a/components/xtensa/CMakeLists.txt b/components/xtensa/CMakeLists.txt index 5c335ffdd..a360fb2ed 100644 --- a/components/xtensa/CMakeLists.txt +++ b/components/xtensa/CMakeLists.txt @@ -5,6 +5,8 @@ else() set(priv_requires soc freertos) set(srcs "debug_helpers.c" "debug_helpers_asm.S" + "expression_with_stack_xtensa_asm.S" + "expression_with_stack_xtensa.c" "eri.c" ) diff --git a/components/xtensa/expression_with_stack_xtensa.c b/components/xtensa/expression_with_stack_xtensa.c new file mode 100644 index 000000000..6f9cf4ca3 --- /dev/null +++ b/components/xtensa/expression_with_stack_xtensa.c @@ -0,0 +1,23 @@ +#include +#include +#include + +StackType_t * esp_switch_stack_setup(StackType_t *stack, size_t stack_size) +{ + int watchpoint_place = (((int)stack + 31) & ~31); + StackType_t *top_of_stack = (StackType_t *)&stack[0] + + ((stack_size * sizeof(StackType_t)) / sizeof(StackType_t)); + + //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; + + esp_set_watchpoint(2, (char*)watchpoint_place, 32, ESP_WATCHPOINT_STORE); + return top_of_stack; +} \ No newline at end of file diff --git a/components/xtensa/expression_with_stack_xtensa_asm.S b/components/xtensa/expression_with_stack_xtensa_asm.S new file mode 100644 index 000000000..106eb6f9c --- /dev/null +++ b/components/xtensa/expression_with_stack_xtensa_asm.S @@ -0,0 +1,57 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + + .extern esp_clear_watchpoint + .text + +/** + * extern void switch_stack_enter(portSTACK_TYPE *stack, portSTACK_TYPE *backup_stack); + */ + .globl esp_switch_stack_enter + .type esp_switch_stack_enter,@function + .align 4 +esp_switch_stack_enter: + + #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 + movi a6, 2 + movi a4, esp_clear_watchpoint + callx4 a4 /* clear the watchpoint before releasing stack */ + l32i a4, a2, 0 /* recover the original task stack */ + mov a1, a4 /* put it on sp register again */ + retw + #else + #error "this code is written for Window ABI" + #endif diff --git a/docs/Doxyfile b/docs/Doxyfile index c880f3b68..a56f13cf4 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -223,6 +223,8 @@ INPUT = \ ../../components/esp_common/include/esp_freertos_hooks.h \ ## Inter-Processor Call ../../components/esp_common/include/esp_ipc.h \ + ## Call Function with External stack + ../../components/esp_common/include/esp_expression_with_stack.h \ ## Over The Air Updates (OTA) ../../components/app_update/include/esp_ota_ops.h \ ## ESP HTTPS OTA diff --git a/docs/en/api-reference/system/esp_expression_with_stack.rst b/docs/en/api-reference/system/esp_expression_with_stack.rst new file mode 100644 index 000000000..dd7b994f2 --- /dev/null +++ b/docs/en/api-reference/system/esp_expression_with_stack.rst @@ -0,0 +1,56 @@ +Call function with external stack +================================= + +Overview +-------- + +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. + +Usage +----- + +:cpp:func:`ESP_EXECUTE_EXPRESSION_WITH_STACK` takes three 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. + +The usage may looks like the code below: + +.. code-block:: c + + //Let's suppose we wanting to call printf using a separated stack space + //allowing app to reduce its stack size. + void app_main() + { + //Allocate a stack buffer, from heap or as a static form: + portSTACK_TYPE *shared_stack = malloc(8192 * sizeof(portSTACK_TYPE)); + assert(shared_stack != NULL); + + //Allocate a mutex to protect its usage: + SemaphoreHandle_t printf_lock = xSemaphoreCreateMutex(); + 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")); + vSemaphoreDelete(printf_lock); + free(shared_stack); + } + +.. _esp-call-with-stack-basic_usage: + +API Reference +------------- + +.. include:: /_build/inc/esp_expression_with_stack.inc + + diff --git a/docs/en/api-reference/system/index.rst b/docs/en/api-reference/system/index.rst index 8d0a659e2..65f06522c 100644 --- a/docs/en/api-reference/system/index.rst +++ b/docs/en/api-reference/system/index.rst @@ -18,6 +18,7 @@ System API High Resolution Timer Himem (large external SPI RAM) API Inter-Processor Call + Call function with external stack Interrupt Allocation Logging Miscellaneous System APIs diff --git a/docs/zh_CN/api-reference/system/esp_expression_with_stack.rst b/docs/zh_CN/api-reference/system/esp_expression_with_stack.rst new file mode 100644 index 000000000..ee9cb17b2 --- /dev/null +++ b/docs/zh_CN/api-reference/system/esp_expression_with_stack.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/system/esp_expression_with_stack.rst \ No newline at end of file