mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-02 12:14:32 +02:00
Merge branch 'feature/malloc_psram' into 'master'
Add logic to make external RAM usable with malloc() See merge request !1278
This commit is contained in:
@@ -47,7 +47,7 @@ config SPIRAM_BOOT_INIT
|
|||||||
|
|
||||||
choice SPIRAM_USE
|
choice SPIRAM_USE
|
||||||
prompt "SPI RAM access method"
|
prompt "SPI RAM access method"
|
||||||
default SPIRAM_USE_MEMMAP
|
default SPIRAM_USE_MALLOC
|
||||||
help
|
help
|
||||||
The SPI RAM can be accessed in multiple methods: by just having it available as an unmanaged
|
The SPI RAM can be accessed in multiple methods: by just having it available as an unmanaged
|
||||||
memory region in the ESP32 memory map, by integrating it in the ESP32s heap as 'special' memory
|
memory region in the ESP32 memory map, by integrating it in the ESP32s heap as 'special' memory
|
||||||
@@ -59,8 +59,7 @@ config SPIRAM_USE_MEMMAP
|
|||||||
config SPIRAM_USE_CAPS_ALLOC
|
config SPIRAM_USE_CAPS_ALLOC
|
||||||
bool "Make RAM allocatable using heap_caps_malloc(..., MALLOC_CAP_SPIRAM)"
|
bool "Make RAM allocatable using heap_caps_malloc(..., MALLOC_CAP_SPIRAM)"
|
||||||
config SPIRAM_USE_MALLOC
|
config SPIRAM_USE_MALLOC
|
||||||
bool "Make RAM allocatable using malloc as well"
|
bool "Make RAM allocatable using malloc() as well"
|
||||||
depends on TO_BE_DONE
|
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
choice SPIRAM_TYPE
|
choice SPIRAM_TYPE
|
||||||
@@ -118,6 +117,46 @@ config SPIRAM_CACHE_WORKAROUND
|
|||||||
with the workaround and located in flash instead.
|
with the workaround and located in flash instead.
|
||||||
|
|
||||||
|
|
||||||
|
config SPIRAM_MALLOC_ALWAYSINTERNAL
|
||||||
|
int "Maximum malloc() size, in bytes, to always put in internal memory"
|
||||||
|
depends on SPIRAM_USE_MALLOC
|
||||||
|
default 16384
|
||||||
|
range 0 131072
|
||||||
|
help
|
||||||
|
If malloc() is capable of also allocating SPI-connected ram, its allocation strategy will prefer to allocate chunks less
|
||||||
|
than this size in internal memory, while allocations larger than this will be done from external RAM.
|
||||||
|
If allocation from the preferred region fails, an attempt is made to allocate from the non-preferred
|
||||||
|
region instead, so malloc() will not suddenly fail when either internal or external memory is full.
|
||||||
|
|
||||||
|
config SPIRAM_MALLOC_RESERVE_INTERNAL
|
||||||
|
int "Reserve this amount of bytes for data that specifically needs to be in DMA or internal memory"
|
||||||
|
depends on SPIRAM_USE_MALLOC
|
||||||
|
default 32768
|
||||||
|
range 0 131072
|
||||||
|
help
|
||||||
|
Because the external/internal RAM allocation strategy is not always perfect, it sometimes may happen
|
||||||
|
that the internal memory is entirely filled up. This causes allocations that are specifically done in
|
||||||
|
internal memory, for example the stack for new tasks or memory to service DMA or have memory that's
|
||||||
|
also available when SPI cache is down, to fail. This option reserves a pool specifically for requests
|
||||||
|
like that; the memory in this pool is not given out when a normal malloc() is called.
|
||||||
|
|
||||||
|
Set this to 0 to disable this feature.
|
||||||
|
|
||||||
|
Note that because FreeRTOS stacks are forced to internal memory, they will also use this memory pool;
|
||||||
|
be sure to keep this in mind when adjusting this value.
|
||||||
|
|
||||||
|
config SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
|
||||||
|
bool "Allow external memory as an argument to xTaskCreateStatic"
|
||||||
|
default n
|
||||||
|
depends on SPIRAM_USE_MALLOC
|
||||||
|
help
|
||||||
|
Because some bits of the ESP32 code environment cannot be recompiled with the cache workaround, normally
|
||||||
|
tasks cannot be safely run with their stack residing in external memory; for this reason xTaskCreate and
|
||||||
|
friends always allocate stack in internal memory and xTaskCreateStatic will check if the memory passed
|
||||||
|
to it is in internal memory. If you have a task that needs a large amount of stack and does not call on
|
||||||
|
ROM code in any way (no direct calls, but also no Bluetooth/WiFi), you can try to disable this and use
|
||||||
|
xTaskCreateStatic to create the tasks stack in external memory.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
config MEMMAP_TRACEMEM
|
config MEMMAP_TRACEMEM
|
||||||
@@ -776,6 +815,7 @@ config ESP32_WIFI_STATIC_TX_BUFFER
|
|||||||
bool "STATIC"
|
bool "STATIC"
|
||||||
config ESP32_WIFI_DYNAMIC_TX_BUFFER
|
config ESP32_WIFI_DYNAMIC_TX_BUFFER
|
||||||
bool "DYNAMIC"
|
bool "DYNAMIC"
|
||||||
|
depends on !SPIRAM_USE_MALLOC
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
config ESP32_WIFI_TX_BUFFER_TYPE
|
config ESP32_WIFI_TX_BUFFER_TYPE
|
||||||
|
@@ -262,6 +262,16 @@ void start_cpu0_default(void)
|
|||||||
ESP_EARLY_LOGE(TAG, "External RAM could not be added to heap!");
|
ESP_EARLY_LOGE(TAG, "External RAM could not be added to heap!");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
#if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL
|
||||||
|
r=esp_spiram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL);
|
||||||
|
if (r != ESP_OK) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "Could not reserve internal/DMA pool!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if CONFIG_SPIRAM_USE_MALLOC
|
||||||
|
heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//Enable trace memory and immediately start trace.
|
//Enable trace memory and immediately start trace.
|
||||||
|
@@ -64,4 +64,16 @@ void esp_spiram_writeback_cache();
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reserve a pool of internal memory for specific DMA/internal allocations
|
||||||
|
*
|
||||||
|
* @param size Size of reserved pool in bytes
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NO_MEM when no memory available for pool
|
||||||
|
*/
|
||||||
|
esp_err_t esp_spiram_reserve_dma_pool(size_t size);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@@ -222,7 +222,7 @@ static vector_desc_t *get_desc_for_int(int intno, int cpu)
|
|||||||
{
|
{
|
||||||
vector_desc_t *vd=find_desc_for_int(intno, cpu);
|
vector_desc_t *vd=find_desc_for_int(intno, cpu);
|
||||||
if (vd==NULL) {
|
if (vd==NULL) {
|
||||||
vector_desc_t *newvd=malloc(sizeof(vector_desc_t));
|
vector_desc_t *newvd=heap_caps_malloc(sizeof(vector_desc_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
|
||||||
if (newvd==NULL) return NULL;
|
if (newvd==NULL) return NULL;
|
||||||
memset(newvd, 0, sizeof(vector_desc_t));
|
memset(newvd, 0, sizeof(vector_desc_t));
|
||||||
newvd->intno=intno;
|
newvd->intno=intno;
|
||||||
@@ -574,7 +574,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
|
|||||||
if (source==ETS_INTERNAL_PROFILING_INTR_SOURCE) force=ETS_INTERNAL_PROFILING_INTR_NO;
|
if (source==ETS_INTERNAL_PROFILING_INTR_SOURCE) force=ETS_INTERNAL_PROFILING_INTR_NO;
|
||||||
|
|
||||||
//Allocate a return handle. If we end up not needing it, we'll free it later on.
|
//Allocate a return handle. If we end up not needing it, we'll free it later on.
|
||||||
ret=malloc(sizeof(intr_handle_data_t));
|
ret=heap_caps_malloc(sizeof(intr_handle_data_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
|
||||||
if (ret==NULL) return ESP_ERR_NO_MEM;
|
if (ret==NULL) return ESP_ERR_NO_MEM;
|
||||||
|
|
||||||
portENTER_CRITICAL(&spinlock);
|
portENTER_CRITICAL(&spinlock);
|
||||||
|
@@ -124,11 +124,24 @@ esp_err_t esp_spiram_init()
|
|||||||
|
|
||||||
esp_err_t esp_spiram_add_to_heapalloc()
|
esp_err_t esp_spiram_add_to_heapalloc()
|
||||||
{
|
{
|
||||||
|
ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", CONFIG_SPIRAM_SIZE/1024);
|
||||||
//Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, so there's
|
//Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, so there's
|
||||||
//no need to explicitly specify them.
|
//no need to explicitly specify them.
|
||||||
return heap_caps_add_region((intptr_t)SOC_EXTRAM_DATA_LOW, (intptr_t)SOC_EXTRAM_DATA_LOW + CONFIG_SPIRAM_SIZE-1);
|
return heap_caps_add_region((intptr_t)SOC_EXTRAM_DATA_LOW, (intptr_t)SOC_EXTRAM_DATA_LOW + CONFIG_SPIRAM_SIZE-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t *dma_heap;
|
||||||
|
|
||||||
|
esp_err_t esp_spiram_reserve_dma_pool(size_t size) {
|
||||||
|
if (size==0) return ESP_OK; //no-op
|
||||||
|
ESP_EARLY_LOGI(TAG, "Reserving pool of %dK of internal memory for DMA/internal allocations", size/1024);
|
||||||
|
dma_heap=heap_caps_malloc(size, MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL);
|
||||||
|
if (!dma_heap) return ESP_ERR_NO_MEM;
|
||||||
|
uint32_t caps[]={MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT};
|
||||||
|
return heap_caps_add_region_with_caps(caps, dma_heap, dma_heap+size-1);
|
||||||
|
}
|
||||||
|
|
||||||
size_t esp_spiram_get_size()
|
size_t esp_spiram_get_size()
|
||||||
{
|
{
|
||||||
return CONFIG_SPIRAM_SIZE;
|
return CONFIG_SPIRAM_SIZE;
|
||||||
|
@@ -360,12 +360,12 @@ void system_restore(void)
|
|||||||
|
|
||||||
uint32_t esp_get_free_heap_size( void )
|
uint32_t esp_get_free_heap_size( void )
|
||||||
{
|
{
|
||||||
return heap_caps_get_free_size( MALLOC_CAP_8BIT );
|
return heap_caps_get_free_size( MALLOC_CAP_DEFAULT );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t esp_get_minimum_free_heap_size( void )
|
uint32_t esp_get_minimum_free_heap_size( void )
|
||||||
{
|
{
|
||||||
return heap_caps_get_minimum_free_size( MALLOC_CAP_8BIT );
|
return heap_caps_get_minimum_free_size( MALLOC_CAP_DEFAULT );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t system_get_free_heap_size(void) __attribute__((alias("esp_get_free_heap_size")));
|
uint32_t system_get_free_heap_size(void) __attribute__((alias("esp_get_free_heap_size")));
|
||||||
|
@@ -82,6 +82,9 @@ extern "C" {
|
|||||||
#include "esp_crosscore_int.h"
|
#include "esp_crosscore_int.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <esp_heap_caps.h>
|
||||||
|
#include "soc/soc_memory_layout.h"
|
||||||
|
|
||||||
//#include "xtensa_context.h"
|
//#include "xtensa_context.h"
|
||||||
|
|
||||||
/*-----------------------------------------------------------
|
/*-----------------------------------------------------------
|
||||||
@@ -245,6 +248,18 @@ static inline unsigned portENTER_CRITICAL_NESTED() { unsigned state = XTOS_SET_I
|
|||||||
#define portSET_INTERRUPT_MASK_FROM_ISR() portENTER_CRITICAL_NESTED()
|
#define portSET_INTERRUPT_MASK_FROM_ISR() portENTER_CRITICAL_NESTED()
|
||||||
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state)
|
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state)
|
||||||
|
|
||||||
|
//Because the ROM routines don't necessarily handle a stack in external RAM correctly, we force
|
||||||
|
//the stack memory to always be internal.
|
||||||
|
#define pvPortMallocTcbMem(size) heap_caps_malloc(size, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
|
||||||
|
#define pvPortMallocStackMem(size) heap_caps_malloc(size, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
|
||||||
|
|
||||||
|
//xTaskCreateStatic uses these functions to check incoming memory.
|
||||||
|
#define portVALID_TCB_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr))
|
||||||
|
#ifndef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
|
||||||
|
#define portVALID_STACK_MEM(ptr) esp_ptr_byte_accessible(ptr)
|
||||||
|
#else
|
||||||
|
#define portVALID_STACK_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr))
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare
|
* Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare
|
||||||
|
@@ -677,8 +677,8 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
|
|||||||
TCB_t *pxNewTCB;
|
TCB_t *pxNewTCB;
|
||||||
TaskHandle_t xReturn;
|
TaskHandle_t xReturn;
|
||||||
|
|
||||||
configASSERT( puxStackBuffer != NULL );
|
configASSERT( portVALID_TCB_MEM(pxTaskBuffer) );
|
||||||
configASSERT( pxTaskBuffer != NULL );
|
configASSERT( portVALID_STACK_MEM(puxStackBuffer) );
|
||||||
configASSERT( (xCoreID>=0 && xCoreID<portNUM_PROCESSORS) || (xCoreID==tskNO_AFFINITY) );
|
configASSERT( (xCoreID>=0 && xCoreID<portNUM_PROCESSORS) || (xCoreID==tskNO_AFFINITY) );
|
||||||
|
|
||||||
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
|
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
|
||||||
@@ -724,7 +724,7 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
|
|||||||
/* Allocate space for the TCB. Where the memory comes from depends
|
/* Allocate space for the TCB. Where the memory comes from depends
|
||||||
on the implementation of the port malloc function and whether or
|
on the implementation of the port malloc function and whether or
|
||||||
not static allocation is being used. */
|
not static allocation is being used. */
|
||||||
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
|
pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) );
|
||||||
|
|
||||||
if( pxNewTCB != NULL )
|
if( pxNewTCB != NULL )
|
||||||
{
|
{
|
||||||
@@ -777,14 +777,14 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
|
|||||||
/* Allocate space for the TCB. Where the memory comes from depends on
|
/* Allocate space for the TCB. Where the memory comes from depends on
|
||||||
the implementation of the port malloc function and whether or not static
|
the implementation of the port malloc function and whether or not static
|
||||||
allocation is being used. */
|
allocation is being used. */
|
||||||
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
|
pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) );
|
||||||
|
|
||||||
if( pxNewTCB != NULL )
|
if( pxNewTCB != NULL )
|
||||||
{
|
{
|
||||||
/* Allocate space for the stack used by the task being created.
|
/* Allocate space for the stack used by the task being created.
|
||||||
The base of the stack memory stored in the TCB so the task can
|
The base of the stack memory stored in the TCB so the task can
|
||||||
be deleted later if required. */
|
be deleted later if required. */
|
||||||
pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
|
pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStackMem( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
|
||||||
|
|
||||||
if( pxNewTCB->pxStack == NULL )
|
if( pxNewTCB->pxStack == NULL )
|
||||||
{
|
{
|
||||||
@@ -799,12 +799,12 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
|
|||||||
StackType_t *pxStack;
|
StackType_t *pxStack;
|
||||||
|
|
||||||
/* Allocate space for the stack used by the task being created. */
|
/* Allocate space for the stack used by the task being created. */
|
||||||
pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
|
pxStack = ( StackType_t * ) pvPortMallocStackMem( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
|
||||||
|
|
||||||
if( pxStack != NULL )
|
if( pxStack != NULL )
|
||||||
{
|
{
|
||||||
/* Allocate space for the TCB. */
|
/* Allocate space for the TCB. */
|
||||||
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */
|
pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */
|
||||||
|
|
||||||
if( pxNewTCB != NULL )
|
if( pxNewTCB != NULL )
|
||||||
{
|
{
|
||||||
|
@@ -133,12 +133,36 @@ IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps )
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define MALLOC_DISABLE_EXTERNAL_ALLOCS -1
|
||||||
|
//Dual-use: -1 (=MALLOC_DISABLE_EXTERNAL_ALLOCS) disables allocations in external memory, >=0 sets the limit for allocations preferring internal memory.
|
||||||
|
static int malloc_alwaysinternal_limit=MALLOC_DISABLE_EXTERNAL_ALLOCS;
|
||||||
|
|
||||||
|
void heap_caps_malloc_extmem_enable(size_t limit)
|
||||||
|
{
|
||||||
|
malloc_alwaysinternal_limit=limit;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Default memory allocation implementation. Should return standard 8-bit memory. malloc() essentially resolves to this function.
|
Default memory allocation implementation. Should return standard 8-bit memory. malloc() essentially resolves to this function.
|
||||||
*/
|
*/
|
||||||
IRAM_ATTR void *heap_caps_malloc_default( size_t size )
|
IRAM_ATTR void *heap_caps_malloc_default( size_t size )
|
||||||
{
|
{
|
||||||
return heap_caps_malloc( size, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL );
|
if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {
|
||||||
|
return heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
|
||||||
|
} else {
|
||||||
|
void *r;
|
||||||
|
if (size <= malloc_alwaysinternal_limit) {
|
||||||
|
r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
|
||||||
|
} else {
|
||||||
|
r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM );
|
||||||
|
}
|
||||||
|
if (r==NULL) {
|
||||||
|
//try again while being less picky
|
||||||
|
r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT );
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -147,7 +171,21 @@ IRAM_ATTR void *heap_caps_malloc_default( size_t size )
|
|||||||
*/
|
*/
|
||||||
IRAM_ATTR void *heap_caps_realloc_default( void *ptr, size_t size )
|
IRAM_ATTR void *heap_caps_realloc_default( void *ptr, size_t size )
|
||||||
{
|
{
|
||||||
return heap_caps_realloc( ptr, size, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL );
|
if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {
|
||||||
|
return heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
|
||||||
|
} else {
|
||||||
|
void *r;
|
||||||
|
if (size <= malloc_alwaysinternal_limit) {
|
||||||
|
r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
|
||||||
|
} else {
|
||||||
|
r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM );
|
||||||
|
}
|
||||||
|
if (r==NULL && size>0) {
|
||||||
|
//We needed to allocate memory, but we didn't. Try again while being less picky.
|
||||||
|
r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT );
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -230,6 +230,14 @@ esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start,
|
|||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check if region overlaps the start and/or end of an existing region. If so, the
|
||||||
|
//region is invalid (or maybe added twice)
|
||||||
|
heap_t *heap;
|
||||||
|
SLIST_FOREACH(heap, ®istered_heaps, next) {
|
||||||
|
if ( start <= heap->start && heap->start <=end ) return ESP_FAIL;
|
||||||
|
if ( start <= heap->end && heap->end <=end ) return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
heap_t *p_new = malloc(sizeof(heap_t));
|
heap_t *p_new = malloc(sizeof(heap_t));
|
||||||
if (p_new == NULL) {
|
if (p_new == NULL) {
|
||||||
err = ESP_ERR_NO_MEM;
|
err = ESP_ERR_NO_MEM;
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
#define MALLOC_CAP_PID7 (1<<9) ///< Memory must be mapped to PID7 memory space (PIDs are not currently used)
|
#define MALLOC_CAP_PID7 (1<<9) ///< Memory must be mapped to PID7 memory space (PIDs are not currently used)
|
||||||
#define MALLOC_CAP_SPIRAM (1<<10) ///< Memory must be in SPI RAM
|
#define MALLOC_CAP_SPIRAM (1<<10) ///< Memory must be in SPI RAM
|
||||||
#define MALLOC_CAP_INTERNAL (1<<11) ///< Memory must be internal; specifically it should not disappear when flash/spiram cache is switched off
|
#define MALLOC_CAP_INTERNAL (1<<11) ///< Memory must be internal; specifically it should not disappear when flash/spiram cache is switched off
|
||||||
|
#define MALLOC_CAP_DEFAULT (1<<12) ///< Memory can be returned in a non-capability-specific memory allocation (e.g. malloc(), calloc()) call
|
||||||
#define MALLOC_CAP_INVALID (1<<31) ///< Memory can't be used / list end marker
|
#define MALLOC_CAP_INVALID (1<<31) ///< Memory can't be used / list end marker
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,3 +173,18 @@ void heap_caps_print_heap_info( uint32_t caps );
|
|||||||
* @return True if all heaps are valid, False if at least one heap is corrupt.
|
* @return True if all heaps are valid, False if at least one heap is corrupt.
|
||||||
*/
|
*/
|
||||||
bool heap_caps_check_integrity(uint32_t caps, bool print_errors);
|
bool heap_caps_check_integrity(uint32_t caps, bool print_errors);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable malloc() in external memory and set limit below which
|
||||||
|
* malloc() attempts are placed in internal memory.
|
||||||
|
*
|
||||||
|
* When external memory is in use, the allocation strategy is to initially try to
|
||||||
|
* satisfy smaller allocation requests with internal memory and larger requests
|
||||||
|
* with external memory. This sets the limit between the two, as well as generally
|
||||||
|
* enabling allocation in external memory.
|
||||||
|
*
|
||||||
|
* @param limit Limit, in bytes.
|
||||||
|
*/
|
||||||
|
void heap_caps_malloc_extmem_enable(size_t limit);
|
||||||
|
@@ -73,8 +73,11 @@ esp_err_t heap_caps_add_region(intptr_t start, intptr_t end);
|
|||||||
* @param start Start address of new region.
|
* @param start Start address of new region.
|
||||||
* @param end End address of new region.
|
* @param end End address of new region.
|
||||||
*
|
*
|
||||||
* @return ESP_OK on success, ESP_ERR_INVALID_ARG if a parameter is invalid, ESP_ERR_NO_MEM if no
|
* @return
|
||||||
* memory to register new heap.
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG if a parameter is invalid
|
||||||
|
* - ESP_ERR_NO_MEM if no memory to register new heap.
|
||||||
|
* - ESP_FAIL if region overlaps the start and/or end of an existing region
|
||||||
*/
|
*/
|
||||||
esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start, intptr_t end);
|
esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start, intptr_t end);
|
||||||
|
|
||||||
|
@@ -15,32 +15,42 @@
|
|||||||
#include "soc/uart_reg.h"
|
#include "soc/uart_reg.h"
|
||||||
#include "soc/dport_reg.h"
|
#include "soc/dport_reg.h"
|
||||||
#include "soc/io_mux_reg.h"
|
#include "soc/io_mux_reg.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
|
||||||
#include "esp_panic.h"
|
#include "esp_panic.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int **allocatedMem;
|
||||||
|
static int noAllocated;
|
||||||
|
|
||||||
|
|
||||||
static int tryAllocMem() {
|
static int tryAllocMem() {
|
||||||
int **mem;
|
int i, j;
|
||||||
int i, noAllocated, j;
|
const int allocateMaxK=1024*5; //try to allocate a max of 5MiB
|
||||||
|
|
||||||
mem=malloc(sizeof(int *)*1024);
|
allocatedMem=malloc(sizeof(int *)*allocateMaxK);
|
||||||
if (!mem) return 0;
|
if (!allocatedMem) return 0;
|
||||||
|
|
||||||
for (i=0; i<1024; i++) {
|
for (i=0; i<allocateMaxK; i++) {
|
||||||
mem[i]=malloc(1024);
|
allocatedMem[i]=malloc(1024);
|
||||||
if (mem[i]==NULL) break;
|
if (allocatedMem[i]==NULL) break;
|
||||||
for (j=0; j<1024/4; j++) mem[i][j]=(0xdeadbeef);
|
for (j=0; j<1024/4; j++) allocatedMem[i][j]=(0xdeadbeef);
|
||||||
}
|
}
|
||||||
|
|
||||||
noAllocated=i;
|
noAllocated=i;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void tryAllocMemFree() {
|
||||||
|
int i, j;
|
||||||
for (i=0; i<noAllocated; i++) {
|
for (i=0; i<noAllocated; i++) {
|
||||||
for (j=0; j<1024/4; j++) {
|
for (j=0; j<1024/4; j++) {
|
||||||
TEST_ASSERT(mem[i][j]==(0xdeadbeef));
|
TEST_ASSERT(allocatedMem[i][j]==(0xdeadbeef));
|
||||||
}
|
}
|
||||||
free(mem[i]);
|
free(allocatedMem[i]);
|
||||||
}
|
}
|
||||||
free(mem);
|
free(allocatedMem);
|
||||||
return noAllocated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -48,8 +58,33 @@ TEST_CASE("Malloc/overwrite, then free all available DRAM", "[heap]")
|
|||||||
{
|
{
|
||||||
int m1=0, m2=0;
|
int m1=0, m2=0;
|
||||||
m1=tryAllocMem();
|
m1=tryAllocMem();
|
||||||
|
tryAllocMemFree();
|
||||||
m2=tryAllocMem();
|
m2=tryAllocMem();
|
||||||
|
tryAllocMemFree();
|
||||||
printf("Could allocate %dK on first try, %dK on 2nd try.\n", m1, m2);
|
printf("Could allocate %dK on first try, %dK on 2nd try.\n", m1, m2);
|
||||||
TEST_ASSERT(m1==m2);
|
TEST_ASSERT(m1==m2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if CONFIG_SPIRAM_USE_MALLOC
|
||||||
|
|
||||||
|
#if (CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL > 1024)
|
||||||
|
TEST_CASE("Check if reserved DMA pool still can allocate even when malloc()'ed memory is exhausted", "[heap]")
|
||||||
|
{
|
||||||
|
char** dmaMem=malloc(sizeof(char*)*512);
|
||||||
|
assert(dmaMem);
|
||||||
|
int m=tryAllocMem();
|
||||||
|
int i=0;
|
||||||
|
for (i=0; i<512; i++) {
|
||||||
|
dmaMem[i]=heap_caps_malloc(1024, MALLOC_CAP_DMA);
|
||||||
|
if (dmaMem[i]==NULL) break;
|
||||||
|
}
|
||||||
|
for (int j=0; j<i; j++) free(dmaMem[j]);
|
||||||
|
free(dmaMem);
|
||||||
|
tryAllocMemFree();
|
||||||
|
printf("Could allocate %dK of DMA memory after allocating all of %dK of normal memory.\n", i, m);
|
||||||
|
TEST_ASSERT(i);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@@ -354,7 +354,7 @@ void esp_log_buffer_hex_internal(const char *tag, const void *buffer, uint16_t b
|
|||||||
} else {
|
} else {
|
||||||
bytes_cur_line = buff_len;
|
bytes_cur_line = buff_len;
|
||||||
}
|
}
|
||||||
if ( !esp_ptr_byte_accesible(buffer) ) {
|
if ( !esp_ptr_byte_accessible(buffer) ) {
|
||||||
//use memcpy to get around alignment issue
|
//use memcpy to get around alignment issue
|
||||||
memcpy( temp_buffer, buffer, (bytes_cur_line+3)/4*4 );
|
memcpy( temp_buffer, buffer, (bytes_cur_line+3)/4*4 );
|
||||||
ptr_line = temp_buffer;
|
ptr_line = temp_buffer;
|
||||||
@@ -386,7 +386,7 @@ void esp_log_buffer_char_internal(const char *tag, const void *buffer, uint16_t
|
|||||||
} else {
|
} else {
|
||||||
bytes_cur_line = buff_len;
|
bytes_cur_line = buff_len;
|
||||||
}
|
}
|
||||||
if ( !esp_ptr_byte_accesible(buffer) ) {
|
if ( !esp_ptr_byte_accessible(buffer) ) {
|
||||||
//use memcpy to get around alignment issue
|
//use memcpy to get around alignment issue
|
||||||
memcpy( temp_buffer, buffer, (bytes_cur_line+3)/4*4 );
|
memcpy( temp_buffer, buffer, (bytes_cur_line+3)/4*4 );
|
||||||
ptr_line = temp_buffer;
|
ptr_line = temp_buffer;
|
||||||
@@ -421,7 +421,7 @@ void esp_log_buffer_hexdump_internal( const char *tag, const void *buffer, uint1
|
|||||||
} else {
|
} else {
|
||||||
bytes_cur_line = buff_len;
|
bytes_cur_line = buff_len;
|
||||||
}
|
}
|
||||||
if ( !esp_ptr_byte_accesible(buffer) ) {
|
if ( !esp_ptr_byte_accessible(buffer) ) {
|
||||||
//use memcpy to get around alignment issue
|
//use memcpy to get around alignment issue
|
||||||
memcpy( temp_buffer, buffer, (bytes_cur_line+3)/4*4 );
|
memcpy( temp_buffer, buffer, (bytes_cur_line+3)/4*4 );
|
||||||
ptr_line = temp_buffer;
|
ptr_line = temp_buffer;
|
||||||
|
@@ -295,10 +295,15 @@
|
|||||||
#define SOC_DMA_LOW 0x3FFAE000
|
#define SOC_DMA_LOW 0x3FFAE000
|
||||||
#define SOC_DMA_HIGH 0x40000000
|
#define SOC_DMA_HIGH 0x40000000
|
||||||
|
|
||||||
// Region of memory that is byte-accessible. See esp_ptr_byte_accesible().
|
// Region of memory that is byte-accessible. See esp_ptr_byte_accessible().
|
||||||
#define SOC_BYTE_ACCESSIBLE_LOW 0x3FFAE000
|
#define SOC_BYTE_ACCESSIBLE_LOW 0x3FFAE000
|
||||||
#define SOC_BYTE_ACCESSIBLE_HIGH 0x40000000
|
#define SOC_BYTE_ACCESSIBLE_HIGH 0x40000000
|
||||||
|
|
||||||
|
//Region of memory that is internal, as in on the same silicon die as the ESP32 CPUs (excluding RTC data region, that's checked separately.) See esp_ptr_internal().
|
||||||
|
#define SOC_MEM_INTERNAL_LOW 0x3F400000
|
||||||
|
#define SOC_MEM_INTERNAL_HIGH 0x400C2000
|
||||||
|
|
||||||
|
|
||||||
//Interrupt hardware source table
|
//Interrupt hardware source table
|
||||||
//This table is decided by hardware, don't touch this.
|
//This table is decided by hardware, don't touch this.
|
||||||
#define ETS_WIFI_MAC_INTR_SOURCE 0/**< interrupt of WiFi MAC, level*/
|
#define ETS_WIFI_MAC_INTR_SOURCE 0/**< interrupt of WiFi MAC, level*/
|
||||||
|
@@ -31,7 +31,7 @@ Each type contains an array of prioritised capabilities; types with later entrie
|
|||||||
ones can't fulfill the memory request.
|
ones can't fulfill the memory request.
|
||||||
|
|
||||||
The prioritised capabilities work roughly like this:
|
The prioritised capabilities work roughly like this:
|
||||||
- For a normal malloc (MALLOC_CAP_8BIT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions,
|
- For a normal malloc (MALLOC_CAP_DEFAULT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions,
|
||||||
finally eat into the application memory.
|
finally eat into the application memory.
|
||||||
- For a malloc where 32-bit-aligned-only access is okay, first allocate IRAM, then DRAM, finally application IRAM.
|
- For a malloc where 32-bit-aligned-only access is okay, first allocate IRAM, then DRAM, finally application IRAM.
|
||||||
- Application mallocs (PIDx) will allocate IRAM first, if possible, then DRAM.
|
- Application mallocs (PIDx) will allocate IRAM first, if possible, then DRAM.
|
||||||
@@ -40,10 +40,10 @@ The prioritised capabilities work roughly like this:
|
|||||||
*/
|
*/
|
||||||
const soc_memory_type_desc_t soc_memory_types[] = {
|
const soc_memory_type_desc_t soc_memory_types[] = {
|
||||||
//Type 0: Plain ole D-port RAM
|
//Type 0: Plain ole D-port RAM
|
||||||
{ "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL, MALLOC_CAP_32BIT, 0 }, false, false},
|
{ "DRAM", { MALLOC_CAP_8BIT|MALLOC_CAP_DEFAULT, MALLOC_CAP_INTERNAL|MALLOC_CAP_DMA|MALLOC_CAP_32BIT, 0 }, false, false},
|
||||||
//Type 1: Plain ole D-port RAM which has an alias on the I-port
|
//Type 1: Plain ole D-port RAM which has an alias on the I-port
|
||||||
//(This DRAM is also the region used by ROM during startup)
|
//(This DRAM is also the region used by ROM during startup)
|
||||||
{ "D/IRAM", { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, true, true},
|
{ "D/IRAM", { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL|MALLOC_CAP_DEFAULT, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, true, true},
|
||||||
//Type 2: IRAM
|
//Type 2: IRAM
|
||||||
{ "IRAM", { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL, 0, 0 }, false, false},
|
{ "IRAM", { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL, 0, 0 }, false, false},
|
||||||
//Type 3-8: PID 2-7 IRAM
|
//Type 3-8: PID 2-7 IRAM
|
||||||
@@ -54,14 +54,14 @@ const soc_memory_type_desc_t soc_memory_types[] = {
|
|||||||
{ "PID6IRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
|
{ "PID6IRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
|
||||||
{ "PID7IRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
|
{ "PID7IRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
|
||||||
//Type 9-14: PID 2-7 DRAM
|
//Type 9-14: PID 2-7 DRAM
|
||||||
{ "PID2DRAM", { MALLOC_CAP_PID2|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
|
{ "PID2DRAM", { MALLOC_CAP_PID2|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
|
||||||
{ "PID3DRAM", { MALLOC_CAP_PID3|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
|
{ "PID3DRAM", { MALLOC_CAP_PID3|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
|
||||||
{ "PID4DRAM", { MALLOC_CAP_PID4|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
|
{ "PID4DRAM", { MALLOC_CAP_PID4|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
|
||||||
{ "PID5DRAM", { MALLOC_CAP_PID5|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
|
{ "PID5DRAM", { MALLOC_CAP_PID5|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
|
||||||
{ "PID6DRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
|
{ "PID6DRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
|
||||||
{ "PID7DRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
|
{ "PID7DRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
|
||||||
//Type 15: SPI SRAM data
|
//Type 15: SPI SRAM data
|
||||||
{ "SPIRAM", { MALLOC_CAP_SPIRAM, 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, false, false},
|
{ "SPIRAM", { MALLOC_CAP_SPIRAM|MALLOC_CAP_DEFAULT, 0, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, false, false},
|
||||||
};
|
};
|
||||||
|
|
||||||
const size_t soc_memory_type_count = sizeof(soc_memory_types)/sizeof(soc_memory_type_desc_t);
|
const size_t soc_memory_type_count = sizeof(soc_memory_types)/sizeof(soc_memory_type_desc_t);
|
||||||
|
@@ -17,6 +17,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "soc/soc.h"
|
#include "soc/soc.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
|
||||||
#define SOC_MEMORY_TYPE_NO_PRIOS 3
|
#define SOC_MEMORY_TYPE_NO_PRIOS 3
|
||||||
|
|
||||||
@@ -58,12 +60,12 @@ typedef struct
|
|||||||
extern const soc_reserved_region_t soc_reserved_regions[];
|
extern const soc_reserved_region_t soc_reserved_regions[];
|
||||||
extern const size_t soc_reserved_region_count;
|
extern const size_t soc_reserved_region_count;
|
||||||
|
|
||||||
inline static bool esp_ptr_dma_capable(const void *p)
|
inline static bool IRAM_ATTR esp_ptr_dma_capable(const void *p)
|
||||||
{
|
{
|
||||||
return (intptr_t)p >= SOC_DMA_LOW && (intptr_t)p < SOC_DMA_HIGH;
|
return (intptr_t)p >= SOC_DMA_LOW && (intptr_t)p < SOC_DMA_HIGH;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static bool esp_ptr_executable(const void *p)
|
inline static bool IRAM_ATTR esp_ptr_executable(const void *p)
|
||||||
{
|
{
|
||||||
intptr_t ip = (intptr_t) p;
|
intptr_t ip = (intptr_t) p;
|
||||||
return (ip >= SOC_IROM_LOW && ip < SOC_IROM_HIGH)
|
return (ip >= SOC_IROM_LOW && ip < SOC_IROM_HIGH)
|
||||||
@@ -71,8 +73,19 @@ inline static bool esp_ptr_executable(const void *p)
|
|||||||
|| (ip >= SOC_RTC_IRAM_LOW && ip < SOC_RTC_IRAM_HIGH);
|
|| (ip >= SOC_RTC_IRAM_LOW && ip < SOC_RTC_IRAM_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool esp_ptr_byte_accesible(const void *p)
|
inline static bool IRAM_ATTR esp_ptr_byte_accessible(const void *p)
|
||||||
{
|
{
|
||||||
//currently only support DRAM, add PSRAM region in the future
|
bool r;
|
||||||
return (intptr_t)p >= SOC_BYTE_ACCESSIBLE_LOW && (intptr_t)p < SOC_BYTE_ACCESSIBLE_HIGH;
|
r = ((intptr_t)p >= SOC_BYTE_ACCESSIBLE_LOW && (intptr_t)p < SOC_BYTE_ACCESSIBLE_HIGH);
|
||||||
|
#if CONFIG_SPIRAM_SUPPORT
|
||||||
|
r |= ((intptr_t)p >= SOC_EXTRAM_DATA_LOW && (intptr_t)p < SOC_EXTRAM_DATA_HIGH);
|
||||||
|
#endif
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool IRAM_ATTR esp_ptr_internal(const void *p) {
|
||||||
|
bool r;
|
||||||
|
r = ((intptr_t)p >= SOC_MEM_INTERNAL_LOW && (intptr_t)p < SOC_MEM_INTERNAL_HIGH);
|
||||||
|
r |= ((intptr_t)p >= SOC_RTC_DATA_LOW && (intptr_t)p < SOC_RTC_DATA_HIGH);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
@@ -135,7 +135,7 @@ esp_err_t IRAM_ATTR spi_flash_mmap_pages(int *pages, size_t page_count, spi_flas
|
|||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mmap_entry_t* new_entry = (mmap_entry_t*) malloc(sizeof(mmap_entry_t));
|
mmap_entry_t* new_entry = (mmap_entry_t*) heap_caps_malloc(sizeof(mmap_entry_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
|
||||||
if (new_entry == 0) {
|
if (new_entry == 0) {
|
||||||
return ESP_ERR_NO_MEM;
|
return ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
|
104
docs/api-guides/external-ram.rst
Normal file
104
docs/api-guides/external-ram.rst
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
Support for external RAM
|
||||||
|
************************
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
The ESP32 has a few hundred KiB of internal RAM, residing on the same die as the rest of the ESP32. For some purposes, this is insufficient,
|
||||||
|
and therefore the ESP32 incorporates the ability to also use up to 4MiB of external SPI RAM memory as memory. The external memory is incorporated
|
||||||
|
in the memory map and is, within certain restrictions, usable in the same way internal data RAM is.
|
||||||
|
|
||||||
|
Hardware
|
||||||
|
========
|
||||||
|
|
||||||
|
The ESP32 supports SPI (P)SRAM connected in parallel with the SPI flash chip. While the ESP32 is capable of supporting several types
|
||||||
|
of RAM chips, the ESP32 SDK at the moment only supports the ESP-PSRAM32 chip.
|
||||||
|
|
||||||
|
The ESP-PSRAM32 chip is an 1.8V device, and can only be used in parallel with an 1.8V flash part. Make sure to either set the MTDI
|
||||||
|
pin to a high signal level on bootup, or program the fuses in the ESP32 to always use a VDD_SIO level of 1.8V. Not doing this risks
|
||||||
|
damaging the PSRAM and/or flash chip.
|
||||||
|
|
||||||
|
To connect the ESP-PSRAM chip to the ESP32D0W*, connect the following signals:
|
||||||
|
* PSRAM /CE (pin 1) - ESP32 GPIO 16
|
||||||
|
* PSRAM SO (pin 2) - flash DO
|
||||||
|
* PSRAM SIO[2] (pin 3) - flash WP
|
||||||
|
* PSRAM SI (pin 5) - flash DI
|
||||||
|
* PSRAM SCLK (pin 6) - ESP32 GPIO 17
|
||||||
|
* PSRAM SIO[3] (pin 7) - flash HOLD
|
||||||
|
* PSRAM Vcc (pin 8) - ESP32 VCC_SDIO
|
||||||
|
|
||||||
|
Connections for the ESP32D2W* chips are TBD.
|
||||||
|
|
||||||
|
.. NOTE::
|
||||||
|
Espressif sells an ESP-WROVER module which contains an ESP32, 1.8V flash and the ESP-PSRAM32 integrated in a module, ready for inclusion
|
||||||
|
on an end product PCB.
|
||||||
|
|
||||||
|
Software
|
||||||
|
========
|
||||||
|
|
||||||
|
ESP-IDF fully supports integrating external memory use into your applications. ESP-IDF can be configured to handle external RAM in several ways:
|
||||||
|
* Only initialize RAM. This allows the application to manually place data here by dereferencing pointers pointed at the external RAM memory
|
||||||
|
region (0x3F800000 and up).
|
||||||
|
* Initialize RAM and add it to the capability allocator. This allows a program to specifically allocate a chunk of external RAM using
|
||||||
|
``heap_caps_malloc(size, MALLOC_CAP_SPIRAM)``. This memory can be used and subsequently freed using a normal ``free()`` call.
|
||||||
|
* Initialize RAM, add it to the capability allocator and add memory to the pool of RAM that can be returned by ``malloc()``. This allows
|
||||||
|
any application to use the external RAM without having to rewrite the code to use ``heap_caps_malloc``.
|
||||||
|
|
||||||
|
All these options can be selected from the menuconfig menu.
|
||||||
|
|
||||||
|
Restrictions
|
||||||
|
------------
|
||||||
|
|
||||||
|
The use of external RAM has a few restrictions:
|
||||||
|
* When disabling flash cache (for example, because the flash is being written to), the external RAM also becomes inaccessible; any reads from or
|
||||||
|
writes to it will lead to an illegal cache access exception. This is also the reason that ESP-IDF will never allocate a tasks stack in external
|
||||||
|
RAM.
|
||||||
|
* External RAM cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into. Any
|
||||||
|
buffers that will be used in combination with DMA must be allocated using ``heap_caps_malloc(size, MALLOC_CAP_DMA)`` (and can be freed using a
|
||||||
|
standard ``free()`` call.)
|
||||||
|
* External RAM uses the same cache region as the external flash. This means that often accessed variables in external RAM can be read and
|
||||||
|
modified almost as quickly as in internal ram. However, when accessing large chunks of data (>32K), the cache can be insufficient and speeds
|
||||||
|
will fall back to the access speed of the external RAM. Moreover, accessing large chunks of data can 'push out' cached flash, possibly making
|
||||||
|
execution of code afterwards slower.
|
||||||
|
* External RAM cannot be used as task stack memory; because of this, xTaskCreate and similar functions will always allocate internal memory
|
||||||
|
for stack and task TCBs and xTaskCreateStatic-type functions will check if the buffers passed are internal. However, for tasks not calling
|
||||||
|
on code in ROM in any way, directly or indirectly, the menuconfig option :ref:`CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY` will eliminate
|
||||||
|
the check in xTaskCreateStatic, allowing task stack in external RAM. Using this is not advised, however.
|
||||||
|
|
||||||
|
|
||||||
|
Because there are a fair few situations that have a specific need for internal memory, but it is also possible to use malloc() to exhaust
|
||||||
|
internal memory, there is a pool reserved specifically for requests that cannot be resolved from external memory; allocating task
|
||||||
|
stack, DMA buffers and memory that stays accessible when cache is disabled is drawn from this pool. The size of this pool is configurable
|
||||||
|
in menuconfig.
|
||||||
|
|
||||||
|
|
||||||
|
Chip revisions
|
||||||
|
==============
|
||||||
|
|
||||||
|
There are some issues with certain revisions of the ESP32 that have repercussions for use with external RAM. These are documented in the ESP32
|
||||||
|
ECO_ document. In particular, ESP-IDF handles the bugs mentioned in the following ways:
|
||||||
|
|
||||||
|
ESP32 rev v0
|
||||||
|
------------
|
||||||
|
ESP-IDF has no workaround for the bugs in this revision of silicon, and it cannot be used to map external PSRAM into the ESP32s main memory map.
|
||||||
|
|
||||||
|
ESP32 rev v1
|
||||||
|
------------
|
||||||
|
The bugs in this silicon revision introduce a hazard when certain sequences of machine instructions operate on external memory locations (ESP32 ECO 3.2).
|
||||||
|
To work around this, the gcc compiler to compile ESP-IDF has been expanded with a flag: ``-mfix-esp32-psram-cache-issue``. With this flag passed to gcc
|
||||||
|
on the command line, the compiler works around these sequences and only outputs code that can safely be executed.
|
||||||
|
|
||||||
|
In ESP-IDF, this flag is enabled when you select :ref:`CONFIG_SPIRAM_CACHE_WORKAROUND`. ESP-IDF also takes other measures to make
|
||||||
|
sure no combination of PSRAM access plus the offending instruction sets are used: it links to a version of Newlib recompiled with the gcc flag, doesn't use
|
||||||
|
some ROM functions and allocates static memory for the WiFi stack.
|
||||||
|
|
||||||
|
.. _ECO: https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -20,3 +20,4 @@ API Guides
|
|||||||
Console Component <console>
|
Console Component <console>
|
||||||
ROM debug console <romconsole>
|
ROM debug console <romconsole>
|
||||||
WiFi Driver <wifi>
|
WiFi Driver <wifi>
|
||||||
|
External SPI-connected RAM <external-ram>
|
||||||
|
Reference in New Issue
Block a user