heap: Refactor heap regions/capabilities out of FreeRTOS

Remove tagged heap API, rename caps_xxx to heap_caps_xxx

Also includes additional heap_caps_xxx inspection functions.
This commit is contained in:
Angus Gratton 2017-05-03 18:03:28 +10:00 committed by Angus Gratton
parent 5ee49fd311
commit 71c70cb15c
37 changed files with 1166 additions and 995 deletions

View File

@ -221,6 +221,15 @@ test_wl_on_host:
- cd components/wear_levelling/test_wl_host
- make test
test_multi_heap_on_host:
stage: test
image: $CI_DOCKER_REGISTRY/esp32-ci-env
tags:
- wl_host_test
script:
- cd components/heap/test_multi_heap_host
- make test
test_build_system:
stage: test
image: $CI_DOCKER_REGISTRY/esp32-ci-env

View File

@ -31,7 +31,7 @@
#include "rom/lldesc.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_heap_alloc_caps.h"
#include "esp_heap_caps.h"
#include "driver/spi_common.h"

View File

@ -57,7 +57,7 @@ queue and re-enabling the interrupt will trigger the interrupt again, which can
#include "rom/lldesc.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_heap_alloc_caps.h"
#include "esp_heap_caps.h"
typedef struct spi_device_t spi_device_t;
@ -122,8 +122,8 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus
int dma_desc_ct=(bus_config->max_transfer_sz+SPI_MAX_DMA_LEN-1)/SPI_MAX_DMA_LEN;
if (dma_desc_ct==0) dma_desc_ct=1; //default to 4k when max is not given
spihost[host]->max_transfer_sz = dma_desc_ct*SPI_MAX_DMA_LEN;
spihost[host]->dmadesc_tx=pvPortMallocCaps(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
spihost[host]->dmadesc_rx=pvPortMallocCaps(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
spihost[host]->dmadesc_tx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
spihost[host]->dmadesc_rx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
if (!spihost[host]->dmadesc_tx || !spihost[host]->dmadesc_rx) goto nomem;
}
esp_intr_alloc(spicommon_irqsource_for_host(host), ESP_INTR_FLAG_INTRDISABLED, spi_intr, (void*)spihost[host], &spihost[host]->intr);

View File

@ -36,7 +36,7 @@
#include "rom/lldesc.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_heap_alloc_caps.h"
#include "esp_heap_caps.h"
static const char *SPI_TAG = "spi_slave";
#define SPI_CHECK(a, str, ret_val) \
@ -89,8 +89,8 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
int dma_desc_ct = (bus_config->max_transfer_sz + SPI_MAX_DMA_LEN - 1) / SPI_MAX_DMA_LEN;
if (dma_desc_ct == 0) dma_desc_ct = 1; //default to 4k when max is not given
spihost[host]->max_transfer_sz = dma_desc_ct * SPI_MAX_DMA_LEN;
spihost[host]->dmadesc_tx = pvPortMallocCaps(sizeof(lldesc_t) * dma_desc_ct, MALLOC_CAP_DMA);
spihost[host]->dmadesc_rx = pvPortMallocCaps(sizeof(lldesc_t) * dma_desc_ct, MALLOC_CAP_DMA);
spihost[host]->dmadesc_tx = heap_caps_malloc(sizeof(lldesc_t) * dma_desc_ct, MALLOC_CAP_DMA);
spihost[host]->dmadesc_rx = heap_caps_malloc(sizeof(lldesc_t) * dma_desc_ct, MALLOC_CAP_DMA);
if (!spihost[host]->dmadesc_tx || !spihost[host]->dmadesc_rx) goto nomem;
} else {
//We're limited to non-DMA transfers: the SPI work registers can hold 64 bytes at most.

View File

@ -18,7 +18,7 @@
#include "soc/dport_reg.h"
#include "soc/spi_reg.h"
#include "soc/spi_struct.h"
#include "esp_heap_alloc_caps.h"
#include "esp_heap_caps.h"
static void check_spi_pre_n_for(int clk, int pre, int n)
@ -119,8 +119,8 @@ static void spi_test(spi_device_handle_t handle, int num_bytes) {
esp_err_t ret;
int x;
srand(num_bytes);
char *sendbuf=pvPortMallocCaps(num_bytes, MALLOC_CAP_DMA);
char *recvbuf=pvPortMallocCaps(num_bytes, MALLOC_CAP_DMA);
char *sendbuf=heap_caps_malloc(num_bytes, MALLOC_CAP_DMA);
char *recvbuf=heap_caps_malloc(num_bytes, MALLOC_CAP_DMA);
for (x=0; x<num_bytes; x++) {
sendbuf[x]=rand()&0xff;
recvbuf[x]=0x55;

View File

@ -40,7 +40,7 @@
#include "tcpip_adapter.h"
#include "esp_heap_alloc_caps.h"
#include "esp_heap_caps.h"
#include "sdkconfig.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
@ -167,8 +167,7 @@ void IRAM_ATTR call_start_cpu0()
memory also used by the ROM. Starting the app cpu will let its ROM initialize that memory,
corrupting those linked lists. Initializing the allocator *after* the app cpu has booted
works around this problem. */
heap_alloc_caps_init();
heap_caps_init();
ESP_EARLY_LOGI(TAG, "Pro cpu start user code");
start_cpu0();
@ -336,7 +335,7 @@ static void main_task(void* args)
}
#endif
//Enable allocation in region where the startup stacks were located.
heap_alloc_enable_nonos_stack_tag();
heap_caps_enable_nonos_stack_heaps();
app_main();
vTaskDelete(NULL);
}

View File

@ -95,6 +95,13 @@ uint32_t esp_get_free_heap_size(void);
*/
uint32_t system_get_free_heap_size(void) __attribute__ ((deprecated));
/**
* @brief Get the minimum heap that has ever been available
*
* @return Minimum free heap ever available
*/
uint32_t esp_get_minimum_free_heap_size( void );
/**
* @brief Get one random 32-bit word from hardware RNG
*

View File

@ -83,12 +83,12 @@ SECTIONS
_iram_text_start = ABSOLUTE(.);
*(.iram1 .iram1.*)
*libfreertos.a:(.literal .text .literal.* .text.*)
*libheap.a:(.literal .text .literal.* .text.*)
*libheap.a:multi_heap.o(.literal .text .literal.* .text.*)
*libesp32.a:panic.o(.literal .text .literal.* .text.*)
*libesp32.a:core_dump.o(.literal .text .literal.* .text.*)
*libesp32.a:heap_alloc_caps.o(.literal .text .literal.* .text.*)
*libapp_trace.a:(.literal .text .literal.* .text.*)
*libxtensa-debug-module.a:eri.o(.literal .text .literal.* .text.*)
*libesp32.a:app_trace.o(.literal .text .literal.* .text.*)
*libphy.a:(.literal .text .literal.* .text.*)
*librtc.a:(.literal .text .literal.* .text.*)
*libsoc.a:(.literal .text .literal.* .text.*)
@ -115,6 +115,7 @@ SECTIONS
*libesp32.a:panic.o(.rodata .rodata.*)
*libphy.a:(.rodata .rodata.*)
*libapp_trace.a:(.rodata .rodata.*)
*libheap.a:multi_heap.o(.rodata .rodata.*)
_data_end = ABSOLUTE(.);
. = ALIGN(4);
} >dram0_0_seg

View File

@ -33,6 +33,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/xtensa_api.h"
#include "esp_heap_caps.h"
static const char* TAG = "system_api";
@ -330,9 +331,19 @@ void IRAM_ATTR esp_restart_noos()
void system_restart(void) __attribute__((alias("esp_restart")));
uint32_t esp_get_free_heap_size(void)
void system_restore(void)
{
return xPortGetFreeHeapSize();
esp_wifi_restore();
}
uint32_t esp_get_free_heap_size( void )
{
return heap_caps_get_free_size( MALLOC_CAP_8BIT );
}
uint32_t esp_get_minimum_free_heap_size( void )
{
return heap_caps_get_minimum_free_size( MALLOC_CAP_8BIT );
}
uint32_t system_get_free_heap_size(void) __attribute__((alias("esp_get_free_heap_size")));

View File

@ -1,64 +0,0 @@
/*
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

@ -1,95 +0,0 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _HEAP_REGIONS_H
#define _HEAP_REGIONS_H
#include "freertos/FreeRTOS.h"
#include <mumm_malloc.h>
/**
* @brief Structure to define a memory region
*/
typedef struct HeapRegionTagged
{
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.
mumm_heap_handle heap;
} HeapRegionTagged_t;
/**
* @brief Initialize the heap allocator by feeding it the usable memory regions and their tags.
*
* This takes an array of heapRegionTagged_t structs, the last entry of which is a dummy entry
* which has pucStartAddress set to NULL. It will initialize the heap allocator to serve memory
* from these ranges.
*
* @param pxHeapRegions Array of region definitions
*/
void vPortDefineHeapRegionsTagged( const HeapRegionTagged_t * const pxHeapRegions );
/**
* @brief Allocate memory from a region with a certain tag
*
* Like pvPortMalloc, this returns an allocated chunk of memory. This function,
* however, forces the allocator to allocate from a region specified by a
* specific tag.
*
* @param xWantedSize Size needed, in bytes
* @param tag Tag of the memory region the allocation has to be from
*
* @return Pointer to allocated memory if succesful.
* NULL if unsuccesful.
*/
void *pvPortMallocTagged( size_t xWantedSize, BaseType_t tag );
/**
* @brief Free memory allocated with pvPortMallocTagged
*
* This is basically an implementation of free().
*
* @param pv Pointer to region allocated by pvPortMallocTagged
*/
void vPortFreeTagged( void *pv );
/**
* @brief Get the lowest amount of memory free for a certain tag
*
* This function allows the user to see what the least amount of
* free memory for a certain tag is.
*
* @param tag Tag of the memory region
*
* @return Minimum amount of free bytes available in the runtime of
* the program
*/
size_t xPortGetMinimumEverFreeHeapSizeTagged( BaseType_t tag );
/**
* @brief Get the amount of free bytes in a certain tagged region
*
* Works like xPortGetFreeHeapSize but allows the user to specify
* a specific tag
*
* @param tag Tag of the memory region
*
* @return Remaining amount of free bytes in region
*/
size_t xPortGetFreeHeapSizeTagged( BaseType_t tag );
#endif

View File

@ -136,29 +136,12 @@ extern "C" {
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) PRIVILEGED_FUNCTION;
#endif
/* Used by heap_5.c. */
typedef struct HeapRegion
{
uint8_t *pucStartAddress;
size_t xSizeInBytes;
} HeapRegion_t;
/*
* Used to define multiple heap regions for use by heap_5.c. This function
* must be called before any calls to pvPortMalloc() - not creating a task,
* queue, semaphore, mutex, software timer, event group, etc. will result in
* pvPortMalloc being called.
*
* pxHeapRegions passes in an array of HeapRegion_t structures - each of which
* defines a region of memory that can be used as the heap. The array is
* terminated by a HeapRegions_t structure that has a size of 0. The region
* with the lowest start address must appear first in the array.
*/
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions );
/*
* Map to the memory management routines required for the port.
*
* Note that libc standard malloc/free are also available for
* non-FreeRTOS-specific code, and behave the same as
* pvPortMalloc()/vPortFree().
*/
void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION;
void vPortFree( void *pv ) PRIVILEGED_FUNCTION;

