Merge branch 'bugfix/heap_alloc_no_iram' into 'master'

Restore ability to alloc IRAM, and more.

- Fix mem regions so allocating IRAM works again
- Optimize allocator slightly, uses 4 less bytes per malloc now
- Allow querying free heap memory space per memory type


See merge request !301
This commit is contained in:
Ivan Grokhotkov 2016-12-15 16:28:29 +08:00
commit 9d5f4e877e
11 changed files with 759 additions and 398 deletions

View file

@ -18,6 +18,7 @@
#include "esp_heap_alloc_caps.h"
#include "spiram.h"
#include "esp_log.h"
#include <stdbool.h>
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<NO_PRIOS; prio++) {
//Iterate over tag descriptors for this priority
for (tag=0; tagDesc[tag][prio]!=MALLOC_CAP_INVALID; tag++) {
if ((tagDesc[tag][prio]&caps)!=0) {
for (tag=0; tag_desc[tag].prio[prio]!=MALLOC_CAP_INVALID; tag++) {
if ((tag_desc[tag].prio[prio]&caps)!=0) {
//Tag has at least one of the caps requested. If caps has other bits set that this prio
//doesn't cover, see if they're available in other prios.
remCaps=caps&(~tagDesc[tag][prio]); //Remaining caps to be fulfilled
remCaps=caps&(~tag_desc[tag].prio[prio]); //Remaining caps to be fulfilled
j=prio+1;
while (remCaps!=0 && j<NO_PRIOS) {
remCaps=remCaps&(~tagDesc[tag][j]);
remCaps=remCaps&(~tag_desc[tag].prio[j]);
j++;
}
if (remCaps==0) {
//This tag can satisfy all the requested capabilities. See if we can grab some memory using it.
ret=pvPortMallocTagged(xWantedSize, tag);
if (ret!=NULL) return ret;
if ((caps & MALLOC_CAP_EXEC) && tag_desc[tag].aliasedIram) {
//This is special, insofar that what we're going to get back is probably a DRAM address. If so,
//we need to 'invert' it (lowest address in DRAM == highest address in IRAM and vice-versa) and
//add a pointer to the DRAM equivalent before the address we're going to return.
ret=pvPortMallocTagged(xWantedSize+4, tag);
if (ret!=NULL) return dram_alloc_to_iram_addr(ret, xWantedSize+4);
} else {
//Just try to alloc, nothing special.
ret=pvPortMallocTagged(xWantedSize, tag);
if (ret!=NULL) return ret;
}
}
}
}
@ -262,3 +340,48 @@ void *pvPortMallocCaps( size_t xWantedSize, uint32_t caps )
//Nothing usable found.
return NULL;
}
size_t xPortGetFreeHeapSizeCaps( uint32_t caps )
{
int prio;
int tag;
size_t ret=0;
for (prio=0; prio<NO_PRIOS; prio++) {
//Iterate over tag descriptors for this priority
for (tag=0; tag_desc[tag].prio[prio]!=MALLOC_CAP_INVALID; tag++) {
if ((tag_desc[tag].prio[prio]&caps)!=0) {
ret+=xPortGetFreeHeapSizeTagged(tag);
}
}
}
return ret;
}
size_t xPortGetMinimumEverFreeHeapSizeCaps( uint32_t caps )
{
int prio;
int tag;
size_t ret=0;
for (prio=0; prio<NO_PRIOS; prio++) {
//Iterate over tag descriptors for this priority
for (tag=0; tag_desc[tag].prio[prio]!=MALLOC_CAP_INVALID; tag++) {
if ((tag_desc[tag].prio[prio]&caps)!=0) {
ret+=xPortGetMinimumEverFreeHeapSizeTagged(tag);
}
}
}
return ret;
}
size_t xPortGetFreeHeapSize( void )
{
return xPortGetFreeHeapSizeCaps( MALLOC_CAP_8BIT );
}
size_t xPortGetMinimumEverFreeHeapSize( void )
{
return xPortGetMinimumEverFreeHeapSizeCaps( MALLOC_CAP_8BIT );
}

View file

@ -14,21 +14,65 @@
#ifndef HEAP_ALLOC_CAPS_H
#define HEAP_ALLOC_CAPS_H
#define MALLOC_CAP_EXEC (1<<0) //Memory must be able to run executable code
#define MALLOC_CAP_32BIT (1<<1) //Memory must allow for aligned 32-bit data accesses
#define MALLOC_CAP_8BIT (1<<2) //Memory must allow for 8/16/...-bit data accesses
#define MALLOC_CAP_DMA (1<<3) //Memory must be able to accessed by DMA
#define MALLOC_CAP_PID2 (1<<4) //Memory must be mapped to PID2 memory space
#define MALLOC_CAP_PID3 (1<<5) //Memory must be mapped to PID3 memory space
#define MALLOC_CAP_PID4 (1<<6) //Memory must be mapped to PID4 memory space
#define MALLOC_CAP_PID5 (1<<7) //Memory must be mapped to PID5 memory space
#define MALLOC_CAP_PID6 (1<<8) //Memory must be mapped to PID6 memory space
#define MALLOC_CAP_PID7 (1<<9) //Memory must be mapped to PID7 memory space
#define MALLOC_CAP_SPISRAM (1<<10) //Memory must be in SPI SRAM
#define MALLOC_CAP_INVALID (1<<31) //Memory can't be used / list end marker
/**
* @brief Flags to indicate the capabilities of the various memory systems
*/
#define MALLOC_CAP_EXEC (1<<0) ///< Memory must be able to run executable code
#define MALLOC_CAP_32BIT (1<<1) ///< Memory must allow for aligned 32-bit data accesses
#define MALLOC_CAP_8BIT (1<<2) ///< Memory must allow for 8/16/...-bit data accesses
#define MALLOC_CAP_DMA (1<<3) ///< Memory must be able to accessed by DMA
#define MALLOC_CAP_PID2 (1<<4) ///< Memory must be mapped to PID2 memory space
#define MALLOC_CAP_PID3 (1<<5) ///< Memory must be mapped to PID3 memory space
#define MALLOC_CAP_PID4 (1<<6) ///< Memory must be mapped to PID4 memory space
#define MALLOC_CAP_PID5 (1<<7) ///< Memory must be mapped to PID5 memory space
#define MALLOC_CAP_PID6 (1<<8) ///< Memory must be mapped to PID6 memory space
#define MALLOC_CAP_PID7 (1<<9) ///< Memory must be mapped to PID7 memory space
#define MALLOC_CAP_SPISRAM (1<<10) ///< Memory must be in SPI SRAM
#define MALLOC_CAP_INVALID (1<<31) ///< Memory can't be used / list end marker
/**
* @brief Initialize the capability-aware heap allocator.
*
* For the ESP32, this is called once in the startup code.
*/
void heap_alloc_caps_init();
/**
* @brief Allocate a chunk of memory which has the given capabilities
*
* @param xWantedSize Size, in bytes, of the amount of memory to allocate
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory to be returned
*
* @return A pointer to the memory allocated on success, NULL on failure
*/
void *pvPortMallocCaps(size_t xWantedSize, uint32_t caps);
/**
* @brief Get the total free size of all the regions that have the given capabilities
*
* This function takes all regions capable of having the given capabilities allocated in them
* and adds up the free space they have.
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory
*
* @return Amount of free bytes in the regions
*/
size_t xPortGetFreeHeapSizeCaps( uint32_t caps );
/**
* @brief Get the total minimum free memory of all regions with the given capabilities
*
* This adds all the lowmarks of the regions capable of delivering the memory with the
* given capabilities
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory
*
* @return Amount of free bytes in the regions
*/
size_t xPortGetMinimumEverFreeHeapSizeCaps( uint32_t caps );
#endif

View file

@ -0,0 +1,64 @@
/*
Tests for the capabilities-based memory allocator.
*/
#include <esp_types.h>
#include <stdio.h>
#include "unity.h"
#include "rom/ets_sys.h"
#include "esp_heap_alloc_caps.h"
#include <stdlib.h>
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");
}

View file

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

View file

@ -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();
}

View file

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

View file

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

View file

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

77
docs/api/mem_alloc.rst Normal file
View file

@ -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 <https://github.com/espressif/esp-idf/blob/master/components/esp32/include/esp_heap_alloc_caps.h>`_
* `heap_regions.h <https://github.com/espressif/esp-idf/blob/master/components/freertos/include/freertos/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

View file

@ -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 <api/wdts>
1.6. ...
1.6. Memory allocation <api/mem_alloc>
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 <api/vfs>
Ethernet <api/esp_eth>
Interrupt Allocation <api/intr_alloc>
Memory Allocation <api/mem_alloc>
deep-sleep-stub
Template <api/template>

View file

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