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:
Angus Gratton 2019-12-31 15:08:27 +08:00
commit 17b5df4d72
9 changed files with 242 additions and 0 deletions

View 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

View 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);
}

View file

@ -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"
)

View 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;
}

View 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

View file

@ -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

View 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

View file

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

View file

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