Add testcase, fix executable memory allocated in shared dram/iram region

This commit is contained in:
Jeroen Domburg 2016-12-13 22:21:20 +08:00
parent 1e117dc3d3
commit 6f0a494cae
6 changed files with 170 additions and 26 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";
@ -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;
}
}
}
}

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

@ -341,7 +341,7 @@ void *pvReturn = NULL;
}
/*-----------------------------------------------------------*/
void vPortFree( void *pv )
void vPortFreeTagged( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;

View file

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

View file

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

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