From 4c492cc85fec216dc9f20607411fc9d991996882 Mon Sep 17 00:00:00 2001 From: Jakob Hasse Date: Tue, 13 Aug 2024 16:02:27 +0200 Subject: [PATCH] refactor(esp_psram): allow external stack also when PSRAM is only available via esp_heap_caps Closes https://github.com/espressif/esp-idf/issues/12722 --- components/esp_psram/esp32/Kconfig.spiram | 2 +- .../esp_psram/test_apps/.build-test-rules.yml | 10 ++- .../psram_no_malloc_task_stack/CMakeLists.txt | 7 ++ .../psram_no_malloc_task_stack/README.md | 2 + .../main/CMakeLists.txt | 3 + .../main/test_psram_no_malloc_task_stack.c | 82 +++++++++++++++++++ .../pytest_psram_no_malloc_task_stack.py | 11 +++ .../sdkconfig.defaults | 4 + 8 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 components/esp_psram/test_apps/psram_no_malloc_task_stack/CMakeLists.txt create mode 100644 components/esp_psram/test_apps/psram_no_malloc_task_stack/README.md create mode 100644 components/esp_psram/test_apps/psram_no_malloc_task_stack/main/CMakeLists.txt create mode 100644 components/esp_psram/test_apps/psram_no_malloc_task_stack/main/test_psram_no_malloc_task_stack.c create mode 100644 components/esp_psram/test_apps/psram_no_malloc_task_stack/pytest_psram_no_malloc_task_stack.py create mode 100644 components/esp_psram/test_apps/psram_no_malloc_task_stack/sdkconfig.defaults diff --git a/components/esp_psram/esp32/Kconfig.spiram b/components/esp_psram/esp32/Kconfig.spiram index 1abfc6067e76..f7eb1725a45e 100644 --- a/components/esp_psram/esp32/Kconfig.spiram +++ b/components/esp_psram/esp32/Kconfig.spiram @@ -262,7 +262,7 @@ menu "SPI RAM config" config SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY bool "Allow external memory as an argument to xTaskCreateStatic" default n - depends on SPIRAM_USE_MALLOC + depends on SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC help Because some bits of the ESP32 code environment cannot be recompiled with the cache workaround, normally tasks cannot be safely run with their stack residing in external memory; for this reason diff --git a/components/esp_psram/test_apps/.build-test-rules.yml b/components/esp_psram/test_apps/.build-test-rules.yml index 116c563dcd90..88e270873461 100644 --- a/components/esp_psram/test_apps/.build-test-rules.yml +++ b/components/esp_psram/test_apps/.build-test-rules.yml @@ -1,6 +1,6 @@ # Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps -components/esp_psram/test_apps: +components/esp_psram/test_apps/psram: disable: - if: SOC_SPIRAM_SUPPORTED != 1 depends_components: @@ -9,3 +9,11 @@ components/esp_psram/test_apps: - esp_driver_gpio - esp_driver_spi - spi_flash + + +components/esp_psram/test_apps/psram_no_malloc_task_stack: + enable: + - if: IDF_TARGET in ["esp32", "esp32p4"] + depends_components: + - esp_psram + - freertos diff --git a/components/esp_psram/test_apps/psram_no_malloc_task_stack/CMakeLists.txt b/components/esp_psram/test_apps/psram_no_malloc_task_stack/CMakeLists.txt new file mode 100644 index 000000000000..9debb1681bd8 --- /dev/null +++ b/components/esp_psram/test_apps/psram_no_malloc_task_stack/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) +project(psram_no_malloc_task_stack) diff --git a/components/esp_psram/test_apps/psram_no_malloc_task_stack/README.md b/components/esp_psram/test_apps/psram_no_malloc_task_stack/README.md new file mode 100644 index 000000000000..4873c15b15b5 --- /dev/null +++ b/components/esp_psram/test_apps/psram_no_malloc_task_stack/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-P4 | +| ----------------- | ----- | -------- | diff --git a/components/esp_psram/test_apps/psram_no_malloc_task_stack/main/CMakeLists.txt b/components/esp_psram/test_apps/psram_no_malloc_task_stack/main/CMakeLists.txt new file mode 100644 index 000000000000..90939ebf279b --- /dev/null +++ b/components/esp_psram/test_apps/psram_no_malloc_task_stack/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "test_psram_no_malloc_task_stack.c" + INCLUDE_DIRS "." + PRIV_REQUIRES unity esp_psram freertos heap) diff --git a/components/esp_psram/test_apps/psram_no_malloc_task_stack/main/test_psram_no_malloc_task_stack.c b/components/esp_psram/test_apps/psram_no_malloc_task_stack/main/test_psram_no_malloc_task_stack.c new file mode 100644 index 000000000000..d0c48c608d25 --- /dev/null +++ b/components/esp_psram/test_apps/psram_no_malloc_task_stack/main/test_psram_no_malloc_task_stack.c @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include "esp_log.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "unity_test_utils.h" +#include "esp_heap_caps.h" +#include "esp_memory_utils.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char* TAG = "psram_no_malloc_task_stack_test"; + +#define STACK_SIZE 4096 +#define WAIT_TICKS 2 +#define TEST_TASK_PRIORITY 6 // relatively high priority to let task finish quickly + +void setUp(void) +{ + unity_utils_set_leak_level(0); + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + unity_utils_evaluate_leaks(); +} + +static uint8_t *stack_memory; +static StaticTask_t *tcb_memory; +static bool is_external; +static SemaphoreHandle_t task_waiter; + +static void test_task(void *arg) +{ + int dummy = 47; + is_external = esp_ptr_external_ram(&dummy); + xSemaphoreGive(task_waiter); + vTaskDelay(portMAX_DELAY); +} + +TEST_CASE("FreeRTOS task with stack on SPIRAM works", "[psram]") +{ + stack_memory = heap_caps_malloc(STACK_SIZE, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + TEST_ASSERT_NOT_NULL(stack_memory); + tcb_memory = heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(tcb_memory); + task_waiter = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(task_waiter); + + TaskHandle_t task = xTaskCreateStatic(test_task, + "heap caps static", + STACK_SIZE, + NULL, + TEST_TASK_PRIORITY, + stack_memory, + tcb_memory); + TEST_ASSERT_NOT_NULL(task); + + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(task_waiter, WAIT_TICKS)); + + TEST_ASSERT_EQUAL(true, is_external); + + // use unity_utils_task_delete() to avoid deleting stack of a still running task + unity_utils_task_delete(task); + + vSemaphoreDelete(task_waiter); + heap_caps_free(tcb_memory); + heap_caps_free(stack_memory); +} + +void app_main(void) +{ + ESP_LOGI(TAG, "Running PSRAM task stack test app with SPIRAM_USE_CAPS_ALLOC"); + + unity_run_menu(); +} diff --git a/components/esp_psram/test_apps/psram_no_malloc_task_stack/pytest_psram_no_malloc_task_stack.py b/components/esp_psram/test_apps/psram_no_malloc_task_stack/pytest_psram_no_malloc_task_stack.py new file mode 100644 index 000000000000..298f6f93c872 --- /dev/null +++ b/components/esp_psram/test_apps/psram_no_malloc_task_stack/pytest_psram_no_malloc_task_stack.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +@pytest.mark.esp32p4 +@pytest.mark.generic +def test_psram_no_malloc_task_stack(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_psram/test_apps/psram_no_malloc_task_stack/sdkconfig.defaults b/components/esp_psram/test_apps/psram_no_malloc_task_stack/sdkconfig.defaults new file mode 100644 index 000000000000..5e67e04b0c44 --- /dev/null +++ b/components/esp_psram/test_apps/psram_no_malloc_task_stack/sdkconfig.defaults @@ -0,0 +1,4 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_SPIRAM=y +CONFIG_SPIRAM_USE_CAPS_ALLOC=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y