From 82be75adb57a062aeca6766c7a27cfa07e43f6ad Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 2 Aug 2022 07:10:51 -0500 Subject: [PATCH] Add ability to reserve psram .. the primary user of which will be the camera, since the framebuffers must be allocated via esp-idf allocation function and never from the gc heap. A board can have a default value, and the value can also be set in the /.env file using the key CIRCUITPY_RESERVED_PSRAM with the value being the reserved size in bytes. Co-authored-by: Dan Halbert --- docs/environment.rst | 6 + locale/circuitpython.pot | 26 +-- main.c | 4 + ports/espressif/Makefile | 2 + ports/espressif/bindings/espidf/__init__.c | 79 ++------ ports/espressif/bindings/espidf/__init__.h | 7 + ports/espressif/common-hal/espidf/__init__.c | 194 +++++++++++++++++++ ports/espressif/common-hal/espidf/__init__.h | 29 +++ ports/espressif/mpconfigport.h | 9 + ports/espressif/supervisor/port.c | 47 ++--- py/circuitpy_mpconfig.h | 4 + shared-module/dotenv/__init__.c | 24 +++ shared-module/dotenv/__init__.h | 8 + supervisor/port.h | 6 + supervisor/shared/memory.c | 2 + supervisor/stub/misc.c | 33 ++++ supervisor/supervisor.mk | 5 +- 17 files changed, 385 insertions(+), 100 deletions(-) create mode 100644 ports/espressif/common-hal/espidf/__init__.c create mode 100644 ports/espressif/common-hal/espidf/__init__.h create mode 100644 supervisor/stub/misc.c diff --git a/docs/environment.rst b/docs/environment.rst index b1619f37bf..5766b54380 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -35,6 +35,12 @@ CIRCUITPY_BLE_NAME ~~~~~~~~~~~~~~~~~~ Default BLE name the board advertises as, including for the BLE workflow. +CIRCUITPY_RESERVED_PSRAM +~~~~~~~~~~~~~~~~~~~~~~~~ +On boards with Espressif microcontrollers with PSRAM (also called SPIRAM), permanently reserve a portion of PSRAM for use by esp-idf. +This storage is removed from the CircuitPython "heap" and is available for allocation by esp-idf routines in the core instead. +Generally, only set this to a non-zero value when it is required by a specific core module. + CIRCUITPY_WEB_API_PASSWORD ~~~~~~~~~~~~~~~~~~~~~~~~~~ Password required to make modifications to the board from the Web Workflow. diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index decd4816a3..332331e14f 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -209,7 +209,7 @@ msgstr "" msgid "%q=%q" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c #, c-format msgid "%s error 0x%x" msgstr "" @@ -645,7 +645,7 @@ msgstr "" msgid "CIRCUITPY drive could not be found or created." msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "CRC or checksum was invalid" msgstr "" @@ -1019,7 +1019,7 @@ msgstr "" msgid "GNSS init" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Generic Failure" msgstr "" @@ -1188,7 +1188,7 @@ msgstr "" msgid "Invalid MAC address" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c #: py/moduerrno.c msgid "Invalid argument" msgstr "" @@ -1218,7 +1218,7 @@ msgstr "" msgid "Invalid pins" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Invalid size" msgstr "" @@ -1226,7 +1226,7 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Invalid state" msgstr "" @@ -1250,7 +1250,7 @@ msgstr "" msgid "Layer must be a Group or TileGrid subclass" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "MAC address was invalid" msgstr "" @@ -1600,15 +1600,15 @@ msgstr "" msgid "Operation not permitted" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Operation or feature not supported" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Operation timed out" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Out of memory" msgstr "" @@ -1783,7 +1783,7 @@ msgstr "" msgid "Read-only object" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Received response was invalid" msgstr "" @@ -1799,7 +1799,7 @@ msgstr "" msgid "Requested AES mode is unsupported" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Requested resource not found" msgstr "" @@ -2221,7 +2221,7 @@ msgstr "" msgid "Value length > max_length" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Version was invalid" msgstr "" diff --git a/main.c b/main.c index d310f571a6..9bea7f891c 100644 --- a/main.c +++ b/main.c @@ -792,8 +792,12 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { usb_get_boot_py_data(usb_boot_py_data, size); #endif + port_post_boot_py(true); + cleanup_after_vm(heap, result.exception); + port_post_boot_py(false); + #if CIRCUITPY_USB // Now give back the data we saved from the heap going away. usb_return_boot_py_data(usb_boot_py_data, size); diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index febc6764bb..e111fa3441 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -281,6 +281,8 @@ ifneq ($(CIRCUITPY_BLEIO),0) SRC_C += common-hal/_bleio/ble_events.c endif +SRC_C += $(wildcard common-hal/espidf/*.c) + SRC_COMMON_HAL_EXPANDED = \ $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ $(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \ diff --git a/ports/espressif/bindings/espidf/__init__.c b/ports/espressif/bindings/espidf/__init__.c index 4f7da39540..c73e494f6c 100644 --- a/ports/espressif/bindings/espidf/__init__.c +++ b/ports/espressif/bindings/espidf/__init__.c @@ -120,6 +120,20 @@ const mp_obj_type_t mp_type_espidf_MemoryError = { .parent = &mp_type_MemoryError, }; +//| def get_total_psram() -> int: +//| """Returns the number of bytes of psram detected, or 0 if psram is not present or not configured""" +STATIC mp_obj_t espidf_get_total_psram(void) { + return MP_OBJ_NEW_SMALL_INT(common_hal_espidf_get_total_psram()); +} +MP_DEFINE_CONST_FUN_OBJ_0(espidf_get_total_psram_obj, espidf_get_total_psram); + +//| def get_reserved_psram() -> int: +//| """Returns number of bytes of psram reserved for use by esp-idf, either a board-specific default value or the value defined in ``/.env``.""" +STATIC mp_obj_t espidf_get_reserved_psram(void) { + return MP_OBJ_NEW_SMALL_INT(common_hal_espidf_get_reserved_psram()); +} +MP_DEFINE_CONST_FUN_OBJ_0(espidf_get_reserved_psram_obj, espidf_get_reserved_psram); + STATIC const mp_rom_map_elem_t espidf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_espidf) }, @@ -129,6 +143,8 @@ STATIC const mp_rom_map_elem_t espidf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_erase_nvs), MP_ROM_PTR(&espidf_erase_nvs_obj)}, + { MP_ROM_QSTR(MP_QSTR_get_total_psram), MP_ROM_PTR(&espidf_get_total_psram_obj)}, + { MP_ROM_QSTR(MP_QSTR_get_reserved_psram), MP_ROM_PTR(&espidf_get_reserved_psram_obj)}, { MP_ROM_QSTR(MP_QSTR_IDFError), MP_ROM_PTR(&mp_type_espidf_IDFError) }, { MP_ROM_QSTR(MP_QSTR_MemoryError), MP_ROM_PTR(&mp_type_espidf_MemoryError) }, }; @@ -139,66 +155,3 @@ const mp_obj_module_t espidf_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&espidf_module_globals, }; - -void raise_esp_error(esp_err_t err) { - const compressed_string_t *msg = NULL; - const mp_obj_type_t *exception_type = &mp_type_espidf_IDFError; - switch (err) { - case ESP_FAIL: - msg = translate("Generic Failure"); - break; - case ESP_ERR_NO_MEM: - exception_type = &mp_type_espidf_MemoryError; - msg = translate("Out of memory"); - break; - case ESP_ERR_INVALID_ARG: - msg = translate("Invalid argument"); - break; - case ESP_ERR_INVALID_STATE: - msg = translate("Invalid state"); - break; - case ESP_ERR_INVALID_SIZE: - msg = translate("Invalid size"); - break; - case ESP_ERR_NOT_FOUND: - msg = translate("Requested resource not found"); - break; - case ESP_ERR_NOT_SUPPORTED: - msg = translate("Operation or feature not supported"); - break; - case ESP_ERR_TIMEOUT: - msg = translate("Operation timed out"); - break; - case ESP_ERR_INVALID_RESPONSE: - msg = translate("Received response was invalid"); - break; - case ESP_ERR_INVALID_CRC: - msg = translate("CRC or checksum was invalid"); - break; - case ESP_ERR_INVALID_VERSION: - msg = translate("Version was invalid"); - break; - case ESP_ERR_INVALID_MAC: - msg = translate("MAC address was invalid"); - break; - } - if (msg) { - mp_raise_msg(exception_type, msg); - } - - const char *group = "ESP-IDF"; - - // tests must be in descending order - MP_STATIC_ASSERT(ESP_ERR_FLASH_BASE > ESP_ERR_MESH_BASE); - MP_STATIC_ASSERT(ESP_ERR_MESH_BASE > ESP_ERR_WIFI_BASE); - if (err >= ESP_ERR_FLASH_BASE) { - group = "Flash"; - } else if (err >= ESP_ERR_MESH_BASE) { - group = "Mesh"; - } else if (err >= ESP_ERR_WIFI_BASE) { - group = "WiFi"; - } - mp_raise_msg_varg(exception_type, translate("%s error 0x%x"), group, err); -} - -MP_REGISTER_MODULE(MP_QSTR_espidf, espidf_module, CIRCUITPY_ESPIDF); diff --git a/ports/espressif/bindings/espidf/__init__.h b/ports/espressif/bindings/espidf/__init__.h index 5891d2c5c0..e091a5c477 100644 --- a/ports/espressif/bindings/espidf/__init__.h +++ b/ports/espressif/bindings/espidf/__init__.h @@ -39,4 +39,11 @@ NORETURN void mp_raise_espidf_MemoryError(void); void raise_esp_error(esp_err_t err) NORETURN; #define CHECK_ESP_RESULT(x) do { int res = (x); if (res != ESP_OK) raise_esp_error(res); } while (0) +void common_hal_espidf_reserve_psram(void); +bool common_hal_espidf_set_reserved_psram(size_t amount); +size_t common_hal_espidf_get_reserved_psram(void); +size_t common_hal_espidf_get_total_psram(void); +intptr_t common_hal_espidf_get_psram_start(void); +intptr_t common_hal_espidf_get_psram_end(void); + #endif // MICROPY_INCLUDED_ESPRESSIF_BINDINGS_ESPIDF___INIT___H diff --git a/ports/espressif/common-hal/espidf/__init__.c b/ports/espressif/common-hal/espidf/__init__.c new file mode 100644 index 0000000000..183ebb3817 --- /dev/null +++ b/ports/espressif/common-hal/espidf/__init__.c @@ -0,0 +1,194 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "bindings/espidf/__init__.h" +#include "supervisor/shared/translate/translate.h" +#include "supervisor/memory.h" +#include "py/runtime.h" + +#include "esp_log.h" +#define TAG "espidf" + +#ifdef CONFIG_SPIRAM +#include "esp32/spiram.h" +#include "esp_heap_caps.h" +#include "esp_heap_caps_init.h" +#include "soc/soc.h" +#ifdef CONFIG_IDF_TARGET_ESP32 +#include "esp32/himem.h" +#else +#define esp_himem_reserved_area_size() (0) +#endif +bool ok_to_reserve_psram = true; +size_t reserved_psram = DEFAULT_RESERVED_PSRAM; +#endif + +static size_t psram_size_usable(void) { + #ifdef CONFIG_SPIRAM + /* PSRAM chip may be larger than the size we can map into address space */ + size_t s = MIN(esp_spiram_get_size(), SOC_EXTRAM_DATA_SIZE); + return s - esp_himem_reserved_area_size(); + #else + return 0; + #endif +} + +bool common_hal_espidf_set_reserved_psram(size_t amount) { + #ifdef CONFIG_SPIRAM + if (!esp_spiram_is_initialized()) { + return false; + } + if (!ok_to_reserve_psram) { + return false; + } + if (amount > psram_size_usable()) { + return false; + } + reserved_psram = amount; + return true; + #else + return false; + #endif +} + +supervisor_allocation *psram_for_idf; + +void common_hal_espidf_reserve_psram(void) { + #ifdef CONFIG_SPIRAM + if (!psram_for_idf) { + ESP_LOGI(TAG, "Reserving %d bytes of psram", reserved_psram); + if (reserved_psram == 0) { + return; + } + psram_for_idf = allocate_memory(reserved_psram, true, false); + if (psram_for_idf) { + intptr_t psram_for_idf_start = (intptr_t)psram_for_idf->ptr; + intptr_t psram_for_idf_end = psram_for_idf_start + reserved_psram; + ESP_LOGI(TAG, "Reserved %x..%x", psram_for_idf_start, psram_for_idf_end); + heap_caps_add_region(psram_for_idf_start, psram_for_idf_end); + } else { + ESP_LOGE(TAG, "supervisor allocation failed"); + } + } + #endif +} + +size_t common_hal_espidf_get_reserved_psram(void) { + #ifdef CONFIG_SPIRAM + return reserved_psram; + #else + return 0; + #endif +} + +size_t common_hal_espidf_get_total_psram(void) { + return psram_size_usable(); +} + +intptr_t common_hal_espidf_get_psram_start(void) { + #ifdef CONFIG_SPIRAM + if (esp_spiram_is_initialized()) { + #ifdef CONFIG_IDF_TARGET_ESP32 + return SOC_EXTRAM_DATA_LOW; + #else + return SOC_EXTRAM_DATA_HIGH - psram_size_usable(); + #endif + } + #endif + return 0; +} + +intptr_t common_hal_espidf_get_psram_end(void) { + #ifdef CONFIG_SPIRAM + if (esp_spiram_is_initialized()) { + return common_hal_espidf_get_psram_start() + psram_size_usable(); + } + #endif + return 0; +} + +void raise_esp_error(esp_err_t err) { + const compressed_string_t *msg = NULL; + const mp_obj_type_t *exception_type = &mp_type_espidf_IDFError; + switch (err) { + case ESP_FAIL: + msg = translate("Generic Failure"); + break; + case ESP_ERR_NO_MEM: + exception_type = &mp_type_espidf_MemoryError; + msg = translate("Out of memory"); + break; + case ESP_ERR_INVALID_ARG: + msg = translate("Invalid argument"); + break; + case ESP_ERR_INVALID_STATE: + msg = translate("Invalid state"); + break; + case ESP_ERR_INVALID_SIZE: + msg = translate("Invalid size"); + break; + case ESP_ERR_NOT_FOUND: + msg = translate("Requested resource not found"); + break; + case ESP_ERR_NOT_SUPPORTED: + msg = translate("Operation or feature not supported"); + break; + case ESP_ERR_TIMEOUT: + msg = translate("Operation timed out"); + break; + case ESP_ERR_INVALID_RESPONSE: + msg = translate("Received response was invalid"); + break; + case ESP_ERR_INVALID_CRC: + msg = translate("CRC or checksum was invalid"); + break; + case ESP_ERR_INVALID_VERSION: + msg = translate("Version was invalid"); + break; + case ESP_ERR_INVALID_MAC: + msg = translate("MAC address was invalid"); + break; + } + if (msg) { + mp_raise_msg(exception_type, msg); + } + + const char *group = "ESP-IDF"; + + // tests must be in descending order + MP_STATIC_ASSERT(ESP_ERR_FLASH_BASE > ESP_ERR_MESH_BASE); + MP_STATIC_ASSERT(ESP_ERR_MESH_BASE > ESP_ERR_WIFI_BASE); + if (err >= ESP_ERR_FLASH_BASE) { + group = "Flash"; + } else if (err >= ESP_ERR_MESH_BASE) { + group = "Mesh"; + } else if (err >= ESP_ERR_WIFI_BASE) { + group = "WiFi"; + } + mp_raise_msg_varg(exception_type, translate("%s error 0x%x"), group, err); +} + +MP_REGISTER_MODULE(MP_QSTR_espidf, espidf_module, CIRCUITPY_ESPIDF); diff --git a/ports/espressif/common-hal/espidf/__init__.h b/ports/espressif/common-hal/espidf/__init__.h new file mode 100644 index 0000000000..0d168b105c --- /dev/null +++ b/ports/espressif/common-hal/espidf/__init__.h @@ -0,0 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "bindings/espidf/__init__.h" diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 72a9b32478..632a7a13cd 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -90,4 +90,13 @@ #define CIRCUITPY_ESP_USB_SERIAL_JTAG (0) #endif +#ifndef DEFAULT_RESERVED_PSRAM +#define DEFAULT_RESERVED_PSRAM (0) +#endif + +#if defined(CONFIG_SPIRAM) +#undef CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS +#define CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS (1) +#endif + #endif // MICROPY_INCLUDED_ESPRESSIF_MPCONFIGPORT_H diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 9f58e8625b..ceedce367d 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -29,11 +29,13 @@ #include #include "supervisor/board.h" #include "supervisor/port.h" +#include "supervisor/filesystem.h" #include "py/runtime.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "bindings/espidf/__init__.h" #include "common-hal/microcontroller/Pin.h" #include "common-hal/analogio/AnalogOut.h" #include "common-hal/busio/I2C.h" @@ -53,6 +55,7 @@ #include "shared-bindings/microcontroller/RunMode.h" #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/socketpool/__init__.h" +#include "shared-module/dotenv/__init__.h" #include "peripherals/rmt.h" #include "peripherals/timer.h" @@ -95,20 +98,8 @@ #include "esp32/rom/efuse.h" #endif -#ifdef CONFIG_SPIRAM -#include "esp32/spiram.h" -#ifdef CONFIG_IDF_TARGET_ESP32 -#include "esp32/himem.h" -#else -#define esp_himem_reserved_area_size() (0) -#endif - -static size_t spiram_size_usable(void) { - /* SPIRAM chip may be larger than the size we can map into address space */ - size_t s = MIN(esp_spiram_get_size(), SOC_EXTRAM_DATA_SIZE); - return s - esp_himem_reserved_area_size(); -} -#endif +#include "esp_log.h" +#define TAG "port" uint32_t *heap; uint32_t heap_size; @@ -298,14 +289,16 @@ safe_mode_t port_init(void) { #endif #ifdef CONFIG_SPIRAM - if (esp_spiram_is_initialized()) { - size_t spiram_size = spiram_size_usable(); - #ifdef CONFIG_IDF_TARGET_ESP32 - heap = (uint32_t *)SOC_EXTRAM_DATA_LOW; - #else - heap = (uint32_t *)(SOC_EXTRAM_DATA_HIGH - spiram_size); - #endif - heap_size = spiram_size / sizeof(uint32_t); + { + intptr_t heap_start = common_hal_espidf_get_psram_start(); + intptr_t heap_end = common_hal_espidf_get_psram_end(); + size_t spiram_size = heap_end - heap_start; + if (spiram_size > 0) { + heap = (uint32_t *)heap_start; + heap_size = (heap_end - heap_start) / sizeof(uint32_t); + } else { + ESP_LOGE(TAG, "CONFIG_SPIRAM enabled but no spiram heap available"); + } } #endif @@ -514,6 +507,16 @@ void port_idle_until_interrupt(void) { } } +void port_post_boot_py(bool heap_valid) { + if (!heap_valid && filesystem_present()) { + mp_int_t reserved; + if (dotenv_get_key_int("/.env", "CIRCUITPY_RESERVED_PSRAM", &reserved)) { + common_hal_espidf_set_reserved_psram(reserved); + } + common_hal_espidf_reserve_psram(); + } +} + // Wrap main in app_main that the IDF expects. extern void main(void); extern void app_main(void); diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 950cf91e2e..acdf08e6a4 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -564,6 +564,10 @@ void supervisor_run_background_tasks_if_tick(void); #error "CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR must be at least 1" #endif +#ifndef CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS +#define CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS (0) +#endif + #ifndef USB_MIDI_EP_NUM_OUT #define USB_MIDI_EP_NUM_OUT (0) #endif diff --git a/shared-module/dotenv/__init__.c b/shared-module/dotenv/__init__.c index bd1973366d..f251c9ae79 100644 --- a/shared-module/dotenv/__init__.c +++ b/shared-module/dotenv/__init__.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include #include #include "shared-bindings/dotenv/__init__.h" @@ -223,3 +224,26 @@ mp_obj_t common_hal_dotenv_get_key(const char *path, const char *key) { } return mp_obj_new_str(value, actual_len); } + +bool dotenv_get_key_terminated(const char *path, const char *key, char *value, mp_int_t value_len) { + mp_int_t actual_len = dotenv_get_key(path, key, value, value_len - 1); + if (actual_len >= value_len) { + return false; + } + value[actual_len] = '\0'; // terminate string + return true; +} + +bool dotenv_get_key_int(const char *path, const char *key, mp_int_t *value) { + char buf[16]; + if (!dotenv_get_key_terminated(path, key, buf, (mp_int_t)sizeof(buf))) { + return false; + } + char *end; + long result = strtol(buf, &end, 0); + if (end == buf || *end) { // If the whole buffer was not consumed it's an error + return false; + } + *value = (mp_int_t)result; + return true; +} diff --git a/shared-module/dotenv/__init__.h b/shared-module/dotenv/__init__.h index 6cfb209324..fb27233692 100644 --- a/shared-module/dotenv/__init__.h +++ b/shared-module/dotenv/__init__.h @@ -26,3 +26,11 @@ // Allocation free version that returns the full length of the value. mp_int_t dotenv_get_key(const char *path, const char *key, char *value, mp_int_t value_len); + +// Returns true and sets value to a '\0'-terminated string if key is present +// and the value (including the terminating '\0') fits strictly within +// value_len bytes. +bool dotenv_get_key_terminated(const char *path, const char *key, char *value, mp_int_t value_len); + +// Returns true and sets value to the read value. Returns false if the value was not numeric. +bool dotenv_get_key_int(const char *path, const char *key, mp_int_t *value); diff --git a/supervisor/port.h b/supervisor/port.h index d693d49440..0d6862fae8 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -109,4 +109,10 @@ void port_wake_main_task(void); // default weak implementation is provided that does nothing. void port_wake_main_task_from_isr(void); +// Some ports need special handling just after completing boot.py execution. +// This function is called once while boot.py's VM is still valid, and +// then a second time after the VM is finalized. +// A default weak implementation is provided that does nothing. +void port_post_boot_py(bool heap_valid); + #endif // MICROPY_INCLUDED_SUPERVISOR_PORT_H diff --git a/supervisor/shared/memory.c b/supervisor/shared/memory.c index ff0382fc17..06c568b42e 100644 --- a/supervisor/shared/memory.c +++ b/supervisor/shared/memory.c @@ -56,6 +56,8 @@ enum { #if CIRCUITPY_USB_VENDOR + 1 // usb_vendor_add_descriptor #endif + + + CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS , CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT = diff --git a/supervisor/stub/misc.c b/supervisor/stub/misc.c new file mode 100644 index 0000000000..a17188beff --- /dev/null +++ b/supervisor/stub/misc.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "stdbool.h" + +#include "supervisor/port.h" +#include "py/mpconfig.h" + + +MP_WEAK void port_post_boot_py(bool heap_valid) { +} diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 912b5c5f37..8b9f07e362 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -11,14 +11,15 @@ SRC_SUPERVISOR = \ supervisor/shared/port.c \ supervisor/shared/reload.c \ supervisor/shared/safe_mode.c \ - supervisor/shared/serial.c \ + supervisor/shared/serial.c \ supervisor/shared/stack.c \ supervisor/shared/status_leds.c \ supervisor/shared/tick.c \ supervisor/shared/title_bar.c \ supervisor/shared/traceback.c \ supervisor/shared/translate/translate.c \ - supervisor/shared/workflow.c + supervisor/shared/workflow.c \ + supervisor/stub/misc.c \ NO_USB ?= $(wildcard supervisor/usb.c)