View File

@ -102,7 +102,7 @@
#include "task.h"
#include "esp_panic.h"
#include "esp_heap_caps.h"
#include "esp_crosscore_int.h"
#include "esp_intr_alloc.h"
@ -442,5 +442,29 @@ uint32_t xPortGetTickRateHz(void) {
return (uint32_t)configTICK_RATE_HZ;
}
/* Heap functions, wrappers around heap_caps_xxx functions
NB: libc malloc() & free() are also defined & available
for this purpose.
*/
void *pvPortMalloc( size_t xWantedSize )
{
return heap_caps_malloc( MALLOC_CAP_8BIT, xWantedSize);
}
void vPortFree( void *pv )
{
return heap_caps_free(pv);
}
size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION
{
return heap_caps_get_free_size( MALLOC_CAP_8BIT );
}
size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION
{
return heap_caps_get_minimum_free_size( MALLOC_CAP_8BIT );
}

View File

@ -1,16 +0,0 @@
/* mumm_malloc-compatible DBGLOG API, which maps to esp_log
primitives */
#pragma once
#include "esp_log.h"
static const char *TAG = "mumm_malloc";
#define DBGLOG_TRACE(format, ...) ESP_EARLY_LOGV(TAG, format, ## __VA_ARGS__)
#define DBGLOG_DEBUG(format, ...) ESP_EARLY_LOGD(TAG, format, ## __VA_ARGS__)
#define DBGLOG_CRITICAL(format, ...) ESP_EARLY_LOGW(TAG, format, ## __VA_ARGS__)
#define DBGLOG_ERROR(format, ...) ESP_EARLY_LOGE(TAG, format, ## __VA_ARGS__)
#define DBGLOG_WARNING(format, ...) ESP_EARLY_LOGW(TAG, format, ## __VA_ARGS__)
/* DBGLOG_FORCE in mumm_malloc always prints, but in this case we use ESP_LOGI */
#define DBGLOG_FORCE(force, format, ...) do { if(force) { ESP_LOGI(TAG, format, ## __VA_ARGS__); } } while(0)

View File

