mirror of
https://github.com/espressif/esp-idf
synced 2025-03-10 01:29:21 -04:00
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:
commit
9d5f4e877e
@ -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,26 +297,91 @@ 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.
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//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 );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
64
components/esp32/test/test_malloc_caps.c
Normal file
64
components/esp32/test/test_malloc_caps.c
Normal 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");
|
||||
}
|
@ -147,12 +147,15 @@ task.h is included from an application file. */
|
||||
#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 */
|
||||
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
|
||||
@ -179,14 +182,9 @@ 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;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
@ -200,12 +198,6 @@ void *pvReturn = NULL;
|
||||
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. */
|
||||
@ -230,7 +222,7 @@ void *pvReturn = NULL;
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
|
||||
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining[ tag ] ) )
|
||||
{
|
||||
/* Traverse the list from the start (lowest address) block until
|
||||
one of adequate size is found. */
|
||||
@ -294,11 +286,11 @@ void *pvReturn = NULL;
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
xFreeBytesRemaining -= pxBlock->xBlockSize;
|
||||
xFreeBytesRemaining[ tag ] -= pxBlock->xBlockSize;
|
||||
|
||||
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
|
||||
if( xFreeBytesRemaining[ tag ] < xMinimumEverFreeBytesRemaining[ tag ] )
|
||||
{
|
||||
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
|
||||
xMinimumEverFreeBytesRemaining[ tag ] = xFreeBytesRemaining[ tag ];
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -307,7 +299,7 @@ void *pvReturn = NULL;
|
||||
|
||||
/* The block is being returned - it is allocated and owned
|
||||
by the application and has no "next" block. */
|
||||
pxBlock->xBlockSize |= xBlockAllocatedBit;
|
||||
pxBlock->xAllocated = 1;
|
||||
pxBlock->pxNextFreeBlock = NULL;
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
@ -326,11 +318,6 @@ void *pvReturn = NULL;
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
traceMALLOC( pvReturn, xWantedSize );
|
||||
}
|
||||
@ -354,7 +341,7 @@ void *pvReturn = NULL;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortFree( void *pv )
|
||||
void vPortFreeTagged( void *pv )
|
||||
{
|
||||
uint8_t *puc = ( uint8_t * ) pv;
|
||||
BlockLink_t *pxLink;
|
||||
@ -378,21 +365,21 @@ BlockLink_t *pxLink;
|
||||
#endif
|
||||
|
||||
/* Check the block is actually allocated. */
|
||||
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
|
||||
configASSERT( ( pxLink->xAllocated ) != 0 );
|
||||
configASSERT( pxLink->pxNextFreeBlock == NULL );
|
||||
|
||||
if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
|
||||
if( pxLink->xAllocated != 0 )
|
||||
{
|
||||
if( pxLink->pxNextFreeBlock == NULL )
|
||||
{
|
||||
/* The block is being returned to the heap - it is no longer
|
||||
allocated. */
|
||||
pxLink->xBlockSize &= ~xBlockAllocatedBit;
|
||||
pxLink->xAllocated = 0;
|
||||
|
||||
taskENTER_CRITICAL(&xMallocMutex);
|
||||
{
|
||||
/* Add this block to the list of free blocks. */
|
||||
xFreeBytesRemaining += pxLink->xBlockSize;
|
||||
xFreeBytesRemaining[ pxLink->xTag ] += pxLink->xBlockSize;
|
||||
traceFREE( pv, pxLink->xBlockSize );
|
||||
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
|
||||
}
|
||||
@ -411,15 +398,15 @@ BlockLink_t *pxLink;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
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 ];
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
@ -509,6 +496,8 @@ const HeapRegionTagged_t *pxHeapRegion;
|
||||
continue;
|
||||
}
|
||||
|
||||
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. */
|
||||
@ -572,6 +561,8 @@ const HeapRegionTagged_t *pxHeapRegion;
|
||||
}
|
||||
|
||||
xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;
|
||||
xMinimumEverFreeBytesRemaining[ pxHeapRegion->xTag ] += pxFirstFreeBlockInRegion->xBlockSize;
|
||||
xFreeBytesRemaining[ pxHeapRegion->xTag ] += pxFirstFreeBlockInRegion->xBlockSize;
|
||||
|
||||
/* Move onto the next HeapRegionTagged_t structure. */
|
||||
xDefinedRegions++;
|
||||
@ -586,14 +577,9 @@ const HeapRegionTagged_t *pxHeapRegion;
|
||||
#endif
|
||||
}
|
||||
|
||||
xMinimumEverFreeBytesRemaining = xTotalHeapSize;
|
||||
xFreeBytesRemaining = xTotalHeapSize;
|
||||
|
||||
/* Check something was actually defined before it is accessed. */
|
||||
configASSERT( xTotalHeapSize );
|
||||
|
||||
/* Work out the position of the top bit in a size_t variable. */
|
||||
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
|
||||
|
||||
#if (configENABLE_MEMORY_DEBUG == 1)
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
@ -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);
|
||||
|
@ -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
77
docs/api/mem_alloc.rst
Normal 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
|
@ -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>
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user