feat(pthread): Pthread can now use PSRAM as stack

Closes https://github.com/espressif/esp-idf/pull/10623
Closes https://github.com/espressif/esp-idf/issues/8662

Thanks to f-hoepfinger-hr-agrartechnik for the contribution
in https://github.com/espressif/esp-idf/pull/10623
This commit is contained in:
Jakob Hasse
2023-06-30 11:21:12 +08:00
parent 261651fc19
commit be59c94917
13 changed files with 304 additions and 17 deletions

View File

@@ -0,0 +1,6 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
components/pthread/test_apps/pthread_psram_tests:
enable:
- if: IDF_TARGET in ["esp32"]
reason: PSRAM only available on ESP32, S2, S3; code is fairly generic

View File

@@ -0,0 +1,13 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# 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 esp_psram)
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") # For test_utils component
project(pthread_psram_tests)

View File

@@ -0,0 +1,2 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "pthread_psram_tests.c"
INCLUDE_DIRS "."
PRIV_REQUIRES unity test_utils pthread) # note: esp_psram is set in the project's CMakeLists.txt

View File

@@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "unity_test_runner.h"
#include "memory_checks.h"
#include "esp_heap_caps.h"
#include "esp_pthread.h"
#include <errno.h>
#include "pthread.h"
void setUp(void)
{
esp_pthread_cfg_t config = esp_pthread_get_default_config();
TEST_ASSERT_EQUAL(ESP_OK, esp_pthread_set_cfg(&config));
test_utils_record_free_mem();
}
void tearDown(void)
{
test_utils_finish_and_evaluate_leaks(0, 0);
}
TEST_CASE("esp_pthread_get_default_config creates correct stack memory capabilities", "[set_cfg]")
{
esp_pthread_cfg_t default_config = esp_pthread_get_default_config();
// The default must always be internal, 8-bit accessible RAM
TEST_ASSERT_EQUAL_HEX(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL, default_config.stack_alloc_caps);
}
TEST_CASE("correct memory is accepted", "[set_cfg]")
{
esp_pthread_cfg_t default_config = esp_pthread_get_default_config();
default_config.stack_alloc_caps = MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL;
TEST_ASSERT_EQUAL(ESP_OK, esp_pthread_set_cfg(&default_config));
}
TEST_CASE("Setting stack with heap caps 0 sets the default value", "[set_cfg]")
{
esp_pthread_cfg_t config = { .stack_size = 4096 }; // all other values are set to 0
TEST_ASSERT_EQUAL(ESP_OK, esp_pthread_set_cfg(&config));
TEST_ASSERT_EQUAL(ESP_OK, esp_pthread_get_cfg(&config));
TEST_ASSERT_EQUAL(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL, config.stack_alloc_caps);
}
TEST_CASE("Setting stack with non 8-bit caps fails", "[set_cfg]")
{
esp_pthread_cfg_t config = esp_pthread_get_default_config();
config.stack_alloc_caps = MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL;
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_pthread_set_cfg(&config));
}
static void *check_stack_in_spiram(void *arg)
{
int ret_value;
if (esp_ptr_internal(&ret_value)) {
ret_value = 0;
} else {
ret_value = 1;
}
vTaskDelay(2); // ensure the test task has time to continue execution
pthread_exit((void *) ret_value);
return NULL;
}
TEST_CASE("pthread_create fails because out of PSRAM", "[psram]")
{
esp_pthread_cfg_t config = esp_pthread_get_default_config();
config.stack_alloc_caps = MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM;
config.stack_size = 0xFFFFFFFF; // far larger than the virtual address space on ESP32
pthread_t pthread_object = (pthread_t)NULL;
TEST_ASSERT_EQUAL(ESP_OK, esp_pthread_set_cfg(&config));
TEST_ASSERT_EQUAL(ENOMEM, pthread_create(&pthread_object, NULL, check_stack_in_spiram, NULL));
}
TEST_CASE("pthread create large PSRAM stack", "[psram]")
{
esp_pthread_cfg_t config = esp_pthread_get_default_config();
config.stack_alloc_caps = MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM;
config.stack_size = 0x80000; // value is too large for any internal RAM on current chips
int res = -1;
int thread_rval = -1;
pthread_t pthread_object = (pthread_t)NULL;
TEST_ASSERT_EQUAL(ESP_OK, esp_pthread_set_cfg(&config));
res = pthread_create(&pthread_object, NULL, check_stack_in_spiram, NULL);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_join(pthread_object, (void*) &thread_rval);
TEST_ASSERT_EQUAL_INT(0, res);
TEST_ASSERT_EQUAL_INT(1, thread_rval);
// Add a short delay to allow the idle task to free any remaining memory
vTaskDelay(2);
}
TEST_CASE("pthread with stack in internal RAM", "[psram]")
{
int res = -1;
int thread_rval = -1;
pthread_t pthread_object = (pthread_t)NULL;
res = pthread_create(&pthread_object, NULL, check_stack_in_spiram, NULL);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_join(pthread_object, (void*) &thread_rval);
TEST_ASSERT_EQUAL_INT(0, res);
TEST_ASSERT_EQUAL_INT(0, thread_rval);
// Add a short delay to allow the idle task to free any remaining memory
vTaskDelay(2);
}
void app_main(void)
{
vTaskPrioritySet(NULL, CONFIG_UNITY_FREERTOS_PRIORITY);
printf("pthread PSRAM Test");
unity_run_menu();
}

View File

@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
def test_pthread_psram(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=10)

View File

@@ -0,0 +1,5 @@
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_ESP_TASK_WDT_INIT=n
CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_SPIRAM=y

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -13,6 +13,33 @@
#include "unity.h"
TEST_CASE("esp_pthread_get_default_config creates correct stack memory capabilities", "[set_cfg]")
{
esp_pthread_cfg_t default_config = esp_pthread_get_default_config();
// The default must always be internal, 8-bit accessible RAM
TEST_ASSERT_EQUAL_HEX(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL, default_config.stack_alloc_caps);
}
TEST_CASE("wrong heap caps are rejected", "[set_cfg]")
{
esp_pthread_cfg_t default_config = esp_pthread_get_default_config();
default_config.stack_alloc_caps = MALLOC_CAP_32BIT;
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_pthread_set_cfg(&default_config));
default_config.stack_alloc_caps = MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL;
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_pthread_set_cfg(&default_config));
}
TEST_CASE("correct memory is accepted", "[set_cfg]")
{
esp_pthread_cfg_t default_config = esp_pthread_get_default_config();
default_config.stack_alloc_caps = MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL;
TEST_ASSERT_EQUAL(ESP_OK, esp_pthread_set_cfg(&default_config));
}
static void *compute_square(void *arg)
{
int *num = (int *) arg;