diff --git a/CHANGELOG.md b/CHANGELOG.md index 25ff22862..0ca946200 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - PubSubClient library from v2.8.12 to v2.8.13 - From Semantic Versioning (SemVer) to Calendar Versioning (CalVer) - Set ESP32 stack size with ``#define SET_ESP32_STACK_SIZE``, added ``StackLowMark`` metrics +- Berry stores compiled bytecode into IRAM, freeing space in heap ### Fixed - Intermittent exceptions and heap corruption due to PubSubClient library buffer overflow (#13700) diff --git a/lib/libesp32/berry/default/berry_conf.h b/lib/libesp32/berry/default/berry_conf.h index 607a9c612..d796dce02 100644 --- a/lib/libesp32/berry/default/berry_conf.h +++ b/lib/libesp32/berry/default/berry_conf.h @@ -214,6 +214,9 @@ extern "C" { extern void *berry_malloc(size_t size); extern void berry_free(void *ptr); extern void *berry_realloc(void *ptr, size_t size); + extern void *berry_malloc32(size_t size); + extern void berry_free32(void *ptr); + extern void *berry_realloc32(void *ptr, size_t size); #ifdef __cplusplus } #endif @@ -241,7 +244,7 @@ extern "C" { /* Tasmota debug specific */ #ifdef USE_BERRY_DEBUG #undef BE_DEBUG_RUNTIME_INFO - #define BE_DEBUG_RUNTIME_INFO 2 /* record line information in 16 bits */ + #define BE_DEBUG_RUNTIME_INFO 1 /* record line information in 32 bits to be places in IRAM */ #endif // USE_BERRY_DEBUG #endif diff --git a/lib/libesp32/berry/src/be_parser.c b/lib/libesp32/berry/src/be_parser.c index 094a5adef..19b619f4b 100644 --- a/lib/libesp32/berry/src/be_parser.c +++ b/lib/libesp32/berry/src/be_parser.c @@ -313,14 +313,14 @@ static void end_func(bparser *parser) be_code_ret(finfo, NULL); /* append a return to last code */ end_block(parser); /* close block */ setupvals(finfo); /* close upvals */ - proto->code = be_vector_release(vm, &finfo->code); /* compact all vectors and return NULL if empty */ + proto->code = be_vector_release_32(vm, &finfo->code); /* compact all vectors and return NULL if empty */ proto->codesize = finfo->pc; - proto->ktab = be_vector_release(vm, &finfo->kvec); + proto->ktab = be_vector_release_32(vm, &finfo->kvec); proto->nconst = be_vector_count(&finfo->kvec); proto->ptab = be_vector_release(vm, &finfo->pvec); proto->nproto = be_vector_count(&finfo->pvec); #if BE_DEBUG_RUNTIME_INFO - proto->lineinfo = be_vector_release(vm, &finfo->linevec); + proto->lineinfo = be_vector_release_32(vm, &finfo->linevec); proto->nlineinfo = be_vector_count(&finfo->linevec); #endif #if BE_DEBUG_VAR_INFO diff --git a/lib/libesp32/berry/src/be_vector.c b/lib/libesp32/berry/src/be_vector.c index b73479601..b415b9c53 100644 --- a/lib/libesp32/berry/src/be_vector.c +++ b/lib/libesp32/berry/src/be_vector.c @@ -110,6 +110,32 @@ void* be_vector_release(bvm *vm, bvector *vector) return vector->data; } +/* free not used */ +void* be_vector_release_32(bvm *vm, bvector *vector) +{ + size_t size = vector->size; + int count = be_vector_count(vector); + if (count == 0) { + be_free(vm, vector->data, vector->capacity * size); + vector->capacity = 0; + vector->data = NULL; + vector->end = NULL; + } else if (count < vector->capacity) { + vector->data = be_realloc(vm, + vector->data, vector->capacity * size, count * size); // TODO - can we skip that step? + void* iram = berry_malloc32(count * size); + if (iram) { + memcpy(iram, vector->data, count * size); + free(vector->data); + vector->data = iram; + } + // vm->gc.usage = vm->gc.usage + count * size - vector->capacity * size; /* update allocated count */ + vector->end = (char*)vector->data + ((size_t)count - 1) * size; + vector->capacity = count; + } + return vector->data; +} + /* use binary search to find the vector capacity between 0-1024 */ static int binary_search(int value) { diff --git a/lib/libesp32/berry/src/be_vector.h b/lib/libesp32/berry/src/be_vector.h index 15f399cc6..3991986ca 100644 --- a/lib/libesp32/berry/src/be_vector.h +++ b/lib/libesp32/berry/src/be_vector.h @@ -38,6 +38,7 @@ void be_vector_remove_end(bvector *vector); void be_vector_resize(bvm *vm, bvector *vector, int count); void be_vector_clear(bvector *vector); void* be_vector_release(bvm *vm, bvector *vector); +void* be_vector_release_32(bvm *vm, bvector *vector); /* specialized call for 32 bits aligned accesses */ int be_nextsize(int value); #endif diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 1cf048513..099248267 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -1009,6 +1009,7 @@ #define USE_BERRY_PYTHON_COMPAT // Enable by default `import python_compat` #define USE_BERRY_TIMEOUT 4000 // Timeout in ms, will raise an exception if running time exceeds this timeout #define USE_BERRY_PSRAM // Allocate Berry memory in PSRAM if PSRAM is connected - this might be slightly slower but leaves main memory intact + #define USE_BERRY_IRAM // Allocate some data structures in IRAM (which is ususally unused) when possible and if no PSRAM is available // #define USE_BERRY_DEBUG // Compile Berry bytecode with line number information, makes exceptions easier to debug. Adds +8% of memory consumption for compiled code #define USE_WEBCLIENT // Enable `webclient` to make HTTP/HTTPS requests. Can be disabled for security reasons. // #define USE_WEBCLIENT_HTTPS // Enable HTTPS outgoing requests based on BearSSL (much ligher then mbedTLS, 42KB vs 150KB) in insecure mode (no verification of server's certificate) diff --git a/tasmota/support_esp.ino b/tasmota/support_esp.ino index e3a3ba943..71dff7fcb 100644 --- a/tasmota/support_esp.ino +++ b/tasmota/support_esp.ino @@ -503,6 +503,17 @@ void *special_calloc(size_t num, size_t size) { } } +// Variants for IRAM heap, which need all accesses to be 32 bits aligned +void *special_malloc32(uint32_t size) { + return heap_caps_malloc(size, UsePSRAM() ? MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT : MALLOC_CAP_32BIT); +} +void *special_realloc32(void *ptr, size_t size) { + return heap_caps_realloc(ptr, size, UsePSRAM() ? MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT : MALLOC_CAP_32BIT); +} +void *special_calloc32(size_t num, size_t size) { + return heap_caps_calloc(num, size, UsePSRAM() ? MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT : MALLOC_CAP_32BIT); +} + float CpuTemperature(void) { #ifdef CONFIG_IDF_TARGET_ESP32 return (float)temperatureRead(); // In Celsius diff --git a/tasmota/xdrv_52_9_berry.ino b/tasmota/xdrv_52_9_berry.ino index c48ab561d..dcbde05ca 100644 --- a/tasmota/xdrv_52_9_berry.ino +++ b/tasmota/xdrv_52_9_berry.ino @@ -84,6 +84,29 @@ extern "C" { } #endif // USE_BERRY_PSRAM + + void *berry_malloc32(uint32_t size) { + #ifdef USE_BERRY_IRAM + return special_malloc32(size); + #else + return special_malloc(size); + #endif + } + void *berry_realloc32(void *ptr, size_t size) { + #ifdef USE_BERRY_IRAM + return special_realloc32(ptr, size); + #else + return special_realloc(ptr, size); + #endif + } + void *berry_calloc32(size_t num, size_t size) { + #ifdef USE_BERRY_IRAM + return special_calloc32(num, size); + #else + return special_calloc(num, size); + #endif + } + void berry_free(void *ptr) { free(ptr); }