564 lines
17 KiB
C
564 lines
17 KiB
C
/*
|
|
* INCLUDE FILES
|
|
****************************************************************************************
|
|
*/
|
|
#include <stdint.h> // standard definition
|
|
#include <stdbool.h> // standard boolean
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "fr30xx.h"
|
|
#include "heap.h"
|
|
|
|
/*
|
|
* MACRO DEFINITIONS
|
|
****************************************************************************************
|
|
*/
|
|
/// Pattern used to check if memory block is not corrupted
|
|
#define HEAP_LIST_PATTERN (0xA55AA55A)
|
|
#define HEAP_ALLOCATED_PATTERN (0x83388338)
|
|
#define HEAP_FREE_PATTERN (0xF00FF00F)
|
|
|
|
#define CO_ALIGN4_HI(val) (((val)+3)&~3)
|
|
|
|
#define HEAP_MEM_DEBUG 1
|
|
|
|
#if HEAP_MEM_DEBUG
|
|
#if defined (__ARMCC_VERSION) || defined(__ICCARM__)
|
|
/// Assertions showing a critical error that could require a full system reset
|
|
#define ASSERT_ERR(cond) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
assert_err(#cond, __FILE__, __LINE__); \
|
|
} \
|
|
} while(0)
|
|
|
|
/// Assertions showing a critical error that could require a full system reset
|
|
#define ASSERT_INFO(cond, param0, param1) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
assert_param((int)param0, (int)param1, __FILE__, __LINE__); \
|
|
} \
|
|
} while(0)
|
|
|
|
/// Assertions showing a non-critical problem that has to be fixed by the SW
|
|
#define ASSERT_WARN(cond, param0, param1) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
assert_warn((int)param0, (int)param1, __FILE__, __LINE__); \
|
|
} \
|
|
} while(0)
|
|
#elif defined(__CC_ARM) || defined(__GNUC__)
|
|
/// Assertions showing a critical error that could require a full system reset
|
|
#define ASSERT_ERR(cond) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
assert_err(#cond, __MODULE__, __LINE__); \
|
|
} \
|
|
} while(0)
|
|
|
|
/// Assertions showing a critical error that could require a full system reset
|
|
#define ASSERT_INFO(cond, param0, param1) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
assert_param((int)param0, (int)param1, __MODULE__, __LINE__); \
|
|
} \
|
|
} while(0)
|
|
|
|
/// Assertions showing a non-critical problem that has to be fixed by the SW
|
|
#define ASSERT_WARN(cond, param0, param1) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
assert_warn((int)param0, (int)param1, __MODULE__, __LINE__); \
|
|
} \
|
|
} while(0)
|
|
#else
|
|
#error "Unsupported platform"
|
|
#endif
|
|
#else
|
|
/// Assertions showing a critical error that could require a full system reset
|
|
#define ASSERT_ERR(cond)
|
|
|
|
/// Assertions showing a critical error that could require a full system reset
|
|
#define ASSERT_INFO(cond, param0, param1)
|
|
|
|
/// Assertions showing a non-critical problem that has to be fixed by the SW
|
|
#define ASSERT_WARN(cond, param0, param1)
|
|
|
|
/// DUMP data array present in the SW.
|
|
#define DUMP_DATA(data, length)
|
|
#endif //PLF_DEBUG
|
|
|
|
/*
|
|
* LOCAL TYPE DEFINITIONS
|
|
****************************************************************************************
|
|
*/
|
|
/// Free memory block delimiter structure (size must be word multiple)
|
|
/// Heap can be represented as a doubled linked list.
|
|
struct mblock_free
|
|
{
|
|
/// Used to check if memory block has been corrupted or not
|
|
uint32_t corrupt_check;
|
|
/// Size of the current free block (including delimiter)
|
|
uint32_t free_size;
|
|
|
|
#if HEAP_MEM_DEBUG
|
|
uint32_t return_addr;
|
|
#endif
|
|
|
|
/// Next free block pointer
|
|
struct mblock_free* next;
|
|
/// Previous free block pointer
|
|
struct mblock_free* previous;
|
|
};
|
|
|
|
/// Used memory block delimiter structure (size must be word multiple)
|
|
struct mblock_used
|
|
{
|
|
/// Used to check if memory block has been corrupted or not
|
|
uint32_t corrupt_check;
|
|
/// Size of the current used block (including delimiter)
|
|
uint32_t size;
|
|
|
|
#if HEAP_MEM_DEBUG
|
|
uint32_t return_addr;
|
|
#endif
|
|
};
|
|
|
|
/// heap environment definition
|
|
struct heap_env_tag
|
|
{
|
|
/// Root pointer = pointer to first element of heap linked lists
|
|
struct mblock_free * heap[HEAP_TYPE_BLOCKS];
|
|
/// Size of heaps
|
|
uint32_t heap_size[HEAP_TYPE_BLOCKS];
|
|
|
|
#if (HEAP_MEM_DEBUG)
|
|
/// Size of heap used
|
|
uint32_t heap_used[HEAP_TYPE_BLOCKS];
|
|
/// Maximum heap memory used
|
|
uint32_t max_heap_used_single[HEAP_TYPE_BLOCKS];
|
|
/// Maximum heap memory used
|
|
uint32_t max_heap_used;
|
|
#endif //HEAP_MEM_DEBUG
|
|
};
|
|
|
|
/// Heap environment
|
|
struct heap_env_tag heap_env_app;
|
|
|
|
#undef configTOTAL_HEAP_SIZE
|
|
#define configTOTAL_HEAP_SIZE 0x19000
|
|
|
|
#undef configTOTAL_DRAM_SIZE
|
|
#define configTOTAL_DRAM_SIZE 0x5000
|
|
|
|
static uint32_t ucHeap[ (configTOTAL_HEAP_SIZE >> 2)];
|
|
static __attribute__((section("dram_section"))) uint32_t ucHeap_dram[ (configTOTAL_DRAM_SIZE >> 2)];
|
|
|
|
/*
|
|
* LOCAL FUNCTION DEFINITIONS
|
|
****************************************************************************************
|
|
*/
|
|
|
|
static void assert_err(const char *condition, const char * file, int line)
|
|
{
|
|
// printf("%s[%d]: %s\r\n", file, line, condition);
|
|
while(1);
|
|
}
|
|
|
|
static void assert_param(int param0, int param1, const char * file, int line)
|
|
{
|
|
// printf("%s[%d]: 0x%08x, 0x%08x\r\n", file, line, param0, param1);
|
|
while(1);
|
|
}
|
|
|
|
static void assert_warn(int param0, int param1, const char * file, int line)
|
|
{
|
|
// printf("%s[%d]: 0x%08x, 0x%08x\r\n", file, line, param0, param1);
|
|
while(1);
|
|
}
|
|
|
|
/**
|
|
* Check if memory pointer is within heap address range
|
|
*
|
|
* @param[in] type Memory type.
|
|
* @param[in] mem_ptr Memory pointer
|
|
* @return True if it's in memory heap, False else.
|
|
*/
|
|
static bool mem_is_in_heap(uint8_t type, void* mem_ptr)
|
|
{
|
|
bool ret = false;
|
|
uint8_t* block = (uint8_t*)heap_env_app.heap[type];
|
|
uint32_t size = heap_env_app.heap_size[type];
|
|
|
|
if((((uint32_t)mem_ptr) >= ((uint32_t)block))
|
|
&& (((uint32_t)mem_ptr) <= (((uint32_t)block) + size)))
|
|
{
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION DEFINITIONS
|
|
****************************************************************************************
|
|
*/
|
|
void heap_mem_init(uint8_t type, uint8_t* heap, uint32_t heap_size)
|
|
{
|
|
// align first free descriptor to word boundary
|
|
heap_env_app.heap[type] = (struct mblock_free*)CO_ALIGN4_HI((uint32_t)heap);
|
|
|
|
GLOBAL_INT_DISABLE();
|
|
|
|
// initialize the first block
|
|
// + compute the size from the last aligned word before heap_end
|
|
heap_env_app.heap[type]->free_size = ((uint32_t)&heap[heap_size] & (~3)) - (uint32_t)(heap_env_app.heap[type]);
|
|
heap_env_app.heap[type]->corrupt_check = HEAP_LIST_PATTERN;
|
|
heap_env_app.heap[type]->next = NULL;
|
|
heap_env_app.heap[type]->previous = NULL;
|
|
|
|
heap_env_app.heap_size[type] = heap_size;
|
|
|
|
GLOBAL_INT_RESTORE();
|
|
}
|
|
|
|
__RAM_CODE __attribute__((noinline)) void *heap_mem_alloc(uint8_t type, uint32_t size)
|
|
{
|
|
struct mblock_free *node = NULL,*found = NULL;
|
|
uint8_t cursor = 0;
|
|
struct mblock_used *alloc = NULL;
|
|
uint32_t totalsize;
|
|
|
|
#if HEAP_MEM_DEBUG
|
|
volatile uint32_t address;
|
|
__asm("MOV %[result], LR":[result] "=r" (address));
|
|
#endif
|
|
|
|
// compute overall block size (including requested size PLUS descriptor size)
|
|
totalsize = CO_ALIGN4_HI(size) + sizeof(struct mblock_used);
|
|
if(totalsize < sizeof(struct mblock_free))
|
|
{
|
|
totalsize = sizeof(struct mblock_free);
|
|
}
|
|
|
|
// sanity check: the totalsize should be large enough to hold free block descriptor
|
|
ASSERT_ERR(totalsize >= sizeof(struct mblock_free));
|
|
|
|
// protect accesses to descriptors
|
|
GLOBAL_INT_DISABLE();
|
|
|
|
#if HEAP_MEM_DEBUG
|
|
uint32_t totalfreesize = 0;
|
|
#endif //HEAP_MEM_DEBUG
|
|
|
|
// Select Heap to use, first try to use current heap.
|
|
node = heap_env_app.heap[type];
|
|
ASSERT_ERR(node != NULL);
|
|
|
|
// go through free memory blocks list
|
|
while (node != NULL)
|
|
{
|
|
ASSERT_ERR(node->corrupt_check == HEAP_LIST_PATTERN);
|
|
#if HEAP_MEM_DEBUG
|
|
totalfreesize += node->free_size;
|
|
#endif //HEAP_MEM_DEBUG
|
|
|
|
// check if there is enough room in this free block
|
|
if (node->free_size >= (totalsize))
|
|
{
|
|
if ((node->free_size >= (totalsize + sizeof(struct mblock_free)))
|
|
|| (node->previous != NULL))
|
|
{
|
|
// if a match was already found, check if this one is smaller
|
|
if ((found == NULL) || (found->free_size > node->free_size))
|
|
{
|
|
found = node;
|
|
}
|
|
}
|
|
}
|
|
|
|
// move to next block
|
|
node = node->next;
|
|
}
|
|
|
|
// Update size to use complete list if possible.
|
|
if(found != NULL)
|
|
{
|
|
if (found->free_size < (totalsize + sizeof(struct mblock_free)))
|
|
{
|
|
totalsize = found->free_size;
|
|
}
|
|
}
|
|
|
|
#if HEAP_MEM_DEBUG
|
|
heap_env_app.heap_used[type] = heap_env_app.heap_size[type] - totalfreesize;
|
|
if(found != NULL)
|
|
{
|
|
heap_env_app.heap_used[type] += totalsize;
|
|
if (heap_env_app.max_heap_used_single[type] < heap_env_app.heap_used[type])
|
|
{
|
|
heap_env_app.max_heap_used_single[type] = heap_env_app.heap_used[type];
|
|
}
|
|
}
|
|
|
|
{
|
|
// calculate max used size
|
|
uint32_t totalusedsize = 0;
|
|
for(cursor = 0 ; cursor < HEAP_TYPE_BLOCKS ; cursor ++)
|
|
{
|
|
totalusedsize += heap_env_app.heap_used[cursor];
|
|
}
|
|
|
|
if(heap_env_app.max_heap_used < totalusedsize)
|
|
{
|
|
heap_env_app.max_heap_used = totalusedsize;
|
|
}
|
|
}
|
|
#endif //HEAP_MEM_DEBUG
|
|
|
|
// Re-boot platform if no more empty space
|
|
if(found == NULL)
|
|
{
|
|
while(1);
|
|
}
|
|
else
|
|
{
|
|
// sublist completely reused
|
|
if (found->free_size == totalsize)
|
|
{
|
|
ASSERT_ERR(found->previous != NULL);
|
|
|
|
// update double linked list
|
|
found->previous->next = found->next;
|
|
if(found->next != NULL)
|
|
{
|
|
found->next->previous = found->previous;
|
|
}
|
|
|
|
// compute the pointer to the beginning of the free space
|
|
alloc = (struct mblock_used*) ((uint32_t)found);
|
|
}
|
|
else
|
|
{
|
|
// found a free block that matches, subtract the allocation size from the
|
|
// free block size. If equal, the free block will be kept with 0 size... but
|
|
// moving it out of the linked list is too much work.
|
|
found->free_size -= totalsize;
|
|
|
|
// compute the pointer to the beginning of the free space
|
|
alloc = (struct mblock_used*) ((uint32_t)found + found->free_size);
|
|
}
|
|
|
|
// save the size of the allocated block
|
|
alloc->size = totalsize;
|
|
alloc->corrupt_check = HEAP_ALLOCATED_PATTERN;
|
|
|
|
#if HEAP_MEM_DEBUG
|
|
alloc->return_addr = address;
|
|
#endif
|
|
|
|
// move to the user memory space
|
|
alloc++;
|
|
}
|
|
|
|
// end of protection (as early as possible)
|
|
GLOBAL_INT_RESTORE();
|
|
ASSERT_ERR(node == NULL);
|
|
|
|
return (void*)alloc;
|
|
}
|
|
|
|
__RAM_CODE __attribute__((noinline)) void heap_mem_free(void* mem_ptr)
|
|
{
|
|
struct mblock_free *freed;
|
|
struct mblock_used *bfreed;
|
|
struct mblock_free *node, *next_node, *prev_node;
|
|
uint32_t size;
|
|
uint8_t cursor = 0;
|
|
|
|
#if HEAP_MEM_DEBUG
|
|
volatile uint32_t address;
|
|
__asm("MOV %[result], LR":[result] "=r" (address));
|
|
#endif
|
|
|
|
// sanity checks
|
|
ASSERT_INFO(mem_ptr != NULL, mem_ptr, 0);
|
|
|
|
// point to the block descriptor (before user memory so decrement)
|
|
bfreed = ((struct mblock_used *)mem_ptr) - 1;
|
|
|
|
// check if memory block has been corrupted or not
|
|
ASSERT_INFO(bfreed->corrupt_check == HEAP_ALLOCATED_PATTERN, bfreed->corrupt_check, mem_ptr);
|
|
// change corruption token in order to know if buffer has been already freed.
|
|
bfreed->corrupt_check = HEAP_FREE_PATTERN;
|
|
|
|
// point to the first node of the free elements linked list
|
|
size = bfreed->size;
|
|
node = NULL;
|
|
|
|
freed = ((struct mblock_free *)bfreed);
|
|
#if HEAP_MEM_DEBUG
|
|
freed->return_addr = address;
|
|
#endif
|
|
|
|
// protect accesses to descriptors
|
|
GLOBAL_INT_DISABLE();
|
|
|
|
// Retrieve where memory block comes from
|
|
while(((cursor < HEAP_TYPE_BLOCKS)) && (node == NULL))
|
|
{
|
|
if(mem_is_in_heap(cursor, mem_ptr))
|
|
{
|
|
// Select Heap to use, first try to use current heap.
|
|
node = heap_env_app.heap[cursor];
|
|
}
|
|
else
|
|
{
|
|
cursor ++;
|
|
}
|
|
}
|
|
|
|
// sanity checks
|
|
ASSERT_ERR(node != NULL);
|
|
ASSERT_ERR(((uint32_t)mem_ptr > (uint32_t)node));
|
|
|
|
prev_node = NULL;
|
|
|
|
while(node != NULL)
|
|
{
|
|
ASSERT_ERR(node->corrupt_check == HEAP_LIST_PATTERN);
|
|
// check if the freed block is right after the current block
|
|
if ((uint32_t)freed == ((uint32_t)node + node->free_size))
|
|
{
|
|
// append the freed block to the current one
|
|
node->free_size += size;
|
|
|
|
// check if this merge made the link between free blocks
|
|
if (((uint32_t) node->next) == (((uint32_t)node) + node->free_size))
|
|
{
|
|
next_node = node->next;
|
|
// add the size of the next node to the current node
|
|
node->free_size += next_node->free_size;
|
|
// update the next of the current node
|
|
ASSERT_ERR(next_node != NULL);
|
|
node->next = next_node->next;
|
|
// update linked list.
|
|
if(next_node->next != NULL)
|
|
{
|
|
next_node->next->previous = node;
|
|
}
|
|
}
|
|
goto free_end;
|
|
}
|
|
else if ((uint32_t)freed < (uint32_t)node)
|
|
{
|
|
// sanity check: can not happen before first node
|
|
ASSERT_ERR(prev_node != NULL);
|
|
|
|
// update the next pointer of the previous node
|
|
prev_node->next = freed;
|
|
freed->previous = prev_node;
|
|
|
|
freed->corrupt_check = HEAP_LIST_PATTERN;
|
|
|
|
// check if the released node is right before the free block
|
|
if (((uint32_t)freed + size) == (uint32_t)node)
|
|
{
|
|
// merge the two nodes
|
|
freed->next = node->next;
|
|
if(node->next != NULL)
|
|
{
|
|
node->next->previous = freed;
|
|
}
|
|
freed->free_size = node->free_size + size;
|
|
}
|
|
else
|
|
{
|
|
// insert the new node
|
|
freed->next = node;
|
|
node->previous = freed;
|
|
freed->free_size = size;
|
|
}
|
|
goto free_end;
|
|
}
|
|
|
|
// move to the next free block node
|
|
prev_node = node;
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
freed->corrupt_check = HEAP_LIST_PATTERN;
|
|
|
|
// if reached here, freed block is after last free block and not contiguous
|
|
prev_node->next = (struct mblock_free*)freed;
|
|
freed->next = NULL;
|
|
freed->previous = prev_node;
|
|
freed->free_size = size;
|
|
freed->corrupt_check = HEAP_LIST_PATTERN;
|
|
|
|
free_end:
|
|
|
|
#if HEAP_MEM_DEBUG
|
|
heap_env_app.heap_used[cursor] -= size;
|
|
#endif //HEAP_MEM_DEBUG
|
|
|
|
// end of protection
|
|
GLOBAL_INT_RESTORE();
|
|
}
|
|
|
|
#if (HEAP_MEM_DEBUG)
|
|
uint32_t heap_get_mem_usage(uint8_t type)
|
|
{
|
|
ASSERT_ERR(type < HEAP_TYPE_BLOCKS);
|
|
|
|
return heap_env_app.heap_used[type];
|
|
}
|
|
|
|
uint32_t heap_get_mem_usage_single(uint8_t type)
|
|
{
|
|
ASSERT_ERR(type < HEAP_TYPE_BLOCKS);
|
|
|
|
return heap_env_app.max_heap_used_single[type];
|
|
}
|
|
|
|
uint32_t heap_get_max_mem_usage(void)
|
|
{
|
|
uint32_t ret = heap_env_app.max_heap_used;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void heap_dump_used_mem(uint8_t type)
|
|
{
|
|
struct mblock_free *node;
|
|
uint32_t heap_size;
|
|
|
|
#define HEAP_RSV_TAIL_SIZE 0x10
|
|
|
|
if (type >= HEAP_TYPE_BLOCKS) {
|
|
return;
|
|
}
|
|
|
|
node = heap_env_app.heap[type];
|
|
heap_size = heap_env_app.heap_size[type];
|
|
GLOBAL_INT_DISABLE();
|
|
while (heap_size > HEAP_RSV_TAIL_SIZE) {
|
|
if (node->corrupt_check == HEAP_ALLOCATED_PATTERN) {
|
|
struct mblock_used *used_node = (void *)node;
|
|
printf("LR: 0x%08x, SIZE: 0x%08x, ADDR: 0x%08x.\r\n", used_node->return_addr, used_node->size, (uint32_t)used_node);
|
|
heap_size -= used_node->size;
|
|
node = (void *)((uint32_t)node + used_node->size);
|
|
}
|
|
else {
|
|
heap_size -= node->free_size;
|
|
node = (void *)((uint32_t)node + node->free_size);
|
|
}
|
|
}
|
|
GLOBAL_INT_RESTORE();
|
|
}
|
|
#endif // (HEAP_MEM_DEBUG)
|
|
|