From e42f52aef8311a85c8028ac574b09c3cd61562c7 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 19 Mar 2020 02:16:22 +0100 Subject: [PATCH 1/6] [driver] Move BlockAllocator out of :architecture:heap --- .../driver/heap/block_allocator.hpp | 98 ------------------- src/modm/architecture/module.lb | 16 --- src/modm/driver/storage/block_allocator.hpp | 88 +++++++++++++++++ src/modm/driver/storage/block_allocator.lb | 21 ++++ .../storage}/block_allocator_impl.hpp | 16 ++- .../core/cortex/heap_block_allocator.cpp | 2 +- src/modm/platform/core/cortex/module.lb | 2 +- test/modm/architecture/module.lb | 1 - test/modm/driver/module.lb | 1 + .../storage}/block_allocator_test.cpp | 2 +- .../storage}/block_allocator_test.hpp | 0 tools/scripts/synchronize_docs.py | 2 +- 12 files changed, 121 insertions(+), 128 deletions(-) delete mode 100644 src/modm/architecture/driver/heap/block_allocator.hpp create mode 100644 src/modm/driver/storage/block_allocator.hpp create mode 100644 src/modm/driver/storage/block_allocator.lb rename src/modm/{architecture/driver/heap => driver/storage}/block_allocator_impl.hpp (95%) rename test/modm/{architecture/driver/heap => driver/storage}/block_allocator_test.cpp (97%) rename test/modm/{architecture/driver/heap => driver/storage}/block_allocator_test.hpp (100%) diff --git a/src/modm/architecture/driver/heap/block_allocator.hpp b/src/modm/architecture/driver/heap/block_allocator.hpp deleted file mode 100644 index 8f44cf44ca..0000000000 --- a/src/modm/architecture/driver/heap/block_allocator.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2009-2012, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2012, Sascha Schade - * Copyright (c) 2012, 2016, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_BLOCK_ALLOCATOR_HPP -#define MODM_BLOCK_ALLOCATOR_HPP - -#include -#include - -#include -#include - -namespace modm -{ - /** - * Memory allocator. - * - * - * \tparam T - * TODO - * - * \tparam BLOCK_SIZE - * Size of one allocatable block in words (sizeof(T) bytes) - * (BLOCKSIZE * sizeof(T) * n) - 4 has to be dividable by 4 for every n - * - * \author Fabian Greif - * \ingroup modm_architecture_heap - */ - template - class BlockAllocator - { - typedef modm::SignedType SignedType; - - public: - /** - * Initialize the raw memory. - * - * Needs to called before any calls to allocate() or free(). Must - * be called only once! - * - * \param heapStart - * Needs to point to the first available byte - * \param heapEnd - * Needs to point directly above the last available memory - * position. - */ - modm_always_inline void - initialize(void * heapStart, void * heapEnd); - - /** - * Allocate memory - * - */ - modm_always_inline void * - allocate(std::size_t requestedSize); - - /** - * Free memory in O(1) - * - * \param ptr - * Must be the same pointer previously acquired by - * allocate(). - */ - modm_always_inline void - free(void *ptr); - - public: - std::size_t - getAvailableSize() const; - - private: - // Align the pointer to a multiple of MODM_ALIGNMENT - modm_always_inline T * - alignPointer(void * ptr) const; - - //static const int MAX_BLOCK_PARTS = 2048; - - T* start; - T* end; - - T* freeHint; - }; -} - -#include "block_allocator_impl.hpp" - -#endif // MODM_BLOCK_ALLOCATOR_HPP diff --git a/src/modm/architecture/module.lb b/src/modm/architecture/module.lb index c73c8b28c9..866af76ea3 100644 --- a/src/modm/architecture/module.lb +++ b/src/modm/architecture/module.lb @@ -189,21 +189,6 @@ class GpioExpander(Module): env.copy("interface/gpio_expander.hpp") # ----------------------------------------------------------------------------- -class Heap(Module): - def init(self, module): - module.name = "heap" - module.description = "Memory Allocators" - - def prepare(self, module, options): - module.depends(":utils") - return True - - def build(self, env): - env.outbasepath = "modm/src/modm/architecture" - env.copy("driver/heap/block_allocator.hpp") - env.copy("driver/heap/block_allocator_impl.hpp") -# ----------------------------------------------------------------------------- - class I2c(Module): def init(self, module): module.name = "i2c" @@ -384,7 +369,6 @@ def prepare(module, options): module.add_submodule(Delay()) module.add_submodule(Gpio()) module.add_submodule(GpioExpander()) - module.add_submodule(Heap()) module.add_submodule(I2c()) module.add_submodule(I2cDevice()) module.add_submodule(I2cMultiplexer()) diff --git a/src/modm/driver/storage/block_allocator.hpp b/src/modm/driver/storage/block_allocator.hpp new file mode 100644 index 0000000000..3a62dab43f --- /dev/null +++ b/src/modm/driver/storage/block_allocator.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2009-2012, Fabian Greif + * Copyright (c) 2010, Martin Rosekeit + * Copyright (c) 2012, Sascha Schade + * Copyright (c) 2012, 2016, 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include + + +namespace modm +{ + +/** + * Memory allocator. + * + * @tparam BLOCK_SIZE + * Size of one allocatable block in words (sizeof(T) bytes) + * (BLOCKSIZE * sizeof(T) * n) - 4 has to be dividable by 4 for every n + * + * @author Fabian Greif + * @ingroup modm_driver_block_allocator + */ +template +class BlockAllocator +{ + using SignedType = std::make_signed_t; +public: + /** + * Initialize the raw memory. + * + * Needs to called before any calls to allocate() or free(). Must + * be called only once! + * + * @param heapStart + * Needs to point to the first available byte + * @param heapEnd + * Needs to point directly above the last available memory + * position. + */ + void + initialize(void * heapStart, void * heapEnd); + + /// Allocate memory + void * + allocate(std::size_t requestedSize); + + /** + * Free memory in O(1) + * + * @param ptr + * Must be the same pointer previously acquired by + * allocate(). + */ + void + free(void *ptr); + +public: + std::size_t + getAvailableSize() const; + +private: + // Align the pointer to a multiple of MODM_ALIGNMENT + T * + alignPointer(void * ptr) const; + + //static const int MAX_BLOCK_PARTS = 2048; + + T* start; + T* end; + T* freeHint; +}; + +} // namespace modm + +#include "block_allocator_impl.hpp" diff --git a/src/modm/driver/storage/block_allocator.lb b/src/modm/driver/storage/block_allocator.lb new file mode 100644 index 0000000000..9edbe114bc --- /dev/null +++ b/src/modm/driver/storage/block_allocator.lb @@ -0,0 +1,21 @@ +# Copyright (c) 2020, Niklas Hauser +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":driver:block.allocator" + module.description = "Block Allocator" + +def prepare(module, options): + module.depends(":architecture", ":math:utils") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/storage" + env.copy("block_allocator.hpp") + env.copy("block_allocator_impl.hpp") diff --git a/src/modm/architecture/driver/heap/block_allocator_impl.hpp b/src/modm/driver/storage/block_allocator_impl.hpp similarity index 95% rename from src/modm/architecture/driver/heap/block_allocator_impl.hpp rename to src/modm/driver/storage/block_allocator_impl.hpp index 3b0c61a118..9a561a488f 100644 --- a/src/modm/architecture/driver/heap/block_allocator_impl.hpp +++ b/src/modm/driver/storage/block_allocator_impl.hpp @@ -2,7 +2,7 @@ * Copyright (c) 2009-2013, Fabian Greif * Copyright (c) 2010, Martin Rosekeit * Copyright (c) 2012, Sascha Schade - * Copyright (c) 2012, 2016, Niklas Hauser + * Copyright (c) 2012, 2016, 2020, Niklas Hauser * * This file is part of the modm project. * @@ -12,9 +12,7 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_BLOCK_ALLOCATOR_HPP - #error "Don't include this file directly use 'block_allocator.hpp' instead!" -#endif +#pragma once // ---------------------------------------------------------------------------- /* @@ -58,7 +56,7 @@ * \-- end */ template -modm_always_inline void +void modm::BlockAllocator::initialize(void * heapStart, void * heapEnd) { start = alignPointer(heapStart); @@ -83,7 +81,7 @@ modm::BlockAllocator::initialize(void * heapStart, void * heapEnd * */ template -modm_always_inline void * +void * modm::BlockAllocator::allocate(std::size_t requestedSize) { requestedSize += 4; // bytes needed for the management @@ -130,7 +128,7 @@ modm::BlockAllocator::allocate(std::size_t requestedSize) // ---------------------------------------------------------------------------- template -modm_always_inline void +void modm::BlockAllocator::free(void *ptr) { if (ptr == 0) { @@ -183,7 +181,7 @@ modm::BlockAllocator::free(void *ptr) // ---------------------------------------------------------------------------- template -modm_always_inline std::size_t +std::size_t modm::BlockAllocator::getAvailableSize() const { T *p = start; @@ -208,7 +206,7 @@ modm::BlockAllocator::getAvailableSize() const // ---------------------------------------------------------------------------- template -modm_always_inline T * +T * modm::BlockAllocator::alignPointer(void * ptr) const { // (MODM_ALIGNMENT - 1) is used as a bitmask diff --git a/src/modm/platform/core/cortex/heap_block_allocator.cpp b/src/modm/platform/core/cortex/heap_block_allocator.cpp index 3cf8f3cba7..35740fb071 100644 --- a/src/modm/platform/core/cortex/heap_block_allocator.cpp +++ b/src/modm/platform/core/cortex/heap_block_allocator.cpp @@ -19,7 +19,7 @@ // ---------------------------------------------------------------------------- // Using the MODM Block Allocator -#include +#include #ifndef MODM_MEMORY_BLOCK_ALLOCATOR_TYPE #define MODM_MEMORY_BLOCK_ALLOCATOR_TYPE uint16_t diff --git a/src/modm/platform/core/cortex/module.lb b/src/modm/platform/core/cortex/module.lb index efa0efd808..8fc1f72c9c 100644 --- a/src/modm/platform/core/cortex/module.lb +++ b/src/modm/platform/core/cortex/module.lb @@ -165,7 +165,7 @@ def prepare(module, options): enumeration=["newlib", "block", "tlsf"], default="newlib", dependencies=lambda v: {"newlib": None, - "block": ":architecture:heap", + "block": ":driver:block.allocator", "tlsf": ":tlsf"}[v])) module.add_option( NumericOption( diff --git a/test/modm/architecture/module.lb b/test/modm/architecture/module.lb index 2d7a7e7c09..1ab8b4a665 100644 --- a/test/modm/architecture/module.lb +++ b/test/modm/architecture/module.lb @@ -21,7 +21,6 @@ def prepare(module, options): ":architecture:atomic", ":architecture:can", ":architecture:clock", - ":architecture:heap", ":architecture:register") return True diff --git a/test/modm/driver/module.lb b/test/modm/driver/module.lb index 8e3dfa1a10..553c67cd42 100644 --- a/test/modm/driver/module.lb +++ b/test/modm/driver/module.lb @@ -27,6 +27,7 @@ def prepare(module, options): ":driver:ltc2984", ":driver:drv832x_spi", ":driver:mcp2515", + ":driver:block.allocator", "modm-test:test:platform:spi", ":platform:gpio") return True diff --git a/test/modm/architecture/driver/heap/block_allocator_test.cpp b/test/modm/driver/storage/block_allocator_test.cpp similarity index 97% rename from test/modm/architecture/driver/heap/block_allocator_test.cpp rename to test/modm/driver/storage/block_allocator_test.cpp index 20220e6bbf..6b3ed1c0c5 100644 --- a/test/modm/architecture/driver/heap/block_allocator_test.cpp +++ b/test/modm/driver/storage/block_allocator_test.cpp @@ -14,7 +14,7 @@ #include "block_allocator_test.hpp" -#include +#include void BlockAllocatorTest::testAvailableSize() diff --git a/test/modm/architecture/driver/heap/block_allocator_test.hpp b/test/modm/driver/storage/block_allocator_test.hpp similarity index 100% rename from test/modm/architecture/driver/heap/block_allocator_test.hpp rename to test/modm/driver/storage/block_allocator_test.hpp diff --git a/tools/scripts/synchronize_docs.py b/tools/scripts/synchronize_docs.py index 82eeaaa8bb..acb5586a8f 100755 --- a/tools/scripts/synchronize_docs.py +++ b/tools/scripts/synchronize_docs.py @@ -60,7 +60,7 @@ def name(raw_name): .replace("BLOCK-DEVICE-", "")\ .replace("BLOCK-", "")\ .replace("-SPI", "") - if result in ["DEVICE", "LIS3-TRANSPORT", "MEMORY-BUS", "TERMINAL", + if result in ["DEVICE", "LIS3-TRANSPORT", "MEMORY-BUS", "TERMINAL", "ALLOCATOR", "MIRROR", "ADC-SAMPLER", "FAT", "HEAP", "--PYCACHE--"]: return None return result From 5bd3596c4a1be1f43b1427d93cb779e5fadd64f6 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 23 Mar 2020 04:52:39 +0100 Subject: [PATCH 2/6] [platform] Move heap to :platform:heap module --- ext/gcc/module_c++.lb | 11 +- ext/gcc/new_delete.cpp.in | 100 +++++++ ext/gcc/newdelete_avr.cpp | 99 ------- ext/gcc/newdelete_cortex.cpp | 97 ------- src/modm/architecture/module.lb | 17 +- src/modm/board/disco_f469ni/board.xml | 5 +- src/modm/board/disco_f469ni/module.lb | 5 +- src/modm/board/nucleo_f031k6/board.xml | 3 +- src/modm/board/nucleo_f042k6/board.xml | 3 +- src/modm/platform/core/avr/module.lb | 20 +- src/modm/platform/core/avr/ram.hpp | 41 --- src/modm/platform/core/cortex/heap_newlib.cpp | 95 ------- .../platform/core/cortex/heap_table.cpp.in | 17 +- .../platform/core/cortex/heap_table.hpp.in | 26 +- src/modm/platform/core/cortex/linker.macros | 9 + src/modm/platform/core/cortex/module.lb | 41 ++- src/modm/platform/core/cortex/module.md | 8 +- .../platform/core/cortex/option/allocator.md | 41 --- src/modm/platform/core/hosted/module.lb | 3 +- src/modm/platform/heap/avr/module.lb | 43 +++ .../platform/{core => heap}/avr/ram.cpp.in | 16 +- .../cortex/heap_block.cpp} | 51 +--- src/modm/platform/heap/cortex/heap_newlib.cpp | 52 ++++ .../{core => heap}/cortex/heap_tlsf.cpp | 35 +-- src/modm/platform/heap/cortex/module.lb | 44 +++ src/modm/platform/heap/cortex/module.md | 252 ++++++++++++++++++ src/modm/platform/heap/cortex/no_heap.c.in | 28 ++ test/config/nucleo-f103.xml | 1 + test/config/nucleo-f401.xml | 1 + test/config/nucleo-f411.xml | 1 + test/config/nucleo-f446.xml | 1 + test/config/nucleo-l432.xml | 1 + 32 files changed, 651 insertions(+), 516 deletions(-) create mode 100644 ext/gcc/new_delete.cpp.in delete mode 100644 ext/gcc/newdelete_avr.cpp delete mode 100644 ext/gcc/newdelete_cortex.cpp delete mode 100644 src/modm/platform/core/avr/ram.hpp delete mode 100644 src/modm/platform/core/cortex/heap_newlib.cpp delete mode 100644 src/modm/platform/core/cortex/option/allocator.md create mode 100644 src/modm/platform/heap/avr/module.lb rename src/modm/platform/{core => heap}/avr/ram.cpp.in (97%) rename src/modm/platform/{core/cortex/heap_block_allocator.cpp => heap/cortex/heap_block.cpp} (61%) create mode 100644 src/modm/platform/heap/cortex/heap_newlib.cpp rename src/modm/platform/{core => heap}/cortex/heap_tlsf.cpp (85%) create mode 100644 src/modm/platform/heap/cortex/module.lb create mode 100644 src/modm/platform/heap/cortex/module.md create mode 100644 src/modm/platform/heap/cortex/no_heap.c.in diff --git a/ext/gcc/module_c++.lb b/ext/gcc/module_c++.lb index df21a47420..45fa807b05 100644 --- a/ext/gcc/module_c++.lb +++ b/ext/gcc/module_c++.lb @@ -63,7 +63,7 @@ def prepare(module, options): name="safe_statics", default=True, description=descr_safe_statics)) - module.depends(":architecture:assert", ":architecture:memory", ":stdc") + module.depends(":architecture:assert", ":stdc") return True @@ -77,19 +77,18 @@ def build(env): env.substitutions = { "core": core, "with_exceptions": with_exceptions, - "with_threadsafe_statics": with_threadsafe_statics + "with_threadsafe_statics": with_threadsafe_statics, + "with_memory_traits": env.has_module(":architecture:memory"), + "is_avr": is_avr, } env.outbasepath = "modm/ext/gcc" env.template("cxxabi.cpp.in") + env.template("new_delete.cpp.in") if is_avr: env.collect(":build:path.include", "modm/ext/gcc/libstdc++/include") env.copy("libstdc++", ignore=env.ignore_files("*.lb", "*.md", "*.in", "examples")) env.template("assert.cpp.in", "assert.cpp") - env.copy("newdelete_avr.cpp", "newdelete.cpp") - - elif is_cortex_m: - env.copy("newdelete_cortex.cpp", "newdelete.cpp") env.collect(":build:cxxflags", "-fuse-cxa-atexit") # Threadsafe statics diff --git a/ext/gcc/new_delete.cpp.in b/ext/gcc/new_delete.cpp.in new file mode 100644 index 0000000000..91fcbd4cf7 --- /dev/null +++ b/ext/gcc/new_delete.cpp.in @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2009-2010, Martin Rosekeit + * Copyright (c) 2009-2011, Fabian Greif + * Copyright (c) 2011-2012, 2014, 2016, 2020, Niklas Hauser + * Copyright (c) 2012, Sascha Schade + * Copyright (c) 2013, Kevin Läufer + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include // for prototypes of malloc() and free() +%% if with_memory_traits +#include +%% endif +#include + +%% if with_memory_traits +%% if is_avr +static inline +%% else +extern "C" modm_weak +%% endif +void* malloc_traits(std::size_t size, uint32_t) +{ return malloc(size); } +%% endif + +template +static inline void* +%% if with_memory_traits +new_assert(size_t size, modm_unused modm::MemoryTraits traits = modm::MemoryDefault) +%% else +new_assert(size_t size) +%% endif +{ + void *ptr; +%% if not is_avr + while(1) + { +%% if with_memory_traits + if constexpr (with_traits) { + ptr = malloc_traits(size, traits.value); + } else { + ptr = malloc(size); + } +%% else + ptr = malloc(size); +%% endif + if (ptr) break; + /* See footnote 1) in https://en.cppreference.com/w/cpp/memory/new/operator_new + * + * In case of failure, the standard library implementation calls the + * function pointer returned by std::get_new_handler and repeats + * allocation attempts until new handler does not return or becomes a + * null pointer, at which time it throws std::bad_alloc. + */ + if (std::get_new_handler()) std::get_new_handler()(); + else break; + } +%% else + ptr = malloc(size); +%% endif + + if (ptr == nullptr) { +%% if options.get("with_exceptions", False) + throw std::bad_alloc(); +%% else + modm_assert(0, "new", + "C++ new() operator failed to allocate!", size); +%% endif + } + return ptr; +} + +// ---------------------------------------------------------------------------- +void* operator new (std::size_t size) { return new_assert(size); } +void* operator new[](std::size_t size) { return new_assert(size); } + +void* operator new (std::size_t size, const std::nothrow_t&) noexcept { return malloc(size); } +void* operator new[](std::size_t size, const std::nothrow_t&) noexcept { return malloc(size); } + +%% if with_memory_traits +void* operator new (std::size_t size, modm::MemoryTraits traits) noexcept { return new_assert(size, traits); } +void* operator new[](std::size_t size, modm::MemoryTraits traits) noexcept { return new_assert(size, traits); } +%% endif + +// ---------------------------------------------------------------------------- +void operator delete (void *ptr) noexcept { free(ptr); } +void operator delete[](void* ptr) noexcept { free(ptr); } + +void operator delete (void* ptr, std::size_t) noexcept { free(ptr); } +void operator delete[](void* ptr, std::size_t) noexcept { free(ptr); } + +void operator delete (void* ptr, const std::nothrow_t&) noexcept { free(ptr); } +void operator delete[](void* ptr, const std::nothrow_t&) noexcept { free(ptr); } diff --git a/ext/gcc/newdelete_avr.cpp b/ext/gcc/newdelete_avr.cpp deleted file mode 100644 index ee1b1fe679..0000000000 --- a/ext/gcc/newdelete_avr.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2012-2014, 2016-2017, Niklas Hauser - * Copyright (c) 2015, Christian Menard - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include -#include -#include -#include - -static inline void * -modm_new(size_t size) -{ - void * ptr = modm::platform::allocateMemory(size); - modm_assert_continue_fail(ptr, "new", - "C++ new() operator failed to allocate!", size); - return ptr; -} - -void * -operator new(size_t size) -{ - return modm_new(size); -} - -void * -operator new[](size_t size) -{ - return modm_new(size); -} - -void * -operator new(size_t size, modm::MemoryTraits) -{ - return modm_new(size); -} - -void * -operator new[](size_t size, modm::MemoryTraits) -{ - return modm_new(size); -} - -void* -operator new(std::size_t size, const std::nothrow_t&) noexcept -{ - return modm::platform::allocateMemory(size); -} - -void* -operator new[](std::size_t size, const std::nothrow_t&) noexcept -{ - return modm::platform::allocateMemory(size); -} - -void -operator delete(void* ptr) -{ - modm::platform::freeMemory(ptr); -} - -void -operator delete(void* ptr, size_t) -{ - modm::platform::freeMemory(ptr); -} - -void -operator delete[](void* ptr) -{ - modm::platform::freeMemory(ptr); -} - -void -operator delete[](void* ptr, size_t) -{ - modm::platform::freeMemory(ptr); -} - -void -operator delete(void* ptr, const std::nothrow_t&) noexcept -{ - modm::platform::freeMemory(ptr); -} - -void -operator delete[](void* ptr, const std::nothrow_t&) noexcept -{ - modm::platform::freeMemory(ptr); -} diff --git a/ext/gcc/newdelete_cortex.cpp b/ext/gcc/newdelete_cortex.cpp deleted file mode 100644 index b53c05ef4e..0000000000 --- a/ext/gcc/newdelete_cortex.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2009-2010, Martin Rosekeit - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2011-2012, 2014, 2016, 2020, Niklas Hauser - * Copyright (c) 2012, Sascha Schade - * Copyright (c) 2013, Kevin Läufer - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include // for prototypes of malloc() and free() -#include -#include -#include - -extern "C" void * malloc_tr(size_t, uint32_t); - -static inline void -assert_ptr(void *ptr, size_t size) -{ - modm_assert(ptr, "new", - "C++ new() operator failed to allocate!", size); -} - -// ---------------------------------------------------------------------------- -void * -operator new(size_t size) throw () -{ - void * ptr = malloc(size); - assert_ptr(ptr, size); - return ptr; -} - -void * -operator new[](size_t size) throw () -{ - void * ptr = malloc(size); - assert_ptr(ptr, size); - return ptr; -} - -void * -operator new(size_t size, const std::nothrow_t&) noexcept -{ - return malloc(size); -} - -void * -operator new[](size_t size, const std::nothrow_t&) noexcept -{ - return malloc(size); -} - -void * -operator new(size_t size, modm::MemoryTraits traits) noexcept -{ - void * ptr = malloc_tr(size, traits.value); - assert_ptr(ptr, size); - return ptr; -} - -void * -operator new[](size_t size, modm::MemoryTraits traits) noexcept -{ - void * ptr = malloc_tr(size, traits.value); - assert_ptr(ptr, size); - return ptr; -} - -void -operator delete(void *ptr) noexcept -{ - free(ptr); -} - -void -operator delete(void* ptr, size_t) noexcept -{ - free(ptr); -} - -void -operator delete[](void* ptr) noexcept -{ - free(ptr); -} - -void -operator delete[](void* ptr, size_t) noexcept -{ - free(ptr); -} diff --git a/src/modm/architecture/module.lb b/src/modm/architecture/module.lb index 866af76ea3..f39c81d062 100644 --- a/src/modm/architecture/module.lb +++ b/src/modm/architecture/module.lb @@ -251,7 +251,22 @@ class Interrupt(Module): class Memory(Module): def init(self, module): module.name = "memory" - module.description = "Memory Traits" + module.description = """ +# Memory Traits + +Memories have different traits, such as DMA-ability or access time. The memory +allocator functions (`malloc()`, `operator new`, etc) by default only return +DMA-able memories, ordered by fastest access time. To allocate memory with +specific traits, use the overloaded `operator new`: + +```cpp +auto *p = new (modm::MemoryFastData) Foo; +auto *q = new (modm::MemoryExternal, std::nothrow) Foo; +``` + +!!! tip "You need to choose a heap implementation!" + You can include the `:platform:heap` module or supply your own implementation. +""" def prepare(self, module, options): module.depends(":architecture:register") diff --git a/src/modm/board/disco_f469ni/board.xml b/src/modm/board/disco_f469ni/board.xml index 8432dc1a3f..ac45abb550 100644 --- a/src/modm/board/disco_f469ni/board.xml +++ b/src/modm/board/disco_f469ni/board.xml @@ -8,10 +8,11 @@ - - + + modm:board:disco-f469ni + modm:platform:heap diff --git a/src/modm/board/disco_f469ni/module.lb b/src/modm/board/disco_f469ni/module.lb index f86563e070..5b284a1137 100644 --- a/src/modm/board/disco_f469ni/module.lb +++ b/src/modm/board/disco_f469ni/module.lb @@ -61,7 +61,7 @@ linkerscript_sections = """\ __sdramdata_load = LOADADDR (.sdramdata); /* address in FLASH */ __sdramdata_start = .; /* address in RAM */ - KEEP(*(.sdramdata)) + *(.sdramdata) . = ALIGN(4); __sdramdata_end = .; @@ -69,6 +69,9 @@ linkerscript_sections = """\ .heap_extern (NOLOAD) : ALIGN(4) { + *(.heap_extern) + . = ALIGN(4); + __heap_extern_start = .; . = ORIGIN(SDRAM) + LENGTH(SDRAM); __heap_extern_end = .; diff --git a/src/modm/board/nucleo_f031k6/board.xml b/src/modm/board/nucleo_f031k6/board.xml index b29c8accd0..43821db41e 100644 --- a/src/modm/board/nucleo_f031k6/board.xml +++ b/src/modm/board/nucleo_f031k6/board.xml @@ -9,8 +9,7 @@ - - + modm:board:nucleo-f031k6 diff --git a/src/modm/board/nucleo_f042k6/board.xml b/src/modm/board/nucleo_f042k6/board.xml index 7b82db59c0..bb220d79e8 100644 --- a/src/modm/board/nucleo_f042k6/board.xml +++ b/src/modm/board/nucleo_f042k6/board.xml @@ -8,8 +8,7 @@ - - + modm:board:nucleo-f042k6 diff --git a/src/modm/platform/core/avr/module.lb b/src/modm/platform/core/avr/module.lb index c90172d5e5..37142743f0 100644 --- a/src/modm/platform/core/avr/module.lb +++ b/src/modm/platform/core/avr/module.lb @@ -28,23 +28,7 @@ def prepare(module, options): module.depends( ":architecture:interrupt", ":architecture:assert", - ":architecture:memory", - ":stdc++", - ) - - module.add_option( - EnumerationOption( - name="ram_block_length", - description="", - enumeration=["2", "4", "8", "16", "32", "64"], - default="16")) - module.add_option( - NumericOption( - name="ram_length", - description="", - minimum=64, - maximum=32768, - default=1024)) + ":stdc++") return True @@ -63,8 +47,6 @@ def build(env): env.outbasepath = "modm/src/modm/platform/core" env.copy("main.hpp") - env.copy("ram.hpp") - env.template("ram.cpp.in") # dealing with runtime assertions if env.has_module(":architecture:assert"): diff --git a/src/modm/platform/core/avr/ram.hpp b/src/modm/platform/core/avr/ram.hpp deleted file mode 100644 index 919b3bb6b5..0000000000 --- a/src/modm/platform/core/avr/ram.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2012-2014, 2017, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_AVR_RAM_HPP -#define MODM_AVR_RAM_HPP - -#include -#include - -namespace modm -{ - -namespace platform -{ - -/// @cond -void * -allocateMemory(std::size_t requestedSize); - -void -freeMemory(void *ptr); -/// @endcond - -// TODO functions to retrieve status informations about used memory - -} // namespace platform - -} // namespace modm - -#endif // MODM_AVR_RAM_HPP - diff --git a/src/modm/platform/core/cortex/heap_newlib.cpp b/src/modm/platform/core/cortex/heap_newlib.cpp deleted file mode 100644 index 4d79a904b9..0000000000 --- a/src/modm/platform/core/cortex/heap_newlib.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2016, 2019 Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include -#include -#include -#include -#include -#include -#include "heap_table.hpp" - -// ---------------------------------------------------------------------------- -extern "C" -{ - -const uint8_t *__brkval = 0; -const uint8_t *heap_end = 0; - -void __modm_initialize_memory(void) -{ - // find the largest heap that is DMA-able and S-Bus accessible - modm::platform::HeapTable::find_largest( - (const uint32_t **) &__brkval, (const uint32_t **) &heap_end); - modm_assert(__brkval, "heap.init", "Could not find main heap memory!"); -} - -/* Support function. Adjusts end of heap to provide more memory to - * memory allocator. Simple and dumb with no sanity checks. - * - * struct _reent *r -- re-entrancy structure, used by newlib to - * support multiple threads of operation. - * ptrdiff_t size -- number of bytes to add. - * Returns pointer to start of new heap area. - * - * Note: This implementation is not thread safe (despite taking a - * _reent structure as a parameter). - * Since the _reent struct is not used in the current implementation, - * the following messages must be suppressed. - */ -void * -_sbrk_r(struct _reent *r, ptrdiff_t size) -{ - (void) r; - // move heap pointer - const uint8_t *heap = __brkval; - __brkval += size; - - modm_assert(__brkval < heap_end, "heap.sbrk", - "Heap could not be enlarged!", size); - - // Return pointer to start of new heap area. - return (void*)heap; -} -void *__real__malloc_r(struct _reent *r, size_t size); -void *__wrap__malloc_r(struct _reent *r, size_t size) { - void * ptr = __real__malloc_r(r, size); - modm_assert_continue_fail_debug(ptr, "malloc", - "No memory left in Newlib heap!", size); - return ptr; -} -void *__real__calloc_r(struct _reent *r, size_t size); -void *__wrap__calloc_r(struct _reent *r, size_t size) { - void * ptr = __real__calloc_r(r, size); - modm_assert_continue_fail_debug(ptr, "calloc", - "No memory left in Newlib heap!", size); - return ptr; -} -void *__real__realloc_r(struct _reent *r, void *p, size_t size); -void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) { - void * ptr = __real__realloc_r(r, p, size); - modm_assert_continue_fail_debug(ptr, "realloc", - "No memory left in Newlib heap!", size); - return ptr; -} -void __real__free_r(struct _reent *r, void *p); -void __wrap__free_r(struct _reent *r, void *p) { - __real__free_r(r, p); -} - -// memory traits are ignored for newlib allocator -void *malloc_tr(size_t size, uint32_t traits) -{ - (void) traits; - return malloc(size); -} - -} diff --git a/src/modm/platform/core/cortex/heap_table.cpp.in b/src/modm/platform/core/cortex/heap_table.cpp.in index 17eec44c3c..9efd0ec373 100644 --- a/src/modm/platform/core/cortex/heap_table.cpp.in +++ b/src/modm/platform/core/cortex/heap_table.cpp.in @@ -17,8 +17,8 @@ typedef struct { uint32_t traits; - uint32_t *const start; - uint32_t *const end; + uint8_t *const start; + uint8_t *const end; } __attribute__((packed)) table_pool_t; extern "C" const table_pool_t __table_heap_start[]; @@ -27,7 +27,7 @@ extern "C" const table_pool_t __table_heap_end[]; namespace modm::platform { %% if with_fault_storage -extern const uint32_t *fault_storage_heap_start; +extern const uint8_t *fault_storage_heap_start; %% endif HeapTable::Iterator @@ -65,7 +65,7 @@ HeapTable::Iterator::operator*() const { const auto t{reinterpret_cast(table)}; %% if with_fault_storage - const uint32_t *const start = (const uint32_t *)((size_t)t->start + offset); + const uint8_t *const start = (const uint8_t *)((size_t)t->start + offset); return {MemoryTraits(t->traits), start, t->end, (size_t)t->end - (size_t)start}; %% else return {MemoryTraits(t->traits), t->start, t->end, (size_t)t->end - (size_t)t->start}; @@ -101,12 +101,12 @@ HeapTable::Iterator::operator!=(const Iterator& other) const } // Finds the largest heap with declared traits -void -HeapTable::find_largest(const uint32_t **const start, - const uint32_t **const end, +bool +HeapTable::find_largest(const uint8_t **const start, + const uint8_t **const end, const MemoryTraits trait_mask) { - uint32_t current_size = 0; + size_t current_size = 0; *start = nullptr; *end = nullptr; @@ -129,6 +129,7 @@ HeapTable::find_largest(const uint32_t **const start, } } } + return *start; } } // namespace modm::platform diff --git a/src/modm/platform/core/cortex/heap_table.hpp.in b/src/modm/platform/core/cortex/heap_table.hpp.in index 1cd42d042b..16cc008a50 100644 --- a/src/modm/platform/core/cortex/heap_table.hpp.in +++ b/src/modm/platform/core/cortex/heap_table.hpp.in @@ -19,7 +19,18 @@ namespace modm::platform { -/// @cond +/** + * Provides information about the unused memory sections in RAM listed by + * size and memory traits. + * + * The iterator returns a tuple of values for each memory section: + * 1. MemoryTraits traits, + * 2. const uint32_t *start_addr, + * 3. const uint32_t *end_addr, + * 4. size_t size in Bytes. + * + * @ingroup modm_platform_cortex_m + */ class HeapTable { public: @@ -27,15 +38,18 @@ public: Iterator begin(); Iterator end(); - static void - find_largest(const uint32_t **start, - const uint32_t **end, + /// Find the largest *continuous* memory section which satisfies *at least* + /// the selected memory traits. + static bool + find_largest(const uint8_t **start, + const uint8_t **end, MemoryTraits trait_mask = MemoryDefault); + /// @cond public: class Iterator { - using Type = std::tuple; + using Type = std::tuple; const void* table; %% if with_fault_storage friend class HeapTable; @@ -56,7 +70,7 @@ public: bool operator==(const Iterator& other) const; bool operator!=(const Iterator& other) const; }; + /// @endcond }; -/// @endcond } diff --git a/src/modm/platform/core/cortex/linker.macros b/src/modm/platform/core/cortex/linker.macros index ff9cf2480f..7b0152e560 100644 --- a/src/modm/platform/core/cortex/linker.macros +++ b/src/modm/platform/core/cortex/linker.macros @@ -182,6 +182,8 @@ TOTAL_STACK_SIZE = MAIN_STACK_SIZE + PROCESS_STACK_SIZE; %% macro section_heap(memory, name, placement=None) .{{name}} (NOLOAD) : { + *(.{{name}}) + . = ALIGN(4); __{{name}}_start = .; . = ORIGIN({{memory}}) + LENGTH({{memory}}); __{{name}}_end = .; @@ -244,6 +246,13 @@ TOTAL_STACK_SIZE = MAIN_STACK_SIZE + PROCESS_STACK_SIZE; *(.eh_frame*) } %% endif +%% if not with_heap + /DISCARD/ : + { + /* See `modm:platform:heap` module for an explanation! */ + *({{no_heap_section}}) + } +%% endif /* Hardware init table */ .hardware_init : ALIGN(4) diff --git a/src/modm/platform/core/cortex/module.lb b/src/modm/platform/core/cortex/module.lb index 8fc1f72c9c..4d84b9924b 100644 --- a/src/modm/platform/core/cortex/module.lb +++ b/src/modm/platform/core/cortex/module.lb @@ -11,11 +11,9 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # ----------------------------------------------------------------------------- - def common_vector_table_location(env): return env.get(":platform:cortex-m:vector_table_location", "rom") - def common_vector_table(env): """ Computes vector table properties: @@ -92,6 +90,9 @@ def common_memories(env): } return properties +# See :platform:heap for explanation +common_no_heap_section = (".Heap_is_not_implemented!__" + "Please_include_the__modm:platform:heap__module_in_your_project!") def common_linkerscript(env): """ @@ -136,6 +137,8 @@ def common_linkerscript(env): env.collector_values(":platform:cortex-m:linkerscript.table_extern.heap")]), "with_cpp_exceptions": env.get(":stdc++:exceptions", False), + "with_heap": env.has_module(":platform:heap"), + "no_heap_section": common_no_heap_section, } properties.update(common_memories(env)) return properties @@ -153,27 +156,17 @@ def prepare(module, options): module.depends( ":architecture:interrupt", ":architecture:assert", - ":architecture:memory", ":platform:clock", ":cmsis:device", ":stdc++") - module.add_option( - EnumerationOption( - name="allocator", - description=FileReader("option/allocator.md"), - enumeration=["newlib", "block", "tlsf"], - default="newlib", - dependencies=lambda v: {"newlib": None, - "block": ":driver:block.allocator", - "tlsf": ":tlsf"}[v])) module.add_option( NumericOption( name="main_stack_size", description=FileReader("option/main_stack_size.md"), minimum=2 ** 8, maximum=2 ** 16, - default=2 ** 10 * 3 - 32)) + default="3*1024")) memories = listify(options[":target"].get_driver("core")["memory"]) @@ -256,6 +249,7 @@ def build(env): env.substitutions.update({ "target": env[":target"].identifier, "with_fault_storage": env.has_module(":platform:fault"), + "with_memory_traits": env.has_module(":architecture:memory"), }) env.outbasepath = "modm/src/modm/platform/core" @@ -288,19 +282,14 @@ def build(env): if not env.has_module(":test:architecture"): env.copy("clock.cpp") - env.template("heap_table.cpp.in") - env.template("heap_table.hpp.in") - # everything to do with dynamic memory - if env["allocator"] == "newlib": - env.copy("heap_newlib.cpp") - elif env["allocator"] == "tlsf": - env.copy("heap_tlsf.cpp") - elif env["allocator"] == "block": - env.copy("heap_block_allocator.cpp") - env.collect(":build:linkflags", "-Wl,-wrap,_calloc_r", - "-Wl,-wrap,_free_r", - "-Wl,-wrap,_malloc_r", - "-Wl,-wrap,_realloc_r") + # Heap table from linkerscript + if env.has_module(":architecture:memory"): + env.template("heap_table.cpp.in") + env.template("heap_table.hpp.in") + # Deprecate malloc and new if no heap is implemented! + if not env.has_module(":platform:heap"): + env.substitutions["no_heap_section"] = common_no_heap_section + env.template("../../heap/cortex/no_heap.c.in", "no_heap.c") if env.has_module(":architecture:atomic"): env.copy("atomic_lock.hpp") diff --git a/src/modm/platform/core/cortex/module.md b/src/modm/platform/core/cortex/module.md index a7374a01aa..6332643a93 100644 --- a/src/modm/platform/core/cortex/module.md +++ b/src/modm/platform/core/cortex/module.md @@ -22,11 +22,11 @@ implemented as follows: 6. Execute shared hardware initialization functions. 7. Copy data from internal flash to *external* RAM. 8. Zero sections in *external* RAM. -9. Initialize heap via `__modm_initialize_memory()` (provided by the - `modm:platform:cortex-m:allocator` option). +9. Initialize heap via `__modm_initialize_memory()` (implemented by the + `modm:platform:heap` module). 10. Call static constructors. 11. Call `main()` application entry point. -12. If `main()` returns, assert on `core.main.exit` (only in debug profile). +12. If `main()` returns, assert on `main.exit` (only in debug profile). 13. Reboot if assertion returns. @@ -101,7 +101,7 @@ linkerscript and the `Reset_Handler` is defined by the startup script. All handlers are weakly aliased to `Undefined_Handler`, which is called if an IRQ is enabled, but no handler is defined for it. This default handler determines the currectly active IRQ, sets its priority to the lowest level, and -disables the IRQ from firing again and then asserts on `core.nvic.undefined` +disables the IRQ from firing again and then asserts on `nvic.undef` with the (signed) IRQ number as context. The lowering of the priority is necessary, since the assertion handlers (see diff --git a/src/modm/platform/core/cortex/option/allocator.md b/src/modm/platform/core/cortex/option/allocator.md deleted file mode 100644 index 0698b552a2..0000000000 --- a/src/modm/platform/core/cortex/option/allocator.md +++ /dev/null @@ -1,41 +0,0 @@ -# Dynamic memory allocation strategy - -By default, the arm-none-eabi toolchain ships with the `newlib` libc, which uses -`dlmalloc` as the underlying allocator algorithm and only requires the -implementation of the `void * sbrk(ptrdiff_t size)` hook. However, this limits -the allocator to use just _one_ memory region, which must then also be of -_continuous_ extend, since `sbrk` can only grow and shrink, but not jump. -Therefore, when using the `newlib` strategy, only the largest memory region is -used as heap! Depending on the device memory architecture this can leave large -memory regions unused. - -For devices with very small memories, we recommend using the block allocator -strategy, which uses a very light-weight and simple algorithm. This also only -operates on one continuous memory region as heap. - -!!! note "Memory traits" - Memories can have different traits, such as DMA-ability or access time. The - default memory allocator functions (malloc, new, etc) only return DMA-able - memories, ordered by fastest access time. Similarly the search for the - largest memory region only considers DMA-able memory. - -!!! warning "On multi-SRAM devices" - For devices which contain separate memories laid out in a continuous way - (often called SRAM1, SRAM2, etc.) the `newlib` and `block` strategies choose - the largest continuous memory region, *even though* unaligned accesses - across memory regions may not be supported in hardware and lead to a bus - fault! Consider using the TLSF implementation, which does not suffer from - this issue. - -To use all non-statically allocated memory for heap, use the TLSF strategy, -which natively supports multiple memory regions. Our implementation treats -all internal memories as separate regions, so unaligned accesses across memory -boundaries are not an issue. To request heap memory of different traits, see -`modm::MemoryTraits`. - -!!! note "TLSF has static overhead" - The TLSF implementation has a static overhead of about 1kB per memory trait - group, however, these can then contain multiple non-continuous memory - regions. The upside of this large static allocation is very fast allocation - times of O(1), but we recommend using TLSF only for devices with multiple - memory regions. diff --git a/src/modm/platform/core/hosted/module.lb b/src/modm/platform/core/hosted/module.lb index 6af622ba31..848789563c 100644 --- a/src/modm/platform/core/hosted/module.lb +++ b/src/modm/platform/core/hosted/module.lb @@ -30,7 +30,8 @@ def build(env): env.substitutions = {"target": target, "core": "hosted"} env.outbasepath = "modm/src/modm/platform/core" - env.copy("memory.cpp") + if env.has_module(":architecture:memory"): + env.copy("memory.cpp") if env.has_module(":architecture:atomic"): env.copy("atomic_lock.hpp") diff --git a/src/modm/platform/heap/avr/module.lb b/src/modm/platform/heap/avr/module.lb new file mode 100644 index 0000000000..a22c87d6ef --- /dev/null +++ b/src/modm/platform/heap/avr/module.lb @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017-2018, 2020 Niklas Hauser +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":platform:heap" + module.description = "Dynamic Memory" + +def prepare(module, options): + if not options[":target"].has_driver("core:avr*"): + return False + + module.depends( + ":architecture:assert", + ":architecture:memory") + + module.add_option( + EnumerationOption( + name="ram_block_length", + description="", + enumeration=["2", "4", "8", "16", "32", "64"], + default="16")) + module.add_option( + NumericOption( + name="ram_length", + description="", + minimum=64, + maximum=32768, + default=1024)) + + return True + +def build(env): + env.outbasepath = "modm/src/modm/platform/heap" + env.template("ram.cpp.in") diff --git a/src/modm/platform/core/avr/ram.cpp.in b/src/modm/platform/heap/avr/ram.cpp.in similarity index 97% rename from src/modm/platform/core/avr/ram.cpp.in rename to src/modm/platform/heap/avr/ram.cpp.in index 17898876c9..f59e1a8f3b 100644 --- a/src/modm/platform/core/avr/ram.cpp.in +++ b/src/modm/platform/heap/avr/ram.cpp.in @@ -16,8 +16,6 @@ #include #include -#include "ram.hpp" - // ---------------------------------------------------------------------------- // must be a power of two! #define BLOCK_SIZE {{ options["ram_block_length"] }} @@ -80,7 +78,7 @@ initializeMemory(void) * TODO description */ void * -modm::platform::allocateMemory(std::size_t requestedSize) +allocateMemory(std::size_t requestedSize) { if (!modm_assert_continue_fail_debug( (requestedSize > 0 && requestedSize <= MAX_BLOCK_PARTS * BLOCK_SIZE), @@ -158,7 +156,7 @@ modm::platform::allocateMemory(std::size_t requestedSize) // ---------------------------------------------------------------------------- void -modm::platform::freeMemory(void *ptr) +freeMemory(void *ptr) { if (ptr == 0) { return; @@ -204,6 +202,16 @@ modm::platform::freeMemory(void *ptr) } } +extern "C" +{ + +void* malloc(size_t size) +{ return allocateMemory(size); } +void free(void *ptr) +{ return freeMemory(ptr); } + +} + // ---------------------------------------------------------------------------- #ifdef ALLOC_TEST diff --git a/src/modm/platform/core/cortex/heap_block_allocator.cpp b/src/modm/platform/heap/cortex/heap_block.cpp similarity index 61% rename from src/modm/platform/core/cortex/heap_block_allocator.cpp rename to src/modm/platform/heap/cortex/heap_block.cpp index 35740fb071..cf7043f3c4 100644 --- a/src/modm/platform/core/cortex/heap_block_allocator.cpp +++ b/src/modm/platform/heap/cortex/heap_block.cpp @@ -15,7 +15,7 @@ #include #include #include -#include "heap_table.hpp" +#include // ---------------------------------------------------------------------------- // Using the MODM Block Allocator @@ -37,71 +37,46 @@ const size_t max_heap_size = (1 << (sizeof(MODM_MEMORY_BLOCK_ALLOCATOR_TYPE) * 8 extern "C" { + void __modm_initialize_memory(void) { - const uint32_t *heap_start{nullptr}, *heap_end; + const uint8_t *heap_start{nullptr}, *heap_end{nullptr}; // find the largest heap that is DMA-able and S-Bus accessible - modm::platform::HeapTable::find_largest(&heap_start, &heap_end); - modm_assert(heap_start, "heap.init", "Could not find main heap memory!"); + bool success = modm::platform::HeapTable::find_largest(&heap_start, &heap_end); + modm_assert(success, "heap.init", "Could not find main heap memory!"); // clamp the heap size to the maximum - if ((size_t) heap_end - (size_t) heap_start > max_heap_size) { - heap_end = (const uint32_t *) ((char *) heap_start + max_heap_size); + if ((heap_end - heap_start) > max_heap_size) { + heap_end = heap_start + max_heap_size; } // initialize the heap allocator.initialize((void*)heap_start, (void*)heap_end); } -void *__wrap__malloc_r(struct _reent *r, size_t size) +void* __wrap__malloc_r(struct _reent *, size_t size) { - (void) r; void *ptr = allocator.allocate(size); modm_assert_continue_fail_debug(ptr, "malloc", "No memory left in Block heap!", size); return ptr; } -void *__wrap__calloc_r(struct _reent *r, size_t size) +void* __wrap__calloc_r(struct _reent *r, size_t size) { void *ptr = __wrap__malloc_r(r, size); - - if (modm_assert_continue_fail_debug(ptr, "calloc", - "No memory left in Block heap!", size)) { - memset(ptr, 0, size); - } + if (ptr) memset(ptr, 0, size); return ptr; } -void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) +void* __wrap__realloc_r(struct _reent *, void *, size_t) { - (void) r; - (void) p; - (void) size; // NOT IMPLEMENTED! - modm_assert_continue_fail_debug(0, "realloc", - "Not implemented for Block heap!", size); + modm_assert(0, "realloc", "Realloc is not implemented for Block heap!", size); return NULL; } -void __wrap__free_r(struct _reent *r, void *p) +void __wrap__free_r(struct _reent *, void *p) { - (void) r; allocator.free(p); } -// _sbrk_r is empty -void * -_sbrk_r(struct _reent *r, ptrdiff_t size) -{ - (void) r; - (void) size; - return NULL; -} - -// memory traits are ignored for newlib allocator -void *malloc_tr(size_t size, uint32_t traits) -{ - (void) traits; - return malloc(size); -} - } // extern "C" diff --git a/src/modm/platform/heap/cortex/heap_newlib.cpp b/src/modm/platform/heap/cortex/heap_newlib.cpp new file mode 100644 index 0000000000..0987802536 --- /dev/null +++ b/src/modm/platform/heap/cortex/heap_newlib.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016, 2019 Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +extern "C" +{ + +const uint8_t *heap_top{nullptr}; +const uint8_t *heap_end{nullptr}; + +void __modm_initialize_memory(void) +{ + // find the largest heap that is DMA-able and S-Bus accessible + bool success = modm::platform::HeapTable::find_largest(&heap_top, &heap_end); + modm_assert(success, "heap.init", "Could not find main heap memory!"); +} + +/* Support function. Adjusts end of heap to provide more memory to + * memory allocator. + * + * struct _reent *r -- re-entrancy structure, used by newlib to + * support multiple threads of operation. + * ptrdiff_t size -- number of bytes to add. + * Returns pointer to start of new heap area. + * + * Note: This implementation is not thread safe (despite taking a + * _reent structure as a parameter). + */ +void * +_sbrk_r(struct _reent *, ptrdiff_t size) +{ + const uint8_t *const heap = heap_top; + heap_top += size; + modm_assert(heap_top < heap_end, "heap.sbrk", "Heap overflowed!", size); + return (void*) heap; +} + +} diff --git a/src/modm/platform/core/cortex/heap_tlsf.cpp b/src/modm/platform/heap/cortex/heap_tlsf.cpp similarity index 85% rename from src/modm/platform/core/cortex/heap_tlsf.cpp rename to src/modm/platform/heap/cortex/heap_tlsf.cpp index 1d0daecdcb..00bdc5d8fe 100644 --- a/src/modm/platform/core/cortex/heap_tlsf.cpp +++ b/src/modm/platform/heap/cortex/heap_tlsf.cpp @@ -15,7 +15,8 @@ #include #include #include -#include "heap_table.hpp" +#include +#include // ---------------------------------------------------------------------------- #include @@ -28,7 +29,7 @@ typedef struct { uint16_t traits; tlsf_t tlsf; - const uint32_t* end; + const uint8_t* end; } mem_pool_t; static mem_pool_t mem_pools[MODM_TLSF_MAX_MEM_POOL_COUNT]; @@ -87,7 +88,7 @@ get_tlsf_for_ptr(void *p) return NULL; } -void * malloc_tr(size_t size, uint32_t traits) +void * malloc_traits(size_t size, uint32_t traits) { try_again: for (mem_pool_t *pool = mem_pools; @@ -102,13 +103,14 @@ void * malloc_tr(size_t size, uint32_t traits) } // clear the types core coupled and external, but not non-volatile - const uint32_t clear_mask = 0x8000 | 0x2000; + const uint32_t clear_mask = (modm::MemoryTrait::TypeExternal | + modm::MemoryTrait::TypeCoreCoupled).value; if (traits & clear_mask) { traits &= ~clear_mask; goto try_again; } // set the SBus and try again - const uint32_t set_mask = 0x0001; + const uint32_t set_mask = uint32_t(modm::MemoryTrait::AccessSBus); if (!(traits & set_mask)) { traits |= set_mask; goto try_again; @@ -119,24 +121,21 @@ void * malloc_tr(size_t size, uint32_t traits) return NULL; } -void *__wrap__malloc_r(struct _reent *r, size_t size) +void *__wrap__malloc_r(struct _reent *, size_t size) { - (void) r; // default is accessible by S-Bus and DMA-able - return malloc_tr(size, 0x8 | 0x1); + return malloc_traits(size, 0x8 | 0x1); } void *__wrap__calloc_r(struct _reent *r, size_t size) { - (void) r; void *ptr = __wrap__malloc_r(r, size); if (ptr) memset(ptr, 0, size); return ptr; } -void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) +void *__wrap__realloc_r(struct _reent *, void *p, size_t size) { - (void) r; tlsf_t pool = get_tlsf_for_ptr(p); // if pointer belongs to no pool, exit. if (!pool) return NULL; @@ -147,22 +146,12 @@ void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) return ptr; } -void __wrap__free_r(struct _reent *r, void *p) +void __wrap__free_r(struct _reent *, void *p) { - (void) r; tlsf_t pool = get_tlsf_for_ptr(p); // if pointer belongs to no pool, exit. if (!pool) return; tlsf_free(pool, p); } -// _sbrk_r is empty -void * -_sbrk_r(struct _reent *r, ptrdiff_t size) -{ - (void) r; - (void) size; - return NULL; -} - -} +} // extern "C" diff --git a/src/modm/platform/heap/cortex/module.lb b/src/modm/platform/heap/cortex/module.lb new file mode 100644 index 0000000000..17a4094c9f --- /dev/null +++ b/src/modm/platform/heap/cortex/module.lb @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, 2020, Niklas Hauser +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":platform:heap" + module.description = FileReader("module.md") + +def prepare(module, options): + if not options[":target"].has_driver("core:cortex-m*"): + return False + + module.add_option( + EnumerationOption( + name="allocator", + description="Heap allocator algorithms", + enumeration=["newlib", "block", "tlsf"], + default="newlib", + dependencies=lambda v: {"newlib": None, + "block": ":driver:block.allocator", + "tlsf": ":tlsf"}[v])) + + module.depends(":architecture:assert", ":architecture:memory") + return True + +def build(env): + env.outbasepath = "modm/src/modm/platform/heap" + env.copy("heap_{}.cpp".format(env["allocator"])) + + if env["allocator"] != "newlib": + env.collect(":build:linkflags", "-Wl,-wrap,_malloc_r", + "-Wl,-wrap,_calloc_r", + "-Wl,-wrap,_realloc_r", + "-Wl,-wrap,_free_r") + + # Note: no_heap.c.in is used by :platform:cortex-m diff --git a/src/modm/platform/heap/cortex/module.md b/src/modm/platform/heap/cortex/module.md new file mode 100644 index 0000000000..ecae42f517 --- /dev/null +++ b/src/modm/platform/heap/cortex/module.md @@ -0,0 +1,252 @@ +# Heap Memory + +Your applicaton is linked against the `newlib-nano` libc, which only requires +the implementation of the `void* sbrk(ptrdiff_t size)` hook to use the heap. + +However, the `sbrk` mechanism can only grow and shrink forward and backwards in +memory and in particular it does not support discontinuous jumps from one memory +section to another. The limitation stems from being designed for use with a MMU +which would simply map another physical page into the linear virtual address +space so that the heap section appears continuous to `sbrk`. + +Since we do not have a MMU on Cortex-M, this strategy limits the use of the +default newlib allocator to *one* continuous memory region. Therefore this +module implements alternative allocators for devices with multiple memory +regions with different traits and extends the C++ `operator new` to access +them. + +See the `modm:architecture:memory` module for what kind of memory traits exist. + + +## Heap is not Implemented Error + +This module is not included by default, and any attempt to use the heap +fails with one or multiple linker error messages similiar to this: + +``` +`_sbrk_r' referenced in section `.text._malloc_r' + of libc_nano.a(lib_a-nano-mallocr.o): defined in discarded section + `.Heap_is_not_implemented!_ + _Please_include_the__modm:platform:heap__module_in_your_project!' + of libmodm.a(no_heap.o) +``` + +This is to prevent you from *accidentally* using the heap, which may not be +desirable for your application. If this occurs you have three choices. You can: + +1. find and remove calls to malloc/new in your application, or +2. include this module with its predefined allocators, or +3. implement your own allocator. + + +## Predefined Allocators + +There are several trade-offs to each allocator, however, as a rule of thumb, +choose: + +- `newlib` for devices with one large continuous RAM region. +- `block` for devices with one very small RAM region. +- `tlsf` for devices with multiple, different discontinuous RAM regions. + +!!! warning "Multi-SRAM regions" + For devices which contain separate memories laid out in a continuous way + (often called SRAM1, SRAM2, SRAM3, etc.) the `newlib` and `block` strategies + choose the largest continuous memory region, *even though* unaligned + accesses across memory regions may not be supported in hardware and lead to + a bus fault! Consider using the TLSF implementation, which does not suffer + from this issue. + +!!! warning "Allocators are not interrupt- or thread-safe" + No locking is implemented by default, if you need this feature, consider + implementing your own custom allocator algorithm! + + +### Newlib + +The newlib-nano allocator is a simple linked list, its overhead is therefore +low, but the access time may not be good. +Due to the limitations of the `sbrk` mechanism only the largest memory region is +used as heap! Depending on the device memory architecture this can leave large +memory regions unused. + +Consider using the TLSF allocator for devices with multiple discontinuous +memories. + + +### Block + +For devices with very small memories, we recommend using the block allocator +strategy, which uses a very light-weight and simple algorithm. This also only +operates on one continuous memory region as heap. + +!!! bug "The Block allocator does not implement realloc!" + This is a bug in `modm:driver:block.allocator` and currently a `modm_assert` + will fail. + + +### TLSF + +To use all non-statically allocated memory for heap, use the TLSF strategy, +which natively supports multiple memory regions. This implementation treats +all internal memories as separate regions, so unaligned access across memory +boundaries is not an issue. To request heap memory of different traits, see +the `modm:architecture:memory` module. + +!!! note "TLSF has static overhead" + The TLSF implementation has a static overhead of about 1kB per memory trait + group, however, these can then contain multiple discontinuous regions. The + upside of this large static allocation is very fast allocation times of + O(1), but we recommend using TLSF only for devices with multiple large + memory regions. + + +## Custom Allocator + +To implement your own allocator **do not** include this module. Instead +initialize your heap in the function `__modm_initialize_memory()`, which gets +called by the startup script after hardware init, but before static constructors +are called (see `modm:platform:cortex-m` for details). + +The simplest way to do so is to allocate a huge array into one of the heap +sections and use this as your heap. Consult `modm:platform:core` for what +heap sections your target provides! + +```cpp +modm_section(".heap1") // always the main heap section +uint8_t heap_begin[10*1024]; // 10 kB heap +const uint8_t *const heap_end{heap_begin + sizeof(heap_begin)}; + +extern "C" void __modm_initialize_memory() +{ + // Initialize your specific allocator algorithm here + allocator.initialize(); +} +``` + +!!! warning "Static constructors are only called afterwards!" + Since constructors may call the heap, it must be initialized before static + constructors are called. Only trivially constructed (POD) objects are + already initialized! + + +### Using the HeapTable + +If you prefer a little more control, include the `modm:architecture:memory` +module to get access to the internal `modm::platform::HeapTable` API, which +lists memory regions by traits and sizes. + +For example to find the largest continuous memory section with default traits +you can use this code: + +```cpp +const uint8_t *heap_begin{nullptr}; +const uint8_t *heap_end{nullptr}; +extern "C" void __modm_initialize_memory() +{ + bool success = HeapTable::find_largest(&heap_begin, &heap_end, + modm::MemoryDefault); + modm_assert(success, "heap.init", "No default memory section found!"); +} +``` + +If you want to know more about the available memory regions, you can iterate +over the heap table directly. This gives you full control over *where* you want +to place you heap. You can print this table at runtime to get a feel for it: + +```cpp +for (const auto [traits, start, end, size] : modm::platform::HeapTable()) +{ + MODM_LOG_INFO.printf("Memory section %#x @[0x%p,0x%p](%u)\n", + traits.value, start, end, size); +} +``` + + +### Providing sbrk + +To use the builtin allocator from newlib, all you need to provide is an +implementation of the `sbrk` function. A simple implementation for a +[`heap_begin`, `heap_end`] memory region looks like this: + +```cpp +const uint8_t *heap_top{heap_begin}; +extern "C" void* _sbrk_r(struct _reent *, ptrdiff_t size) +{ + const uint8_t *const heap = heap_top; + heap_top += size; + modm_assert(heap_top < heap_end, "heap.sbrk", "Heap overflowed!"); + return (void*) heap; +} +``` + + +### Wrapping malloc + +To use a completely custom allocator, you need to replace the newlib allocator +by wrapping the `malloc`, `calloc`, `realloc` and `free` functions via the +linker by adding this to your project configuration: + +```xml + + + + -Wl,-wrap,_malloc_r + -Wl,-wrap,_calloc_r + -Wl,-wrap,_realloc_r + -Wl,-wrap,_free_r + + +``` + +And then implement the following functions with your custom allocator: + +```cpp +extern "C" void * +__wrap__malloc_r(struct _reent *, size_t size) +{ + return allocator.malloc(size); +} +extern "C" void * +__wrap__calloc_r(struct _reent *, size_t size) +{ + return allocator.calloc(size); +} +extern "C" void * +__wrap__realloc_r(struct _reent *, void *ptr, size_t size) +{ + return allocator.realloc(ptr, size); +} +extern "C" void +__wrap__free_r(struct _reent *, void *p) +{ + allocator.free(p); +} +``` + +This is particularly recommended if you need a thread-safe malloc, which you +implement here via the `_reent` struct. Consult newlib docs for details. + +!!! tip "sbrk is not called anymore" + When wrapping these malloc functions, `_sbrk_r` is not called anymore, and + therefore is thrown away by the linker, thus the linker error disappears. + You therefore do not need to implement it, not even as a stub. + +To also support memory traits, you need to overwrite the default implementation +of `malloc_traits(size, traits)` which would otherwise just ignore the traits: + +```cpp +extern "C" void * +malloc_traits(size_t size, uint32_t ctraits) +{ + // Convert back from C land to C++ land: + const modm::MemoryTraits traits{ctraits}; + if (traits & modm::MemoryTrait::AccessDMA) { + // check for space in DMA-able heap regions + } else { + // check other regions + } + return ptr; +} +``` + + diff --git a/src/modm/platform/heap/cortex/no_heap.c.in b/src/modm/platform/heap/cortex/no_heap.c.in new file mode 100644 index 0000000000..4c963b885e --- /dev/null +++ b/src/modm/platform/heap/cortex/no_heap.c.in @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include + +modm_weak +void __modm_initialize_memory(void) +{ + /* tumbleweed */ +} + +// ---------------------------------------------------------------------------- +modm_weak modm_section("{{ no_heap_section }}") +void* _sbrk_r(struct _reent *r, ptrdiff_t size) +{ + (void) r; + (void) size; + return NULL; +} diff --git a/test/config/nucleo-f103.xml b/test/config/nucleo-f103.xml index f8eedf1e64..9a30a1073e 100644 --- a/test/config/nucleo-f103.xml +++ b/test/config/nucleo-f103.xml @@ -6,6 +6,7 @@ + modm:platform:heap