diff --git a/components/esp32/heap_alloc_caps.c b/components/esp32/heap_alloc_caps.c index 04e2dc8c83..7bb3b40879 100644 --- a/components/esp32/heap_alloc_caps.c +++ b/components/esp32/heap_alloc_caps.c @@ -18,6 +18,7 @@ #include "esp_heap_alloc_caps.h" #include "spiram.h" #include "esp_log.h" +#include static const char* TAG = "heap_alloc_caps"; @@ -35,28 +36,35 @@ hardwiring addresses. //Amount of priority slots for the tag descriptors. #define NO_PRIOS 3 +typedef struct { + const char *name; + uint32_t prio[NO_PRIOS]; + bool aliasedIram; +} tag_desc_t; + /* Tag descriptors. These describe the capabilities of a bit of memory that's tagged with the index into this table. Each tag contains NO_PRIOS entries; later entries are only taken if earlier ones can't fulfill the memory request. +Make sure there are never more than HEAPREGIONS_MAX_TAGCOUNT (in heap_regions.h) tags (ex the last empty marker) */ -static const uint32_t tagDesc[][NO_PRIOS]={ - { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }, //Tag 0: Plain ole D-port RAM - { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, //Tag 1: Plain ole D-port RAM which has an alias on the I-port - { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT, 0, 0 }, //Tag 2: IRAM - { MALLOC_CAP_PID2, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, //Tag 3-8: PID 2-7 IRAM - { MALLOC_CAP_PID3, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, // - { MALLOC_CAP_PID4, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, // - { MALLOC_CAP_PID5, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, // - { MALLOC_CAP_PID6, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, // - { MALLOC_CAP_PID7, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, // - { MALLOC_CAP_PID2, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, //Tag 9-14: PID 2-7 DRAM - { MALLOC_CAP_PID3, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, // - { MALLOC_CAP_PID4, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, // - { MALLOC_CAP_PID5, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, // - { MALLOC_CAP_PID6, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, // - { MALLOC_CAP_PID7, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, // - { MALLOC_CAP_SPISRAM, 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, //Tag 15: SPI SRAM data - { MALLOC_CAP_INVALID, MALLOC_CAP_INVALID, MALLOC_CAP_INVALID } //End +static const tag_desc_t tag_desc[]={ + { "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }, false}, //Tag 0: Plain ole D-port RAM + { "D/IRAM", { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, true}, //Tag 1: Plain ole D-port RAM which has an alias on the I-port + { "IRAM", { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT, 0, 0 }, false}, //Tag 2: IRAM + { "PID2IRAM", { MALLOC_CAP_PID2, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false}, //Tag 3-8: PID 2-7 IRAM + { "PID3IRAM", { MALLOC_CAP_PID3, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false}, // + { "PID4IRAM", { MALLOC_CAP_PID4, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false}, // + { "PID5IRAM", { MALLOC_CAP_PID5, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false}, // + { "PID6IRAM", { MALLOC_CAP_PID6, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false}, // + { "PID7IRAM", { MALLOC_CAP_PID7, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false}, // + { "PID2DRAM", { MALLOC_CAP_PID2, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false}, //Tag 9-14: PID 2-7 DRAM + { "PID3DRAM", { MALLOC_CAP_PID3, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false}, // + { "PID4DRAM", { MALLOC_CAP_PID4, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false}, // + { "PID5DRAM", { MALLOC_CAP_PID5, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false}, // + { "PID6DRAM", { MALLOC_CAP_PID6, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false}, // + { "PID7DRAM", { MALLOC_CAP_PID7, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false}, // + { "SPISRAM", { MALLOC_CAP_SPISRAM, 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, false}, //Tag 15: SPI SRAM data + { "", { MALLOC_CAP_INVALID, MALLOC_CAP_INVALID, MALLOC_CAP_INVALID }, false} //End }; /* @@ -158,10 +166,11 @@ static void disable_mem_region(void *from, void *to) { /* -ToDo: These are very dependent on the linker script, and the logic involving this works only -because we're not using the SPI flash yet! If we enable that, this will break. ToDo: Rewrite by then. +Warning: These variables are assumed to have the start and end of the data and iram +area used statically by the program, respectively. These variables are defined in the ld +file. */ -extern int _bss_start, _heap_start; +extern int _bss_start, _heap_start, _init_start, _iram_text_end; /* Initialize the heap allocator. We pass it a bunch of region descriptors, but we need to modify those first to accommodate for @@ -171,12 +180,14 @@ Same with loading of apps. Same with using SPI RAM. */ void heap_alloc_caps_init() { int i; + //Compile-time assert to see if we don't have more tags than is set in heap_regions.h + _Static_assert((sizeof(tag_desc)/sizeof(tag_desc[0]))-1 <= HEAPREGIONS_MAX_TAGCOUNT, "More than HEAPREGIONS_MAX_TAGCOUNT tags defined!"); //Disable the bits of memory where this code is loaded. - disable_mem_region(&_bss_start, &_heap_start); + disable_mem_region(&_bss_start, &_heap_start); //DRAM used by bss/data static variables + disable_mem_region(&_init_start, &_iram_text_end); //IRAM used by code disable_mem_region((void*)0x3ffae000, (void*)0x3ffb0000); //knock out ROM data region disable_mem_region((void*)0x40070000, (void*)0x40078000); //CPU0 cache region disable_mem_region((void*)0x40078000, (void*)0x40080000); //CPU1 cache region - disable_mem_region((void*)0x40080000, (void*)0x400a0000); //pool 2-5 // TODO: this region should be checked, since we don't need to knock out all region finally disable_mem_region((void*)0x3ffe0000, (void*)0x3ffe8000); //knock out ROM data region @@ -211,25 +222,72 @@ void heap_alloc_caps_init() { } } - ESP_EARLY_LOGI(TAG, "Initializing heap allocator:"); + ESP_EARLY_LOGI(TAG, "Initializing. RAM available for dynamic allocation:"); for (i=0; regions[i].xSizeInBytes!=0; i++) { if (regions[i].xTag != -1) { - ESP_EARLY_LOGI(TAG, "Region %02d: %08X len %08X tag %d", i, - (int)regions[i].pucStartAddress, regions[i].xSizeInBytes, regions[i].xTag); + ESP_EARLY_LOGI(TAG, "At %08X len %08X (%d KiB): %s", + (int)regions[i].pucStartAddress, regions[i].xSizeInBytes, regions[i].xSizeInBytes/1024, tag_desc[regions[i].xTag].name); } } //Initialize the malloc implementation. vPortDefineHeapRegionsTagged( regions ); } +//First and last words of the D/IRAM region, for both the DRAM address as well as the IRAM alias. +#define DIRAM_IRAM_START 0x400A0000 +#define DIRAM_IRAM_END 0x400BFFFC +#define DIRAM_DRAM_START 0x3FFE0000 +#define DIRAM_DRAM_END 0x3FFFFFFC + /* -Standard malloc() implementation. Will return ho-hum byte-accessible data memory. + This takes a memory chunk in a region that can be addressed as both DRAM as well as IRAM. It will convert it to + IRAM in such a way that it can be later freed. It assumes both the address as wel as the length to be word-aligned. + It returns a region that's 1 word smaller than the region given because it stores the original Dram address there. + + In theory, we can also make this work by prepending a struct that looks similar to the block link struct used by the + heap allocator itself, which will allow inspection tools relying on any block returned from any sort of malloc to + have such a block in front of it, work. We may do this later, if/when there is demand for it. For now, a simple + pointer is used. +*/ +static void *dram_alloc_to_iram_addr(void *addr, size_t len) +{ + uint32_t dstart=(int)addr; //First word + uint32_t dend=((int)addr)+len-4; //Last word + configASSERT(dstart>=DIRAM_DRAM_START); + configASSERT(dend<=DIRAM_DRAM_END); + configASSERT((dstart&3)==0); + configASSERT((dend&3)==0); + uint32_t istart=DIRAM_IRAM_START+(DIRAM_DRAM_END-dend); + uint32_t *iptr=(uint32_t*)istart; + *iptr=dstart; + return (void*)(iptr+1); +} + +/* +Standard malloc() implementation. Will return standard no-frills byte-accessible data memory. */ void *pvPortMalloc( size_t xWantedSize ) { return pvPortMallocCaps( xWantedSize, MALLOC_CAP_8BIT ); } +/* + Standard free() implementation. Will pass memory on to the allocator unless it's an IRAM address where the + actual meory is allocated in DRAM, it will convert to the DRAM address then. + */ +void vPortFree( void *pv ) +{ + if (((int)pv>=DIRAM_IRAM_START) && ((int)pv<=DIRAM_IRAM_END)) { + //Memory allocated here is actually allocated in the DRAM alias region and + //cannot be de-allocated as usual. dram_alloc_to_iram_addr stores a pointer to + //the equivalent DRAM address, though; free that. + uint32_t* dramAddrPtr=(uint32_t*)pv; + return vPortFreeTagged((void*)dramAddrPtr[-1]); + } + + return vPortFreeTagged(pv); +} + /* Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits. */ @@ -239,22 +297,42 @@ void *pvPortMallocCaps( size_t xWantedSize, uint32_t caps ) int tag, j; void *ret=NULL; uint32_t remCaps; + if (caps & MALLOC_CAP_EXEC) { + //MALLOC_CAP_EXEC forces an alloc from IRAM. There is a region which has both this + //as well as the following caps, but the following caps are not possible for IRAM. + //Thus, the combination is impossible and we return NULL directly, even although our tag_desc + //table would indicate there is a tag for this. + if ((caps & MALLOC_CAP_8BIT) || (caps & MALLOC_CAP_DMA)) { + return NULL; + } + //If any, EXEC memory should be 32-bit aligned, so round up to the next multiple of 4. + xWantedSize=(xWantedSize+3)&(~3); + } for (prio=0; prio +#include +#include "unity.h" +#include "rom/ets_sys.h" +#include "esp_heap_alloc_caps.h" +#include + + +TEST_CASE("Capabilities allocator test", "[esp32]") +{ + char *m1, *m2[10]; + int x; + size_t free8start, free32start, free8, free32; + free8start=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT); + free32start=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT); + printf("Free 8bit-capable memory: %dK, 32-bit capable memory %dK\n", free8start, free32start); + TEST_ASSERT(free32start>free8start); + printf("Allocating 10K of 8-bit capable RAM\n"); + m1=pvPortMallocCaps(10*1024, MALLOC_CAP_8BIT); + printf("--> %p\n", m1); + free8=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT); + free32=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT); + printf("Free 8bit-capable memory: %dK, 32-bit capable memory %dK\n", free8, free32); + //Both should have gone down by 10K; 8bit capable ram is also 32-bit capable + TEST_ASSERT(free8<(free8start-10*1024)); + TEST_ASSERT(free32<(free32start-10*1024)); + //Assume we got DRAM back + TEST_ASSERT((((int)m1)&0xFF000000)==0x3F000000); + free(m1); + printf("Freeing; allocating 10K of 32K-capable RAM\n"); + m1=pvPortMallocCaps(10*1024, MALLOC_CAP_32BIT); + printf("--> %p\n", m1); + free8=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT); + free32=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT); + printf("Free 8bit-capable memory: %dK, 32-bit capable memory %dK\n", free8, free32); + //Only 32-bit should have gone down by 10K: 32-bit isn't necessarily 8bit capable + TEST_ASSERT(free32<(free32start-10*1024)); + TEST_ASSERT(free8==free8start); + //Assume we got IRAM back + TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000); + free(m1); + printf("Allocating impossible caps\n"); + m1=pvPortMallocCaps(10*1024, MALLOC_CAP_8BIT|MALLOC_CAP_EXEC); + printf("--> %p\n", m1); + TEST_ASSERT(m1==NULL); + printf("Testing changeover iram -> dram"); + for (x=0; x<10; x++) { + m2[x]=pvPortMallocCaps(10*1024, MALLOC_CAP_32BIT); + printf("--> %p\n", m2[x]); + } + TEST_ASSERT((((int)m2[0])&0xFF000000)==0x40000000); + TEST_ASSERT((((int)m2[9])&0xFF000000)==0x3F000000); + printf("Test if allocating executable code still gives IRAM, even with dedicated IRAM region depleted\n"); + m1=pvPortMallocCaps(10*1024, MALLOC_CAP_EXEC); + printf("--> %p\n", m1); + TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000); + free(m1); + for (x=0; x<10; x++) free(m2[x]); + printf("Done.\n"); +} diff --git a/components/freertos/heap_regions.c b/components/freertos/heap_regions.c index 1abcdf3c6f..a7c9606036 100644 --- a/components/freertos/heap_regions.c +++ b/components/freertos/heap_regions.c @@ -10,12 +10,12 @@ the terms of the GNU General Public License (version 2) as published by the Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. - *************************************************************************** + *************************************************************************** >>! NOTE: The modification to the GPL is included to allow you to !<< >>! distribute a combined work that includes FreeRTOS without being !<< >>! obliged to provide the source code for proprietary components !<< >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** + *************************************************************************** FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS @@ -37,17 +37,17 @@ *************************************************************************** http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? + the FAQ page "My application does not run, what could be wrong?". Have you + defined configASSERT()? - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. + http://www.FreeRTOS.org/support - In return for receiving this top quality + embedded software for free we request you assist our global community by + participating in the support forum. - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. + http://www.FreeRTOS.org/training - Investing in training allows your team to + be as productive as possible as early as possible. Now you can receive + FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers + Ltd, and the world's leading authority on the world's leading RTOS. http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, including FreeRTOS+Trace - an indispensable productivity tool, a DOS @@ -85,9 +85,9 @@ * * 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. - * BaseType_t xTag; << Tag + * 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. + * BaseType_t xTag; << Tag * } HeapRegionTagged_t; * * 'Tag' allows you to allocate memory of a certain type. Tag -1 is special; @@ -101,9 +101,9 @@ * * HeapRegionTagged_t xHeapRegions[] = * { - * { ( uint8_t * ) 0x80000000UL, 0x10000, 1 }, << Defines a block of 0x10000 bytes starting at address 0x80000000, tag 1 - * { ( uint8_t * ) 0x90000000UL, 0xa0000, 2 }, << Defines a block of 0xa0000 bytes starting at address of 0x90000000, tag 2 - * { NULL, 0, 0 } << Terminates the array. + * { ( uint8_t * ) 0x80000000UL, 0x10000, 1 }, << Defines a block of 0x10000 bytes starting at address 0x80000000, tag 1 + * { ( uint8_t * ) 0x90000000UL, 0xa0000, 2 }, << Defines a block of 0xa0000 bytes starting at address of 0x90000000, tag 2 + * { NULL, 0, 0 } << Terminates the array. * }; * * vPortDefineHeapRegions( xHeapRegions ); << Pass the array into vPortDefineHeapRegions(). @@ -141,18 +141,21 @@ task.h is included from an application file. */ #include "rom/ets_sys.h" /* Block sizes must not get too small. */ -#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( uxHeapStructSize << 1 ) ) +#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( uxHeapStructSize << 1 ) ) /* Assumes 8bit bytes! */ -#define heapBITS_PER_BYTE ( ( size_t ) 8 ) +#define heapBITS_PER_BYTE ( ( size_t ) 8 ) /* Define the linked list structure. This is used to link free blocks in order -of their memory address. */ + of their memory address. This is optimized for size of the linked list struct + and assumes a region is never larger than 16MiB. */ +#define HEAPREGIONS_MAX_REGIONSIZE (16*1024*1024) typedef struct A_BLOCK_LINK { - struct A_BLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */ - size_t xBlockSize; /*<< The size of the free block. */ - BaseType_t xTag; /*<< Tag of this region */ + struct A_BLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */ + int xBlockSize: 24; /*<< The size of the free block. */ + int xTag: 7; /*<< Tag of this region */ + int xAllocated: 1; /*<< 1 if allocated */ } BlockLink_t; //Mux to protect the memory status data @@ -172,21 +175,16 @@ static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ); /* The size of the structure placed at the beginning of each allocated memory block must by correctly byte aligned. */ -static const uint32_t uxHeapStructSize = ( ( sizeof ( BlockLink_t ) + BLOCK_HEAD_LEN + BLOCK_TAIL_LEN + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); +static const uint32_t uxHeapStructSize = ( ( sizeof ( BlockLink_t ) + BLOCK_HEAD_LEN + BLOCK_TAIL_LEN + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); /* Create a couple of list links to mark the start and end of the list. */ static BlockLink_t xStart, *pxEnd = NULL; /* Keeps track of the number of free bytes remaining, but says nothing about fragmentation. */ -static size_t xFreeBytesRemaining = 0; -static size_t xMinimumEverFreeBytesRemaining = 0; +static size_t xFreeBytesRemaining[HEAPREGIONS_MAX_TAGCOUNT] = {0}; +static size_t xMinimumEverFreeBytesRemaining[HEAPREGIONS_MAX_TAGCOUNT] = {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. */ -static size_t xBlockAllocatedBit = 0; /*-----------------------------------------------------------*/ @@ -195,178 +193,167 @@ void *pvPortMallocTagged( size_t xWantedSize, BaseType_t tag ) BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; void *pvReturn = NULL; - /* The heap must be initialised before the first call to - prvPortMalloc(). */ - configASSERT( pxEnd ); + /* The heap must be initialised before the first call to + prvPortMalloc(). */ + configASSERT( pxEnd ); - taskENTER_CRITICAL(&xMallocMutex); - { - /* 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 - kernel, so it must be free. */ - if( ( xWantedSize & xBlockAllocatedBit ) == 0 ) - { - /* The wanted size is increased so it can contain a BlockLink_t - structure in addition to the requested amount of bytes. */ - if( xWantedSize > 0 ) - { - xWantedSize += uxHeapStructSize; + taskENTER_CRITICAL(&xMallocMutex); + { + /* The wanted size is increased so it can contain a BlockLink_t + structure in addition to the requested amount of bytes. */ + if( xWantedSize > 0 ) + { + xWantedSize += uxHeapStructSize; - /* Ensure that blocks are always aligned to the required number - of bytes. */ - if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) - { - /* Byte alignment required. */ - xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + /* Ensure that blocks are always aligned to the required number + of bytes. */ + if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } - if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) - { - /* Traverse the list from the start (lowest address) block until - one of adequate size is found. */ - pxPreviousBlock = &xStart; - pxBlock = xStart.pxNextFreeBlock; - while( ( ( pxBlock->xTag != tag ) || ( pxBlock->xBlockSize < xWantedSize ) ) && ( pxBlock->pxNextFreeBlock != NULL ) ) - { -// ets_printf("Block %x -> %x\n", (uint32_t)pxBlock, (uint32_t)pxBlock->pxNextFreeBlock); + if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining[ tag ] ) ) + { + /* Traverse the list from the start (lowest address) block until + one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = xStart.pxNextFreeBlock; + while( ( ( pxBlock->xTag != tag ) || ( pxBlock->xBlockSize < xWantedSize ) ) && ( pxBlock->pxNextFreeBlock != NULL ) ) + { +// ets_printf("Block %x -> %x\n", (uint32_t)pxBlock, (uint32_t)pxBlock->pxNextFreeBlock); - #if (configENABLE_MEMORY_DEBUG == 1) - { - mem_check_block(pxBlock); - } - #endif + #if (configENABLE_MEMORY_DEBUG == 1) + { + mem_check_block(pxBlock); + } + #endif - pxPreviousBlock = pxBlock; - pxBlock = pxBlock->pxNextFreeBlock; - } + pxPreviousBlock = pxBlock; + pxBlock = pxBlock->pxNextFreeBlock; + } - /* If the end marker was not reached then a block of adequate size - was found. */ - if( pxBlock != pxEnd ) - { - /* Return the memory space pointed to - jumping over the - BlockLink_t structure at its start. */ - pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + uxHeapStructSize - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN); + /* If the end marker was not reached then a block of adequate size + was found. */ + if( pxBlock != pxEnd ) + { + /* Return the memory space pointed to - jumping over the + BlockLink_t structure at its start. */ + pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + uxHeapStructSize - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN); - /* This block is being returned for use so must be taken out - of the list of free blocks. */ - pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + /* This block is being returned for use so must be taken out + of the list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; - /* If the block is larger than required it can be split into - two. */ + /* If the block is larger than required it can be split into + two. */ - if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) - { - /* This block is to be split into two. Create a new - block following the number of bytes requested. The void - cast is used to prevent byte alignment warnings from the - compiler. */ - pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize); + if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) + { + /* This block is to be split into two. Create a new + block following the number of bytes requested. The void + cast is used to prevent byte alignment warnings from the + compiler. */ + pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize); - /* Calculate the sizes of two blocks split from the - single block. */ - pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; - pxNewBlockLink->xTag = tag; - pxBlock->xBlockSize = xWantedSize; + /* Calculate the sizes of two blocks split from the + single block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxNewBlockLink->xTag = tag; + pxBlock->xBlockSize = xWantedSize; - #if (configENABLE_MEMORY_DEBUG == 1) - { - mem_init_dog(pxNewBlockLink); - } - #endif + #if (configENABLE_MEMORY_DEBUG == 1) + { + mem_init_dog(pxNewBlockLink); + } + #endif - /* Insert the new block into the list of free blocks. */ - prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + /* Insert the new block into the list of free blocks. */ + prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } - xFreeBytesRemaining -= pxBlock->xBlockSize; + xFreeBytesRemaining[ tag ] -= pxBlock->xBlockSize; - if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) - { - xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + if( xFreeBytesRemaining[ tag ] < xMinimumEverFreeBytesRemaining[ tag ] ) + { + xMinimumEverFreeBytesRemaining[ tag ] = xFreeBytesRemaining[ tag ]; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } - /* The block is being returned - it is allocated and owned - by the application and has no "next" block. */ - pxBlock->xBlockSize |= xBlockAllocatedBit; - pxBlock->pxNextFreeBlock = NULL; + /* The block is being returned - it is allocated and owned + by the application and has no "next" block. */ + pxBlock->xAllocated = 1; + pxBlock->pxNextFreeBlock = NULL; - #if (configENABLE_MEMORY_DEBUG == 1) - { - mem_init_dog(pxBlock); - mem_malloc_block(pxBlock); - } - #endif - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + #if (configENABLE_MEMORY_DEBUG == 1) + { + mem_init_dog(pxBlock); + mem_malloc_block(pxBlock); + } + #endif + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } - traceMALLOC( pvReturn, xWantedSize ); - } - taskEXIT_CRITICAL(&xMallocMutex); + traceMALLOC( pvReturn, xWantedSize ); + } + taskEXIT_CRITICAL(&xMallocMutex); - #if( configUSE_MALLOC_FAILED_HOOK == 1 ) - { - if( pvReturn == NULL ) - { - extern void vApplicationMallocFailedHook( void ); - vApplicationMallocFailedHook(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif - return pvReturn; + return pvReturn; } /*-----------------------------------------------------------*/ -void vPortFree( void *pv ) +void vPortFreeTagged( void *pv ) { uint8_t *puc = ( uint8_t * ) pv; BlockLink_t *pxLink; - if( pv != NULL ) - { - /* The memory being freed will have an BlockLink_t structure immediately - before it. */ - puc -= (uxHeapStructSize - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN) ; + if( pv != NULL ) + { + /* The memory being freed will have an BlockLink_t structure immediately + before it. */ + puc -= (uxHeapStructSize - BLOCK_TAIL_LEN - BLOCK_HEAD_LEN) ; - /* This casting is to keep the compiler from issuing warnings. */ - pxLink = ( void * ) puc; + /* This casting is to keep the compiler from issuing warnings. */ + pxLink = ( void * ) puc; #if (configENABLE_MEMORY_DEBUG == 1) { @@ -377,49 +364,49 @@ BlockLink_t *pxLink; } #endif - /* Check the block is actually allocated. */ - configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ); - configASSERT( pxLink->pxNextFreeBlock == NULL ); + /* Check the block is actually allocated. */ + configASSERT( ( pxLink->xAllocated ) != 0 ); + configASSERT( pxLink->pxNextFreeBlock == NULL ); - if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ) - { - if( pxLink->pxNextFreeBlock == NULL ) - { - /* The block is being returned to the heap - it is no longer - allocated. */ - pxLink->xBlockSize &= ~xBlockAllocatedBit; + if( pxLink->xAllocated != 0 ) + { + if( pxLink->pxNextFreeBlock == NULL ) + { + /* The block is being returned to the heap - it is no longer + allocated. */ + pxLink->xAllocated = 0; - taskENTER_CRITICAL(&xMallocMutex); - { - /* Add this block to the list of free blocks. */ - xFreeBytesRemaining += pxLink->xBlockSize; - traceFREE( pv, pxLink->xBlockSize ); - prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); - } - taskEXIT_CRITICAL(&xMallocMutex); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + taskENTER_CRITICAL(&xMallocMutex); + { + /* Add this block to the list of free blocks. */ + xFreeBytesRemaining[ pxLink->xTag ] += pxLink->xBlockSize; + traceFREE( pv, pxLink->xBlockSize ); + prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); + } + taskEXIT_CRITICAL(&xMallocMutex); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } } /*-----------------------------------------------------------*/ -size_t xPortGetFreeHeapSize( void ) +size_t xPortGetFreeHeapSizeTagged( BaseType_t tag ) { - return xFreeBytesRemaining; + return xFreeBytesRemaining[ tag ]; } /*-----------------------------------------------------------*/ -size_t xPortGetMinimumEverFreeHeapSize( void ) +size_t xPortGetMinimumEverFreeHeapSizeTagged( BaseType_t tag ) { - return xMinimumEverFreeBytesRemaining; + return xMinimumEverFreeBytesRemaining[ tag ]; } /*-----------------------------------------------------------*/ @@ -428,59 +415,59 @@ static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ) BlockLink_t *pxIterator; uint8_t *puc; - /* Iterate through the list until a block is found that has a higher address - than the block being inserted. */ - for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ) - { - /* Nothing to do here, just iterate to the right position. */ - } + /* Iterate through the list until a block is found that has a higher address + than the block being inserted. */ + for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ) + { + /* Nothing to do here, just iterate to the right position. */ + } - /* Do the block being inserted, and the block it is being inserted after - make a contiguous block of memory, and are the tags the same? */ - puc = ( uint8_t * ) pxIterator; - if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert && pxBlockToInsert->xTag==pxIterator->xTag) - { - pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; - pxBlockToInsert = pxIterator; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + /* Do the block being inserted, and the block it is being inserted after + make a contiguous block of memory, and are the tags the same? */ + puc = ( uint8_t * ) pxIterator; + if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert && pxBlockToInsert->xTag==pxIterator->xTag) + { + pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; + pxBlockToInsert = pxIterator; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } - /* Do the block being inserted, and the block it is being inserted before - make a contiguous block of memory, and are the tags the same */ - puc = ( uint8_t * ) pxBlockToInsert; - if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock && pxBlockToInsert->xTag==pxIterator->pxNextFreeBlock->xTag ) - { - if( pxIterator->pxNextFreeBlock != pxEnd ) - { - /* Form one big block from the two blocks. */ - pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; - pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; - } - else - { - pxBlockToInsert->pxNextFreeBlock = pxEnd; - } - } - else - { - pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; - } + /* Do the block being inserted, and the block it is being inserted before + make a contiguous block of memory, and are the tags the same */ + puc = ( uint8_t * ) pxBlockToInsert; + if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock && pxBlockToInsert->xTag==pxIterator->pxNextFreeBlock->xTag ) + { + if( pxIterator->pxNextFreeBlock != pxEnd ) + { + /* Form one big block from the two blocks. */ + pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; + } + else + { + pxBlockToInsert->pxNextFreeBlock = pxEnd; + } + } + else + { + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; + } - /* If the block being inserted plugged a gap, so was merged with the block - before and the block after, then it's pxNextFreeBlock pointer will have - already been set, and should not be set here as that would make it point - to itself. */ - if( pxIterator != pxBlockToInsert ) - { - pxIterator->pxNextFreeBlock = pxBlockToInsert; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + /* If the block being inserted plugged a gap, so was merged with the block + before and the block after, then it's pxNextFreeBlock pointer will have + already been set, and should not be set here as that would make it point + to itself. */ + if( pxIterator != pxBlockToInsert ) + { + pxIterator->pxNextFreeBlock = pxBlockToInsert; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } } /*-----------------------------------------------------------*/ @@ -493,90 +480,94 @@ BaseType_t xDefinedRegions = 0, xRegIdx = 0; uint32_t ulAddress; const HeapRegionTagged_t *pxHeapRegion; - /* Can only call once! */ - configASSERT( pxEnd == NULL ); + /* Can only call once! */ + configASSERT( pxEnd == NULL ); - vPortCPUInitializeMutex(&xMallocMutex); + vPortCPUInitializeMutex(&xMallocMutex); - pxHeapRegion = &( pxHeapRegions[ xRegIdx ] ); + pxHeapRegion = &( pxHeapRegions[ xRegIdx ] ); - while( pxHeapRegion->xSizeInBytes > 0 ) - { - if ( pxHeapRegion->xTag == -1 ) { - /* Move onto the next HeapRegionTagged_t structure. */ - xRegIdx++; - pxHeapRegion = &( pxHeapRegions[ xRegIdx ] ); - continue; - } + while( pxHeapRegion->xSizeInBytes > 0 ) + { + if ( pxHeapRegion->xTag == -1 ) { + /* Move onto the next HeapRegionTagged_t structure. */ + xRegIdx++; + pxHeapRegion = &( pxHeapRegions[ xRegIdx ] ); + continue; + } - xTotalRegionSize = pxHeapRegion->xSizeInBytes; + configASSERT(pxHeapRegion->xTag < HEAPREGIONS_MAX_TAGCOUNT); + configASSERT(pxHeapRegion->xSizeInBytes < HEAPREGIONS_MAX_REGIONSIZE); + xTotalRegionSize = pxHeapRegion->xSizeInBytes; - /* Ensure the heap region starts on a correctly aligned boundary. */ - ulAddress = ( uint32_t ) pxHeapRegion->pucStartAddress; - if( ( ulAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) - { - ulAddress += ( portBYTE_ALIGNMENT - 1 ); - ulAddress &= ~portBYTE_ALIGNMENT_MASK; + /* Ensure the heap region starts on a correctly aligned boundary. */ + ulAddress = ( uint32_t ) pxHeapRegion->pucStartAddress; + if( ( ulAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) + { + ulAddress += ( portBYTE_ALIGNMENT - 1 ); + ulAddress &= ~portBYTE_ALIGNMENT_MASK; - /* Adjust the size for the bytes lost to alignment. */ - xTotalRegionSize -= ulAddress - ( uint32_t ) pxHeapRegion->pucStartAddress; - } + /* Adjust the size for the bytes lost to alignment. */ + xTotalRegionSize -= ulAddress - ( uint32_t ) pxHeapRegion->pucStartAddress; + } - pucAlignedHeap = ( uint8_t * ) ulAddress; + pucAlignedHeap = ( uint8_t * ) ulAddress; - /* 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 * ) (pucAlignedHeap + BLOCK_HEAD_LEN); - xStart.xBlockSize = ( size_t ) 0; - } - else - { - /* Should only get here if one region has already been added to the - heap. */ - configASSERT( pxEnd != NULL ); + /* 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 * ) (pucAlignedHeap + BLOCK_HEAD_LEN); + 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( ulAddress > ( uint32_t ) pxEnd ); - } + /* Check blocks are passed in with increasing start addresses. */ + configASSERT( ulAddress > ( uint32_t ) pxEnd ); + } - /* Remember the location of the end marker in the previous region, if - any. */ - pxPreviousFreeBlock = 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. */ - ulAddress = ( ( uint32_t ) pucAlignedHeap ) + xTotalRegionSize; - ulAddress -= uxHeapStructSize; - ulAddress &= ~portBYTE_ALIGNMENT_MASK; - pxEnd = ( BlockLink_t * ) (ulAddress + BLOCK_HEAD_LEN); - pxEnd->xBlockSize = 0; - pxEnd->pxNextFreeBlock = NULL; - pxEnd->xTag = -1; + /* pxEnd is used to mark the end of the list of free blocks and is + inserted at the end of the region space. */ + ulAddress = ( ( uint32_t ) pucAlignedHeap ) + xTotalRegionSize; + ulAddress -= uxHeapStructSize; + ulAddress &= ~portBYTE_ALIGNMENT_MASK; + pxEnd = ( BlockLink_t * ) (ulAddress + BLOCK_HEAD_LEN); + pxEnd->xBlockSize = 0; + pxEnd->pxNextFreeBlock = NULL; + pxEnd->xTag = -1; - /* 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 * ) (pucAlignedHeap + BLOCK_HEAD_LEN); - pxFirstFreeBlockInRegion->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlockInRegion + BLOCK_HEAD_LEN; - pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd; - pxFirstFreeBlockInRegion->xTag=pxHeapRegion->xTag; + /* 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 * ) (pucAlignedHeap + BLOCK_HEAD_LEN); + pxFirstFreeBlockInRegion->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlockInRegion + BLOCK_HEAD_LEN; + pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd; + pxFirstFreeBlockInRegion->xTag=pxHeapRegion->xTag; - /* 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; - } + /* 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; + xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize; + xMinimumEverFreeBytesRemaining[ pxHeapRegion->xTag ] += pxFirstFreeBlockInRegion->xBlockSize; + xFreeBytesRemaining[ pxHeapRegion->xTag ] += pxFirstFreeBlockInRegion->xBlockSize; - /* Move onto the next HeapRegionTagged_t structure. */ - xDefinedRegions++; - xRegIdx++; - pxHeapRegion = &( pxHeapRegions[ xRegIdx ] ); + /* Move onto the next HeapRegionTagged_t structure. */ + xDefinedRegions++; + xRegIdx++; + pxHeapRegion = &( pxHeapRegions[ xRegIdx ] ); #if (configENABLE_MEMORY_DEBUG == 1) { @@ -584,16 +575,11 @@ const HeapRegionTagged_t *pxHeapRegion; mem_init_dog(pxEnd); } #endif - } + } - xMinimumEverFreeBytesRemaining = xTotalHeapSize; - xFreeBytesRemaining = xTotalHeapSize; + /* Check something was actually defined before it is accessed. */ + configASSERT( 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 ); #if (configENABLE_MEMORY_DEBUG == 1) { diff --git a/components/freertos/heap_regions_debug.c b/components/freertos/heap_regions_debug.c index d8d444a537..f221e516b0 100644 --- a/components/freertos/heap_regions_debug.c +++ b/components/freertos/heap_regions_debug.c @@ -12,19 +12,17 @@ static size_t g_heap_struct_size; static mem_dbg_ctl_t g_mem_dbg; char g_mem_print = 0; static portMUX_TYPE *g_malloc_mutex = NULL; -static unsigned int g_alloc_bit; #define MEM_DEBUG(...) -void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex, unsigned int alloc_bit) +void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex) { - MEM_DEBUG("size=%d start=%p end=%p mutex=%p alloc_bit=0x%x\n", size, start, end, mutex, alloc_bit); + MEM_DEBUG("size=%d start=%p end=%p mutex=%p%x\n", size, start, end, mutex); memset(&g_mem_dbg, 0, sizeof(g_mem_dbg)); memset(&g_malloc_list, 0, sizeof(g_malloc_list)); g_malloc_mutex = mutex; g_heap_struct_size = size; g_free_list = start; g_end = end; - g_alloc_bit = alloc_bit; } void mem_debug_push(char type, void *addr) @@ -35,9 +33,9 @@ void mem_debug_push(char type, void *addr) MEM_DEBUG("push type=%d addr=%p\n", type, addr); if (g_mem_print){ if (type == DEBUG_TYPE_MALLOC){ - ets_printf("task=%s t=%s s=%u a=%p\n", debug_b->head.task?debug_b->head.task:"", type==DEBUG_TYPE_MALLOC?"m":"f", b->size&(~g_alloc_bit), addr); + ets_printf("task=%s t=%s s=%u a=%p\n", debug_b->head.task?debug_b->head.task:"", type==DEBUG_TYPE_MALLOC?"m":"f", b->size, addr); } else { - ets_printf("task=%s t=%s s=%u a=%p\n", debug_b->head.task?debug_b->head.task:"", type==DEBUG_TYPE_MALLOC?"m":"f", b->size&(~g_alloc_bit), addr); + ets_printf("task=%s t=%s s=%u a=%p\n", debug_b->head.task?debug_b->head.task:"", type==DEBUG_TYPE_MALLOC?"m":"f", b->size, addr); } } else { mem_dbg_info_t *info = &g_mem_dbg.info[g_mem_dbg.cnt%DEBUG_MAX_INFO_NUM]; @@ -58,7 +56,7 @@ void mem_debug_malloc_show(void) while (b){ d = DEBUG_BLOCK(b); d->head.task[3] = '\0'; - ets_printf("t=%s s=%u a=%p\n", d->head.task?d->head.task:"", b->size&(~g_alloc_bit), b); + ets_printf("t=%s s=%u a=%p\n", d->head.task?d->head.task:"", b->size, b); b = b->next; } taskEXIT_CRITICAL(g_malloc_mutex); @@ -140,7 +138,7 @@ void mem_malloc_show(void) while (b){ debug_b = DEBUG_BLOCK(b); - ets_printf("%s %p %p %u\n", debug_b->head.task, debug_b, b, b->size&(~g_alloc_bit)); + ets_printf("%s %p %p %u\n", debug_b->head.task, debug_b, b, b->size); b = b->next; } } @@ -149,7 +147,7 @@ void mem_malloc_block(void *data) { os_block_t *b = (os_block_t*)data; - MEM_DEBUG("mem malloc block data=%p, size=%u\n", data, b->size&(~g_alloc_bit)); + MEM_DEBUG("mem malloc block data=%p, size=%u\n", data, b->size); mem_debug_push(DEBUG_TYPE_MALLOC, data); if (b){ @@ -165,7 +163,7 @@ void mem_free_block(void *data) os_block_t *pre = &g_malloc_list; debug_block_t *debug_b; - MEM_DEBUG("mem free block data=%p, size=%d\n", data, del->size&(~g_alloc_bit)); + MEM_DEBUG("mem free block data=%p, size=%d\n", data, del->size); mem_debug_push(DEBUG_TYPE_FREE, data); if (!del) { @@ -183,7 +181,7 @@ void mem_free_block(void *data) } debug_b = DEBUG_BLOCK(del); - ets_printf("%s %p %p %u already free\n", debug_b->head.task, debug_b, del, del->size&(~g_alloc_bit)); + ets_printf("%s %p %p %u already free\n", debug_b->head.task, debug_b, del, del->size); mem_malloc_show(); abort(); } diff --git a/components/freertos/include/freertos/heap_regions.h b/components/freertos/include/freertos/heap_regions.h index aedea42a16..090c5b9b36 100644 --- a/components/freertos/include/freertos/heap_regions.h +++ b/components/freertos/include/freertos/heap_regions.h @@ -16,19 +16,81 @@ #include "freertos/FreeRTOS.h" +/* The maximum amount of tags in use */ +#define HEAPREGIONS_MAX_TAGCOUNT 16 +/** + * @brief Structure to define a memory region + */ typedef struct HeapRegionTagged { - uint8_t *pucStartAddress; - size_t xSizeInBytes; - BaseType_t xTag; - uint32_t xExecAddr; + uint8_t *pucStartAddress; ///< Start address of the region + size_t xSizeInBytes; ///< Size of the region + BaseType_t xTag; ///< Tag for the region + uint32_t xExecAddr; ///< If non-zero, indicates the region also has an alias in IRAM. } HeapRegionTagged_t; +/** + * @brief Initialize the heap allocator by feeding it the usable memory regions and their tags. + * + * This takes an array of heapRegionTagged_t structs, the last entry of which is a dummy entry + * which has pucStartAddress set to NULL. It will initialize the heap allocator to serve memory + * from these ranges. + * + * @param pxHeapRegions Array of region definitions + */ void vPortDefineHeapRegionsTagged( const HeapRegionTagged_t * const pxHeapRegions ); + + +/** + * @brief Allocate memory from a region with a certain tag + * + * Like pvPortMalloc, this returns an allocated chunk of memory. This function, + * however, forces the allocator to allocate from a region specified by a + * specific tag. + * + * @param xWantedSize Size needed, in bytes + * @param tag Tag of the memory region the allocation has to be from + * + * @return Pointer to allocated memory if succesful. + * NULL if unsuccesful. + */ void *pvPortMallocTagged( size_t xWantedSize, BaseType_t tag ); +/** + * @brief Free memory allocated with pvPortMallocTagged + * + * This is basically an implementation of free(). + * + * @param pv Pointer to region allocated by pvPortMallocTagged + */ +void vPortFreeTagged( void *pv ); + +/** + * @brief Get the lowest amount of memory free for a certain tag + * + * This function allows the user to see what the least amount of + * free memory for a certain tag is. + * + * @param tag Tag of the memory region + * + * @return Minimum amount of free bytes available in the runtime of + * the program + */ +size_t xPortGetMinimumEverFreeHeapSizeTagged( BaseType_t tag ); + +/** + * @brief Get the amount of free bytes in a certain tagged region + * + * Works like xPortGetFreeHeapSize but allows the user to specify + * a specific tag + * + * @param tag Tag of the memory region + * + * @return Remaining amount of free bytes in region + */ +size_t xPortGetFreeHeapSizeTagged( BaseType_t tag ); #endif \ No newline at end of file diff --git a/components/freertos/include/freertos/heap_regions_debug.h b/components/freertos/include/freertos/heap_regions_debug.h index 81bf1d6c3b..6ab4681f18 100644 --- a/components/freertos/include/freertos/heap_regions_debug.h +++ b/components/freertos/include/freertos/heap_regions_debug.h @@ -60,7 +60,7 @@ typedef struct _mem_dbg_ctl{ extern void mem_check_block(void * data); extern void mem_init_dog(void *data); -extern void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex, unsigned int alloc_bit); +extern void mem_debug_init(size_t size, void *start, void *end, portMUX_TYPE *mutex); extern void mem_malloc_block(void *data); extern void mem_free_block(void *data); extern void mem_check_all(void* pv); diff --git a/docs/Doxyfile b/docs/Doxyfile index bdb91a4dce..2456b6c2ea 100755 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -28,7 +28,9 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/app_update/include/esp_ota_ops.h \ ../components/ethernet/include/esp_eth.h \ ../components/ulp/include/esp32/ulp.h \ - ../components/esp32/include/esp_intr_alloc.h + ../components/esp32/include/esp_intr_alloc.h \ + ../components/esp32/include/esp_heap_alloc_caps.h \ + ../components/freertos/include/freertos/heap_regions.h ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/api/mem_alloc.rst b/docs/api/mem_alloc.rst new file mode 100644 index 0000000000..cebea5b8a5 --- /dev/null +++ b/docs/api/mem_alloc.rst @@ -0,0 +1,77 @@ +Memory allocation +==================== + +Overview +-------- + +The ESP32 has multiple types of RAM. Internally, there's IRAM, DRAM as well as RAM that can be used as both. It's also +possible to connect external SPI flash to the ESP32; it's memory can be integrated into the ESP32s memory map using +the flash cache. + +In order to make use of all this memory, esp-idf has a capabilities-based memory allocator. Basically, if you want to have +memory with certain properties (for example, DMA-capable, accessible by a certain PID, or capable of executing code), you +can create an OR-mask of the required capabilities and pass that to pvPortMallocCaps. For instance, the normal malloc +code internally allocates memory with ```pvPortMallocCaps(size, MALLOC_CAP_8BIT)``` in order to get data memory that is +byte-addressable. + +Because malloc uses this allocation system as well, memory allocated using pvPortMallocCaps can be freed by calling +the standard ```free()``` function. + +Internally, this allocator is split in two pieces. The allocator in the FreeRTOS directory can allocate memory from +tagged regions: a tag is an integer value and every region of free memory has one of these tags. The esp32-specific +code initializes these regions with specific tags, and contains the logic to select applicable tags from the +capabilities given by the user. While shown in the public API, tags are used in the communication between the two parts +and should not be used directly. + +Special Uses +------------ + +If a certain memory structure is only addressed in 32-bit units, for example an array of ints or pointers, it can be +useful to allocate it with the MALLOC_CAP_32BIT flag. This also allows the allocator to give out IRAM memory; something +which it can't do for a normal malloc() call. This can help to use all the available memory in the ESP32. + + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `esp_heap_alloc_caps.h `_ + * `heap_regions.h `_ + + +Macros +^^^^^^ + +.. doxygendefine:: MALLOC_CAP_EXEC +.. doxygendefine:: MALLOC_CAP_32BIT +.. doxygendefine:: MALLOC_CAP_8BIT +.. doxygendefine:: MALLOC_CAP_DMA +.. doxygendefine:: MALLOC_CAP_PID2 +.. doxygendefine:: MALLOC_CAP_PID3 +.. doxygendefine:: MALLOC_CAP_PID4 +.. doxygendefine:: MALLOC_CAP_PID5 +.. doxygendefine:: MALLOC_CAP_PID6 +.. doxygendefine:: MALLOC_CAP_PID7 +.. doxygendefine:: MALLOC_CAP_SPISRAM +.. doxygendefine:: MALLOC_CAP_INVALID + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: HeapRegionTagged_t + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: heap_alloc_caps_init +.. doxygenfunction:: pvPortMallocCaps +.. doxygenfunction:: xPortGetFreeHeapSizeCaps +.. doxygenfunction:: xPortGetMinimumEverFreeHeapSizeCaps +.. doxygenfunction:: vPortDefineHeapRegionsTagged +.. doxygenfunction:: pvPortMallocTagged +.. doxygenfunction:: vPortFreeTagged +.. doxygenfunction:: xPortGetMinimumEverFreeHeapSizeTagged +.. doxygenfunction:: xPortGetFreeHeapSizeTagged diff --git a/docs/index.rst b/docs/index.rst index 4e58cfe02f..8a42e762c0 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -48,7 +48,8 @@ Contents: 1.3. Flash encryption and secure boot: how they work and APIs 1.4. Lower Power Coprocessor - TBA 1.5. Watchdogs - 1.6. ... + 1.6. Memory allocation + 1.7. ... 2. Memory - TBA 2.1. Memory layout of the application (IRAM/IROM, limitations of each) - TBA 2.2. Flash layout and partitions - TBA @@ -111,6 +112,7 @@ Contents: Virtual Filesystem Ethernet Interrupt Allocation + Memory Allocation deep-sleep-stub Template diff --git a/tools/unit-test-app/sdkconfig b/tools/unit-test-app/sdkconfig index 1b9db99518..14b31e0d18 100644 --- a/tools/unit-test-app/sdkconfig +++ b/tools/unit-test-app/sdkconfig @@ -93,6 +93,7 @@ CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 CONFIG_MAIN_TASK_STACK_SIZE=4096 CONFIG_NEWLIB_STDOUT_ADDCR=y +# CONFIG_NEWLIB_NANO_FORMAT is not set CONFIG_CONSOLE_UART_DEFAULT=y # CONFIG_CONSOLE_UART_CUSTOM is not set # CONFIG_CONSOLE_UART_NONE is not set @@ -171,6 +172,8 @@ CONFIG_MBEDTLS_HARDWARE_AES=y CONFIG_MBEDTLS_HARDWARE_MPI=y CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y CONFIG_MBEDTLS_HARDWARE_SHA=y +CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set # # SPI Flash driver