From 234dff26b3816a1ba444a77c66395e5539382199 Mon Sep 17 00:00:00 2001 From: Winford Date: Mon, 12 May 2025 18:02:03 +0000 Subject: [PATCH 1/6] Add memory size information to STM32 build output Adds linker flag to display memory size information after building to STM32 platform libopencm3.cmake configuration file. Signed-off-by: Winford --- src/platforms/stm32/cmake/libopencm3.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/stm32/cmake/libopencm3.cmake b/src/platforms/stm32/cmake/libopencm3.cmake index 6e0ad658c..47d4a2cdf 100644 --- a/src/platforms/stm32/cmake/libopencm3.cmake +++ b/src/platforms/stm32/cmake/libopencm3.cmake @@ -145,7 +145,7 @@ message(STATUS "Generated Linker File : ${CMAKE_CURRENT_BINARY_DIR}/${LINKER_S # ARCH_FLAGS has to be passed as a string here JOIN("${ARCH_FLAGS}" " " ARCH_FLAGS) # Set linker flags -set(LINKER_FLAGS "${LINKER_FLAGS} -specs=nosys.specs -nostartfiles -Wl,--undefined,_printf_float -Wl,--undefined,_scanf_float -T${CMAKE_CURRENT_BINARY_DIR}/${LINKER_SCRIPT} ${ARCH_FLAGS}") +set(LINKER_FLAGS "${LINKER_FLAGS} -specs=nosys.specs -nostartfiles -Wl,--undefined,_printf_float -Wl,--undefined,_scanf_float -Wl,--print-memory-usage -T${CMAKE_CURRENT_BINARY_DIR}/${LINKER_SCRIPT} ${ARCH_FLAGS}") message(STATUS "Linker Flags : ${LINKER_FLAGS}") # Compiler flags From 8cf0e9a7e4bbfe3cbd09c14980b33703faedc547 Mon Sep 17 00:00:00 2001 From: Winford Date: Tue, 13 May 2025 05:22:10 +0000 Subject: [PATCH 2/6] Improve allocation performance on STM32 platform Make the local_heap_setup conditional unlikely to improve performance of memory allocation since this only is true only at the first memory allocation. Signed-off-by: Winford --- src/platforms/stm32/src/lib/sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/stm32/src/lib/sys.c b/src/platforms/stm32/src/lib/sys.c index c65a39cab..cbfd4e3c5 100644 --- a/src/platforms/stm32/src/lib/sys.c +++ b/src/platforms/stm32/src/lib/sys.c @@ -69,7 +69,7 @@ void *_sbrk_r(struct _reent *reent, ptrdiff_t diff) { uint8_t *_old_brk; - if (_heap_end == NULL) { + if (UNLIKELY(_heap_end == NULL)) { local_heap_setup(&_cur_brk, &_heap_end); } From 48f274db1dd8cffbe62744ff3d1c20a860682e69 Mon Sep 17 00:00:00 2001 From: Winford Date: Sat, 10 May 2025 19:56:08 +0000 Subject: [PATCH 3/6] Add support for atomvm_free_heap_size key to system_info/1 on STM32 platform Adds support for getting the current free heap size on STM32 platform using `erlang:system_info(atomvm_free_heap_size)`. Signed-off-by: Winford --- src/platforms/stm32/src/lib/sys.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/platforms/stm32/src/lib/sys.c b/src/platforms/stm32/src/lib/sys.c index cbfd4e3c5..89bfb767e 100644 --- a/src/platforms/stm32/src/lib/sys.c +++ b/src/platforms/stm32/src/lib/sys.c @@ -82,6 +82,11 @@ void *_sbrk_r(struct _reent *reent, ptrdiff_t diff) return _old_brk; } +static int sys_get_free_heap() +{ + return (int) ((uint8_t *) (((uintptr_t) &_stack - RESERVE_STACK_SIZE))) - (_cur_brk == NULL ? (int) &_ebss : (int) _cur_brk); +} + // Monotonically increasing number of milliseconds from reset static volatile uint64_t system_millis; @@ -263,11 +268,14 @@ Context *sys_create_port(GlobalContext *glb, const char *driver_name, term opts) term sys_get_info(Context *ctx, term key) { - UNUSED(ctx); - UNUSED(key); + GlobalContext *glb = ctx->global; + if (key == globalcontext_existing_term_from_atom_string(glb, ATOM_STR("\x15", "atomvm_free_heap_size"))) { + return term_from_int32(sys_get_free_heap()); + } return UNDEFINED_ATOM; } + void sys_enable_flash_cache() { flash_unlock_option_bytes(); From f7eae1a0522b07341bd67190c5f473d5e282310b Mon Sep 17 00:00:00 2001 From: Winford Date: Wed, 14 May 2025 00:29:55 +0000 Subject: [PATCH 4/6] Add support for atomvm_minimum_free_size key to system_info/1 on STM32 platform Adds support for getting the current free heap size on STM32 platform using `erlang:system_info(atomvm_minimum_free_size)`. Signed-off-by: Winford --- CHANGELOG.md | 1 + src/platforms/stm32/src/lib/sys.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e2a9f972..941098c76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added Function.ex and Protocol.ex improving Elixir 1.18 support - Added WiFi support for ESP32P4 via esp-wifi-external for build with ESP-IDF v5.4 and later - Added Process.link/1 and unlink/1 to Elixir Process.ex +- Added `erlang:system_info/1` keys `atomvm_free_heap_size` and `atomvm_minimum_free_size` on STM32 platform ### Changed diff --git a/src/platforms/stm32/src/lib/sys.c b/src/platforms/stm32/src/lib/sys.c index 89bfb767e..f18d0345d 100644 --- a/src/platforms/stm32/src/lib/sys.c +++ b/src/platforms/stm32/src/lib/sys.c @@ -51,6 +51,7 @@ extern uint8_t _ebss, _stack; static uint8_t *_cur_brk = NULL; static uint8_t *_heap_end = NULL; +static uint8_t *_heap_high_mark = NULL; /* * If not overridden, this puts the heap into the left @@ -63,6 +64,7 @@ static void __local_ram(uint8_t **start, uint8_t **end) { *start = &_ebss; *end = (uint8_t *) (((uintptr_t) &_stack - RESERVE_STACK_SIZE)); + _heap_high_mark = 0; } void *_sbrk_r(struct _reent *reent, ptrdiff_t diff) @@ -79,6 +81,10 @@ void *_sbrk_r(struct _reent *reent, ptrdiff_t diff) return (void *) -1; } _cur_brk += diff; + if (_cur_brk > _heap_high_mark) { + _heap_high_mark = _cur_brk; + } + return _old_brk; } @@ -87,6 +93,11 @@ static int sys_get_free_heap() return (int) ((uint8_t *) (((uintptr_t) &_stack - RESERVE_STACK_SIZE))) - (_cur_brk == NULL ? (int) &_ebss : (int) _cur_brk); } +static int sys_least_free_heap() +{ + return (int) ((uint8_t *) (((uintptr_t) &_stack - RESERVE_STACK_SIZE))) - (int) _heap_high_mark; +} + // Monotonically increasing number of milliseconds from reset static volatile uint64_t system_millis; @@ -272,10 +283,12 @@ term sys_get_info(Context *ctx, term key) if (key == globalcontext_existing_term_from_atom_string(glb, ATOM_STR("\x15", "atomvm_free_heap_size"))) { return term_from_int32(sys_get_free_heap()); } + if (key == globalcontext_existing_term_from_atom_string(glb, ATOM_STR("\x18", "atomvm_minimum_free_size"))) { + return term_from_int32(sys_least_free_heap()); + } return UNDEFINED_ATOM; } - void sys_enable_flash_cache() { flash_unlock_option_bytes(); From 9f61f82532297f22e6ce42f795bd292e22fa51f5 Mon Sep 17 00:00:00 2001 From: Winford Date: Sun, 1 Jun 2025 04:20:11 +0000 Subject: [PATCH 5/6] Add ESP32 erlang:system_info/1 memory keys to match new STM32 keys with atomvm_ prefix Adds new ESP32 erlang:system_info/1 heap memory keys `atomvm_free_heap_size` and `atomvm_minimum_free_size` to align with newly added STM32 memory keys. When the old keys are used a deprecation warning will be printed to the console to alert users to update to the new key names before the old keys are removed in the 0.8.x release cycle. Signed-off-by: Winford --- CHANGELOG.md | 3 +++ UPDATING.md | 3 +++ src/platforms/esp32/components/avm_sys/sys.c | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 941098c76..6d8e7d975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `externalterm_to_term_copy` added in [0.6.5] and introduced flags to `externalterm_to_term` to perform copy. - Release images for ESP32 chips are built with ESP-IDF v5.4 - ESP32: SPI peripheral defaults to `"spi2"` instead of deprecated `hspi` +- Deprecated ESP32 `erlang:system_info/1` memory keys `esp32_free_heap_size` and +`esp32_minimum_free_size` in favor of `atomvm_` prefixed keys with warnings to update applications +before the old keys are removed. ### Fixed diff --git a/UPDATING.md b/UPDATING.md index 23803b7ab..ae8a5bb39 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -13,6 +13,9 @@ port socket driver, are also represented by a port and some matching code may ne `is_pid/1` to `is_port/1`. - Ports and pids can be registered. Function `globalcontext_get_registered_process` result now is a term that can be a `port()` or a `pid()`. +- ESP32 `erlang:system_info/1` memory keys `esp32_free_heap_size` and `esp32_minimum_free_size` have +been deprecated in favor of `atomvm_` prefixed keys. Applications should be updated to use +`atomvm_free_heap_size` and `atomvm_minimum_free_size` instead. ## v0.6.4 -> v0.6.5 diff --git a/src/platforms/esp32/components/avm_sys/sys.c b/src/platforms/esp32/components/avm_sys/sys.c index 8318ae759..edcc6e5ac 100644 --- a/src/platforms/esp32/components/avm_sys/sys.c +++ b/src/platforms/esp32/components/avm_sys/sys.c @@ -72,8 +72,12 @@ static void *select_thread_loop(void *); static void select_thread_signal(struct ESP32PlatformData *platform); // clang-format off +// TODO: remove deprecated `atomvm_free_heap_size_atom` for the 0.8.x release cycle +static const char *const atomvm_free_heap_size_atom = "\x15" "atomvm_free_heap_size"; static const char *const esp_free_heap_size_atom = "\x14" "esp32_free_heap_size"; static const char *const esp_largest_free_block_atom = "\x18" "esp32_largest_free_block"; +static const char *const atomvm_get_minimum_free_size_atom = "\x18" "atomvm_minimum_free_size"; +// TODO: remove deprecated `esp_get_minimum_free_size_atom` for the 0.8.x release cycle static const char *const esp_get_minimum_free_size_atom = "\x17" "esp32_minimum_free_size"; static const char *const esp_chip_info_atom = "\xF" "esp32_chip_info"; static const char *const esp_idf_version_atom = "\xF" "esp_idf_version"; @@ -499,13 +503,27 @@ static term get_features(Context *ctx, uint32_t features) term sys_get_info(Context *ctx, term key) { GlobalContext *glb = ctx->global; + // TODO: remove this deprecated key for the 0.8.x release cycle if (key == globalcontext_make_atom(glb, esp_free_heap_size_atom)) { + ESP_LOGW(TAG, "The system_info/1 key 'esp32_free_heap_size' has been deprecated in favor of the \ + muti-platform key 'atomvm_free_heap_size'. This key will be removed in a future release, please \ + update your application to use 'atomvm_free_heap_size'."); + return term_from_int32(esp_get_free_heap_size()); + } + if (key == globalcontext_make_atom(glb, atomvm_free_heap_size_atom)) { return term_from_int32(esp_get_free_heap_size()); } if (key == globalcontext_make_atom(glb, esp_largest_free_block_atom)) { return term_from_int32(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)); } + // TODO: remove this deprecated key for the 0.8.x release cycle if (key == globalcontext_make_atom(glb, esp_get_minimum_free_size_atom)) { + ESP_LOGW(TAG, "The system_info/1 key 'esp32_minimum_free_size' has been deprecated in favor of the \ + muti-platform key 'atomvm_minimum_free_size'. This key will be removed in a future release, please \ + update your application to use 'atomvm_minimum_free_size'."); + return term_from_int32(esp_get_minimum_free_heap_size()); + } + if (key == globalcontext_make_atom(glb, atomvm_get_minimum_free_size_atom)) { return term_from_int32(esp_get_minimum_free_heap_size()); } if (key == globalcontext_make_atom(glb, esp_chip_info_atom)) { From bd195ad0cef8029afcd371f012498c478d490865 Mon Sep 17 00:00:00 2001 From: Winford Date: Wed, 14 May 2025 01:16:34 +0000 Subject: [PATCH 6/6] Add documentation for STM32 system_info/1 keys Adds documentation to the `erlang` module and "Programmer's Guide" for newly added STM32 system_info/1 keys. Signed-off-by: Winford --- doc/src/programmers-guide.md | 9 ++++++--- libs/estdlib/src/erlang.erl | 8 ++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/src/programmers-guide.md b/doc/src/programmers-guide.md index 424700cab..90e84f4f9 100644 --- a/doc/src/programmers-guide.md +++ b/doc/src/programmers-guide.md @@ -624,6 +624,11 @@ For example, io:format("Atom Count: ~p~n", [erlang:system_info(atom_count)]). ``` +In addition AtomVM supports the following keys on ESP32 and STM32 platforms: + +* `atomvm_free_heap_size` Returns the available free space in the heap. +* `atomvm_minimum_free_size` Returns the smallest ever free space available in the heap since boot, this will tell you how close you have come to running out of free memory. + ```{note} Additional platform-specific information is supported, depending on the platform type. See below. ``` @@ -1141,9 +1146,7 @@ As noted above, the [`erlang:system_info/1`](./apidocs/erlang/estdlib/erlang.md# You can request ESP32-specific information using using the following input atoms: -* `esp32_free_heap_size` Returns the available free space in the ESP32 heap. * `esp32_largest_free_block` Returns the size of the largest free continuous block in the ESP32 heap. -* `esp32_minimum_free_size` Returns the smallest ever free space available in the ESP32 heap since boot, this will tell you how close you have come to running out of free memory. * `esp32_chip_info` Returns map of the form `#{features := Features, cores := Cores, revision := Revision, model := Model}`, where `Features` is a list of features enabled in the chip, from among the following atoms: `[emb_flash, bgn, ble, bt]`; `Cores` is the number of CPU cores on the chip; `Revision` is the chip version; and `Model` is one of the following atoms: `esp32`, `esp32_s2`, `esp32_s3`, `esp32_c3`, etc. * `esp_idf_version` Return the IDF SDK version, as a string. @@ -1591,7 +1594,7 @@ The read options take the form of a proplist, if the key `raw` is true (`{raw, t If the key `voltage` is true (or simply appears in the list as an atom), then a calibrated voltage value will be returned in millivolts in the second element of the returned tuple. Otherwise, this element will be the atom `undefined`. -You may specify the number of samples (1 - 100000) to be taken and averaged over using the tuple `{samples, Samples :: 1..100000}`, the default is `64`. +You may specify the number of samples (1 - 100000) to be taken and averaged over using the tuple `{samples, Samples :: 1..100000}`, the default is `64`. ```{warning} Using a large number of samples can significantly increase the amount of time before a response, up to several seconds. diff --git a/libs/estdlib/src/erlang.erl b/libs/estdlib/src/erlang.erl index b588d1e2a..90a6b179b 100644 --- a/libs/estdlib/src/erlang.erl +++ b/libs/estdlib/src/erlang.erl @@ -289,11 +289,15 @@ process_info(_Pid, _Key) -> %%
  • schedulers the number of schedulers, equal to the number of online processors (integer)
  • %%
  • schedulers_online the current number of schedulers (integer)
  • %% +%% The following keys are supported on ESP32 and STM32 platforms: +%%
      +%%
    • atomvm_free_heap_size the number of (noncontiguous) free bytes in the STM32 heap (integer)
    • +%%
    • atomvm_minimum_free_size the smallest number of free bytes in the STM32 heap since boot (integer)
    • +%%
    +%% %% The following keys are supported on the ESP32 platform: %%
      -%%
    • esp32_free_heap_size the number of (noncontiguous) free bytes in the ESP32 heap (integer)
    • %%
    • esp32_largest_free_block the number of the largest contiguous free bytes in the ESP32 heap (integer)
    • -%%
    • esp32_minimum_free_size the smallest number of free bytes in the ESP32 heap since boot (integer)
    • %%
    • esp32_chip_info Details about the model and capabilities of the ESP32 device (map)
    • %%
    %%