From a6542e1448f539ca7c5587ba9d2aad84aebf2e27 Mon Sep 17 00:00:00 2001 From: philmoz Date: Mon, 20 Jan 2025 11:32:20 +1100 Subject: [PATCH] feat(f4): more free memory on handsets with CCM RAM (#5804) Alternative Lua memory allocator for radios with CCM RAM - primarily GX12 at present --- radio/src/CMakeLists.txt | 4 +- .../linker/stm32f40x/extra_sections.ld | 3 + radio/src/edgetx.cpp | 6 +- radio/src/gui/colorlcd/standalone_lua.cpp | 4 +- radio/src/{ => lua}/bin_allocator.cpp | 14 +- radio/src/lua/ccm_allocator.cpp | 202 ++++++++++++++++++ radio/src/lua/custom_allocator.cpp | 26 +++ .../custom_allocator.h} | 11 +- radio/src/lua/interface.cpp | 6 +- radio/src/lua/widgets.cpp | 4 +- 10 files changed, 251 insertions(+), 29 deletions(-) rename radio/src/{ => lua}/bin_allocator.cpp (93%) create mode 100644 radio/src/lua/ccm_allocator.cpp create mode 100644 radio/src/lua/custom_allocator.cpp rename radio/src/{bin_allocator.h => lua/custom_allocator.h} (78%) diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 68892ea18..ea65013dc 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -531,8 +531,8 @@ if(SDRAM) message("Target has SDRAM, do not use BIN_ALLOCATOR") else() # Nano's malloc does work well with lua, use our own - add_definitions(-DUSE_BIN_ALLOCATOR) - set(SRC ${SRC} bin_allocator.cpp) + add_definitions(-DUSE_CUSTOM_ALLOCATOR) + set(SRC ${SRC} lua/custom_allocator.cpp) endif() # Bootloader diff --git a/radio/src/boards/generic_stm32/linker/stm32f40x/extra_sections.ld b/radio/src/boards/generic_stm32/linker/stm32f40x/extra_sections.ld index e1ce179fd..ed7447fab 100644 --- a/radio/src/boards/generic_stm32/linker/stm32f40x/extra_sections.ld +++ b/radio/src/boards/generic_stm32/linker/stm32f40x/extra_sections.ld @@ -3,6 +3,9 @@ PROVIDE(_heap_start = _eram); INCLUDE section_ccm.ld +PROVIDE(_ccm_heap_start = _eccm); +PROVIDE(_ccm_heap_end = _main_stack_start); + /* Reserve heap space in RAM */ ._user_heap(NOLOAD) : { diff --git a/radio/src/edgetx.cpp b/radio/src/edgetx.cpp index abb089959..75b96e008 100644 --- a/radio/src/edgetx.cpp +++ b/radio/src/edgetx.cpp @@ -76,6 +76,7 @@ #if defined(LUA) #include "lua/lua_states.h" +#include "lua/custom_allocator.h" #endif RadioData g_eeGeneral; @@ -1891,9 +1892,8 @@ uint32_t availableMemory() struct mallinfo info = mallinfo(); -#if defined(USE_BIN_ALLOCATOR) - extern int bin_avail(); - return ((uint32_t)((unsigned char *)&_heap_end - heap)) + info.fordblks + bin_avail(); +#if defined(USE_CUSTOM_ALLOCATOR) + return ((uint32_t)((unsigned char *)&_heap_end - heap)) + info.fordblks + custom_avail(); #else return ((uint32_t)((unsigned char *)&_heap_end - heap)) + info.fordblks; #endif diff --git a/radio/src/gui/colorlcd/standalone_lua.cpp b/radio/src/gui/colorlcd/standalone_lua.cpp index 8a2c1c352..b35457384 100644 --- a/radio/src/gui/colorlcd/standalone_lua.cpp +++ b/radio/src/gui/colorlcd/standalone_lua.cpp @@ -51,8 +51,8 @@ static void luaStandaloneInit() { luaClose(&lsStandalone); -#if defined(USE_BIN_ALLOCATOR) - lsStandalone = lua_newstate(bin_l_alloc, nullptr); //we use our own allocator! +#if defined(USE_CUSTOM_ALLOCATOR) + lsStandalone = lua_newstate(custom_l_alloc, nullptr); //we use our own allocator! #elif defined(LUA_ALLOCATOR_TRACER) memclear(&lsStandaloneTrace, sizeof(lsStandaloneTrace)); lsStandaloneTrace.script = "lua_newstate(scripts)"; diff --git a/radio/src/bin_allocator.cpp b/radio/src/lua/bin_allocator.cpp similarity index 93% rename from radio/src/bin_allocator.cpp rename to radio/src/lua/bin_allocator.cpp index 791a18a59..43f2fb105 100644 --- a/radio/src/bin_allocator.cpp +++ b/radio/src/lua/bin_allocator.cpp @@ -19,7 +19,7 @@ * GNU General Public License for more details. */ -#include "bin_allocator.h" +#include #include "edgetx.h" template @@ -94,14 +94,6 @@ class BinAllocator #if defined(SIMU) typedef BinAllocator<40,300> BinAllocator_slots1; typedef BinAllocator<80,100> BinAllocator_slots2; -#elif defined(STM32F4) - #if defined(DEBUG) - typedef BinAllocator<28,395> BinAllocator_slots1; - typedef BinAllocator<92,85> BinAllocator_slots2; - #else - typedef BinAllocator<28,415> BinAllocator_slots1; - typedef BinAllocator<92,98> BinAllocator_slots2; - #endif #else typedef BinAllocator<28,200> BinAllocator_slots1; typedef BinAllocator<92,50> BinAllocator_slots2; @@ -118,7 +110,7 @@ int missedAlloc = 0; int missedFree = 0; #endif -int bin_avail() +int custom_avail() { return slots1.avail() + slots2.avail(); } @@ -188,7 +180,7 @@ static void * bin_realloc(void * ptr, size_t size) } } -void *bin_l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) +void *custom_l_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { // (void)ud; (void)osize; /* not used */ diff --git a/radio/src/lua/ccm_allocator.cpp b/radio/src/lua/ccm_allocator.cpp new file mode 100644 index 000000000..0077b689c --- /dev/null +++ b/radio/src/lua/ccm_allocator.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "edgetx.h" + +/* + Custom memory allocator for Lua that adds all available CCM RAM to the memory pool + - maintains a free list of available CCM RAM, order by address + - allocates from the smallest available free block + - coalesces adjacent blocks on free +*/ + +typedef struct _memblk { + int size; // actual size of block. Available / allocated size = 'size' - sizeof(int) + struct _memblk* next; +} memblk; + +#define MIN_SIZE 12 // Minimum size for free block (4 for 'size' field, 8 for data) + +// CCM RAM available region +extern memblk _ccm_heap_start; +extern int _ccm_heap_end; + +// Init flag +static bool started = false; + +// Free list +static memblk* ccm_list = nullptr; + +// CCM region for checking if blocks belong here +static unsigned char* ccm_start; +static unsigned char* ccm_end; + +// Initialise free list +static void init() +{ + if (!started) { + started = true; + + ccm_start = (unsigned char*)&_ccm_heap_start; + ccm_end = (unsigned char*)&_ccm_heap_end; + + ccm_list = &_ccm_heap_start; + ccm_list->next = nullptr; + ccm_list->size = ccm_end - ccm_start; + } +} + +// Return free memory available +uint32_t custom_avail() +{ + uint32_t free = 0; + for (memblk* b = ccm_list; b; b = b->next) { + free += (b->size - sizeof(int)); + } + return free; +} + +// Free memory block +static void ccm_free(void* ptr) +{ + // Check that block belongs in CCM RAM + if (ptr >= ccm_start && ptr < ccm_end) { + memblk* prev = nullptr; + memblk* next = ccm_list; + memblk* p = (memblk*)(((unsigned char*)ptr) - sizeof(int)); + // Find place in list + while (next && (p > next)) { + prev = next; + next = next->next; + } + // Link into list + p->next = next; + if (prev == nullptr) { + ccm_list = p; + } else { + prev->next = p; + if ((((unsigned char*)prev) + prev->size) == (unsigned char*)p) { + // Merge adjacent + prev->next = p->next; + prev->size += p->size; + p = prev; + } + } + if ((((unsigned char*)p) + p->size) == (unsigned char*)next) { + // Merge adjacent + p->next = next->next; + p->size += next->size; + } + } else { + // Not CCM RAM - let system handle it. + free(ptr); + } +} + +// Allocate new block +static void* ccm_malloc(size_t nsize) +{ + // Add space for 'size' field and align to 32 bit size + int asize = (nsize + sizeof(int) + 3) & 0xFFFFFFFC; + + memblk* prev = nullptr; + memblk* next = ccm_list; + memblk* p = nullptr; + memblk* pprev = nullptr; + // Find smallest block that will fit + while (next) { + if (next->size >= asize) { + if (!p || (p && (p->size < next->size))) { + pprev = prev; + p = next; + if (p->size == asize) break; // exact fit + } + } + prev = next; + next = next->next; + } + + void* res; + + if (p) { + if (p->size <= (asize + MIN_SIZE)) { + // Using entire block - unlink it + if (pprev) + pprev->next = p->next; + else + ccm_list = p->next; + } else { + // reduce size of block and return end part + p->size -= asize; + p = (memblk*)(((unsigned char*)p) + p->size); + p->size = asize; + } + res = ((unsigned char*)p) + sizeof(int); + } else { + // No space - let system handle it + res = malloc(nsize); + } + + return res; +} + +// Re-allocate existing block +static void* ccm_realloc(void* ptr, size_t osize, size_t nsize) +{ + // Add space for 'size' field and align to 32 bit size + int asize = (nsize + sizeof(int) + 3) & 0xFFFFFFFC; + + // Check that block belongs in CCM RAM + if (ptr >= ccm_start && ptr < ccm_end) { + memblk* p = (memblk*)(((unsigned char*)ptr) - sizeof(int)); + if (asize <= p->size) + return ptr; // fits in already allocated space + + void* np = ccm_malloc(nsize); + memcpy(np, ptr, osize); + ccm_free(ptr); + return np; + } else { + // In theory nsize might fit in available CCM RAM; but since block is + // already in normal RAM we just let system handle it. + return realloc(ptr, nsize); + } +} + +void* custom_l_alloc(void *ud, void *ptr, size_t osize, size_t nsize) +{ + (void)ud; /* not used */ + + init(); + + if (nsize == 0) { + if (ptr) { // avoid a bunch of NULL pointer free calls + ccm_free(ptr); + } + return nullptr; + } + else if (ptr) { + return ccm_realloc(ptr, osize, nsize); + } else { + return ccm_malloc(nsize); + } +} diff --git a/radio/src/lua/custom_allocator.cpp b/radio/src/lua/custom_allocator.cpp new file mode 100644 index 000000000..841061fa6 --- /dev/null +++ b/radio/src/lua/custom_allocator.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if defined(STM32F4) +#include "ccm_allocator.cpp" +#else +#include "bin_allocator.cpp" +#endif diff --git a/radio/src/bin_allocator.h b/radio/src/lua/custom_allocator.h similarity index 78% rename from radio/src/bin_allocator.h rename to radio/src/lua/custom_allocator.h index a9e0d9431..e688f597a 100644 --- a/radio/src/bin_allocator.h +++ b/radio/src/lua/custom_allocator.h @@ -21,9 +21,8 @@ #pragma once -#include - -#if defined(USE_BIN_ALLOCATOR) -// wrapper for our BinAllocator for Lua -void *bin_l_alloc (void *ud, void *ptr, size_t osize, size_t nsize); -#endif //#if defined(USE_BIN_ALLOCATOR) +#if defined(USE_CUSTOM_ALLOCATOR) +// wrapper for our custom allocator for Lua +void *custom_l_alloc(void *ud, void *ptr, size_t osize, size_t nsize); +int custom_avail(); +#endif diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index e8ae34157..c531416aa 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -26,7 +26,7 @@ #include #include "edgetx.h" -#include "bin_allocator.h" +#include "custom_allocator.h" #include "lua_api.h" #include "lua_event.h" @@ -1351,8 +1351,8 @@ void luaInit() L = nullptr; if (luaState != INTERPRETER_PANIC) { -#if defined(USE_BIN_ALLOCATOR) - L = lua_newstate(bin_l_alloc, nullptr); //we use our own allocator! +#if defined(USE_CUSTOM_ALLOCATOR) + L = lua_newstate(custom_l_alloc, nullptr); //we use our own allocator! #elif defined(LUA_ALLOCATOR_TRACER) memclear(&lsScriptsTrace, sizeof(lsScriptsTrace)); lsScriptsTrace.script = "lua_newstate(scripts)"; diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index 0b60332b8..cd8857102 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -343,8 +343,8 @@ void luaInitThemesAndWidgets() { TRACE("luaInitThemesAndWidgets"); -#if defined(USE_BIN_ALLOCATOR) - lsWidgets = lua_newstate(bin_l_alloc, NULL); //we use our own allocator! +#if defined(USE_CUSTOM_ALLOCATOR) + lsWidgets = lua_newstate(custom_l_alloc, NULL); //we use our own allocator! #elif defined(LUA_ALLOCATOR_TRACER) memclear(&lsWidgetsTrace, sizeof(lsWidgetsTrace)); lsWidgetsTrace.script = "lua_newstate(widgets)";