@ -1,509 +0,0 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdbool.h>
#include <assert.h>
#include "esp_heap_alloc_caps.h"
#include "esp_log.h"
#include "multi_heap.h"
#include <freertos/FreeRTOS.h>
#include <freertos/heap_regions.h> /* TODO remove this */
/* The maximum amount of tags in use */
#define HEAPREGIONS_MAX_TAGCOUNT 16
static const char* TAG = "heap_alloc_caps";
/*
This file, combined with a region allocator that supports multiple heaps, solves the problem that the ESP32 has RAM
that's slightly heterogeneous. Some RAM can be byte-accessed, some allows only 32-bit accesses, some can execute memory,
some can be remapped by the MMU to only be accessed by a certain PID etc. In order to allow the most flexible memory
allocation possible, this code makes it possible to request memory that has certain capabilities. The code will then use
its knowledge of how the memory is configured along with a priority scheme to allocate that memory in the most sane way
possible. This should optimize the amount of RAM accessible to the code without 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)
WARNING: The current code assumes the ROM stacks are located in tag 1; no allocation from this tag can be done until
the FreeRTOS scheduler has started.
*/
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
};
typedef struct
{
uint8_t *pucStartAddress; ///< Start address of the region
size_t xSizeInBytes; ///< Size of the region
size_t xTag; ///< Tag for the region
uint32_t xExecAddr; ///< If non-zero, indicates the region also has an alias in IRAM.
multi_heap_handle heap;
portMUX_TYPE heap_mux;
} heap_t;
/*
Region descriptors. These describe all regions of memory available, and tag them according to the
capabilities the hardware has. This array is not marked constant; the initialization code may want to
change the tags of some regions because eg BT is detected, applications are loaded etc.
The priorities here roughly work like this:
- For a normal malloc (MALLOC_CAP_8BIT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions,
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.
- Application mallocs (PIDx) will allocate IRAM first, if possible, then DRAM.
- Most other malloc caps only fit in one region anyway.
These region descriptors are very ESP32 specific, because they describe the memory pools available there.
Because of requirements in the coalescing code as well as the heap allocator itself, this list should always
be sorted from low to high start address.
This array is *NOT* const because it gets modified depending on what pools are/aren't available.
*/
static heap_t regions[]={
{ (uint8_t *)0x3F800000, 0x20000, 15, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //SPI SRAM, if available
{ (uint8_t *)0x3FFAE000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 16 <- used for rom code
{ (uint8_t *)0x3FFB0000, 0x8000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 15 <- if BT is enabled, used as BT HW shared memory
{ (uint8_t *)0x3FFB8000, 0x8000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 14 <- if BT is enabled, used data memory for BT ROM functions.
{ (uint8_t *)0x3FFC0000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 0
{ (uint8_t *)0x3FFC2000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 1
{ (uint8_t *)0x3FFC4000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 2
{ (uint8_t *)0x3FFC6000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 3
{ (uint8_t *)0x3FFC8000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 4
{ (uint8_t *)0x3FFCA000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 5
{ (uint8_t *)0x3FFCC000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 6
{ (uint8_t *)0x3FFCE000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 7
{ (uint8_t *)0x3FFD0000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 8
{ (uint8_t *)0x3FFD2000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 9
{ (uint8_t *)0x3FFD4000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 10
{ (uint8_t *)0x3FFD6000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 11
{ (uint8_t *)0x3FFD8000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 12
{ (uint8_t *)0x3FFDA000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 13
{ (uint8_t *)0x3FFDC000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 14
{ (uint8_t *)0x3FFDE000, 0x2000, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 10-13, mmu page 15
{ (uint8_t *)0x3FFE0000, 0x4000, 1, 0x400BC000, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 9 blk 1
{ (uint8_t *)0x3FFE4000, 0x4000, 1, 0x400B8000, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 9 blk 0
{ (uint8_t *)0x3FFE8000, 0x8000, 1, 0x400B0000, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 8 <- can be remapped to ROM, used for MAC dump
{ (uint8_t *)0x3FFF0000, 0x8000, 1, 0x400A8000, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 7 <- can be used for MAC dump
{ (uint8_t *)0x3FFF8000, 0x4000, 1, 0x400A4000, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 6 blk 1 <- can be used as trace memory
{ (uint8_t *)0x3FFFC000, 0x4000, 1, 0x400A0000, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 6 blk 0 <- can be used as trace memory
{ (uint8_t *)0x40070000, 0x8000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 0
{ (uint8_t *)0x40078000, 0x8000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 1
{ (uint8_t *)0x40080000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 0
{ (uint8_t *)0x40082000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 1
{ (uint8_t *)0x40084000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 2
{ (uint8_t *)0x40086000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 3
{ (uint8_t *)0x40088000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 4
{ (uint8_t *)0x4008A000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 5
{ (uint8_t *)0x4008C000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 6
{ (uint8_t *)0x4008E000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 7
{ (uint8_t *)0x40090000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 8
{ (uint8_t *)0x40092000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 9
{ (uint8_t *)0x40094000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 10
{ (uint8_t *)0x40096000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 11
{ (uint8_t *)0x40098000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 12
{ (uint8_t *)0x4009A000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 13
{ (uint8_t *)0x4009C000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 14
{ (uint8_t *)0x4009E000, 0x2000, 2, 0, 0, portMUX_INITIALIZER_UNLOCKED}, //pool 2-5, mmu page 15
{ NULL, 0, 0, 0, 0, portMUX_INITIALIZER_UNLOCKED} //end
};
#define NUM_REGIONS (sizeof(regions)/sizeof(heap_t))
/* For the startup code, the stacks live in memory tagged by this tag. Hence, we only enable allocating from this tag
once FreeRTOS has started up completely. */
#define NONOS_STACK_TAG 1
static void register_heap_region(heap_t *region)
{
region->heap = multi_heap_register(region->pucStartAddress, region->xSizeInBytes, &(region->heap_mux));
ESP_EARLY_LOGI(TAG, "new heap @ %p", region->heap);
assert(region->heap);
}
void heap_alloc_enable_nonos_stack_tag()
{
for (int i = 0; regions[i].xSizeInBytes!=0; i++) {
if (regions[i].xTag == NONOS_STACK_TAG) {
register_heap_region(&regions[i]);
}
}
}
//Modify regions array to disable the given range of memory.
static void disable_mem_region(void *from, void *to) {
int i;
//Align from and to on word boundaries
from=(void*)((uint32_t)from&~3);
to=(void*)(((uint32_t)to+3)&~3);
for (i=0; regions[i].xSizeInBytes!=0; i++) {
void *regStart=regions[i].pucStartAddress;
void *regEnd=regions[i].pucStartAddress+regions[i].xSizeInBytes;
if (regStart>=from && regEnd<=to) {
//Entire region falls in the range. Disable entirely.
regions[i].xTag=-1;
} else if (regStart>=from && regEnd>to && regStart<to) {
//Start of the region falls in the range. Modify address/len.
int overlap=(uint8_t *)to-(uint8_t *)regStart;
regions[i].pucStartAddress+=overlap;
regions[i].xSizeInBytes-=overlap;
if (regions[i].xExecAddr) regions[i].xExecAddr+=overlap;
} else if (regStart<from && regEnd>from && regEnd<=to) {
//End of the region falls in the range. Modify length.
regions[i].xSizeInBytes-=(uint8_t *)regEnd-(uint8_t *)from;
} else if (regStart<from && regEnd>to) {
//Range punches a hole in the region! We do not support this.
ESP_EARLY_LOGE(TAG, "region %d: hole punching is not supported!", i);
regions[i].xTag=-1; //Just disable memory region. That'll teach them!
}
}
}
/*
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 _data_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
the data as loaded by the bootloader.
ToDo: The regions are different when stuff like trace memory, BT, ... is used. Modify the regions struct on the fly for this.
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(&_data_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*)0x40070000, (void*)0x40078000); //CPU0 cache region
disable_mem_region((void*)0x40078000, (void*)0x40080000); //CPU1 cache region
/* Warning: The ROM stack is located in the 0x3ffe0000 area. We do not specifically disable that area here because
after the scheduler has started, the ROM stack is not used anymore by anything. We handle it instead by not allowing
any mallocs from tag 1 (the IRAM/DRAM region) until the scheduler has started.
The 0x3ffe0000 region also contains static RAM for various ROM functions. The following lines
reserve the regions for UART and ETSC, so these functions are usable. Libraries like xtos, which are
not usable in FreeRTOS anyway, are commented out in the linker script so they cannot be used; we
do not disable their memory regions here and they will be used as general purpose heap memory.
Enabling the heap allocator for this region but disabling allocation here until FreeRTOS is started up
is a somewhat risky action in theory, because on initializing the allocator, vPortDefineHeapRegionsTagged
will go and write linked list entries at the start and end of all regions. For the ESP32, these linked
list entries happen to end up in a region that is not touched by the stack; they can be placed safely there.*/
disable_mem_region((void*)0x3ffe0000, (void*)0x3ffe0440); //Reserve ROM PRO data region
disable_mem_region((void*)0x3ffe4000, (void*)0x3ffe4350); //Reserve ROM APP data region
#if CONFIG_BT_ENABLED
#if CONFIG_BT_DRAM_RELEASE
disable_mem_region((void*)0x3ffb0000, (void*)0x3ffb3000); //Reserve BT data region
disable_mem_region((void*)0x3ffb8000, (void*)0x3ffbbb28); //Reserve BT data region
disable_mem_region((void*)0x3ffbdb28, (void*)0x3ffc0000); //Reserve BT data region
#else
disable_mem_region((void*)0x3ffb0000, (void*)0x3ffc0000); //Reserve BT hardware shared memory & BT data region
#endif
disable_mem_region((void*)0x3ffae000, (void*)0x3ffaff10); //Reserve ROM data region, inc region needed for BT ROM routines
#else
disable_mem_region((void*)0x3ffae000, (void*)0x3ffae2a0); //Reserve ROM data region
#endif
#if CONFIG_MEMMAP_TRACEMEM
#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS
disable_mem_region((void*)0x3fff8000, (void*)0x40000000); //Reserve trace mem region
#else
disable_mem_region((void*)0x3fff8000, (void*)0x3fffc000); //Reserve trace mem region
#endif
#endif
#if 0
enable_spi_sram();
#else
disable_mem_region((void*)0x3f800000, (void*)0x3f820000); //SPI SRAM not installed
#endif
//The heap allocator will treat every region given to it as separate. In order to get bigger ranges of contiguous memory,
//it's useful to coalesce adjacent regions that have the same tag.
for (i=1; regions[i].xSizeInBytes!=0; i++) {
if (regions[i].pucStartAddress == (regions[i-1].pucStartAddress + regions[i-1].xSizeInBytes) &&
regions[i].xTag == regions[i-1].xTag ) {
regions[i-1].xTag=-1;
regions[i].pucStartAddress=regions[i-1].pucStartAddress;
regions[i].xSizeInBytes+=regions[i-1].xSizeInBytes;
}
}
ESP_EARLY_LOGI(TAG, "Initializing. RAM available for dynamic allocation:");
for (i=0; regions[i].xSizeInBytes!=0; i++) {
if (regions[i].xTag == -1) {
continue;
}
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);
if (regions[i].xTag == NONOS_STACK_TAG) {
continue; /* Will be registered when OS scheduler starts */
}
register_heap_region(&regions[i]);
}
}
//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
/*
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 );
}
void vPortFreeTagged( void *pv )
{
intptr_t p = (intptr_t)pv;
if (pv == NULL) {
return;
}
for (size_t i = 0; regions[i].xSizeInBytes!=0; i++) {
if (regions[i].xTag == -1) {
continue;
}
intptr_t start = (intptr_t)regions[i].pucStartAddress;
if(p >= start && p < start + regions[i].xSizeInBytes) {
multi_free(regions[i].heap, pv);
return;
}
}
assert(false && "free() target pointer is outside heap areas");
}
/*
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);
}
void *pvPortMallocTagged( size_t xWantedSize, BaseType_t tag )
{
if (tag == -1) {
return NULL;
}
for (size_t i = 0; i < NUM_REGIONS; i++) {
if (regions[i].xTag == tag && regions[i].heap != NULL) {
void * r = multi_malloc(regions[i].heap, xWantedSize);
if (r != NULL) {
return r;
}
}
}
return NULL;
}
/*
Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits.
*/
void *pvPortMallocCaps( size_t xWantedSize, uint32_t caps )
{
int prio;
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++) {
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&(~tag_desc[tag].prio[prio]); //Remaining caps to be fulfilled
j=prio+1;
while (remCaps!=0 && j<NO_PRIOS) {
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 xPortGetFreeHeapSizeTagged(BaseType_t tag)
{
size_t ret = 0;
for (size_t i = 0; regions[i].xSizeInBytes!=0; i++) {
if (regions[i].xTag == tag && regions[i].heap != NULL) {
ret += multi_free_heap_size(regions[i].heap);
}
}
return ret;
}
size_t xPortGetMinimumEverFreeHeapSizeTagged(BaseType_t tag)
{
size_t ret = 0;
for (size_t i = 0; regions[i].xSizeInBytes!=0; i++) {
if (regions[i].xTag == tag && regions[i].heap != NULL) {
ret += multi_minimum_free_heap_size(regions[i].heap);
}
}
return ret;
}
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 );
}

282
components/heap/heap_caps.c Normal file
View File

@ -0,0 +1,282 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_heap_caps.h"
#include "multi_heap.h"
#include "esp_log.h"
#include "heap_private.h"
/*
This file, combined with a region allocator that supports multiple heaps, solves the problem that the ESP32 has RAM
that's slightly heterogeneous. Some RAM can be byte-accessed, some allows only 32-bit accesses, some can execute memory,
some can be remapped by the MMU to only be accessed by a certain PID etc. In order to allow the most flexible memory
allocation possible, this code makes it possible to request memory that has certain capabilities. The code will then use
its knowledge of how the memory is configured along with a priority scheme to allocate that memory in the most sane way
possible. This should optimize the amount of RAM accessible to the code without hardwiring addresses.
*/
/*
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.
*/
IRAM_ATTR 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
assert(dstart >= SOC_DIRAM_DRAM_LOW);
assert(dend <= SOC_DIRAM_DRAM_HIGH);
assert((dstart & 3) == 0);
assert((dend & 3) == 0);
uint32_t istart = SOC_DIRAM_IRAM_LOW + (SOC_DIRAM_DRAM_HIGH - dend);
uint32_t *iptr = (uint32_t *)istart;
*iptr = dstart;
return (void *)(iptr + 1);
}
/* return all possible capabilities (across all priorities) for a given heap */
inline static uint32_t get_all_caps(const heap_t *heap)
{
if (heap->heap == NULL) {
return 0;
}
uint32_t all_caps = 0;
for (int prio = 0; prio < SOC_HEAP_TAG_NO_PRIOS; prio++) {
all_caps |= heap->caps[prio];
}
return all_caps;
}
/*
Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits.
*/
IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps )
{
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 heap capabilities (based on soc_memory_tags & soc_memory_regions) 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.
size = (size + 3) & (~3);
}
for (int prio = 0; prio < SOC_HEAP_TAG_NO_PRIOS; prio++) {
//Iterate over heaps and check capabilities at this priority
for (int heap_idx = 0; heap_idx < num_registered_heaps; heap_idx++) {
heap_t *heap = &registered_heaps[heap_idx];
if ((heap->caps[prio] & caps) != 0) {
//Heap 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 & (~heap->caps[prio]); //Remaining caps to be fulfilled
int j = prio + 1;
while (remCaps != 0 && j < SOC_HEAP_TAG_NO_PRIOS) {
remCaps = remCaps & (~heap->caps[j]);
j++;
}
if (remCaps == 0) {
//This heap can satisfy all the requested capabilities. See if we can grab some memory using it.
if ((caps & MALLOC_CAP_EXEC) && heap->start >= SOC_DIRAM_DRAM_LOW && heap->start < SOC_DIRAM_DRAM_HIGH) {
//This is special, insofar that what we're going to get back is 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 = multi_heap_malloc(heap->heap, size + 4);
if (ret != NULL) {
return dram_alloc_to_iram_addr(ret, size + 4);
}
} else {
//Just try to alloc, nothing special.
ret = multi_heap_malloc(heap->heap, size);
if (ret != NULL) {
return ret;
}
}
}
}
}
}
//Nothing usable found.
return NULL;
}
/* Find the heap which belongs to ptr, or return NULL if it's
not in any heap.
(This confirms if ptr is inside the heap's region, doesn't confirm if 'ptr'
is an allocated block or is some other random address inside the heap.)
*/
IRAM_ATTR static heap_t *find_containing_heap(void *ptr )
{
intptr_t p = (intptr_t)ptr;
for (size_t i = 0; i < num_registered_heaps; i++) {
heap_t *heap = &registered_heaps[i];
if (p >= heap->start && p < heap->end) {
return heap;
}
}
return NULL;
}
IRAM_ATTR void heap_caps_free( void *ptr)
{
intptr_t p = (intptr_t)ptr;
if (ptr == NULL) {
return;
}
if ((p >= SOC_DIRAM_IRAM_LOW) && (p <= SOC_DIRAM_IRAM_HIGH)) {
//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 *)ptr;
ptr = (void *)dramAddrPtr[-1];
}
heap_t *heap = find_containing_heap(ptr);
assert(heap != NULL && "free() target pointer is outside heap areas");
multi_heap_free(heap->heap, ptr);
}
IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, int caps)
{
if (ptr == NULL) {
return heap_caps_malloc(size, caps);
}
if (size == 0) {
heap_caps_free(ptr);
return NULL;
}
heap_t *heap = find_containing_heap(ptr);
assert(heap != NULL && "realloc() pointer is outside heap areas");
// are the existing heap's capabilities compatible with the
// requested ones?
bool compatible_caps = (caps & get_all_caps(heap)) == caps;
if (compatible_caps) {
// try to reallocate this memory within the same heap
// (which will resize the block if it can)
void *r = multi_heap_realloc(heap->heap, ptr, size);
if (r != NULL) {
return r;
}
}
// if we couldn't do that, try to see if we can reallocate
// in a different heap with requested capabilities.
void *new_p = heap_caps_malloc(size, caps);
if (new_p != NULL) {
size_t old_size = multi_heap_get_allocated_size(heap->heap, ptr);
assert(old_size > 0);
memcpy(new_p, ptr, old_size);
heap_caps_free(ptr);
return new_p;
}
return NULL;
}
size_t heap_caps_get_free_size( uint32_t caps )
{
size_t ret = 0;
for (int i = 0; i < num_registered_heaps; i++) {
heap_t *heap = &registered_heaps[i];
if ((get_all_caps(heap) & caps) == caps) {
ret += multi_heap_free_size(heap->heap);
}
}
return ret;
}
size_t heap_caps_get_minimum_free_size( uint32_t caps )
{
size_t ret = 0;
for (int i = 0; i < num_registered_heaps; i++) {
heap_t *heap = &registered_heaps[i];
if ((get_all_caps(heap) & caps) == caps) {
ret += multi_heap_minimum_free_size(heap->heap);
}
}
return ret;
}
size_t heap_caps_get_largest_free_block( uint32_t caps )
{
multi_heap_info_t info;
heap_caps_get_info(&info, caps);
return info.largest_free_block;
}
void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps )
{
bzero(info, sizeof(multi_heap_info_t));
for (int i = 0; i < num_registered_heaps; i++) {
heap_t *heap = &registered_heaps[i];
if ((get_all_caps(heap) & caps) == caps) {
multi_heap_info_t hinfo;
multi_heap_get_info(heap->heap, &hinfo);
info->total_free_bytes += hinfo.total_free_bytes;
info->total_allocated_bytes += hinfo.total_allocated_bytes;
info->largest_free_block = MAX(info->largest_free_block,
hinfo.largest_free_block);
info->minimum_free_bytes += hinfo.minimum_free_bytes;
info->allocated_blocks += hinfo.allocated_blocks;
info->free_blocks += hinfo.free_blocks;
info->total_blocks += hinfo.total_blocks;
}
}
}
void heap_caps_print_heap_info( uint32_t caps )
{
multi_heap_info_t info;
printf("Heap summary for capabilities 0x%08X:\n", caps);
for (int i = 0; i < num_registered_heaps; i++) {
heap_t *heap = &registered_heaps[i];
if ((get_all_caps(heap) & caps) == caps) {
multi_heap_get_info(heap->heap, &info);
printf(" At 0x%08x len %d free %d allocated %d min_free %d\n",
heap->start, heap->end - heap->start, info.total_free_bytes, info.total_allocated_bytes, info.minimum_free_bytes);
printf(" largest_free_block %d alloc_blocks %d free_blocks %d total_blocks %d\n",
info.largest_free_block, info.allocated_blocks,
info.free_blocks, info.total_blocks);
}
}
printf(" Totals:\n");
heap_caps_get_info(&info, caps);
printf(" free %d allocated %d min_free %d largest_free_block %d\n", info.total_free_bytes, info.total_allocated_bytes, info.minimum_free_bytes, info.largest_free_block);
}

View File

@ -0,0 +1,182 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "heap_private.h"
#include <assert.h>
#include <string.h>
#include <esp_log.h>
#include <multi_heap.h>
#include <soc/soc_memory_layout.h>
static const char *TAG = "heap_init";
heap_t *registered_heaps;
size_t num_registered_heaps;
static void register_heap(heap_t *region)
{
region->heap = multi_heap_register((void *)region->start, region->end - region->start);
ESP_EARLY_LOGD(TAG, "New heap initialised at %p", region->heap);
assert(region->heap);
}
void heap_caps_enable_nonos_stack_heaps()
{
for (int i = 0; i < num_registered_heaps; i++) {
// Assume any not-yet-registered heap is
// a nonos-stack heap
heap_t *heap = &registered_heaps[i];
if (heap->heap == NULL) {
register_heap(heap);
multi_heap_set_lock(heap->heap, &heap->heap_mux);
}
}
}
//Modify regions array to disable the given range of memory.
static void disable_mem_region(soc_memory_region_t *regions, intptr_t from, intptr_t to)
{
//Align from and to on word boundaries
from = from & ~3;
to = (to + 3) & ~3;
for (int i = 0; i < soc_memory_region_count; i++) {
soc_memory_region_t *region = &regions[i];
intptr_t regStart = region->start;
intptr_t regEnd = region->start + region->size;
if (regStart >= from && regEnd <= to) {
//Entire region falls in the range. Disable entirely.
regions[i].tag = -1;
} else if (regStart >= from && regEnd > to && regStart < to) {
//Start of the region falls in the range. Modify address/len.
intptr_t overlap = to - regStart;
region->start += overlap;
region->size -= overlap;
if (region->iram_address) {
region->iram_address += overlap;
}
} else if (regStart < from && regEnd > from && regEnd <= to) {
//End of the region falls in the range. Modify length.
region->size -= regEnd - from;
} else if (regStart < from && regEnd > to) {
//Range punches a hole in the region! We do not support this.
ESP_EARLY_LOGE(TAG, "region %d: hole punching is not supported!", i);
regions->tag = -1; //Just disable memory region. That'll teach them!
}
}
}
/*
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 _data_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
the data as loaded by the bootloader.
ToDo: The regions are different when stuff like trace memory, BT, ... is used. Modify the regions struct on the fly for this.
Same with loading of apps. Same with using SPI RAM.
*/
void heap_caps_init()
{
/* Copy the soc_memory_regions data to the stack, so we can
manipulate it. */
soc_memory_region_t regions[soc_memory_region_count];
memcpy(regions, soc_memory_regions, sizeof(soc_memory_region_t)*soc_memory_region_count);
//Disable the bits of memory where this code is loaded.
disable_mem_region(regions, (intptr_t)&_data_start, (intptr_t)&_heap_start); //DRAM used by bss/data static variables
disable_mem_region(regions, (intptr_t)&_init_start, (intptr_t)&_iram_text_end); //IRAM used by code
// Disable all regions reserved on this SoC
for (int i = 0; i < soc_reserved_region_count; i++) {
disable_mem_region(regions, soc_reserved_regions[i].start,
soc_reserved_regions[i].end);
}
//The heap allocator will treat every region given to it as separate. In order to get bigger ranges of contiguous memory,
//it's useful to coalesce adjacent regions that have the same tag.
for (int i = 1; i < soc_memory_region_count; i++) {
soc_memory_region_t *a = &regions[i - 1];
soc_memory_region_t *b = &regions[i];
if (b->start == a->start + a->size && b->tag == a->tag ) {
a->tag = -1;
b->start = a->start;
b->size += a->size;
}
}
/* Count the heaps left after merging */
num_registered_heaps = 0;
for (int i = 0; i < soc_memory_region_count; i++) {
if (regions[i].tag != -1) {
num_registered_heaps++;
}
}
/* Start by allocating the registered heap data on the stack.
Once we have a heap to copy it to, we will copy it to a heap buffer.
*/
multi_heap_handle_t first_heap = NULL;
heap_t temp_heaps[num_registered_heaps];
size_t heap_idx = 0;
ESP_EARLY_LOGI(TAG, "Initializing. RAM available for dynamic allocation:");
for (int i = 0; i < soc_memory_region_count; i++) {
soc_memory_region_t *region = &regions[i];
const soc_memory_tag_desc_t *tag = &soc_memory_tags[region->tag];
heap_t *heap = &temp_heaps[heap_idx];
if (region->tag == -1) {
continue;
}
heap_idx++;
assert(heap_idx <= num_registered_heaps);
heap->tag = region->tag;
heap->start = region->start;
heap->end = region->start + region->size;
memcpy(heap->caps, tag->caps, sizeof(heap->caps));
vPortCPUInitializeMutex(&heap->heap_mux);
ESP_EARLY_LOGI(TAG, "At %08X len %08X (%d KiB): %s",
region->start, region->size, region->size / 1024, tag->name);
if (tag->startup_stack) {
/* Will be registered when OS scheduler starts */
heap->heap = NULL;
} else {
register_heap(heap);
if (first_heap == NULL) {
first_heap = heap->heap;
}
}
}
/* Allocate the permanent heap data that we'll use for runtime */
assert(heap_idx == num_registered_heaps);
registered_heaps = multi_heap_malloc(first_heap, sizeof(heap_t) * num_registered_heaps);
memcpy(registered_heaps, temp_heaps, sizeof(heap_t)*num_registered_heaps);
/* Now the heap_mux fields live on the heap, assign them */
for (int i = 0; i < num_registered_heaps; i++) {
if (registered_heaps[i].heap != NULL) {
multi_heap_set_lock(registered_heaps[i].heap, &registered_heaps[i].heap_mux);
}
}
}

View File

@ -0,0 +1,38 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <freertos/FreeRTOS.h>
#include <soc/soc_memory_layout.h>
#include "multi_heap.h"
/* Some common heap registration data structures used
for heap_caps_init.c to share heap information with heap_caps.c
*/
/* Type for describing each registered heap */
typedef struct {
size_t tag;
uint32_t caps[SOC_HEAP_TAG_NO_PRIOS]; ///< Capabilities for this tag (as a prioritised set). Copied from soc_memory_tags so it's in RAM not flash.
intptr_t start;
intptr_t end;
portMUX_TYPE heap_mux;
multi_heap_handle_t heap;
} heap_t;
extern heap_t *registered_heaps;
extern size_t num_registered_heaps;

View File

@ -11,103 +11,25 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef HEAP_ALLOC_CAPS_H
#define HEAP_ALLOC_CAPS_H
#pragma once
#warning "This header is deprecated, please use functions defined in esp_heap_caps.h instead."
#include "esp_heap_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
/* Deprecated FreeRTOS-style esp_heap_alloc_caps.h functions follow */
/**
* @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
/* Please use heap_caps_malloc() instead of this function */
void *pvPortMallocCaps(size_t xWantedSize, uint32_t caps) asm("heap_caps_malloc") __attribute__((deprecated));
/* Please use heap_caps_get_minimum_free_heap_size() instead of this function */
size_t xPortGetMinimumEverFreeHeapSizeCaps( uint32_t caps ) asm("heap_caps_get_minimum_free_heap_size") __attribute__((deprecated));
/**
* @brief Initialize the capability-aware heap allocator.
*
* For the ESP32, this is called once in the startup code.
*/
void heap_alloc_caps_init();
/**
* @brief Enable the memory region where the startup stacks are located for allocation
*
* On startup, the pro/app CPUs have a certain memory region they use as stack, so we
* cannot do allocations in the regions these stack frames are. When FreeRTOS is
* completely started, they do not use that memory anymore and allocation there can
* be re-enabled.
*/
void heap_alloc_enable_nonos_stack_tag();
/**
* @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 );
/**
* @brief Convenience function to check if a pointer is DMA-capable.
*
* @param ptr Pointer to check
*
* @return True if DMA-capable, false if not.
*/
static inline bool esp_ptr_dma_capable( const void *ptr )
{
return ( (int)ptr >= 0x3FFAE000 && (int)ptr < 0x40000000 );
}
/* Please use heap_caps_get_free_heap_size() instead of this function */
size_t xPortGetFreeHeapSizeCaps( uint32_t caps ) asm("heap_caps_get_free_heap_size") __attribute__((deprecated));
#ifdef __cplusplus
}
#endif
#endif //HEAP_ALLOC_CAPS_H

View File

@ -0,0 +1,175 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include "multi_heap.h"
/**
* @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 (PIDs are not currently used)
#define MALLOC_CAP_PID3 (1<<5) ///< Memory must be mapped to PID3 memory space (PIDs are not currently used)
#define MALLOC_CAP_PID4 (1<<6) ///< Memory must be mapped to PID4 memory space (PIDs are not currently used)
#define MALLOC_CAP_PID5 (1<<7) ///< Memory must be mapped to PID5 memory space (PIDs are not currently used)
#define MALLOC_CAP_PID6 (1<<8) ///< Memory must be mapped to PID6 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_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.
*
* This is called once in the IDF startup code. Do not call it
* at other times.
*/
void heap_caps_init();
/**
* @brief Enable heap(s) in memory regions where the startup stacks are located.
*
* On startup, the pro/app CPUs have a certain memory region they use as stack, so we
* cannot do allocations in the regions these stack frames are. When FreeRTOS is
* completely started, they do not use that memory anymore and heap(s) there can
* be enabled.
*/
void heap_caps_enable_nonos_stack_heaps();
/**
* @brief Allocate a chunk of memory which has the given capabilities
*
* Equivalent semantics to libc malloc(), for capability-aware memory.
*
* In IDF, malloc(p) is equivalent to heaps_caps_malloc(p, MALLOC_CAP_8BIT);
*
* @param size 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 *heap_caps_malloc(size_t size, uint32_t caps);
/**
* @brief Free memory previously allocated via heap_caps_malloc() or heap_caps_realloc().
*
* Equivalent semantics to libc free(), for capability-aware memory.
*
* In IDF, free(p) is equivalent to heap_caps_free(p).
*
* @param ptr Pointer to memory previously returned from heap_caps_malloc() or heap_caps_realloc(). Can be NULL.
*/
void heap_caps_free( void *ptr);
/**
* @brief Reallocate memory previously allocated via heaps_caps_malloc() or heaps_caps_realloc().
*
* Equivalent semantics to libc realloc(), for capability-aware memory.
*
* In IDF, realloc(p, s) is equivalent to heap_caps_realloc(p, s, MALLOC_CAP_8BIT).
*
* 'caps' parameter can be different to the capabilities that any original 'ptr' was allocated with. In this way,
* realloc can be used to "move" a buffer if necessary to ensure it meets new set of capabilities.
*
* @param ptr Pointer to previously allocated memory, or NULL for a new allocation.
* @param size Size of the new buffer requested, or 0 to free the buffer.
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory desired for the new allocation.
*
* @return Pointer to a new buffer of size 'size' with capabilities 'caps', or NULL if allocation failed.
*/
void *heap_caps_realloc( void *ptr, size_t size, int 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.
*
* Note that because of heap fragmentation it is probably not possible to allocate a single block of memory
* of this size. Use heap_caps_get_largest_free_block() for this purpose.
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory
*
* @return Amount of free bytes in the regions
*/
size_t heap_caps_get_free_size( uint32_t caps );
/**
* @brief Get the total minimum free memory of all regions with the given capabilities
*
* This adds all the low water marks of the regions capable of delivering the memory
* with the given capabilities.
*
* Note the result may be less than the global all-time minimum available heap of this kind, as "low water marks" are
* tracked per-heap. Individual heaps may have reached their "low water marks" at different points in time. However
* this result still gives a "worst case" indication for all-time free heap.
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory
*
* @return Amount of free bytes in the regions
*/
size_t heap_caps_get_minimum_free_size( uint32_t caps );
/**
* @brief Get the largest free block of memory able to be allocated with the given capabilities.
*
* Returns the largest value of 's' for which heap_caps_malloc(s, caps) will succeed.
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory
*
* @return Size of largest free block in bytes.
*/
size_t heap_caps_get_largest_free_block( uint32_t caps );
/**
* @brief Get heap info for all regions with the given capabilities.
*
* Calls multi_heap_info() on all heaps which share the given capabilities. The information returned is an aggregate
* across all matching heaps. The meanings of fields are the same as defined for multi_heap_info_t, except that
* minimum_free_bytes has the same caveats described in heap_caps_get_minimum_free_size().
*
* @param info Pointer to a structure which will be filled with relevant
* heap metadata.
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory
*
*/
void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps );
/**
* @brief Print a summary of all memory with the given capabilities.
*
* Calls multi_heap_info() on all heaps which share the given capabilities, and
* prints a two-line summary for each, then a total summary.
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory
*
*/
void heap_caps_print_heap_info( uint32_t caps );

View File

@ -104,7 +104,7 @@ void multi_heap_set_lock(multi_heap_handle_t heap, void* lock);
*
* @param heap Handle to a registered heap.
*/
void multi_heap_dump(multi_heap_handle_t handle);
void multi_heap_dump(multi_heap_handle_t heap);
/** @brief Check heap integrity
*
@ -144,6 +144,7 @@ size_t multi_heap_free_size(multi_heap_handle_t heap);
*/
size_t multi_heap_minimum_free_size(multi_heap_handle_t heap);
/** @brief Structure to access heap metadata via multi_get_heap_info */
typedef struct {
size_t total_free_bytes; ///< Total free bytes in the heap. Equivalent to multi_free_heap_size().
size_t total_allocated_bytes; ///< Total bytes allocated to data in the heap.

View File

@ -1,19 +0,0 @@
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
/* Simple wrapper means that mumm_malloc src/ directory doesn't need
to be in the global search path.
*/
#include "../mumm_malloc/src/mumm_malloc.h"

View File

@ -1,117 +0,0 @@
/*
* Configuration for mumm_malloc in IDF
*
* Unlike umm_malloc, this config doesn't include
* much heap configuration - just compiler configuration
*/
#ifndef _MUMM_MALLOC_CFG_H
#define _MUMM_MALLOC_CFG_H
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
/* A couple of macros to make packing structures less compiler dependent */
#define MUMM_H_ATTPACKPRE
#define MUMM_H_ATTPACKSUF __attribute__((__packed__))
#define MUMM_INFO
#ifdef MUMM_INFO
typedef struct MUMM_HEAP_INFO_t {
unsigned short int totalEntries;
unsigned short int usedEntries;
unsigned short int freeEntries;
unsigned short int totalBlocks;
unsigned short int usedBlocks;
unsigned short int freeBlocks;
unsigned short int maxFreeContiguousBlocks;
unsigned short int blockSize;
unsigned short int numBlocks; /* configured, not counted */
}
MUMM_HEAP_INFO;
void *mumm_info( mumm_heap_handle heap, void *ptr, int force, MUMM_HEAP_INFO* info );
size_t mumm_free_heap_size( mumm_heap_handle heap );
#else
#endif
#define MUMM_CRITICAL_ENTRY(PLOCK) taskENTER_CRITICAL((portMUX_TYPE *)(PLOCK))
#define MUMM_CRITICAL_EXIT(PLOCK) taskEXIT_CRITICAL((portMUX_TYPE *)(PLOCK))
/*
* -D MUMM_INTEGRITY_CHECK :
*
* Enables heap integrity check before any heap operation. It affects
* performance, but does NOT consume extra memory.
*
* If integrity violation is detected, the message is printed and user-provided
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
*
* Note that not all buffer overruns are detected: each buffer is aligned by
* 4 bytes, so there might be some trailing "extra" bytes which are not checked
* for corruption.
*/
//#define MUMM_INTEGRITY_CHECK
#ifdef MUMM_INTEGRITY_CHECK
int mumm_integrity_check( mumm_heap_handle heap );
# define INTEGRITY_CHECK(HEAP) mumm_integrity_check(HEAP)
extern void mumm_corruption(void);
# define MUMM_HEAP_CORRUPTION_CB(heap) printf( "Heap Corruption in heap %p!\n", heap )
#else
# define INTEGRITY_CHECK(HEAP) 0
#endif
/*
* -D MUMM_POISON :
*
* Enables heap poisoning: add predefined value (poison) before and after each
* allocation, and check before each heap operation that no poison is
* corrupted.
*
* Other than the poison itself, we need to store exact user-requested length
* for each buffer, so that overrun by just 1 byte will be always noticed.
*
* Customizations:
*
* UMM_POISON_SIZE_BEFORE:
* Number of poison bytes before each block, e.g. 2
* UMM_POISON_SIZE_AFTER:
* Number of poison bytes after each block e.g. 2
* UMM_POISONED_BLOCK_LEN_TYPE
* Type of the exact buffer length, e.g. `short`
*
* NOTE: each allocated buffer is aligned by 4 bytes. But when poisoning is
* enabled, actual pointer returned to user is shifted by
* `(sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE)`.
* It's your responsibility to make resulting pointers aligned appropriately.
*
* If poison corruption is detected, the message is printed and user-provided
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
*/
//#define MUMM_POISON_CHECK
#define UMM_POISON_SIZE_BEFORE 4
#define UMM_POISON_SIZE_AFTER 4
#define UMM_POISONED_BLOCK_LEN_TYPE short
#ifdef MUMM_POISON_CHECK
void *mumm_poison_malloc( mumm_heap_handle heap, size_t size );
void *mumm_poison_calloc( mumm_heap_handle heap, size_t num, size_t size );
void *mumm_poison_realloc( mumm_heap_handle heap, void *ptr, size_t size );
void mumm_poison_free( mumm_heap_handle heap, void *ptr );
int mumm_poison_check( mumm_heap_handle heap );
# define POISON_CHECK(HEAP) mumm_poison_check(HEAP)
#else
# define POISON_CHECK(HEAP) 0
#endif
#endif /* _MUMM_MALLOC_CFG_H */

View File

@ -433,10 +433,11 @@ void *multi_heap_realloc(multi_heap_handle_t heap, void *p, size_t size)
}
// Can grow into previous block?
// (do this even if we're already big enough from growing into 'next', as it reduces fragmentation)
// (try this even if we're already big enough from growing into 'next', as it reduces fragmentation)
if (prev_grow_size > 0 && (block_data_size(pb) + prev_grow_size >= size)) {
pb = merge_adjacent(heap, prev, pb);
assert(block_data_size(pb) >= size);
// this doesn't guarantee we'll be left with a big enough block, as it's
// possible for the merge to fail if prev == heap->first_block
}
if (block_data_size(pb) >= size) {
@ -546,7 +547,7 @@ void multi_heap_dump(multi_heap_handle_t heap)
MULTI_HEAP_UNLOCK(heap->lock);
}
size_t multi_heap_free_heap_size(multi_heap_handle_t heap)
size_t multi_heap_free_size(multi_heap_handle_t heap)
{
if (heap == NULL) {
return 0;
@ -554,7 +555,7 @@ size_t multi_heap_free_heap_size(multi_heap_handle_t heap)
return heap->free_bytes;
}
size_t multi_heap_minimum_free_heap_size(multi_heap_handle_t heap)
size_t multi_heap_minimum_free_size(multi_heap_handle_t heap)
{
if (heap == NULL) {
return 0;
@ -562,7 +563,7 @@ size_t multi_heap_minimum_free_heap_size(multi_heap_handle_t heap)
return heap->minimum_free_bytes;
}
void multi_heap_get_heap_info(multi_heap_handle_t heap, multi_heap_info_t *info)
void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info)
{
memset(info, 0, sizeof(multi_heap_info_t));

View File

@ -0,0 +1,5 @@
#
#Component Makefile
#
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View File

@ -16,17 +16,23 @@
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include "esp_panic.h"
static int tryAllocMem() {
int **mem;
int i, noAllocated, j;
mem=malloc(sizeof(int)*1024);
mem=malloc(sizeof(int *)*1024);
if (!mem) return 0;
for (i=0; i<1024; i++) {
mem[i]=malloc(1024);
if (mem[i]==NULL) break;
for (j=0; j<1024/4; j++) mem[i][j]=(0xdeadbeef);
}
noAllocated=i;
for (i=0; i<noAllocated; i++) {
for (j=0; j<1024/4; j++) {
TEST_ASSERT(mem[i][j]==(0xdeadbeef));
@ -38,7 +44,7 @@ static int tryAllocMem() {
}
TEST_CASE("Malloc/overwrite, then free all available DRAM", "[freertos]")
TEST_CASE("Malloc/overwrite, then free all available DRAM", "[heap]")
{
int m1=0, m2=0;
m1=tryAllocMem();

View File

@ -0,0 +1,125 @@
/*
Tests for the capabilities-based memory allocator.
*/
#include <esp_types.h>
#include <stdio.h>
#include "unity.h"
#include "esp_attr.h"
#include "esp_heap_caps.h"
#include "esp_spi_flash.h"
#include <stdlib.h>
TEST_CASE("Capabilities allocator test", "[heap]")
{
char *m1, *m2[10];
int x;
size_t free8start, free32start, free8, free32;
/* It's important we printf() something before we take the empty heap sizes,
as the first printf() in a task allocates heap resources... */
printf("Testing capabilities allocator...\n");
free8start = heap_caps_get_free_size(MALLOC_CAP_8BIT);
free32start = heap_caps_get_free_size(MALLOC_CAP_32BIT);
printf("Free 8bit-capable memory (start): %dK, 32-bit capable memory %dK\n", free8start, free32start);
TEST_ASSERT(free32start>free8start);
printf("Allocating 10K of 8-bit capable RAM\n");
m1= heap_caps_malloc(10*1024, MALLOC_CAP_8BIT);
printf("--> %p\n", m1);
free8 = heap_caps_get_free_size(MALLOC_CAP_8BIT);
free32 = heap_caps_get_free_size(MALLOC_CAP_32BIT);
printf("Free 8bit-capable memory (both reduced): %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 = heap_caps_malloc(10*1024, MALLOC_CAP_32BIT);
printf("--> %p\n", m1);
free8 = heap_caps_get_free_size(MALLOC_CAP_8BIT);
free32 = heap_caps_get_free_size(MALLOC_CAP_32BIT);
printf("Free 8bit-capable memory (after 32-bit): %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= heap_caps_malloc(10*1024, MALLOC_CAP_8BIT|MALLOC_CAP_EXEC);
printf("--> %p\n", m1);
TEST_ASSERT(m1==NULL);
printf("Testing changeover iram -> dram");
// priorities will exhaust IRAM first, then start allocating from DRAM
for (x=0; x<10; x++) {
m2[x]= heap_caps_malloc(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");
// (the allocation should come from D/IRAM)
m1= heap_caps_malloc(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");
}
TEST_CASE("heap_caps metadata test", "[heap]")
{
/* need to print something as first printf allocates some heap */
printf("heap_caps metadata test\n");
heap_caps_print_heap_info(MALLOC_CAP_8BIT);
heap_caps_print_heap_info(MALLOC_CAP_32BIT);
multi_heap_info_t original;
heap_caps_get_info(&original, MALLOC_CAP_8BIT);
void *b = heap_caps_malloc(original.largest_free_block, MALLOC_CAP_8BIT);
TEST_ASSERT_NOT_NULL(b);
printf("After allocating %d bytes:\n", original.largest_free_block);
heap_caps_print_heap_info(MALLOC_CAP_8BIT);
multi_heap_info_t after;
heap_caps_get_info(&after, MALLOC_CAP_8BIT);
TEST_ASSERT(after.largest_free_block < original.largest_free_block);
TEST_ASSERT(after.total_free_bytes < original.total_free_bytes);
free(b);
heap_caps_get_info(&after, MALLOC_CAP_8BIT);
TEST_ASSERT_EQUAL(after.total_free_bytes, original.total_free_bytes);
TEST_ASSERT_EQUAL(after.largest_free_block, original.largest_free_block);
TEST_ASSERT(after.minimum_free_bytes < original.total_free_bytes);
}
/* Small function runs from IRAM to check that malloc/free/realloc
all work OK when cache is disabled...
*/
static IRAM_ATTR __attribute__((noinline)) bool iram_malloc_test()
{
g_flash_guard_default_ops.start(); // Disables flash cache
bool result = true;
void *x = heap_caps_malloc(64, MALLOC_CAP_32BIT);
result = result && (x != NULL);
void *y = heap_caps_realloc(x, 32, MALLOC_CAP_32BIT);
result = result && (y != NULL);
heap_caps_free(y);
g_flash_guard_default_ops.end(); // Re-enables flash cache
return result;
}
TEST_CASE("heap_caps_xxx functions work with flash cache disabled", "[heap]")
{
TEST_ASSERT( iram_malloc_test() );
}

View File

@ -11,7 +11,7 @@ INCLUDE_FLAGS = -I../include -I../../../tools/catch
GCOV ?= gcov
CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -m32
CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -g -fstack-protector-all -m32
CFLAGS += -fprofile-arcs -ftest-coverage
CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -ftest-coverage
LDFLAGS += -lstdc++ -fprofile-arcs -ftest-coverage -m32

View File

@ -86,14 +86,12 @@ static struct syscall_stub_table s_stub_table = {
#endif
};
void *pvPortMalloc(size_t);
void esp_setup_syscall_table()
{
syscall_table_ptr_pro = &s_stub_table;
syscall_table_ptr_app = &s_stub_table;
_GLOBAL_REENT = &s_reent;
environ = pvPortMalloc(sizeof(char*));
environ = malloc(sizeof(char*));
environ[0] = NULL;
}

View File

@ -21,42 +21,28 @@
#include <stdlib.h>
#include "esp_attr.h"
#include "freertos/FreeRTOS.h"
#include "esp_heap_caps.h"
void* IRAM_ATTR _malloc_r(struct _reent *r, size_t size)
{
return pvPortMalloc(size);
return heap_caps_malloc( size, MALLOC_CAP_8BIT );
}
void IRAM_ATTR _free_r(struct _reent *r, void* ptr)
{
vPortFree(ptr);
heap_caps_free( ptr );
}
void* IRAM_ATTR _realloc_r(struct _reent *r, void* ptr, size_t size)
{
void* new_chunk;
if (size == 0) {
if (ptr) {
vPortFree(ptr);
}
return NULL;
}
new_chunk = pvPortMalloc(size);
if (new_chunk && ptr) {
memcpy(new_chunk, ptr, size);
vPortFree(ptr);
}
// realloc behaviour: don't free original chunk if alloc failed
return new_chunk;
return heap_caps_realloc( ptr, size, MALLOC_CAP_8BIT );
}
void* IRAM_ATTR _calloc_r(struct _reent *r, size_t count, size_t size)
{
void* result = pvPortMalloc(count * size);
if (result)
{
memset(result, 0, count * size);
void* result = heap_caps_malloc(count * size, MALLOC_CAP_8BIT);
if (result) {
bzero(result, count * size);
}
return result;
}

View File

@ -17,7 +17,7 @@
#include <string.h>
#include "esp_log.h"
#include "esp_heap_alloc_caps.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/sdmmc_defs.h"
@ -413,7 +413,7 @@ static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr)
static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr)
{
size_t datalen = 8;
uint32_t* buf = (uint32_t*) pvPortMallocCaps(datalen, MALLOC_CAP_DMA);
uint32_t* buf = (uint32_t*) heap_caps_malloc(datalen, MALLOC_CAP_DMA);
if (buf == NULL) {
return ESP_ERR_NO_MEM;
}

View File

@ -2,4 +2,4 @@
SOC_NAME := esp32
COMPONENT_SRCDIRS := $(SOC_NAME)
COMPONENT_ADD_INCLUDEDIRS := $(SOC_NAME)/include
COMPONENT_ADD_INCLUDEDIRS := $(SOC_NAME)/include include

View File

@ -281,6 +281,12 @@
#define SOC_RTC_DATA_LOW 0x50000000
#define SOC_RTC_DATA_HIGH 0x50002000
//First and last words of the D/IRAM region, for both the DRAM address as well as the IRAM alias.
#define SOC_DIRAM_IRAM_LOW 0x400A0000
#define SOC_DIRAM_IRAM_HIGH 0x400BFFFC
#define SOC_DIRAM_DRAM_LOW 0x3FFE0000
#define SOC_DIRAM_DRAM_HIGH 0x3FFFFFFC
//Interrupt hardware source table
//This table is decided by hardware, don't touch this.
#define ETS_WIFI_MAC_INTR_SOURCE 0/**< interrupt of WiFi MAC, level*/

View File

@ -0,0 +1,174 @@
// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BOOTLOADER_BUILD
#include <stdlib.h>
#include <stdint.h>
#include "soc/soc.h"
#include "soc/soc_memory_layout.h"
#include "esp_heap_caps.h"
#include "sdkconfig.h"
/* Memory layout for ESP32 SoC */
/*
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.
*/
const soc_memory_tag_desc_t soc_memory_tags[] = {
//Tag 0: Plain ole D-port RAM
{ "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }, false, false},
//Tag 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)
{ "D/IRAM", { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, true, true},
//Tag 2: IRAM
{ "IRAM", { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT, 0, 0 }, false, false},
//Tag 3-8: PID 2-7 IRAM
{ "PID2IRAM", { MALLOC_CAP_PID2, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
{ "PID3IRAM", { MALLOC_CAP_PID3, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
{ "PID4IRAM", { MALLOC_CAP_PID4, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
{ "PID5IRAM", { MALLOC_CAP_PID5, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
{ "PID6IRAM", { MALLOC_CAP_PID6, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
{ "PID7IRAM", { MALLOC_CAP_PID7, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
//Tag 9-14: PID 2-7 DRAM
{ "PID2DRAM", { MALLOC_CAP_PID2, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
{ "PID3DRAM", { MALLOC_CAP_PID3, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
{ "PID4DRAM", { MALLOC_CAP_PID4, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
{ "PID5DRAM", { MALLOC_CAP_PID5, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
{ "PID6DRAM", { MALLOC_CAP_PID6, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
{ "PID7DRAM", { MALLOC_CAP_PID7, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
//Tag 15: SPI SRAM data
{ "SPISRAM", { MALLOC_CAP_SPISRAM, 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, false, false},
};
const size_t soc_memory_tag_count = sizeof(soc_memory_tags)/sizeof(soc_memory_tag_desc_t);
/*
Region descriptors. These describe all regions of memory available, and tag them according to the
capabilities the hardware has. This array is not marked constant; the initialization code may want to
change the tags of some regions because eg BT is detected, applications are loaded etc.
The priorities here roughly work like this:
- For a normal malloc (MALLOC_CAP_8BIT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions,
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.
- Application mallocs (PIDx) will allocate IRAM first, if possible, then DRAM.
- Most other malloc caps only fit in one region anyway.
These region descriptors are very ESP32 specific, because they describe the memory pools available there.
Because of requirements in the coalescing code as well as the heap allocator itself, this list should always
be sorted from low to high start address.
*/
const soc_memory_region_t soc_memory_regions[] = {
{ 0x3F800000, 0x20000, 15, 0}, //SPI SRAM, if available
{ 0x3FFAE000, 0x2000, 0, 0}, //pool 16 <- used for rom code
{ 0x3FFB0000, 0x8000, 0, 0}, //pool 15 <- if BT is enabled, used as BT HW shared memory
{ 0x3FFB8000, 0x8000, 0, 0}, //pool 14 <- if BT is enabled, used data memory for BT ROM functions.
{ 0x3FFC0000, 0x2000, 0, 0}, //pool 10-13, mmu page 0
{ 0x3FFC2000, 0x2000, 0, 0}, //pool 10-13, mmu page 1
{ 0x3FFC4000, 0x2000, 0, 0}, //pool 10-13, mmu page 2
{ 0x3FFC6000, 0x2000, 0, 0}, //pool 10-13, mmu page 3
{ 0x3FFC8000, 0x2000, 0, 0}, //pool 10-13, mmu page 4
{ 0x3FFCA000, 0x2000, 0, 0}, //pool 10-13, mmu page 5
{ 0x3FFCC000, 0x2000, 0, 0}, //pool 10-13, mmu page 6
{ 0x3FFCE000, 0x2000, 0, 0}, //pool 10-13, mmu page 7
{ 0x3FFD0000, 0x2000, 0, 0}, //pool 10-13, mmu page 8
{ 0x3FFD2000, 0x2000, 0, 0}, //pool 10-13, mmu page 9
{ 0x3FFD4000, 0x2000, 0, 0}, //pool 10-13, mmu page 10
{ 0x3FFD6000, 0x2000, 0, 0}, //pool 10-13, mmu page 11
{ 0x3FFD8000, 0x2000, 0, 0}, //pool 10-13, mmu page 12
{ 0x3FFDA000, 0x2000, 0, 0}, //pool 10-13, mmu page 13
{ 0x3FFDC000, 0x2000, 0, 0}, //pool 10-13, mmu page 14
{ 0x3FFDE000, 0x2000, 0, 0}, //pool 10-13, mmu page 15
{ 0x3FFE0000, 0x4000, 1, 0x400BC000}, //pool 9 blk 1
{ 0x3FFE4000, 0x4000, 1, 0x400B8000}, //pool 9 blk 0
{ 0x3FFE8000, 0x8000, 1, 0x400B0000}, //pool 8 <- can be remapped to ROM, used for MAC dump
{ 0x3FFF0000, 0x8000, 1, 0x400A8000}, //pool 7 <- can be used for MAC dump
{ 0x3FFF8000, 0x4000, 1, 0x400A4000}, //pool 6 blk 1 <- can be used as trace memory
{ 0x3FFFC000, 0x4000, 1, 0x400A0000}, //pool 6 blk 0 <- can be used as trace memory
{ 0x40070000, 0x8000, 2, 0}, //pool 0
{ 0x40078000, 0x8000, 2, 0}, //pool 1
{ 0x40080000, 0x2000, 2, 0}, //pool 2-5, mmu page 0
{ 0x40082000, 0x2000, 2, 0}, //pool 2-5, mmu page 1
{ 0x40084000, 0x2000, 2, 0}, //pool 2-5, mmu page 2
{ 0x40086000, 0x2000, 2, 0}, //pool 2-5, mmu page 3
{ 0x40088000, 0x2000, 2, 0}, //pool 2-5, mmu page 4
{ 0x4008A000, 0x2000, 2, 0}, //pool 2-5, mmu page 5
{ 0x4008C000, 0x2000, 2, 0}, //pool 2-5, mmu page 6
{ 0x4008E000, 0x2000, 2, 0}, //pool 2-5, mmu page 7
{ 0x40090000, 0x2000, 2, 0}, //pool 2-5, mmu page 8
{ 0x40092000, 0x2000, 2, 0}, //pool 2-5, mmu page 9
{ 0x40094000, 0x2000, 2, 0}, //pool 2-5, mmu page 10
{ 0x40096000, 0x2000, 2, 0}, //pool 2-5, mmu page 11
{ 0x40098000, 0x2000, 2, 0}, //pool 2-5, mmu page 12
{ 0x4009A000, 0x2000, 2, 0}, //pool 2-5, mmu page 13
{ 0x4009C000, 0x2000, 2, 0}, //pool 2-5, mmu page 14
{ 0x4009E000, 0x2000, 2, 0}, //pool 2-5, mmu page 15
};
const size_t soc_memory_region_count = sizeof(soc_memory_regions)/sizeof(soc_memory_region_t);
/* Reserved memory regions */
const soc_reserved_region_t soc_reserved_regions[] = {
{ 0x40070000, 0x40078000 }, //CPU0 cache region
{ 0x40078000, 0x40080000 }, //CPU1 cache region
/* Warning: The ROM stack is located in the 0x3ffe0000 area. We do not specifically disable that area here because
after the scheduler has started, the ROM stack is not used anymore by anything. We handle it instead by not allowing
any mallocs from tag 1 (the IRAM/DRAM region) until the scheduler has started.
The 0x3ffe0000 region also contains static RAM for various ROM functions. The following lines
reserve the regions for UART and ETSC, so these functions are usable. Libraries like xtos, which are
not usable in FreeRTOS anyway, are commented out in the linker script so they cannot be used; we
do not disable their memory regions here and they will be used as general purpose heap memory.
Enabling the heap allocator for this region but disabling allocation here until FreeRTOS is started up
is a somewhat risky action in theory, because on initializing the allocator, the multi_heap implementation
will go and write metadata at the start and end of all regions. For the ESP32, these linked
list entries happen to end up in a region that is not touched by the stack; they can be placed safely there.*/
{ 0x3ffe0000, 0x3ffe0440 }, //Reserve ROM PRO data region
{ 0x3ffe4000, 0x3ffe4350 }, //Reserve ROM APP data region
#if CONFIG_BT_ENABLED
#if CONFIG_BT_DRAM_RELEASE
{ 0x3ffb0000, 0x3ffb3000 }, //Reserve BT data region
{ 0x3ffb8000, 0x3ffbbb28 }, //Reserve BT data region
{ 0x3ffbdb28, 0x3ffc0000 }, //Reserve BT data region
#else
{ 0x3ffb0000, 0x3ffc0000 }, //Reserve BT hardware shared memory & BT data region
#endif
{ 0x3ffae000, 0x3ffaff10 }, //Reserve ROM data region, inc region needed for BT ROM routines
#else
{ 0x3ffae000, 0x3ffae2a0 }, //Reserve ROM data region
#endif
#if CONFIG_MEMMAP_TRACEMEM
#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS
{ 0x3fff8000, 0x40000000 }, //Reserve trace mem region
#else
{ 0x3fff8000, 0x3fffc000 }, //Reserve trace mem region
#endif
#endif
#if 1 // SPI ram not supported yet
{ 0x3f800000, 0x3f820000 }, //SPI SRAM not installed
#endif
};
const size_t soc_reserved_region_count = sizeof(soc_reserved_regions)/sizeof(soc_reserved_region_t);
#endif

View File

@ -0,0 +1,61 @@
// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "soc/soc.h"
#define SOC_HEAP_TAG_NO_PRIOS 3
/* Tag descriptor holds a description for a particular 'tagged' type of memory on a particular SoC.
*/
typedef struct {
const char *name; ///< Name of this tag
uint32_t caps[SOC_HEAP_TAG_NO_PRIOS]; ///< Capabilities for this tag (as a prioritised set)
bool aliased_iram; ///< If true, this tag is also mapped in IRAM
bool startup_stack; ///< If true, this tag is used for ROM stack during startup
} soc_memory_tag_desc_t;
/* Constant table of tag descriptors for all this SoC's tags */
extern const soc_memory_tag_desc_t soc_memory_tags[];
extern const size_t soc_memory_tag_count;
/* Region descriptor holds a description for a particular region of memory on a particular SoC.
*/
typedef struct
{
intptr_t start; ///< Start address of the region
size_t size; ///< Size of the region in bytes
size_t tag; ///< Tag for the region (index into soc_memory_tag_descriptors)
intptr_t iram_address; ///< If non-zero, is equivalent address in IRAM
} soc_memory_region_t;
extern const soc_memory_region_t soc_memory_regions[];
extern const size_t soc_memory_region_count;
/* Region descriptor holds a description for a particular region of
memory reserved on this SoC for a particular use (ie not available
for stack/heap usage.) */
typedef struct
{
intptr_t start;
intptr_t end;
} soc_reserved_region_t;
extern const soc_reserved_region_t soc_reserved_regions[];
extern const size_t soc_reserved_region_count;

View File

@ -104,8 +104,8 @@ INPUT = \
## System - API Reference
##
## Memory Allocation #
../components/esp32/include/esp_heap_alloc_caps.h \
../components/freertos/include/freertos/heap_regions.h \
../components/heap/include/esp_heap_caps.h \
../components/heap/include/multi_heap.h \
## Interrupt Allocation
../components/esp32/include/esp_intr_alloc.h \
## Watchdogs

View File

@ -9,19 +9,15 @@ possible to connect external SPI flash to the ESP32; it's memory can be integrat
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
memory with certain properties (for example, DMA-capable, 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
code internally allocates memory with ```heap_caps_malloc(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
Because malloc uses this allocation system as well, memory allocated using ```heap_caps_malloc()``` 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.
The "soc" component contains a list of memory regions for the chip, along with the type of each memory (aka its tag) and the associated capabilities for that memory type. On startup, a separate heap is initialised for each contiguous memory region. The capabilities-based allocator chooses the best heap for each allocation, based on the requested capabilities.
Special Uses
------------
@ -39,4 +35,3 @@ API Reference - Heap Regions
----------------------------
.. include:: /_build/inc/heap_regions.inc