Merge branch 'feature/function_call_with_stack' into 'master'
esp_common: added a macro to allow call functions using user allocated stack Closes IDF-535 See merge request espressif/esp-idf!6709
This commit is contained in:
commit
17b5df4d72
69
components/esp_common/include/esp_expression_with_stack.h
Normal file
69
components/esp_common/include/esp_expression_with_stack.h
Normal file
|
@ -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
|
31
components/newlib/test/test_shared_stack_printf.c
Normal file
31
components/newlib/test/test_shared_stack_printf.c
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#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);
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ else()
|
||||||
set(priv_requires soc freertos)
|
set(priv_requires soc freertos)
|
||||||
set(srcs "debug_helpers.c"
|
set(srcs "debug_helpers.c"
|
||||||
"debug_helpers_asm.S"
|
"debug_helpers_asm.S"
|
||||||
|
"expression_with_stack_xtensa_asm.S"
|
||||||
|
"expression_with_stack_xtensa.c"
|
||||||
"eri.c"
|
"eri.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
23
components/xtensa/expression_with_stack_xtensa.c
Normal file
23
components/xtensa/expression_with_stack_xtensa.c
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include <esp_expression_with_stack.h>
|
||||||
|
#include <freertos/xtensa_rtos.h>
|
||||||
|
#include <freertos/xtensa_context.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
57
components/xtensa/expression_with_stack_xtensa_asm.S
Normal file
57
components/xtensa/expression_with_stack_xtensa_asm.S
Normal file
|
@ -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 <freertos/xtensa_context.h>
|
||||||
|
|
||||||
|
.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
|
|
@ -223,6 +223,8 @@ INPUT = \
|
||||||
../../components/esp_common/include/esp_freertos_hooks.h \
|
../../components/esp_common/include/esp_freertos_hooks.h \
|
||||||
## Inter-Processor Call
|
## Inter-Processor Call
|
||||||
../../components/esp_common/include/esp_ipc.h \
|
../../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)
|
## Over The Air Updates (OTA)
|
||||||
../../components/app_update/include/esp_ota_ops.h \
|
../../components/app_update/include/esp_ota_ops.h \
|
||||||
## ESP HTTPS OTA
|
## ESP HTTPS OTA
|
||||||
|
|
56
docs/en/api-reference/system/esp_expression_with_stack.rst
Normal file
56
docs/en/api-reference/system/esp_expression_with_stack.rst
Normal file
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -18,6 +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>
|
||||||
Interrupt Allocation <intr_alloc>
|
Interrupt Allocation <intr_alloc>
|
||||||
Logging <log>
|
Logging <log>
|
||||||
Miscellaneous System APIs <system>
|
Miscellaneous System APIs <system>
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
.. include:: ../../../en/api-reference/system/esp_expression_with_stack.rst
|
Loading…
Reference in a new issue