From b090d89486b499c981dd1375a37633c552962e6b Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 30 Mar 2016 23:12:35 +0100 Subject: [PATCH 1/7] [ext] Add TLSF source code with readme. --- ext/SConscript | 3 +- ext/tlsf/README.md | 92 ++++ ext/tlsf/tlsf.c | 1248 ++++++++++++++++++++++++++++++++++++++++++++ ext/tlsf/tlsf.h | 90 ++++ 4 files changed, 1432 insertions(+), 1 deletion(-) create mode 100644 ext/tlsf/README.md create mode 100644 ext/tlsf/tlsf.c create mode 100644 ext/tlsf/tlsf.h diff --git a/ext/SConscript b/ext/SConscript index 964ea9415..f5bc43120 100644 --- a/ext/SConscript +++ b/ext/SConscript @@ -43,7 +43,8 @@ buildIncludes = [] globalIncludes = [] if env['ARCHITECTURE'].startswith('cortex-m'): - globalIncludes += ['cmsis/Include'] + globalIncludes += ['cmsis/Include', 'tlsf'] + sourcePath += ['tlsf'] # ----------------------------------------------------------------------------- # Add the STM32 device header files diff --git a/ext/tlsf/README.md b/ext/tlsf/README.md new file mode 100644 index 000000000..18643844c --- /dev/null +++ b/ext/tlsf/README.md @@ -0,0 +1,92 @@ +# [tlsf](https://github.com/mattconte/tlsf) +Two-Level Segregated Fit memory allocator implementation. +Written by Matthew Conte (matt@baisoku.org). +Released under the BSD license. + +Features +-------- + * O(1) cost for malloc, free, realloc, memalign + * Extremely low overhead per allocation (4 bytes) + * Low overhead per TLSF management of pools (~3kB) + * Low fragmentation + * Compiles to only a few kB of code and data + * Support for adding and removing memory pool regions on the fly + +Caveats +------- + * Currently, assumes architecture can make 4-byte aligned accesses + * Not designed to be thread safe; the user must provide this + +Notes +----- +This code was based on the TLSF 1.4 spec and documentation found at: + + http://rtportal.upv.es/rtmalloc/allocators/tlsf/index.shtml + +It also leverages the TLSF 2.0 improvement to shrink the per-block overhead from 8 to 4 bytes. + +History +------- +2016/04/10 - v3.1 + * Code moved to github + * tlsfbits.h rolled into tlsf.c + * License changed to BSD + +2014/02/08 - v3.0 + * This version is based on improvements from 3DInteractive GmbH + * Interface changed to allow more than one memory pool + * Separated pool handling from control structure (adding, removing, debugging) + * Control structure and pools can still be constructed in the same memory block + * Memory blocks for control structure and pools are checked for alignment + * Added functions to retrieve control structure size, alignment size, min and max block size, overhead of pool structure, and overhead of a single allocation + * Minimal Pool size is tlsf_block_size_min() + tlsf_pool_overhead() + * Pool must be empty when it is removed, in order to allow O(1) removal + +2011/10/20 - v2.0 + * 64-bit support + * More compiler intrinsics for ffs/fls + * ffs/fls verification during TLSF creation in debug builds + +2008/04/04 - v1.9 + * Add tlsf_heap_check, a heap integrity check + * Support a predefined tlsf_assert macro + * Fix realloc case where block should shrink; if adjacent block is in use, execution would go down the slow path + +2007/02/08 - v1.8 + * Fix for unnecessary reallocation in tlsf_realloc + +2007/02/03 - v1.7 + * tlsf_heap_walk takes a callback + * tlsf_realloc now returns NULL on failure + * tlsf_memalign optimization for 4-byte alignment + * Usage of size_t where appropriate + +2006/11/21 - v1.6 + * ffs/fls broken out into tlsfbits.h + * tlsf_overhead queries per-pool overhead + +2006/11/07 - v1.5 + * Smart realloc implementation + * Smart memalign implementation + +2006/10/11 - v1.4 + * Add some ffs/fls implementations + * Minor code footprint reduction + +2006/09/14 - v1.3 + * Profiling indicates heavy use of blocks of size 1-128, so implement small block handling + * Reduce pool overhead by about 1kb + * Reduce minimum block size from 32 to 12 bytes + * Realloc bug fix + +2006/09/09 - v1.2 + * Add tlsf_block_size + * Static assertion mechanism for invariants + * Minor bugfixes + +2006/09/01 - v1.1 + * Add tlsf_realloc + * Add tlsf_walk_heap + +2006/08/25 - v1.0 + * First release diff --git a/ext/tlsf/tlsf.c b/ext/tlsf/tlsf.c new file mode 100644 index 000000000..012bc7e42 --- /dev/null +++ b/ext/tlsf/tlsf.c @@ -0,0 +1,1248 @@ +#include +#include +#include +#include +#include +#include + +#include "tlsf.h" + +#if defined(__cplusplus) +#define tlsf_decl inline +#else +#define tlsf_decl static +#endif + +/* +** Architecture-specific bit manipulation routines. +** +** TLSF achieves O(1) cost for malloc and free operations by limiting +** the search for a free block to a free list of guaranteed size +** adequate to fulfill the request, combined with efficient free list +** queries using bitmasks and architecture-specific bit-manipulation +** routines. +** +** Most modern processors provide instructions to count leading zeroes +** in a word, find the lowest and highest set bit, etc. These +** specific implementations will be used when available, falling back +** to a reasonably efficient generic implementation. +** +** NOTE: TLSF spec relies on ffs/fls returning value 0..31. +** ffs/fls return 1-32 by default, returning 0 for error. +*/ + +/* +** Detect whether or not we are building for a 32- or 64-bit (LP/LLP) +** architecture. There is no reliable portable method at compile-time. +*/ +#if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) \ + || defined (_WIN64) || defined (__LP64__) || defined (__LLP64__) +#define TLSF_64BIT +#endif + +/* +** gcc 3.4 and above have builtin support, specialized for architecture. +** Some compilers masquerade as gcc; patchlevel test filters them out. +*/ +#if defined (__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \ + && defined (__GNUC_PATCHLEVEL__) + +#if defined (__SNC__) +/* SNC for Playstation 3. */ + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __builtin_clz(reverse); + return bit - 1; +} + +#else + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + return __builtin_ffs(word) - 1; +} + +#endif + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __builtin_clz(word) : 0; + return bit - 1; +} + +#elif defined (_MSC_VER) && (_MSC_VER >= 1400) && (defined (_M_IX86) || defined (_M_X64)) +/* Microsoft Visual C++ support on x86/X64 architectures. */ + +#include + +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) + +tlsf_decl int tlsf_fls(unsigned int word) +{ + unsigned long index; + return _BitScanReverse(&index, word) ? index : -1; +} + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + unsigned long index; + return _BitScanForward(&index, word) ? index : -1; +} + +#elif defined (_MSC_VER) && defined (_M_PPC) +/* Microsoft Visual C++ support on PowerPC architectures. */ + +#include + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = 32 - _CountLeadingZeros(word); + return bit - 1; +} + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - _CountLeadingZeros(reverse); + return bit - 1; +} + +#elif defined (__ARMCC_VERSION) +/* RealView Compilation Tools for ARM */ + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __clz(reverse); + return bit - 1; +} + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __clz(word) : 0; + return bit - 1; +} + +#elif defined (__ghs__) +/* Green Hills support for PowerPC */ + +#include + +tlsf_decl int tlsf_ffs(unsigned int word) +{ + const unsigned int reverse = word & (~word + 1); + const int bit = 32 - __CLZ32(reverse); + return bit - 1; +} + +tlsf_decl int tlsf_fls(unsigned int word) +{ + const int bit = word ? 32 - __CLZ32(word) : 0; + return bit - 1; +} + +#else +/* Fall back to generic implementation. */ + +tlsf_decl int tlsf_fls_generic(unsigned int word) +{ + int bit = 32; + + if (!word) bit -= 1; + if (!(word & 0xffff0000)) { word <<= 16; bit -= 16; } + if (!(word & 0xff000000)) { word <<= 8; bit -= 8; } + if (!(word & 0xf0000000)) { word <<= 4; bit -= 4; } + if (!(word & 0xc0000000)) { word <<= 2; bit -= 2; } + if (!(word & 0x80000000)) { word <<= 1; bit -= 1; } + + return bit; +} + +/* Implement ffs in terms of fls. */ +tlsf_decl int tlsf_ffs(unsigned int word) +{ + return tlsf_fls_generic(word & (~word + 1)) - 1; +} + +tlsf_decl int tlsf_fls(unsigned int word) +{ + return tlsf_fls_generic(word) - 1; +} + +#endif + +/* Possibly 64-bit version of tlsf_fls. */ +#if defined (TLSF_64BIT) +tlsf_decl int tlsf_fls_sizet(size_t size) +{ + int high = (int)(size >> 32); + int bits = 0; + if (high) + { + bits = 32 + tlsf_fls(high); + } + else + { + bits = tlsf_fls((int)size & 0xffffffff); + + } + return bits; +} +#else +#define tlsf_fls_sizet tlsf_fls +#endif + +#undef tlsf_decl + +/* +** Constants. +*/ + +/* Public constants: may be modified. */ +enum tlsf_public +{ + /* log2 of number of linear subdivisions of block sizes. Larger + ** values require more memory in the control structure. Values of + ** 4 or 5 are typical. + */ + SL_INDEX_COUNT_LOG2 = 5, +}; + +/* Private constants: do not modify. */ +enum tlsf_private +{ +#if defined (TLSF_64BIT) + /* All allocation sizes and addresses are aligned to 8 bytes. */ + ALIGN_SIZE_LOG2 = 3, +#else + /* All allocation sizes and addresses are aligned to 4 bytes. */ + ALIGN_SIZE_LOG2 = 2, +#endif + ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2), + + /* + ** We support allocations of sizes up to (1 << FL_INDEX_MAX) bits. + ** However, because we linearly subdivide the second-level lists, and + ** our minimum size granularity is 4 bytes, it doesn't make sense to + ** create first-level lists for sizes smaller than SL_INDEX_COUNT * 4, + ** or (1 << (SL_INDEX_COUNT_LOG2 + 2)) bytes, as there we will be + ** trying to split size ranges into more slots than we have available. + ** Instead, we calculate the minimum threshold size, and place all + ** blocks below that size into the 0th first-level list. + */ + +#if defined (TLSF_64BIT) + /* + ** TODO: We can increase this to support larger sizes, at the expense + ** of more overhead in the TLSF structure. + */ + FL_INDEX_MAX = 32, +#else + FL_INDEX_MAX = 30, +#endif + SL_INDEX_COUNT = (1 << SL_INDEX_COUNT_LOG2), + FL_INDEX_SHIFT = (SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2), + FL_INDEX_COUNT = (FL_INDEX_MAX - FL_INDEX_SHIFT + 1), + + SMALL_BLOCK_SIZE = (1 << FL_INDEX_SHIFT), +}; + +/* +** Cast and min/max macros. +*/ + +#define tlsf_cast(t, exp) ((t) (exp)) +#define tlsf_min(a, b) ((a) < (b) ? (a) : (b)) +#define tlsf_max(a, b) ((a) > (b) ? (a) : (b)) + +/* +** Set assert macro, if it has not been provided by the user. +*/ +#if !defined (tlsf_assert) +#define tlsf_assert assert +#endif + +/* +** Static assertion mechanism. +*/ + +#define _tlsf_glue2(x, y) x ## y +#define _tlsf_glue(x, y) _tlsf_glue2(x, y) +#define tlsf_static_assert(exp) \ + typedef char _tlsf_glue(static_assert, __LINE__) [(exp) ? 1 : -1] + +/* This code has been tested on 32- and 64-bit (LP/LLP) architectures. */ +tlsf_static_assert(sizeof(int) * CHAR_BIT == 32); +tlsf_static_assert(sizeof(size_t) * CHAR_BIT >= 32); +tlsf_static_assert(sizeof(size_t) * CHAR_BIT <= 64); + +/* SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage type. */ +tlsf_static_assert(sizeof(unsigned int) * CHAR_BIT >= SL_INDEX_COUNT); + +/* Ensure we've properly tuned our sizes. */ +tlsf_static_assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT); + +/* +** Data structures and associated constants. +*/ + +/* +** Block header structure. +** +** There are several implementation subtleties involved: +** - The prev_phys_block field is only valid if the previous block is free. +** - The prev_phys_block field is actually stored at the end of the +** previous block. It appears at the beginning of this structure only to +** simplify the implementation. +** - The next_free / prev_free fields are only valid if the block is free. +*/ +typedef struct block_header_t +{ + /* Points to the previous physical block. */ + struct block_header_t* prev_phys_block; + + /* The size of this block, excluding the block header. */ + size_t size; + + /* Next and previous free blocks. */ + struct block_header_t* next_free; + struct block_header_t* prev_free; +} block_header_t; + +/* +** Since block sizes are always at least a multiple of 4, the two least +** significant bits of the size field are used to store the block status: +** - bit 0: whether block is busy or free +** - bit 1: whether previous block is busy or free +*/ +static const size_t block_header_free_bit = 1 << 0; +static const size_t block_header_prev_free_bit = 1 << 1; + +/* +** The size of the block header exposed to used blocks is the size field. +** The prev_phys_block field is stored *inside* the previous free block. +*/ +static const size_t block_header_overhead = sizeof(size_t); + +/* User data starts directly after the size field in a used block. */ +static const size_t block_start_offset = + offsetof(block_header_t, size) + sizeof(size_t); + +/* +** A free block must be large enough to store its header minus the size of +** the prev_phys_block field, and no larger than the number of addressable +** bits for FL_INDEX. +*/ +static const size_t block_size_min = + sizeof(block_header_t) - sizeof(block_header_t*); +static const size_t block_size_max = tlsf_cast(size_t, 1) << FL_INDEX_MAX; + + +/* The TLSF control structure. */ +typedef struct control_t +{ + /* Empty lists point at this block to indicate they are free. */ + block_header_t block_null; + + /* Bitmaps for free lists. */ + unsigned int fl_bitmap; + unsigned int sl_bitmap[FL_INDEX_COUNT]; + + /* Head of free lists. */ + block_header_t* blocks[FL_INDEX_COUNT][SL_INDEX_COUNT]; +} control_t; + +/* A type used for casting when doing pointer arithmetic. */ +typedef ptrdiff_t tlsfptr_t; + +/* +** block_header_t member functions. +*/ + +static size_t block_size(const block_header_t* block) +{ + return block->size & ~(block_header_free_bit | block_header_prev_free_bit); +} + +static void block_set_size(block_header_t* block, size_t size) +{ + const size_t oldsize = block->size; + block->size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit)); +} + +static int block_is_last(const block_header_t* block) +{ + return block_size(block) == 0; +} + +static int block_is_free(const block_header_t* block) +{ + return tlsf_cast(int, block->size & block_header_free_bit); +} + +static void block_set_free(block_header_t* block) +{ + block->size |= block_header_free_bit; +} + +static void block_set_used(block_header_t* block) +{ + block->size &= ~block_header_free_bit; +} + +static int block_is_prev_free(const block_header_t* block) +{ + return tlsf_cast(int, block->size & block_header_prev_free_bit); +} + +static void block_set_prev_free(block_header_t* block) +{ + block->size |= block_header_prev_free_bit; +} + +static void block_set_prev_used(block_header_t* block) +{ + block->size &= ~block_header_prev_free_bit; +} + +static block_header_t* block_from_ptr(const void* ptr) +{ + return tlsf_cast(block_header_t*, + tlsf_cast(unsigned char*, ptr) - block_start_offset); +} + +static void* block_to_ptr(const block_header_t* block) +{ + return tlsf_cast(void*, + tlsf_cast(unsigned char*, block) + block_start_offset); +} + +/* Return location of next block after block of given size. */ +static block_header_t* offset_to_block(const void* ptr, size_t size) +{ + return tlsf_cast(block_header_t*, tlsf_cast(tlsfptr_t, ptr) + size); +} + +/* Return location of previous block. */ +static block_header_t* block_prev(const block_header_t* block) +{ + tlsf_assert(block_is_prev_free(block) && "previous block must be free"); + return block->prev_phys_block; +} + +/* Return location of next existing block. */ +static block_header_t* block_next(const block_header_t* block) +{ + block_header_t* next = offset_to_block(block_to_ptr(block), + block_size(block) - block_header_overhead); + tlsf_assert(!block_is_last(block)); + return next; +} + +/* Link a new block with its physical neighbor, return the neighbor. */ +static block_header_t* block_link_next(block_header_t* block) +{ + block_header_t* next = block_next(block); + next->prev_phys_block = block; + return next; +} + +static void block_mark_as_free(block_header_t* block) +{ + /* Link the block to the next block, first. */ + block_header_t* next = block_link_next(block); + block_set_prev_free(next); + block_set_free(block); +} + +static void block_mark_as_used(block_header_t* block) +{ + block_header_t* next = block_next(block); + block_set_prev_used(next); + block_set_used(block); +} + +static size_t align_up(size_t x, size_t align) +{ + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return (x + (align - 1)) & ~(align - 1); +} + +static size_t align_down(size_t x, size_t align) +{ + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return x - (x & (align - 1)); +} + +static void* align_ptr(const void* ptr, size_t align) +{ + const tlsfptr_t aligned = + (tlsf_cast(tlsfptr_t, ptr) + (align - 1)) & ~(align - 1); + tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two"); + return tlsf_cast(void*, aligned); +} + +/* +** Adjust an allocation size to be aligned to word size, and no smaller +** than internal minimum. +*/ +static size_t adjust_request_size(size_t size, size_t align) +{ + size_t adjust = 0; + if (size && size < block_size_max) + { + const size_t aligned = align_up(size, align); + adjust = tlsf_max(aligned, block_size_min); + } + return adjust; +} + +/* +** TLSF utility functions. In most cases, these are direct translations of +** the documentation found in the white paper. +*/ + +static void mapping_insert(size_t size, int* fli, int* sli) +{ + int fl, sl; + if (size < SMALL_BLOCK_SIZE) + { + /* Store small blocks in first list. */ + fl = 0; + sl = tlsf_cast(int, size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT); + } + else + { + fl = tlsf_fls_sizet(size); + sl = tlsf_cast(int, size >> (fl - SL_INDEX_COUNT_LOG2)) ^ (1 << SL_INDEX_COUNT_LOG2); + fl -= (FL_INDEX_SHIFT - 1); + } + *fli = fl; + *sli = sl; +} + +/* This version rounds up to the next block size (for allocations) */ +static void mapping_search(size_t size, int* fli, int* sli) +{ + if (size >= SMALL_BLOCK_SIZE) + { + const size_t round = (1 << (tlsf_fls_sizet(size) - SL_INDEX_COUNT_LOG2)) - 1; + size += round; + } + mapping_insert(size, fli, sli); +} + +static block_header_t* search_suitable_block(control_t* control, int* fli, int* sli) +{ + int fl = *fli; + int sl = *sli; + + /* + ** First, search for a block in the list associated with the given + ** fl/sl index. + */ + unsigned int sl_map = control->sl_bitmap[fl] & (~0U << sl); + if (!sl_map) + { + /* No block exists. Search in the next largest first-level list. */ + const unsigned int fl_map = control->fl_bitmap & (~0U << (fl + 1)); + if (!fl_map) + { + /* No free blocks available, memory has been exhausted. */ + return 0; + } + + fl = tlsf_ffs(fl_map); + *fli = fl; + sl_map = control->sl_bitmap[fl]; + } + tlsf_assert(sl_map && "internal error - second level bitmap is null"); + sl = tlsf_ffs(sl_map); + *sli = sl; + + /* Return the first block in the free list. */ + return control->blocks[fl][sl]; +} + +/* Remove a free block from the free list.*/ +static void remove_free_block(control_t* control, block_header_t* block, int fl, int sl) +{ + block_header_t* prev = block->prev_free; + block_header_t* next = block->next_free; + tlsf_assert(prev && "prev_free field can not be null"); + tlsf_assert(next && "next_free field can not be null"); + next->prev_free = prev; + prev->next_free = next; + + /* If this block is the head of the free list, set new head. */ + if (control->blocks[fl][sl] == block) + { + control->blocks[fl][sl] = next; + + /* If the new head is null, clear the bitmap. */ + if (next == &control->block_null) + { + control->sl_bitmap[fl] &= ~(1 << sl); + + /* If the second bitmap is now empty, clear the fl bitmap. */ + if (!control->sl_bitmap[fl]) + { + control->fl_bitmap &= ~(1 << fl); + } + } + } +} + +/* Insert a free block into the free block list. */ +static void insert_free_block(control_t* control, block_header_t* block, int fl, int sl) +{ + block_header_t* current = control->blocks[fl][sl]; + tlsf_assert(current && "free list cannot have a null entry"); + tlsf_assert(block && "cannot insert a null entry into the free list"); + block->next_free = current; + block->prev_free = &control->block_null; + current->prev_free = block; + + tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE) + && "block not aligned properly"); + /* + ** Insert the new block at the head of the list, and mark the first- + ** and second-level bitmaps appropriately. + */ + control->blocks[fl][sl] = block; + control->fl_bitmap |= (1 << fl); + control->sl_bitmap[fl] |= (1 << sl); +} + +/* Remove a given block from the free list. */ +static void block_remove(control_t* control, block_header_t* block) +{ + int fl, sl; + mapping_insert(block_size(block), &fl, &sl); + remove_free_block(control, block, fl, sl); +} + +/* Insert a given block into the free list. */ +static void block_insert(control_t* control, block_header_t* block) +{ + int fl, sl; + mapping_insert(block_size(block), &fl, &sl); + insert_free_block(control, block, fl, sl); +} + +static int block_can_split(block_header_t* block, size_t size) +{ + return block_size(block) >= sizeof(block_header_t) + size; +} + +/* Split a block into two, the second of which is free. */ +static block_header_t* block_split(block_header_t* block, size_t size) +{ + /* Calculate the amount of space left in the remaining block. */ + block_header_t* remaining = + offset_to_block(block_to_ptr(block), size - block_header_overhead); + + const size_t remain_size = block_size(block) - (size + block_header_overhead); + + tlsf_assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE) + && "remaining block not aligned properly"); + + tlsf_assert(block_size(block) == remain_size + size + block_header_overhead); + block_set_size(remaining, remain_size); + tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size"); + + block_set_size(block, size); + block_mark_as_free(remaining); + + return remaining; +} + +/* Absorb a free block's storage into an adjacent previous free block. */ +static block_header_t* block_absorb(block_header_t* prev, block_header_t* block) +{ + tlsf_assert(!block_is_last(prev) && "previous block can't be last"); + /* Note: Leaves flags untouched. */ + prev->size += block_size(block) + block_header_overhead; + block_link_next(prev); + return prev; +} + +/* Merge a just-freed block with an adjacent previous free block. */ +static block_header_t* block_merge_prev(control_t* control, block_header_t* block) +{ + if (block_is_prev_free(block)) + { + block_header_t* prev = block_prev(block); + tlsf_assert(prev && "prev physical block can't be null"); + tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such"); + block_remove(control, prev); + block = block_absorb(prev, block); + } + + return block; +} + +/* Merge a just-freed block with an adjacent free block. */ +static block_header_t* block_merge_next(control_t* control, block_header_t* block) +{ + block_header_t* next = block_next(block); + tlsf_assert(next && "next physical block can't be null"); + + if (block_is_free(next)) + { + tlsf_assert(!block_is_last(block) && "previous block can't be last"); + block_remove(control, next); + block = block_absorb(block, next); + } + + return block; +} + +/* Trim any trailing block space off the end of a block, return to pool. */ +static void block_trim_free(control_t* control, block_header_t* block, size_t size) +{ + tlsf_assert(block_is_free(block) && "block must be free"); + if (block_can_split(block, size)) + { + block_header_t* remaining_block = block_split(block, size); + block_link_next(block); + block_set_prev_free(remaining_block); + block_insert(control, remaining_block); + } +} + +/* Trim any trailing block space off the end of a used block, return to pool. */ +static void block_trim_used(control_t* control, block_header_t* block, size_t size) +{ + tlsf_assert(!block_is_free(block) && "block must be used"); + if (block_can_split(block, size)) + { + /* If the next block is free, we must coalesce. */ + block_header_t* remaining_block = block_split(block, size); + block_set_prev_used(remaining_block); + + remaining_block = block_merge_next(control, remaining_block); + block_insert(control, remaining_block); + } +} + +static block_header_t* block_trim_free_leading(control_t* control, block_header_t* block, size_t size) +{ + block_header_t* remaining_block = block; + if (block_can_split(block, size)) + { + /* We want the 2nd block. */ + remaining_block = block_split(block, size - block_header_overhead); + block_set_prev_free(remaining_block); + + block_link_next(block); + block_insert(control, block); + } + + return remaining_block; +} + +static block_header_t* block_locate_free(control_t* control, size_t size) +{ + int fl = 0, sl = 0; + block_header_t* block = 0; + + if (size) + { + mapping_search(size, &fl, &sl); + block = search_suitable_block(control, &fl, &sl); + } + + if (block) + { + tlsf_assert(block_size(block) >= size); + remove_free_block(control, block, fl, sl); + } + + return block; +} + +static void* block_prepare_used(control_t* control, block_header_t* block, size_t size) +{ + void* p = 0; + if (block) + { + tlsf_assert(size && "size must be non-zero"); + block_trim_free(control, block, size); + block_mark_as_used(block); + p = block_to_ptr(block); + } + return p; +} + +/* Clear structure and point all empty lists at the null block. */ +static void control_construct(control_t* control) +{ + int i, j; + + control->block_null.next_free = &control->block_null; + control->block_null.prev_free = &control->block_null; + + control->fl_bitmap = 0; + for (i = 0; i < FL_INDEX_COUNT; ++i) + { + control->sl_bitmap[i] = 0; + for (j = 0; j < SL_INDEX_COUNT; ++j) + { + control->blocks[i][j] = &control->block_null; + } + } +} + +/* +** Debugging utilities. +*/ + +typedef struct integrity_t +{ + int prev_status; + int status; +} integrity_t; + +#define tlsf_insist(x) { tlsf_assert(x); if (!(x)) { status--; } } + +static void integrity_walker(void* ptr, size_t size, int used, void* user) +{ + block_header_t* block = block_from_ptr(ptr); + integrity_t* integ = tlsf_cast(integrity_t*, user); + const int this_prev_status = block_is_prev_free(block) ? 1 : 0; + const int this_status = block_is_free(block) ? 1 : 0; + const size_t this_block_size = block_size(block); + + int status = 0; + tlsf_insist(integ->prev_status == this_prev_status && "prev status incorrect"); + tlsf_insist(size == this_block_size && "block size incorrect"); + + integ->prev_status = this_status; + integ->status += status; +} + +int tlsf_check(tlsf_t tlsf) +{ + int i, j; + + control_t* control = tlsf_cast(control_t*, tlsf); + int status = 0; + + /* Check that the free lists and bitmaps are accurate. */ + for (i = 0; i < FL_INDEX_COUNT; ++i) + { + for (j = 0; j < SL_INDEX_COUNT; ++j) + { + const int fl_map = control->fl_bitmap & (1 << i); + const int sl_list = control->sl_bitmap[i]; + const int sl_map = sl_list & (1 << j); + const block_header_t* block = control->blocks[i][j]; + + /* Check that first- and second-level lists agree. */ + if (!fl_map) + { + tlsf_insist(!sl_map && "second-level map must be null"); + } + + if (!sl_map) + { + tlsf_insist(block == &control->block_null && "block list must be null"); + continue; + } + + /* Check that there is at least one free block. */ + tlsf_insist(sl_list && "no free blocks in second-level map"); + tlsf_insist(block != &control->block_null && "block should not be null"); + + while (block != &control->block_null) + { + int fli, sli; + tlsf_insist(block_is_free(block) && "block should be free"); + tlsf_insist(!block_is_prev_free(block) && "blocks should have coalesced"); + tlsf_insist(!block_is_free(block_next(block)) && "blocks should have coalesced"); + tlsf_insist(block_is_prev_free(block_next(block)) && "block should be free"); + tlsf_insist(block_size(block) >= block_size_min && "block not minimum size"); + + mapping_insert(block_size(block), &fli, &sli); + tlsf_insist(fli == i && sli == j && "block size indexed in wrong list"); + block = block->next_free; + } + } + } + + return status; +} + +#undef tlsf_insist + +static void default_walker(void* ptr, size_t size, int used, void* user) +{ + (void)user; + printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, block_from_ptr(ptr)); +} + +void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user) +{ + tlsf_walker pool_walker = walker ? walker : default_walker; + block_header_t* block = + offset_to_block(pool, -(int)block_header_overhead); + + while (block && !block_is_last(block)) + { + pool_walker( + block_to_ptr(block), + block_size(block), + !block_is_free(block), + user); + block = block_next(block); + } +} + +size_t tlsf_block_size(void* ptr) +{ + size_t size = 0; + if (ptr) + { + const block_header_t* block = block_from_ptr(ptr); + size = block_size(block); + } + return size; +} + +int tlsf_check_pool(pool_t pool) +{ + /* Check that the blocks are physically correct. */ + integrity_t integ = { 0, 0 }; + tlsf_walk_pool(pool, integrity_walker, &integ); + + return integ.status; +} + +/* +** Size of the TLSF structures in a given memory block passed to +** tlsf_create, equal to the size of a control_t +*/ +size_t tlsf_size() +{ + return sizeof(control_t); +} + +size_t tlsf_align_size() +{ + return ALIGN_SIZE; +} + +size_t tlsf_block_size_min() +{ + return block_size_min; +} + +size_t tlsf_block_size_max() +{ + return block_size_max; +} + +/* +** Overhead of the TLSF structures in a given memory block passed to +** tlsf_add_pool, equal to the overhead of a free block and the +** sentinel block. +*/ +size_t tlsf_pool_overhead() +{ + return 2 * block_header_overhead; +} + +size_t tlsf_alloc_overhead() +{ + return block_header_overhead; +} + +pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes) +{ + block_header_t* block; + block_header_t* next; + + const size_t pool_overhead = tlsf_pool_overhead(); + const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE); + + if (((ptrdiff_t)mem % ALIGN_SIZE) != 0) + { + printf("tlsf_add_pool: Memory must be aligned by %u bytes.\n", + (unsigned int)ALIGN_SIZE); + return 0; + } + + if (pool_bytes < block_size_min || pool_bytes > block_size_max) + { +#if defined (TLSF_64BIT) + printf("tlsf_add_pool: Memory size must be between 0x%x and 0x%x00 bytes.\n", + (unsigned int)(pool_overhead + block_size_min), + (unsigned int)((pool_overhead + block_size_max) / 256)); +#else + printf("tlsf_add_pool: Memory size must be between %u and %u bytes.\n", + (unsigned int)(pool_overhead + block_size_min), + (unsigned int)(pool_overhead + block_size_max)); +#endif + return 0; + } + + /* + ** Create the main free block. Offset the start of the block slightly + ** so that the prev_phys_block field falls outside of the pool - + ** it will never be used. + */ + block = offset_to_block(mem, -(tlsfptr_t)block_header_overhead); + block_set_size(block, pool_bytes); + block_set_free(block); + block_set_prev_used(block); + block_insert(tlsf_cast(control_t*, tlsf), block); + + /* Split the block to create a zero-size sentinel block. */ + next = block_link_next(block); + block_set_size(next, 0); + block_set_used(next); + block_set_prev_free(next); + + return mem; +} + +void tlsf_remove_pool(tlsf_t tlsf, pool_t pool) +{ + control_t* control = tlsf_cast(control_t*, tlsf); + block_header_t* block = offset_to_block(pool, -(int)block_header_overhead); + + int fl = 0, sl = 0; + + tlsf_assert(block_is_free(block) && "block should be free"); + tlsf_assert(!block_is_free(block_next(block)) && "next block should not be free"); + tlsf_assert(block_size(block_next(block)) == 0 && "next block size should be zero"); + + mapping_insert(block_size(block), &fl, &sl); + remove_free_block(control, block, fl, sl); +} + +/* +** TLSF main interface. +*/ + +#if _DEBUG +int test_ffs_fls() +{ + /* Verify ffs/fls work properly. */ + int rv = 0; + rv += (tlsf_ffs(0) == -1) ? 0 : 0x1; + rv += (tlsf_fls(0) == -1) ? 0 : 0x2; + rv += (tlsf_ffs(1) == 0) ? 0 : 0x4; + rv += (tlsf_fls(1) == 0) ? 0 : 0x8; + rv += (tlsf_ffs(0x80000000) == 31) ? 0 : 0x10; + rv += (tlsf_ffs(0x80008000) == 15) ? 0 : 0x20; + rv += (tlsf_fls(0x80000008) == 31) ? 0 : 0x40; + rv += (tlsf_fls(0x7FFFFFFF) == 30) ? 0 : 0x80; + +#if defined (TLSF_64BIT) + rv += (tlsf_fls_sizet(0x80000000) == 31) ? 0 : 0x100; + rv += (tlsf_fls_sizet(0x100000000) == 32) ? 0 : 0x200; + rv += (tlsf_fls_sizet(0xffffffffffffffff) == 63) ? 0 : 0x400; +#endif + + if (rv) + { + printf("test_ffs_fls: %x ffs/fls tests failed.\n", rv); + } + return rv; +} +#endif + +tlsf_t tlsf_create(void* mem) +{ +#if _DEBUG + if (test_ffs_fls()) + { + return 0; + } +#endif + + if (((tlsfptr_t)mem % ALIGN_SIZE) != 0) + { + printf("tlsf_create: Memory must be aligned to %u bytes.\n", + (unsigned int)ALIGN_SIZE); + return 0; + } + + control_construct(tlsf_cast(control_t*, mem)); + + return tlsf_cast(tlsf_t, mem); +} + +tlsf_t tlsf_create_with_pool(void* mem, size_t bytes) +{ + tlsf_t tlsf = tlsf_create(mem); + tlsf_add_pool(tlsf, (char*)mem + tlsf_size(), bytes - tlsf_size()); + return tlsf; +} + +void tlsf_destroy(tlsf_t tlsf) +{ + /* Nothing to do. */ + (void)tlsf; +} + +pool_t tlsf_get_pool(tlsf_t tlsf) +{ + return tlsf_cast(pool_t, (char*)tlsf + tlsf_size()); +} + +void* tlsf_malloc(tlsf_t tlsf, size_t size) +{ + control_t* control = tlsf_cast(control_t*, tlsf); + const size_t adjust = adjust_request_size(size, ALIGN_SIZE); + block_header_t* block = block_locate_free(control, adjust); + return block_prepare_used(control, block, adjust); +} + +void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t size) +{ + control_t* control = tlsf_cast(control_t*, tlsf); + const size_t adjust = adjust_request_size(size, ALIGN_SIZE); + + /* + ** We must allocate an additional minimum block size bytes so that if + ** our free block will leave an alignment gap which is smaller, we can + ** trim a leading free block and release it back to the pool. We must + ** do this because the previous physical block is in use, therefore + ** the prev_phys_block field is not valid, and we can't simply adjust + ** the size of that block. + */ + const size_t gap_minimum = sizeof(block_header_t); + const size_t size_with_gap = adjust_request_size(adjust + align + gap_minimum, align); + + /* + ** If alignment is less than or equals base alignment, we're done. + ** If we requested 0 bytes, return null, as tlsf_malloc(0) does. + */ + const size_t aligned_size = (adjust && align > ALIGN_SIZE) ? size_with_gap : adjust; + + block_header_t* block = block_locate_free(control, aligned_size); + + /* This can't be a static assert. */ + tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead); + + if (block) + { + void* ptr = block_to_ptr(block); + void* aligned = align_ptr(ptr, align); + size_t gap = tlsf_cast(size_t, + tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); + + /* If gap size is too small, offset to next aligned boundary. */ + if (gap && gap < gap_minimum) + { + const size_t gap_remain = gap_minimum - gap; + const size_t offset = tlsf_max(gap_remain, align); + const void* next_aligned = tlsf_cast(void*, + tlsf_cast(tlsfptr_t, aligned) + offset); + + aligned = align_ptr(next_aligned, align); + gap = tlsf_cast(size_t, + tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr)); + } + + if (gap) + { + tlsf_assert(gap >= gap_minimum && "gap size too small"); + block = block_trim_free_leading(control, block, gap); + } + } + + return block_prepare_used(control, block, adjust); +} + +void tlsf_free(tlsf_t tlsf, void* ptr) +{ + /* Don't attempt to free a NULL pointer. */ + if (ptr) + { + control_t* control = tlsf_cast(control_t*, tlsf); + block_header_t* block = block_from_ptr(ptr); + tlsf_assert(!block_is_free(block) && "block already marked as free"); + block_mark_as_free(block); + block = block_merge_prev(control, block); + block = block_merge_next(control, block); + block_insert(control, block); + } +} + +/* +** The TLSF block information provides us with enough information to +** provide a reasonably intelligent implementation of realloc, growing or +** shrinking the currently allocated block as required. +** +** This routine handles the somewhat esoteric edge cases of realloc: +** - a non-zero size with a null pointer will behave like malloc +** - a zero size with a non-null pointer will behave like free +** - a request that cannot be satisfied will leave the original buffer +** untouched +** - an extended buffer size will leave the newly-allocated area with +** contents undefined +*/ +void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size) +{ + control_t* control = tlsf_cast(control_t*, tlsf); + void* p = 0; + + /* Zero-size requests are treated as free. */ + if (ptr && size == 0) + { + tlsf_free(tlsf, ptr); + } + /* Requests with NULL pointers are treated as malloc. */ + else if (!ptr) + { + p = tlsf_malloc(tlsf, size); + } + else + { + block_header_t* block = block_from_ptr(ptr); + block_header_t* next = block_next(block); + + const size_t cursize = block_size(block); + const size_t combined = cursize + block_size(next) + block_header_overhead; + const size_t adjust = adjust_request_size(size, ALIGN_SIZE); + + tlsf_assert(!block_is_free(block) && "block already marked as free"); + + /* + ** If the next block is used, or when combined with the current + ** block, does not offer enough space, we must reallocate and copy. + */ + if (adjust > cursize && (!block_is_free(next) || adjust > combined)) + { + p = tlsf_malloc(tlsf, size); + if (p) + { + const size_t minsize = tlsf_min(cursize, size); + memcpy(p, ptr, minsize); + tlsf_free(tlsf, ptr); + } + } + else + { + /* Do we need to expand to the next block? */ + if (adjust > cursize) + { + block_merge_next(control, block); + block_mark_as_used(block); + } + + /* Trim the resulting block and return the original pointer. */ + block_trim_used(control, block, adjust); + p = ptr; + } + } + + return p; +} diff --git a/ext/tlsf/tlsf.h b/ext/tlsf/tlsf.h new file mode 100644 index 000000000..c86a54876 --- /dev/null +++ b/ext/tlsf/tlsf.h @@ -0,0 +1,90 @@ +#ifndef INCLUDED_tlsf +#define INCLUDED_tlsf + +/* +** Two Level Segregated Fit memory allocator, version 3.1. +** Written by Matthew Conte +** http://tlsf.baisoku.org +** +** Based on the original documentation by Miguel Masmano: +** http://rtportal.upv.es/rtmalloc/allocators/tlsf/index.shtml +** +** This implementation was written to the specification +** of the document, therefore no GPL restrictions apply. +** +** Copyright (c) 2006-2016, Matthew Conte +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* tlsf_t: a TLSF structure. Can contain 1 to N pools. */ +/* pool_t: a block of memory that TLSF can manage. */ +typedef void* tlsf_t; +typedef void* pool_t; + +/* Create/destroy a memory pool. */ +tlsf_t tlsf_create(void* mem); +tlsf_t tlsf_create_with_pool(void* mem, size_t bytes); +void tlsf_destroy(tlsf_t tlsf); +pool_t tlsf_get_pool(tlsf_t tlsf); + +/* Add/remove memory pools. */ +pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes); +void tlsf_remove_pool(tlsf_t tlsf, pool_t pool); + +/* malloc/memalign/realloc/free replacements. */ +void* tlsf_malloc(tlsf_t tlsf, size_t bytes); +void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t bytes); +void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size); +void tlsf_free(tlsf_t tlsf, void* ptr); + +/* Returns internal block size, not original request size */ +size_t tlsf_block_size(void* ptr); + +/* Overheads/limits of internal structures. */ +size_t tlsf_size(); +size_t tlsf_align_size(); +size_t tlsf_block_size_min(); +size_t tlsf_block_size_max(); +size_t tlsf_pool_overhead(); +size_t tlsf_alloc_overhead(); + +/* Debugging. */ +typedef void (*tlsf_walker)(void* ptr, size_t size, int used, void* user); +void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user); +/* Returns nonzero if any internal consistency check fails. */ +int tlsf_check(tlsf_t tlsf); +int tlsf_check_pool(pool_t pool); + +#if defined(__cplusplus) +}; +#endif + +#endif From e0ba706f54a3ec30b82e1c16a9a9ea0560a3f405 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 11 Apr 2016 21:14:31 +0100 Subject: [PATCH 2/7] [ext] Fix [-Wstrict-prototypes] warnings in TLSF. --- ext/tlsf/tlsf.c | 13 +++++++------ ext/tlsf/tlsf.h | 12 ++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ext/tlsf/tlsf.c b/ext/tlsf/tlsf.c index 012bc7e42..6b473a9d6 100644 --- a/ext/tlsf/tlsf.c +++ b/ext/tlsf/tlsf.c @@ -811,6 +811,7 @@ typedef struct integrity_t static void integrity_walker(void* ptr, size_t size, int used, void* user) { + (void)used; block_header_t* block = block_from_ptr(ptr); integrity_t* integ = tlsf_cast(integrity_t*, user); const int this_prev_status = block_is_prev_free(block) ? 1 : 0; @@ -926,22 +927,22 @@ int tlsf_check_pool(pool_t pool) ** Size of the TLSF structures in a given memory block passed to ** tlsf_create, equal to the size of a control_t */ -size_t tlsf_size() +size_t tlsf_size(void) { return sizeof(control_t); } -size_t tlsf_align_size() +size_t tlsf_align_size(void) { return ALIGN_SIZE; } -size_t tlsf_block_size_min() +size_t tlsf_block_size_min(void) { return block_size_min; } -size_t tlsf_block_size_max() +size_t tlsf_block_size_max(void) { return block_size_max; } @@ -951,12 +952,12 @@ size_t tlsf_block_size_max() ** tlsf_add_pool, equal to the overhead of a free block and the ** sentinel block. */ -size_t tlsf_pool_overhead() +size_t tlsf_pool_overhead(void) { return 2 * block_header_overhead; } -size_t tlsf_alloc_overhead() +size_t tlsf_alloc_overhead(void) { return block_header_overhead; } diff --git a/ext/tlsf/tlsf.h b/ext/tlsf/tlsf.h index c86a54876..8f8f061e8 100644 --- a/ext/tlsf/tlsf.h +++ b/ext/tlsf/tlsf.h @@ -69,12 +69,12 @@ void tlsf_free(tlsf_t tlsf, void* ptr); size_t tlsf_block_size(void* ptr); /* Overheads/limits of internal structures. */ -size_t tlsf_size(); -size_t tlsf_align_size(); -size_t tlsf_block_size_min(); -size_t tlsf_block_size_max(); -size_t tlsf_pool_overhead(); -size_t tlsf_alloc_overhead(); +size_t tlsf_size(void); +size_t tlsf_align_size(void); +size_t tlsf_block_size_min(void); +size_t tlsf_block_size_max(void); +size_t tlsf_pool_overhead(void); +size_t tlsf_alloc_overhead(void); /* Debugging. */ typedef void (*tlsf_walker)(void* ptr, size_t size, int used, void* user); From 1ce1b16c5937af7b572e98f60c4316f0d6506048 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 3 Apr 2016 19:25:22 +0100 Subject: [PATCH 3/7] [ext] Choose better default parameters for TLSF. These parameters reduce tlsf_size() from ~3kB to <1kB. The maximum block size is defaulted at 512kB, which covers the all internal SRAM of any STM32 device and the subdivision is halved. If these paramters are too restrictive they may be overwritten in the project.cfg (here showing the original settings with 1GB block size): [defines] XPCC_TLFS_FL_INDEX_MAX = 30 XPCC_TLFS_SL_INDEX_COUNT_LOG2 = 5 --- ext/tlsf/tlsf.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ext/tlsf/tlsf.c b/ext/tlsf/tlsf.c index 6b473a9d6..0cd544524 100644 --- a/ext/tlsf/tlsf.c +++ b/ext/tlsf/tlsf.c @@ -7,6 +7,22 @@ #include "tlsf.h" + +#ifndef _DEBUG +# define _DEBUG 0 +#endif + +#ifndef XPCC_TLFS_SL_INDEX_COUNT_LOG2 +// 4 or 5 are acceptable values (ie. 16 or 32 subdivisions) +# define XPCC_TLFS_SL_INDEX_COUNT_LOG2 4 +#endif + +#ifndef XPCC_TLFS_FL_INDEX_MAX +// 512kB covers all internal SRAM of any STM32 device +# define XPCC_TLFS_FL_INDEX_MAX 19 +#endif + + #if defined(__cplusplus) #define tlsf_decl inline #else @@ -208,7 +224,7 @@ enum tlsf_public ** values require more memory in the control structure. Values of ** 4 or 5 are typical. */ - SL_INDEX_COUNT_LOG2 = 5, + SL_INDEX_COUNT_LOG2 = XPCC_TLFS_SL_INDEX_COUNT_LOG2, }; /* Private constants: do not modify. */ @@ -241,7 +257,7 @@ enum tlsf_private */ FL_INDEX_MAX = 32, #else - FL_INDEX_MAX = 30, + FL_INDEX_MAX = XPCC_TLFS_FL_INDEX_MAX, #endif SL_INDEX_COUNT = (1 << SL_INDEX_COUNT_LOG2), FL_INDEX_SHIFT = (SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2), From 0fb6757e3b60030b7baed5d2fc5c59ae05d78f11 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 30 Mar 2016 23:15:55 +0100 Subject: [PATCH 4/7] [cortex] Add TLSF init and malloc to syscalls. --- .../platform/driver/core/cortex/startup.c.in | 6 +- .../driver/core/cortex/syscalls.cpp.in | 114 ++++++++++++++++++ 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/xpcc/architecture/platform/driver/core/cortex/startup.c.in b/src/xpcc/architecture/platform/driver/core/cortex/startup.c.in index 22b9ce990..3ae298888 100644 --- a/src/xpcc/architecture/platform/driver/core/cortex/startup.c.in +++ b/src/xpcc/architecture/platform/driver/core/cortex/startup.c.in @@ -157,7 +157,7 @@ __libc_init_array(void); extern void exit(int) __attribute__ ((noreturn, weak)); -%% if parameters.allocator == "block_allocator" +%% if parameters.allocator in ["block_allocator", "tlsf"] extern void __xpcc_initialize_memory(void); %% endif @@ -303,8 +303,8 @@ Reset_Handler(void) xpcc_gpio_enable(); %% endif -%% if parameters.allocator == "block_allocator" - // initialize xpcc block allocator +%% if parameters.allocator in ["block_allocator", "tlsf"] + // initialize xpcc block or TLSF allocator // needs to be done before calling the CTORS of static objects __xpcc_initialize_memory(); %% endif diff --git a/src/xpcc/architecture/platform/driver/core/cortex/syscalls.cpp.in b/src/xpcc/architecture/platform/driver/core/cortex/syscalls.cpp.in index fc6fccaf8..be5586972 100644 --- a/src/xpcc/architecture/platform/driver/core/cortex/syscalls.cpp.in +++ b/src/xpcc/architecture/platform/driver/core/cortex/syscalls.cpp.in @@ -187,5 +187,119 @@ extern "C" void free(void *p) { allocator.free(p); +} +%% elif parameters.allocator == "tlsf" +extern "C" +void * +_sbrk_r(struct _reent *, ptrdiff_t) +{ + return 0; +} + +// Using the TLSF Allocator +#include + +%% set regions = [] +%% for memory in memorys + %% do regions.append(memory.name) +%% endfor + +%% set heap0_available = ('ccm' in regions and target is not stm32f3) or 'dtcm' in regions +%% set heap2_available = 'sram2' in regions +%% set heap3_available = 'sram3' in regions +%% set heap4_available = ('ccm' in regions and target is stm32f3) or 'itcm' in regions + +%% if heap0_available +tlsf_t tlsf_heap0; +extern uint32_t __heap0_start; +extern uint32_t __heap0_end; +%% endif + +tlsf_t tlsf_heap123; +extern uint32_t __heap1_start; +extern uint32_t __heap1_end; +%% if heap2_available +extern uint32_t __heap2_start; +extern uint32_t __heap2_end; +%% endif +%% if heap3_available +extern uint32_t __heap3_start; +extern uint32_t __heap3_end; +%% endif + +%% if heap4_available +tlsf_t tlsf_heap4; +extern uint32_t __heap4_start; +extern uint32_t __heap4_end; +%% endif + +extern "C" { + +void __xpcc_initialize_memory(void) +{ +%% if heap0_available + tlsf_heap0 = tlsf_create_with_pool((void*)&__heap0_start, (size_t)&__heap0_end - (size_t)&__heap0_start); +%% endif + + tlsf_heap123 = tlsf_create_with_pool((void*)&__heap1_start, (size_t)&__heap1_end - (size_t)&__heap1_start); +%% if heap2_available + tlsf_add_pool(tlsf_heap123, (void*)&__heap2_start, (size_t)&__heap2_end - (size_t)&__heap2_start); +%% endif +%% if heap3_available + tlsf_add_pool(tlsf_heap123, (void*)&__heap3_start, (size_t)&__heap3_end - (size_t)&__heap3_start); +%% endif + +%% if heap4_available + tlsf_heap4 = tlsf_create_with_pool((void*)&__heap4_start, (size_t)&__heap4_end - (size_t)&__heap4_start); +%% endif +} + +static inline tlsf_t +get_tlsf_for_ptr(void *p) +{ + /* Heap order by address space: + * 1. heap4 + * 2. heap0 + * 3. heap1 + * 4. heap2 + * 5. heap3 + */ +%% if heap0_available or heap4_available + if (p >= (void*)&__heap1_start) +%% endif + return tlsf_heap123; +%% if heap4_available + %% if heap0_available + else if (p < (void*)&__heap4_end) + %% endif + return tlsf_heap4; +%% endif +%% if heap0_available + return tlsf_heap0; +%% endif +} + +void *malloc(size_t size) +{ + void* p = tlsf_malloc(tlsf_heap123, size); +%% if heap0_available + if (p) return p; + // fail back to .heap0 + p = tlsf_malloc(tlsf_heap0, size); +%% endif + return p; +} + +void *realloc(void *p, size_t size) +{ + return tlsf_realloc(get_tlsf_for_ptr(p), p, size); +} + +void free(void *p) +{ + tlsf_free(get_tlsf_for_ptr(p), p); + p = NULL; +} + } %% endif From eeb48284fc0f0d5ff86b643b0b0aec1a943c79be Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 30 Mar 2016 23:16:54 +0100 Subject: [PATCH 5/7] [cortex] Enable .heap0 and .heap4. --- .../platform/driver/core/cortex/stm32/stm32_dccm.ld.in | 4 ++-- .../platform/driver/core/cortex/stm32/stm32_iccm.ld.in | 4 ++-- .../driver/core/cortex/stm32/stm32_idtcm.ld.in | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_dccm.ld.in b/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_dccm.ld.in index 430fe141b..bb986d82a 100644 --- a/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_dccm.ld.in +++ b/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_dccm.ld.in @@ -221,7 +221,7 @@ SECTIONS __fastdata_end = .; } >CCM AT>FLASH - /* This is currently not used by the heap allocator! +%% if parameters.allocator == "tlsf" .heap0 (NOLOAD) : { . = ALIGN(4); @@ -230,7 +230,7 @@ SECTIONS . = ORIGIN(CCM) + LENGTH(CCM); __heap0_end = .; } >CCM - */ +%% endif .text : { diff --git a/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_iccm.ld.in b/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_iccm.ld.in index 29a14bfa1..9d4e1dfee 100644 --- a/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_iccm.ld.in +++ b/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_iccm.ld.in @@ -208,7 +208,7 @@ SECTIONS __fastdata_end = .; } >CCM AT>FLASH - /* This is currently not used by the heap allocator! +%% if parameters.allocator == "tlsf" .heap4 (NOLOAD) : { . = ALIGN(4); @@ -217,7 +217,7 @@ SECTIONS . = ORIGIN(CCM) + LENGTH(CCM); __heap4_end = .; } >CCM - */ +%% endif .text : { diff --git a/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_idtcm.ld.in b/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_idtcm.ld.in index f34781109..15ca344e5 100644 --- a/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_idtcm.ld.in +++ b/src/xpcc/architecture/platform/driver/core/cortex/stm32/stm32_idtcm.ld.in @@ -17,7 +17,7 @@ * | | 0x2005 0000 <---- __ram_end, __heap2_end, __heap_end * +-------> |---------------------------------| 0x2004 FFFF * | | | - * heap2 | | + * .heap2 | | * | | | * +-------> |---------------------------------| 0x2004 C000 <--- __heap2_start * @@ -215,7 +215,7 @@ SECTIONS __fastcode_end = .; } >ITCM AT>FLASH - /* This is currently not used by the heap allocator! +%% if parameters.allocator == "tlsf" .heap4 (NOLOAD) : { . = ALIGN(4); @@ -224,7 +224,7 @@ SECTIONS . = ORIGIN(ITCM) + LENGTH(ITCM); __heap4_end = .; } >ITCM - */ +%% endif .stack (NOLOAD) : { @@ -252,7 +252,7 @@ SECTIONS __fastdata_end = .; } >DTCM AT>FLASH - /* This is currently not used by the heap allocator! +%% if parameters.allocator == "tlsf" .heap0 (NOLOAD) : { . = ALIGN(4); @@ -261,7 +261,7 @@ SECTIONS . = ORIGIN(DTCM) + LENGTH(DTCM); __heap0_end = .; } >DTCM - */ +%% endif .text : { From 0b457d65525a9ad8f50bd99d8ec961e8ae807c9b Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 30 Mar 2016 23:17:28 +0100 Subject: [PATCH 6/7] [cortex] Enable TLSF as default allocator. --- src/xpcc/architecture/platform/driver/core/cortex/driver.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xpcc/architecture/platform/driver/core/cortex/driver.xml b/src/xpcc/architecture/platform/driver/core/cortex/driver.xml index 76f48bc95..dba303295 100644 --- a/src/xpcc/architecture/platform/driver/core/cortex/driver.xml +++ b/src/xpcc/architecture/platform/driver/core/cortex/driver.xml @@ -2,8 +2,8 @@ - - newlib + + tlsf true false From ba243c9a9cd7ea3e1f06f737831f6c44db6505bb Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 31 Mar 2016 00:13:45 +0100 Subject: [PATCH 7/7] [examples] Add TLSF allocator example. --- .../tlsf-allocator/SConstruct | 4 ++ .../tlsf-allocator/main.cpp | 54 +++++++++++++++++++ .../tlsf-allocator/project.cfg | 6 +++ 3 files changed, 64 insertions(+) create mode 100644 examples/stm32f469_discovery/tlsf-allocator/SConstruct create mode 100644 examples/stm32f469_discovery/tlsf-allocator/main.cpp create mode 100644 examples/stm32f469_discovery/tlsf-allocator/project.cfg diff --git a/examples/stm32f469_discovery/tlsf-allocator/SConstruct b/examples/stm32f469_discovery/tlsf-allocator/SConstruct new file mode 100644 index 000000000..54cafe644 --- /dev/null +++ b/examples/stm32f469_discovery/tlsf-allocator/SConstruct @@ -0,0 +1,4 @@ +# path to the xpcc root directory +xpccpath = '../../..' +# execute the common SConstruct file +execfile(xpccpath + '/scons/SConstruct') diff --git a/examples/stm32f469_discovery/tlsf-allocator/main.cpp b/examples/stm32f469_discovery/tlsf-allocator/main.cpp new file mode 100644 index 000000000..c5b1deeef --- /dev/null +++ b/examples/stm32f469_discovery/tlsf-allocator/main.cpp @@ -0,0 +1,54 @@ +#include +#include + +extern tlsf_t tlsf_heap0; +extern uint32_t __heap1_start; + +int main() +{ + Board::initialize(); + Board::Leds::setOutput(); + + // 200 pointers to allocated memories + void *d[200]; + int free_ii = 0; + + for (int ii=0; ii < 200; ii++) + { + d[ii] = nullptr; + uint32_t size = rand() % (1024*20); + + if ((rand() % 100) >= 75) { + d[ii] = tlsf_malloc(tlsf_heap0, size); + // explicit allocation in CCM + XPCC_LOG_INFO << ".heap0 "; + } else { + d[ii] = malloc(size); + if (d[ii] && d[ii] < &__heap1_start) { + // allocation on SRAM has failed and fallback uses CCM! + XPCC_LOG_INFO << ".heap0FB"; + } + else XPCC_LOG_INFO << ".heap123"; + } + + // print what size we requested and if it succeeded + XPCC_LOG_INFO.printf(" malloc(%2ukB) = ", size/1024); + if (d[ii]) XPCC_LOG_INFO << d[ii]; + else XPCC_LOG_INFO << "NO MEMORY "; + + if ((rand() % 100) >= 50) { + // only some memory is returned to the system + void* df = d[free_ii++]; + XPCC_LOG_INFO << " ...freeing " << df; + free(df); + } + XPCC_LOG_INFO << xpcc::endl; + } + + while (1) + { + Board::Leds::toggle(); + xpcc::delayMilliseconds(500); + } + return 0; +} diff --git a/examples/stm32f469_discovery/tlsf-allocator/project.cfg b/examples/stm32f469_discovery/tlsf-allocator/project.cfg new file mode 100644 index 000000000..c26cf74f8 --- /dev/null +++ b/examples/stm32f469_discovery/tlsf-allocator/project.cfg @@ -0,0 +1,6 @@ +[build] +board = stm32f469_discovery +buildpath = ${xpccpath}/build/stm32f469_discovery/${name} + +[parameters] +uart.stm32.3.tx_buffer = 2048