|
|
|
|
@@ -22,15 +22,50 @@
|
|
|
|
|
* https://www.FreeRTOS.org
|
|
|
|
|
* https://github.com/FreeRTOS
|
|
|
|
|
*
|
|
|
|
|
* 1 tab == 4 spaces!
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* A sample implementation of pvPortMalloc() and vPortFree() that combines
|
|
|
|
|
* (coalescences) adjacent memory blocks as they are freed, and in so doing
|
|
|
|
|
* limits memory fragmentation.
|
|
|
|
|
* A sample implementation of pvPortMalloc() that allows the heap to be defined
|
|
|
|
|
* across multiple non-contigous blocks and combines (coalescences) adjacent
|
|
|
|
|
* memory blocks as they are freed.
|
|
|
|
|
*
|
|
|
|
|
* See heap_1.c, heap_2.c, heap_3.c and heap_4.c for alternative
|
|
|
|
|
* implementations, and the memory management pages of https://www.FreeRTOS.org
|
|
|
|
|
* for more information.
|
|
|
|
|
*
|
|
|
|
|
* Usage notes:
|
|
|
|
|
*
|
|
|
|
|
* vPortDefineHeapRegions() ***must*** be called before pvPortMalloc().
|
|
|
|
|
* pvPortMalloc() will be called if any task objects (tasks, queues, event
|
|
|
|
|
* groups, etc.) are created, therefore vPortDefineHeapRegions() ***must*** be
|
|
|
|
|
* called before any other objects are defined.
|
|
|
|
|
*
|
|
|
|
|
* vPortDefineHeapRegions() takes a single parameter. The parameter is an array
|
|
|
|
|
* of HeapRegion_t structures. HeapRegion_t is defined in portable.h as
|
|
|
|
|
*
|
|
|
|
|
* typedef struct HeapRegion
|
|
|
|
|
* {
|
|
|
|
|
* uint8_t *pucStartAddress; << Start address of a block of memory that will be part of the heap.
|
|
|
|
|
* size_t xSizeInBytes; << Size of the block of memory.
|
|
|
|
|
* } HeapRegion_t;
|
|
|
|
|
*
|
|
|
|
|
* The array is terminated using a NULL zero sized region definition, and the
|
|
|
|
|
* memory regions defined in the array ***must*** appear in address order from
|
|
|
|
|
* low address to high address. So the following is a valid example of how
|
|
|
|
|
* to use the function.
|
|
|
|
|
*
|
|
|
|
|
* HeapRegion_t xHeapRegions[] =
|
|
|
|
|
* {
|
|
|
|
|
* { ( uint8_t * ) 0x80000000UL, 0x10000 }, << Defines a block of 0x10000 bytes starting at address 0x80000000
|
|
|
|
|
* { ( uint8_t * ) 0x90000000UL, 0xa0000 }, << Defines a block of 0xa0000 bytes starting at address of 0x90000000
|
|
|
|
|
* { NULL, 0 } << Terminates the array.
|
|
|
|
|
* };
|
|
|
|
|
*
|
|
|
|
|
* vPortDefineHeapRegions( xHeapRegions ); << Pass the array into vPortDefineHeapRegions().
|
|
|
|
|
*
|
|
|
|
|
* Note 0x80000000 is the lower address so appears in the array first.
|
|
|
|
|
*
|
|
|
|
|
* See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the
|
|
|
|
|
* memory management pages of https://www.FreeRTOS.org for more information.
|
|
|
|
|
*/
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
@@ -54,16 +89,6 @@
|
|
|
|
|
/* Assumes 8bit bytes! */
|
|
|
|
|
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
|
|
|
|
|
|
|
|
|
|
/* Allocate the memory for the heap. */
|
|
|
|
|
#if ( configAPPLICATION_ALLOCATED_HEAP == 1 )
|
|
|
|
|
|
|
|
|
|
/* The application writer has already defined the array used for the RTOS
|
|
|
|
|
* heap - probably so it can be placed in a special segment or address. */
|
|
|
|
|
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
|
|
|
|
|
#else
|
|
|
|
|
PRIVILEGED_DATA static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
|
|
|
|
|
#endif /* configAPPLICATION_ALLOCATED_HEAP */
|
|
|
|
|
|
|
|
|
|
/* Define the linked list structure. This is used to link free blocks in order
|
|
|
|
|
* of their memory address. */
|
|
|
|
|
typedef struct A_BLOCK_LINK
|
|
|
|
|
@@ -80,13 +105,7 @@ typedef struct A_BLOCK_LINK
|
|
|
|
|
* the block in front it and/or the block behind it if the memory blocks are
|
|
|
|
|
* adjacent to each other.
|
|
|
|
|
*/
|
|
|
|
|
static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) PRIVILEGED_FUNCTION;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Called automatically to setup the required heap structures the first time
|
|
|
|
|
* pvPortMalloc() is called.
|
|
|
|
|
*/
|
|
|
|
|
static void prvHeapInit( void ) PRIVILEGED_FUNCTION;
|
|
|
|
|
static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert );
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
@@ -95,20 +114,20 @@ static void prvHeapInit( void ) PRIVILEGED_FUNCTION;
|
|
|
|
|
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
|
|
|
|
|
|
|
|
|
|
/* Create a couple of list links to mark the start and end of the list. */
|
|
|
|
|
PRIVILEGED_DATA static BlockLink_t xStart, * pxEnd = NULL;
|
|
|
|
|
static BlockLink_t xStart, * pxEnd = NULL;
|
|
|
|
|
|
|
|
|
|
/* Keeps track of the number of calls to allocate and free memory as well as the
|
|
|
|
|
* number of free bytes remaining, but says nothing about fragmentation. */
|
|
|
|
|
PRIVILEGED_DATA static size_t xFreeBytesRemaining = 0U;
|
|
|
|
|
PRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = 0U;
|
|
|
|
|
PRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = 0;
|
|
|
|
|
PRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = 0;
|
|
|
|
|
static size_t xFreeBytesRemaining = 0U;
|
|
|
|
|
static size_t xMinimumEverFreeBytesRemaining = 0U;
|
|
|
|
|
static size_t xNumberOfSuccessfulAllocations = 0;
|
|
|
|
|
static size_t xNumberOfSuccessfulFrees = 0;
|
|
|
|
|
|
|
|
|
|
/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize
|
|
|
|
|
* member of an BlockLink_t structure is set then the block belongs to the
|
|
|
|
|
* application. When the bit is free the block is still part of the free heap
|
|
|
|
|
* space. */
|
|
|
|
|
PRIVILEGED_DATA static size_t xBlockAllocatedBit = 0;
|
|
|
|
|
static size_t xBlockAllocatedBit = 0;
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
@@ -117,19 +136,12 @@ void * pvPortMalloc( size_t xWantedSize )
|
|
|
|
|
BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink;
|
|
|
|
|
void * pvReturn = NULL;
|
|
|
|
|
|
|
|
|
|
/* The heap must be initialised before the first call to
|
|
|
|
|
* prvPortMalloc(). */
|
|
|
|
|
configASSERT( pxEnd );
|
|
|
|
|
|
|
|
|
|
vTaskSuspendAll();
|
|
|
|
|
{
|
|
|
|
|
/* If this is the first call to malloc then the heap will require
|
|
|
|
|
* initialisation to setup the list of free blocks. */
|
|
|
|
|
if( pxEnd == NULL )
|
|
|
|
|
{
|
|
|
|
|
prvHeapInit();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mtCOVERAGE_TEST_MARKER();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check the requested block size is not so large that the top bit is
|
|
|
|
|
* set. The top bit of the block size member of the BlockLink_t structure
|
|
|
|
|
* is used to determine who owns the block - the application or the
|
|
|
|
|
@@ -148,7 +160,6 @@ void * pvPortMalloc( size_t xWantedSize )
|
|
|
|
|
{
|
|
|
|
|
/* Byte alignment required. */
|
|
|
|
|
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
|
|
|
|
|
configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
@@ -194,7 +205,6 @@ void * pvPortMalloc( size_t xWantedSize )
|
|
|
|
|
* cast is used to prevent byte alignment warnings from the
|
|
|
|
|
* compiler. */
|
|
|
|
|
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
|
|
|
|
|
configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );
|
|
|
|
|
|
|
|
|
|
/* Calculate the sizes of two blocks split from the
|
|
|
|
|
* single block. */
|
|
|
|
|
@@ -202,7 +212,7 @@ void * pvPortMalloc( size_t xWantedSize )
|
|
|
|
|
pxBlock->xBlockSize = xWantedSize;
|
|
|
|
|
|
|
|
|
|
/* Insert the new block into the list of free blocks. */
|
|
|
|
|
prvInsertBlockIntoFreeList( pxNewBlockLink );
|
|
|
|
|
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
@@ -259,7 +269,6 @@ void * pvPortMalloc( size_t xWantedSize )
|
|
|
|
|
}
|
|
|
|
|
#endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */
|
|
|
|
|
|
|
|
|
|
configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
|
|
|
|
|
return pvReturn;
|
|
|
|
|
}
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
@@ -325,61 +334,7 @@ size_t xPortGetMinimumEverFreeHeapSize( void )
|
|
|
|
|
}
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
void vPortInitialiseBlocks( void )
|
|
|
|
|
{
|
|
|
|
|
/* This just exists to keep the linker quiet. */
|
|
|
|
|
}
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
|
|
|
|
|
{
|
|
|
|
|
BlockLink_t * pxFirstFreeBlock;
|
|
|
|
|
uint8_t * pucAlignedHeap;
|
|
|
|
|
size_t uxAddress;
|
|
|
|
|
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;
|
|
|
|
|
|
|
|
|
|
/* Ensure the heap starts on a correctly aligned boundary. */
|
|
|
|
|
uxAddress = ( size_t ) ucHeap;
|
|
|
|
|
|
|
|
|
|
if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
|
|
|
|
|
{
|
|
|
|
|
uxAddress += ( portBYTE_ALIGNMENT - 1 );
|
|
|
|
|
uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
|
|
|
|
|
xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pucAlignedHeap = ( uint8_t * ) uxAddress;
|
|
|
|
|
|
|
|
|
|
/* xStart is used to hold a pointer to the first item in the list of free
|
|
|
|
|
* blocks. The void cast is used to prevent compiler warnings. */
|
|
|
|
|
xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
|
|
|
|
|
xStart.xBlockSize = ( size_t ) 0;
|
|
|
|
|
|
|
|
|
|
/* pxEnd is used to mark the end of the list of free blocks and is inserted
|
|
|
|
|
* at the end of the heap space. */
|
|
|
|
|
uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
|
|
|
|
|
uxAddress -= xHeapStructSize;
|
|
|
|
|
uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
|
|
|
|
|
pxEnd = ( void * ) uxAddress;
|
|
|
|
|
pxEnd->xBlockSize = 0;
|
|
|
|
|
pxEnd->pxNextFreeBlock = NULL;
|
|
|
|
|
|
|
|
|
|
/* To start with there is a single free block that is sized to take up the
|
|
|
|
|
* entire heap space, minus the space taken by pxEnd. */
|
|
|
|
|
pxFirstFreeBlock = ( void * ) pucAlignedHeap;
|
|
|
|
|
pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
|
|
|
|
|
pxFirstFreeBlock->pxNextFreeBlock = pxEnd;
|
|
|
|
|
|
|
|
|
|
/* Only one block exists - and it covers the entire usable heap space. */
|
|
|
|
|
xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
|
|
|
|
|
xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
|
|
|
|
|
|
|
|
|
|
/* Work out the position of the top bit in a size_t variable. */
|
|
|
|
|
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
|
|
|
|
|
}
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVILEGED_FUNCTION */
|
|
|
|
|
static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert )
|
|
|
|
|
{
|
|
|
|
|
BlockLink_t * pxIterator;
|
|
|
|
|
uint8_t * puc;
|
|
|
|
|
@@ -442,6 +397,101 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVI
|
|
|
|
|
}
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
|
|
|
|
|
{
|
|
|
|
|
BlockLink_t * pxFirstFreeBlockInRegion = NULL, * pxPreviousFreeBlock;
|
|
|
|
|
size_t xAlignedHeap;
|
|
|
|
|
size_t xTotalRegionSize, xTotalHeapSize = 0;
|
|
|
|
|
BaseType_t xDefinedRegions = 0;
|
|
|
|
|
size_t xAddress;
|
|
|
|
|
const HeapRegion_t * pxHeapRegion;
|
|
|
|
|
|
|
|
|
|
/* Can only call once! */
|
|
|
|
|
configASSERT( pxEnd == NULL );
|
|
|
|
|
|
|
|
|
|
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
|
|
|
|
|
|
|
|
|
|
while( pxHeapRegion->xSizeInBytes > 0 )
|
|
|
|
|
{
|
|
|
|
|
xTotalRegionSize = pxHeapRegion->xSizeInBytes;
|
|
|
|
|
|
|
|
|
|
/* Ensure the heap region starts on a correctly aligned boundary. */
|
|
|
|
|
xAddress = ( size_t ) pxHeapRegion->pucStartAddress;
|
|
|
|
|
|
|
|
|
|
if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
|
|
|
|
|
{
|
|
|
|
|
xAddress += ( portBYTE_ALIGNMENT - 1 );
|
|
|
|
|
xAddress &= ~portBYTE_ALIGNMENT_MASK;
|
|
|
|
|
|
|
|
|
|
/* Adjust the size for the bytes lost to alignment. */
|
|
|
|
|
xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xAlignedHeap = xAddress;
|
|
|
|
|
|
|
|
|
|
/* Set xStart if it has not already been set. */
|
|
|
|
|
if( xDefinedRegions == 0 )
|
|
|
|
|
{
|
|
|
|
|
/* xStart is used to hold a pointer to the first item in the list of
|
|
|
|
|
* free blocks. The void cast is used to prevent compiler warnings. */
|
|
|
|
|
xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap;
|
|
|
|
|
xStart.xBlockSize = ( size_t ) 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Should only get here if one region has already been added to the
|
|
|
|
|
* heap. */
|
|
|
|
|
configASSERT( pxEnd != NULL );
|
|
|
|
|
|
|
|
|
|
/* Check blocks are passed in with increasing start addresses. */
|
|
|
|
|
configASSERT( xAddress > ( size_t ) pxEnd );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remember the location of the end marker in the previous region, if
|
|
|
|
|
* any. */
|
|
|
|
|
pxPreviousFreeBlock = pxEnd;
|
|
|
|
|
|
|
|
|
|
/* pxEnd is used to mark the end of the list of free blocks and is
|
|
|
|
|
* inserted at the end of the region space. */
|
|
|
|
|
xAddress = xAlignedHeap + xTotalRegionSize;
|
|
|
|
|
xAddress -= xHeapStructSize;
|
|
|
|
|
xAddress &= ~portBYTE_ALIGNMENT_MASK;
|
|
|
|
|
pxEnd = ( BlockLink_t * ) xAddress;
|
|
|
|
|
pxEnd->xBlockSize = 0;
|
|
|
|
|
pxEnd->pxNextFreeBlock = NULL;
|
|
|
|
|
|
|
|
|
|
/* To start with there is a single free block in this region that is
|
|
|
|
|
* sized to take up the entire heap region minus the space taken by the
|
|
|
|
|
* free block structure. */
|
|
|
|
|
pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap;
|
|
|
|
|
pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion;
|
|
|
|
|
pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
|
|
|
|
|
|
|
|
|
|
/* If this is not the first region that makes up the entire heap space
|
|
|
|
|
* then link the previous region to this region. */
|
|
|
|
|
if( pxPreviousFreeBlock != NULL )
|
|
|
|
|
{
|
|
|
|
|
pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;
|
|
|
|
|
|
|
|
|
|
/* Move onto the next HeapRegion_t structure. */
|
|
|
|
|
xDefinedRegions++;
|
|
|
|
|
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xMinimumEverFreeBytesRemaining = xTotalHeapSize;
|
|
|
|
|
xFreeBytesRemaining = xTotalHeapSize;
|
|
|
|
|
|
|
|
|
|
/* Check something was actually defined before it is accessed. */
|
|
|
|
|
configASSERT( xTotalHeapSize );
|
|
|
|
|
|
|
|
|
|
/* Work out the position of the top bit in a size_t variable. */
|
|
|
|
|
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
|
|
|
|
|
}
|
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
void vPortGetHeapStats( HeapStats_t * pxHeapStats )
|
|
|
|
|
{
|
|
|
|
|
BlockLink_t * pxBlock;
|
|
|
|
|
@@ -466,9 +516,15 @@ void vPortGetHeapStats( HeapStats_t * pxHeapStats )
|
|
|
|
|
xMaxSize = pxBlock->xBlockSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( pxBlock->xBlockSize < xMinSize )
|
|
|
|
|
/* Heap five will have a zero sized block at the end of each
|
|
|
|
|
* each region - the block is only used to link to the next
|
|
|
|
|
* heap region so it not a real block. */
|
|
|
|
|
if( pxBlock->xBlockSize != 0 )
|
|
|
|
|
{
|
|
|
|
|
xMinSize = pxBlock->xBlockSize;
|
|
|
|
|
if( pxBlock->xBlockSize < xMinSize )
|
|
|
|
|
{
|
|
|
|
|
xMinSize = pxBlock->xBlockSize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move to the next block in the chain until the last block is
|