diff --git a/apps/heap/Makefile b/apps/heap/Makefile new file mode 100644 index 0000000..45b1e81 --- /dev/null +++ b/apps/heap/Makefile @@ -0,0 +1,12 @@ +TOPDIR=../.. +PROJECT:=$(shell basename ${PWD}) + +HEAP_SIZE=$(shell echo "1024 * 512" | bc) +OPT=-Os +CFLAGS=-DHEAP_SIZE=${HEAP_SIZE} +SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S malloc_freelist.c +HOST_EXTRA=-e ${HEAP_SIZE} -i 100000 +all: all_common +test: test_common +clean: clean_common +include ${TOPDIR}/apps/makefile.common diff --git a/apps/heap/heap.c b/apps/heap/heap.c new file mode 100644 index 0000000..5c468e9 --- /dev/null +++ b/apps/heap/heap.c @@ -0,0 +1,66 @@ +#include "uvm32_target.h" +#include "malloc_freelist.h" + +uint32_t* extram = (uint32_t*)UVM32_EXTRAM_BASE; +uint32_t extram_len = HEAP_SIZE; + +void* memcpy(void* dst, const void* src, int len) { + uint8_t* d = (uint8_t*)dst; + const uint8_t* s = (const uint8_t*)src; + while (len--) { + *(d++) = *(s++); + } + return dst; +} + +void* memset(void* buf, int c, int len) { + uint8_t* b = (uint8_t*)buf; + while (len--) { + *(b++) = c; + } + return buf; +} + +void* memmove(void* dest, const void* src, size_t len) { + char* d = dest; + const char* s = src; + if (d < s) + while (len--) + *d++ = *s++; + else { + const char* lasts = s + (len - 1); + char* lastd = d + (len - 1); + while (len--) + *lastd-- = *lasts--; + } + return dest; +} + +void force_crash(void) { + uint8_t *p = (uint8_t *)0; + p[0] = 0; +} + +void main(void) { + malloc_addblock(extram, extram_len); + + uint8_t* p1 = fl_malloc(128); + if (p1 == NULL) { + println("malloc failed"); + } else { + println("malloc ok"); + } + memset(p1, 'a', 128); + fl_free(p1); + + uint8_t* p2 = fl_malloc(256); + if (p2 == NULL) { + println("malloc failed"); + } else { + println("malloc ok"); + } + memset(p2, 'b', 256); + fl_free(p2); + + //force_crash(); // allows dump to be inspected +} diff --git a/apps/heap/linkedlist.h b/apps/heap/linkedlist.h new file mode 100644 index 0000000..6defeb2 --- /dev/null +++ b/apps/heap/linkedlist.h @@ -0,0 +1,109 @@ +// Copyright Embedded Artistry LLC 2017 +// Released under CC0 1.0 Universal License + +#ifndef __LL_H_ +#define __LL_H_ + +#include "uvm32_target.h" +//#include //size_t, NULL + +/** + * Define offsetof and container_of if we don't have them already + */ +#ifndef offsetof + #ifdef __compiler_offsetof + #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER) + #else + #define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE*)0)->MEMBER) + #endif +#endif // offsetof + +#ifndef container_of + #define container_of(ptr, type, member) \ + ({ \ + const __typeof__(((type*)0)->member)* __mptr = (ptr); \ + (type*)((char*)__mptr - offsetof(type, member)); \ + }) +#endif // container_of + +#ifdef __cplusplus +extern "C" +{ +#endif //__cplusplus + + /* + * This is a doubly linked list structure. + * This structure should be embedded in a container structure that you want to list. + */ + typedef struct ll_head + { + struct ll_head* next; /**< Next pointer */ + struct ll_head* prev; /**< Previous pointer */ + } ll_t; + +#define list_entry(ptr, type, member) container_of(ptr, type, member) + +#define list_first_entry(head, type, member) list_entry((head)->next, type, member) + +#define list_for_each(pos, head) for(pos = (head)->next; pos != (head); pos = pos->next) + +#define list_for_each_safe(pos, n, head) \ + for(pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next) + +#define list_for_each_entry(pos, head, member) \ + for(pos = list_entry((head)->next, __typeof__(*pos), member); &pos->member != (head); \ + pos = list_entry(pos->member.next, __typeof__(*pos), member)) + +#define list_for_each_entry_safe(pos, n, head, member) \ + for(pos = list_entry((head)->next, __typeof__(*pos), member), \ + n = list_entry(pos->member.next, __typeof__(*pos), member); \ + &pos->member != (head); pos = n, n = list_entry(n->member.next, __typeof__(*n), member)) + +#pragma mark - Init - + +#define ll_head_INIT(name) \ + { \ + &(name), &(name) \ + } +#define LIST_INIT(name) struct ll_head name = ll_head_INIT(name) + +#pragma mark - Add - + + static inline void list_add_(struct ll_head* n, struct ll_head* prev, struct ll_head* next) + { + next->prev = n; + n->next = next; + n->prev = prev; + prev->next = n; + } + + static inline void list_add(struct ll_head* n, struct ll_head* head) + { + list_add_(n, head, head->next); + } + + static inline void list_add_tail(struct ll_head* n, struct ll_head* head) + { + list_add_(n, head->prev, head); + } + +#pragma mark - Delete - + + static inline void list_del_(struct ll_head* prev, struct ll_head* next) + { + next->prev = prev; + prev->next = next; + } + + static inline void list_del(struct ll_head* entry) + { + list_del_(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; + } + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif // __LL_H_ diff --git a/apps/heap/malloc_freelist.c b/apps/heap/malloc_freelist.c new file mode 100644 index 0000000..e98f0f4 --- /dev/null +++ b/apps/heap/malloc_freelist.c @@ -0,0 +1,152 @@ +//#include +#include "uvm32_target.h" +#include "linkedlist.h" + +#pragma mark - Definitions - + +/** + * Simple macro for making sure memory addresses are aligned + * to the nearest power of two + */ +#ifndef align_up + #define align_up(num, align) (((num) + ((align)-1)) & ~((align)-1)) +#endif + +/* + * This is the container for our free-list. + * Node the usage of the linked list here: the library uses offsetof + * and container_of to manage the list and get back to the parent struct. + */ +typedef struct +{ + ll_t node; + size_t size; + char* block; +} alloc_node_t; + +/** + * We vend a memory address to the user. This lets us translate back and forth + * between the vended pointer and the container we use for managing the data. + */ +#define ALLOC_HEADER_SZ offsetof(alloc_node_t, block) + +// We are enforcing a minimum allocation size of 32B. +#define MIN_ALLOC_SZ ALLOC_HEADER_SZ + 32 + +#pragma mark - Prototypes - + +static void defrag_free_list(void); + +#pragma mark - Declarations - + +// This macro simply declares and initializes our linked list +static LIST_INIT(free_list); + +#pragma mark - Private Functions - + +/** + * When we free, we can take our node and check to see if any memory blocks + * can be combined into larger blocks. This will help us fight against + * memory fragmentation in a simple way. + */ +void defrag_free_list(void) +{ + alloc_node_t *block, *last_block = NULL, *t; + + list_for_each_entry_safe(block, t, &free_list, node) + { + if(last_block) + { + if((((uintptr_t)&last_block->block) + last_block->size) == (uintptr_t)block) + { + last_block->size += ALLOC_HEADER_SZ + block->size; + list_del(&block->node); + continue; + } + } + last_block = block; + } +} +#pragma mark - APIs - + +void* fl_malloc(size_t size) +{ + void* ptr = NULL; + alloc_node_t* blk = NULL; + + if(size > 0) + { + // Align the pointer + size = align_up(size, sizeof(void*)); + + // try to find a big enough block to alloc + list_for_each_entry(blk, &free_list, node) + { + if(blk->size >= size) + { + ptr = &blk->block; + break; + } + } + + // we found something + if(ptr) + { + // Can we split the block? + if((blk->size - size) >= MIN_ALLOC_SZ) + { + alloc_node_t* new_blk; + new_blk = (alloc_node_t*)((uintptr_t)(&blk->block) + size); + new_blk->size = blk->size - size - ALLOC_HEADER_SZ; + blk->size = size; + list_add_(&new_blk->node, &blk->node, blk->node.next); + } + + list_del(&blk->node); + } + + } // else NULL + + return ptr; +} + +void fl_free(void* ptr) +{ + alloc_node_t *blk, *free_blk; + + // Don't free a NULL pointer.. + if(ptr) + { + // we take the pointer and use container_of to get the corresponding alloc block + blk = container_of(ptr, alloc_node_t, block); + + // Let's put it back in the proper spot + list_for_each_entry(free_blk, &free_list, node) + { + if(free_blk > blk) + { + list_add_(&blk->node, free_blk->node.prev, &free_blk->node); + goto blockadded; + } + } + list_add_tail(&blk->node, &free_list); + + blockadded: + // Let's see if we can combine any memory + defrag_free_list(); + } +} + +void malloc_addblock(void* addr, size_t size) +{ + alloc_node_t* blk; + + // let's align the start address of our block to the next pointer aligned number + blk = (void*)align_up((uintptr_t)addr, sizeof(void*)); + + // calculate actual size - remove our alignment and our header space from the availability + blk->size = (uintptr_t)addr + size - (uintptr_t)blk - ALLOC_HEADER_SZ; + + // and now our giant block of memory is added to the list! + list_add(&blk->node, &free_list); +} diff --git a/apps/heap/malloc_freelist.h b/apps/heap/malloc_freelist.h new file mode 100644 index 0000000..6b0e033 --- /dev/null +++ b/apps/heap/malloc_freelist.h @@ -0,0 +1,30 @@ +#ifndef __MALLOC_FREELIST_H_ +#define __MALLOC_FREELIST_H_ + +#include "uvm32_target.h" + +#ifdef __cplusplus +extern "C" +{ +#endif //__cplusplus + + /** + * Initialize malloc with a memory address and pool size + */ + void malloc_addblock(void* addr, size_t size); + + /** + * Free-list malloc implementation + */ + void* fl_malloc(size_t size); + + /** + * Corresponding free-list free implementation + */ + void fl_free(void* ptr); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif //__MALLOC_FREELIST_H_