Add testcase, fix executable memory allocated in shared dram/iram region
This commit is contained in:
parent
1e117dc3d3
commit
6f0a494cae
6 changed files with 170 additions and 26 deletions
|
@ -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";
|
||||
|
||||
|
@ -38,6 +39,7 @@ hardwiring addresses.
|
|||
typedef struct {
|
||||
const char *name;
|
||||
uint32_t prio[NO_PRIOS];
|
||||
bool aliasedIram;
|
||||
} tag_desc_t;
|
||||
|
||||
/*
|
||||
|
@ -46,23 +48,23 @@ Each tag contains NO_PRIOS entries; later entries are only taken if earlier ones
|
|||
Make sure there are never more than HEAPREGIONS_MAX_TAGCOUNT (in heap_regions.h) tags (ex the last empty marker)
|
||||
*/
|
||||
static const tag_desc_t tag_desc[]={
|
||||
{ "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }}, //Tag 0: Plain ole D-port RAM
|
||||
{ "D/IRAM", { 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
|
||||
{ "IRAM", { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT, 0, 0 }}, //Tag 2: IRAM
|
||||
{ "PID2IRAM", { MALLOC_CAP_PID2, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }}, //Tag 3-8: PID 2-7 IRAM
|
||||
{ "PID3IRAM", { MALLOC_CAP_PID3, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }}, //
|
||||
{ "PID4IRAM", { MALLOC_CAP_PID4, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }}, //
|
||||
{ "PID5IRAM", { MALLOC_CAP_PID5, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }}, //
|
||||
{ "PID6IRAM", { MALLOC_CAP_PID6, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }}, //
|
||||
{ "PID7IRAM", { MALLOC_CAP_PID7, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }}, //
|
||||
{ "PID2DRAM", { MALLOC_CAP_PID2, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }}, //Tag 9-14: PID 2-7 DRAM
|
||||
{ "PID3DRAM", { MALLOC_CAP_PID3, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }}, //
|
||||
{ "PID4DRAM", { MALLOC_CAP_PID4, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }}, //
|
||||
{ "PID5DRAM", { MALLOC_CAP_PID5, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }}, //
|
||||
{ "PID6DRAM", { MALLOC_CAP_PID6, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }}, //
|
||||
{ "PID7DRAM", { MALLOC_CAP_PID7, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }}, //
|
||||
{ "SPISRAM", { 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
|
||||
{ "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
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -231,14 +233,61 @@ void heap_alloc_caps_init() {
|
|||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -248,6 +297,17 @@ 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; tag_desc[tag].prio[prio]!=MALLOC_CAP_INVALID; tag++) {
|
||||
|
@ -262,8 +322,17 @@ void *pvPortMallocCaps( size_t xWantedSize, uint32_t caps )
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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");
|
||||
}
|
|
@ -341,7 +341,7 @@ void *pvReturn = NULL;
|
|||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortFree( void *pv )
|
||||
void vPortFreeTagged( void *pv )
|
||||
{
|
||||
uint8_t *puc = ( uint8_t * ) pv;
|
||||
BlockLink_t *pxLink;
|
||||
|
|
|
@ -58,6 +58,15 @@ void vPortDefineHeapRegionsTagged( const HeapRegionTagged_t * const pxHeapRegion
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
|
|
@ -14,6 +14,9 @@ can create an OR-mask of the required capabilities and pass that to pvPortMalloc
|
|||
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
|
||||
|
@ -59,11 +62,6 @@ Type Definitions
|
|||
|
||||
.. doxygentypedef:: HeapRegionTagged_t
|
||||
|
||||
Enumerations
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Structures
|
||||
^^^^^^^^^^
|
||||
|
||||
Functions
|
||||
^^^^^^^^^
|
||||
|
@ -74,5 +72,6 @@ Functions
|
|||
.. doxygenfunction:: xPortGetMinimumEverFreeHeapSizeCaps
|
||||
.. doxygenfunction:: vPortDefineHeapRegionsTagged
|
||||
.. doxygenfunction:: pvPortMallocTagged
|
||||
.. doxygenfunction:: vPortFreeTagged
|
||||
.. doxygenfunction:: xPortGetMinimumEverFreeHeapSizeTagged
|
||||
.. doxygenfunction:: xPortGetFreeHeapSizeTagged
|
||||
|
|
|
@ -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…
Reference in a new issue