Example of setting up malloc heap on extram

This commit is contained in:
Toby Jaffey 2025-12-11 20:35:11 +00:00
parent 7fae8c129f
commit f27ed6213c
5 changed files with 369 additions and 0 deletions

12
apps/heap/Makefile Normal file
View file

@ -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

66
apps/heap/heap.c Normal file
View file

@ -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
}

109
apps/heap/linkedlist.h Normal file
View file

@ -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 <stdlib.h> //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_

152
apps/heap/malloc_freelist.c Normal file
View file

@ -0,0 +1,152 @@
//#include <stdint.h>
#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);
}

View file

@ -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_