]> granicus.if.org Git - esp-idf/commitdiff
Add testcase, fix executable memory allocated in shared dram/iram region
authorJeroen Domburg <jeroen@espressif.com>
Tue, 13 Dec 2016 14:21:20 +0000 (22:21 +0800)
committerJeroen Domburg <jeroen@espressif.com>
Thu, 15 Dec 2016 07:03:22 +0000 (15:03 +0800)
components/esp32/heap_alloc_caps.c
components/esp32/test/test_malloc_caps.c [new file with mode: 0644]
components/freertos/heap_regions.c
components/freertos/include/freertos/heap_regions.h
docs/api/mem_alloc.rst
tools/unit-test-app/sdkconfig

index 511903c4384d9847bf7eeaefda278bb44d221392..a77d372b4f3e7f6e9e873b8c8402a2a24743aab6 100644 (file)
@@ -18,6 +18,7 @@
 #include "esp_heap_alloc_caps.h"
 #include "spiram.h"
 #include "esp_log.h"
+#include <stdbool.h>
 
 static const char* TAG = "heap_alloc_caps";
 
@@ -38,6 +39,7 @@ hardwiring addresses.
 typedef struct {
     const char *name;
     uint32_t prio[NO_PRIOS];
+    bool aliasedIram;
 } tag_desc_t;
 
 /*
@@ -46,23 +48,23 @@ Each tag contains NO_PRIOS entries; later entries are only taken if earlier ones
 Make sure there are never more than HEAPREGIONS_MAX_TAGCOUNT (in heap_regions.h) tags (ex the last empty marker)
 */
 static const tag_desc_t tag_desc[]={
-    { "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }},                    //Tag 0: Plain ole D-port RAM
-    { "D/IRAM", { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }},    //Tag 1: Plain ole D-port RAM which has an alias on the I-port
-    { "IRAM", { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT, 0, 0 }},                                 //Tag 2: IRAM
-    { "PID2IRAM", { MALLOC_CAP_PID2, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }},                   //Tag 3-8: PID 2-7 IRAM
-    { "PID3IRAM", { MALLOC_CAP_PID3, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }},                   //
-    { "PID4IRAM", { MALLOC_CAP_PID4, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }},                   //
-    { "PID5IRAM", { MALLOC_CAP_PID5, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }},                   //
-    { "PID6IRAM", { MALLOC_CAP_PID6, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }},                   //
-    { "PID7IRAM", { MALLOC_CAP_PID7, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }},                   //
-    { "PID2DRAM", { MALLOC_CAP_PID2, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }},                     //Tag 9-14: PID 2-7 DRAM
-    { "PID3DRAM", { MALLOC_CAP_PID3, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }},                     //
-    { "PID4DRAM", { MALLOC_CAP_PID4, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }},                     //
-    { "PID5DRAM", { MALLOC_CAP_PID5, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }},                     //
-    { "PID6DRAM", { MALLOC_CAP_PID6, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }},                     //
-    { "PID7DRAM", { MALLOC_CAP_PID7, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }},                     //
-    { "SPISRAM", { MALLOC_CAP_SPISRAM, 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}}, //Tag 15: SPI SRAM data
-    { "", { MALLOC_CAP_INVALID, MALLOC_CAP_INVALID, MALLOC_CAP_INVALID }} //End
+    { "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }, false},                        //Tag 0: Plain ole D-port RAM
+    { "D/IRAM", { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, true},       //Tag 1: Plain ole D-port RAM which has an alias on the I-port
+    { "IRAM", { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT, 0, 0 }, false},                                     //Tag 2: IRAM
+    { "PID2IRAM", { MALLOC_CAP_PID2, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false},                   //Tag 3-8: PID 2-7 IRAM
+    { "PID3IRAM", { MALLOC_CAP_PID3, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false},                   //
+    { "PID4IRAM", { MALLOC_CAP_PID4, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false},                   //
+    { "PID5IRAM", { MALLOC_CAP_PID5, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false},                   //
+    { "PID6IRAM", { MALLOC_CAP_PID6, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false},                   //
+    { "PID7IRAM", { MALLOC_CAP_PID7, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false},                   //
+    { "PID2DRAM", { MALLOC_CAP_PID2, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false},                     //Tag 9-14: PID 2-7 DRAM
+    { "PID3DRAM", { MALLOC_CAP_PID3, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false},                     //
+    { "PID4DRAM", { MALLOC_CAP_PID4, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false},                     //
+    { "PID5DRAM", { MALLOC_CAP_PID5, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false},                     //
+    { "PID6DRAM", { MALLOC_CAP_PID6, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false},                     //
+    { "PID7DRAM", { MALLOC_CAP_PID7, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false},                     //
+    { "SPISRAM", { MALLOC_CAP_SPISRAM, 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, false},   //Tag 15: SPI SRAM data
+    { "", { MALLOC_CAP_INVALID, MALLOC_CAP_INVALID, MALLOC_CAP_INVALID }, false} //End
 };
 
 /*
@@ -231,14 +233,61 @@ void heap_alloc_caps_init() {
     vPortDefineHeapRegionsTagged( regions );
 }
 
+//First and last words of the D/IRAM region, for both the DRAM address as well as the IRAM alias.
+#define DIRAM_IRAM_START 0x400A0000
+#define DIRAM_IRAM_END   0x400BFFFC
+#define DIRAM_DRAM_START 0x3FFE0000
+#define DIRAM_DRAM_END   0x3FFFFFFC
+
 /*
-Standard malloc() implementation. Will return ho-hum byte-accessible data memory.
+  This takes a memory chunk in a region that can be addressed as both DRAM as well as IRAM. It will convert it to
+  IRAM in such a way that it can be later freed. It assumes both the address as wel as the length to be word-aligned.
+  It returns a region that's 1 word smaller than the region given because it stores the original Dram address there.
+  
+  In theory, we can also make this work by prepending a struct that looks similar to the block link struct used by the
+  heap allocator itself, which will allow inspection tools relying on any block returned from any sort of malloc to
+  have such a block in front of it, work. We may do this later, if/when there is demand for it. For now, a simple
+  pointer is used.
+*/
+void *dram_alloc_to_iram_addr(void *addr, size_t len) 
+{
+    uint32_t dstart=(int)addr; //First word
+    uint32_t dend=((int)addr)+len-4; //Last word
+    configASSERT(dstart>=DIRAM_DRAM_START);
+    configASSERT(dend<=DIRAM_DRAM_END);
+    configASSERT((dstart&3)==0);
+    configASSERT((dend&3)==0);
+    uint32_t istart=DIRAM_IRAM_START+(DIRAM_DRAM_END-dend);
+    uint32_t *iptr=(uint32_t*)istart;
+    *iptr=dstart;
+    return (void*)(iptr+1);
+}
+
+/*
+Standard malloc() implementation. Will return standard no-frills byte-accessible data memory.
 */
 void *pvPortMalloc( size_t xWantedSize )
 {
     return pvPortMallocCaps( xWantedSize, MALLOC_CAP_8BIT );
 }
 
+/*
+ Standard free() implementation. Will pass memory on to the allocator unless it's an IRAM address where the
+ actual meory is allocated in DRAM, it will convert to the DRAM address then.
+ */
+void vPortFree( void *pv )
+{
+    if (((int)pv>=DIRAM_IRAM_START) && ((int)pv<=DIRAM_IRAM_END)) {
+        //Memory allocated here is actually allocated in the DRAM alias region and
+        //cannot be de-allocated as usual. dram_alloc_to_iram_addr stores a pointer to
+        //the equivalent DRAM address, though; free that.
+        uint32_t* dramAddrPtr=(uint32_t*)pv;
+        return vPortFreeTagged((void*)dramAddrPtr[-1]);
+    }
+
+    return vPortFreeTagged(pv);
+}
+
 /*
 Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits.
 */
@@ -248,6 +297,17 @@ void *pvPortMallocCaps( size_t xWantedSize, uint32_t caps )
     int tag, j;
     void *ret=NULL;
     uint32_t remCaps;
+    if (caps & MALLOC_CAP_EXEC) {
+        //MALLOC_CAP_EXEC forces an alloc from IRAM. There is a region which has both this
+        //as well as the following caps, but the following caps are not possible for IRAM.
+        //Thus, the combination is impossible and we return NULL directly, even although our tag_desc
+        //table would indicate there is a tag for this.
+        if ((caps & MALLOC_CAP_8BIT) || (caps & MALLOC_CAP_DMA)) {
+            return NULL;
+        }
+        //If any, EXEC memory should be 32-bit aligned, so round up to the next multiple of 4.
+        xWantedSize=(xWantedSize+3)&(~3);
+    }
     for (prio=0; prio<NO_PRIOS; prio++) {
         //Iterate over tag descriptors for this priority
         for (tag=0; tag_desc[tag].prio[prio]!=MALLOC_CAP_INVALID; tag++) {
@@ -262,8 +322,17 @@ void *pvPortMallocCaps( size_t xWantedSize, uint32_t caps )
                 }
                 if (remCaps==0) {
                     //This tag can satisfy all the requested capabilities. See if we can grab some memory using it.
-                    ret=pvPortMallocTagged(xWantedSize, tag);
-                    if (ret!=NULL) return ret;
+                    if ((caps & MALLOC_CAP_EXEC) && tag_desc[tag].aliasedIram) {
+                        //This is special, insofar that what we're going to get back is probably a DRAM address. If so,
+                        //we need to 'invert' it (lowest address in DRAM == highest address in IRAM and vice-versa) and
+                        //add a pointer to the DRAM equivalent before the address we're going to return.
+                        ret=pvPortMallocTagged(xWantedSize+4, tag);
+                        if (ret!=NULL) return dram_alloc_to_iram_addr(ret, xWantedSize+4);
+                    } else {
+                        //Just try to alloc, nothing special.
+                        ret=pvPortMallocTagged(xWantedSize, tag);
+                        if (ret!=NULL) return ret;
+                    }
                 }
             }
         }
diff --git a/components/esp32/test/test_malloc_caps.c b/components/esp32/test/test_malloc_caps.c
new file mode 100644 (file)
index 0000000..0f5129a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ Tests for the capabilities-based memory allocator.
+*/
+
+#include <esp_types.h>
+#include <stdio.h>
+#include "unity.h"
+#include "rom/ets_sys.h"
+#include "esp_heap_alloc_caps.h"
+#include <stdlib.h>
+
+
+TEST_CASE("Capabilities allocator test", "[esp32]")
+{
+    char *m1, *m2[10];
+    int x;
+    size_t free8start, free32start, free8, free32;
+    free8start=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT);
+    free32start=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT);
+    printf("Free 8bit-capable memory: %dK, 32-bit capable memory %dK\n", free8start, free32start);
+    TEST_ASSERT(free32start>free8start);
+    printf("Allocating 10K of 8-bit capable RAM\n");
+    m1=pvPortMallocCaps(10*1024, MALLOC_CAP_8BIT);
+    printf("--> %p\n", m1);
+    free8=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT);
+    free32=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT);
+    printf("Free 8bit-capable memory: %dK, 32-bit capable memory %dK\n", free8, free32);
+    //Both should have gone down by 10K; 8bit capable ram is also 32-bit capable
+    TEST_ASSERT(free8<(free8start-10*1024));
+    TEST_ASSERT(free32<(free32start-10*1024));
+    //Assume we got DRAM back
+    TEST_ASSERT((((int)m1)&0xFF000000)==0x3F000000);
+    free(m1);
+    printf("Freeing; allocating 10K of 32K-capable RAM\n");
+    m1=pvPortMallocCaps(10*1024, MALLOC_CAP_32BIT);
+    printf("--> %p\n", m1);
+    free8=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT);
+    free32=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT);
+    printf("Free 8bit-capable memory: %dK, 32-bit capable memory %dK\n", free8, free32);
+    //Only 32-bit should have gone down by 10K: 32-bit isn't necessarily 8bit capable
+    TEST_ASSERT(free32<(free32start-10*1024));
+    TEST_ASSERT(free8==free8start);
+    //Assume we got IRAM back
+    TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000);
+    free(m1);
+    printf("Allocating impossible caps\n");
+    m1=pvPortMallocCaps(10*1024, MALLOC_CAP_8BIT|MALLOC_CAP_EXEC);
+    printf("--> %p\n", m1);
+    TEST_ASSERT(m1==NULL);
+    printf("Testing changeover iram -> dram");
+    for (x=0; x<10; x++) {
+        m2[x]=pvPortMallocCaps(10*1024, MALLOC_CAP_32BIT);
+        printf("--> %p\n", m2[x]);
+    }
+    TEST_ASSERT((((int)m2[0])&0xFF000000)==0x40000000);
+    TEST_ASSERT((((int)m2[9])&0xFF000000)==0x3F000000);
+    printf("Test if allocating executable code still gives IRAM, even with dedicated IRAM region depleted\n");
+    m1=pvPortMallocCaps(10*1024, MALLOC_CAP_EXEC);
+    printf("--> %p\n", m1);
+    TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000);
+    free(m1);
+    for (x=0; x<10; x++) free(m2[x]);
+    printf("Done.\n");
+}
index 5ff1f2817e7a83c3feb1f1cd9faeafbb7f87c2e1..a7c9606036d73c851d9e887253c03cee6cbe8b62 100644 (file)
@@ -341,7 +341,7 @@ void *pvReturn = NULL;
 }
 /*-----------------------------------------------------------*/
 
-void vPortFree( void *pv )
+void vPortFreeTagged( void *pv )
 {
 uint8_t *puc = ( uint8_t * ) pv;
 BlockLink_t *pxLink;
index 30d0dcb39cce7a8b337eb7648a98c5a48e12200c..090c5b9b36fcf44cbc05d3f024fd31268e2aebc8 100644 (file)
@@ -58,6 +58,15 @@ void vPortDefineHeapRegionsTagged( const HeapRegionTagged_t * const pxHeapRegion
  */
 void *pvPortMallocTagged( size_t xWantedSize, BaseType_t tag );
 
+/**
+ * @brief Free memory allocated with pvPortMallocTagged
+ *
+ * This is basically an implementation of free().
+ *
+ * @param  pv Pointer to region allocated by pvPortMallocTagged
+ */
+void vPortFreeTagged( void *pv );
+
 /**
  * @brief Get the lowest amount of memory free for a certain tag
  *
index d2af7f2875c544224c87876b58984d64e878b3fa..cebea5b8a55985583de1384263896f379618018d 100644 (file)
@@ -14,6 +14,9 @@ can create an OR-mask of the required capabilities and pass that to pvPortMalloc
 code internally allocates memory with ```pvPortMallocCaps(size, MALLOC_CAP_8BIT)``` in order to get data memory that is 
 byte-addressable.
 
+Because malloc uses this allocation system as well, memory allocated using pvPortMallocCaps can be freed by calling
+the standard ```free()``` function.
+
 Internally, this allocator is split in two pieces. The allocator in the FreeRTOS directory can allocate memory from
 tagged regions: a tag is an integer value and every region of free memory has one of these tags. The esp32-specific
 code initializes these regions with specific tags, and contains the logic to select applicable tags from the
@@ -59,11 +62,6 @@ Type Definitions
 
 .. doxygentypedef:: HeapRegionTagged_t
 
-Enumerations
-^^^^^^^^^^^^
-
-Structures
-^^^^^^^^^^
 
 Functions
 ^^^^^^^^^
@@ -74,5 +72,6 @@ Functions
 .. doxygenfunction:: xPortGetMinimumEverFreeHeapSizeCaps
 .. doxygenfunction:: vPortDefineHeapRegionsTagged
 .. doxygenfunction:: pvPortMallocTagged
+.. doxygenfunction:: vPortFreeTagged
 .. doxygenfunction:: xPortGetMinimumEverFreeHeapSizeTagged
 .. doxygenfunction:: xPortGetFreeHeapSizeTagged
index 1b9db995185dc48660ce7093ed7b2770c08b2b1c..14b31e0d18102eafd4d4062ceabb459d5a2e48d1 100644 (file)
@@ -93,6 +93,7 @@ CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048
 CONFIG_MAIN_TASK_STACK_SIZE=4096
 CONFIG_NEWLIB_STDOUT_ADDCR=y
+# CONFIG_NEWLIB_NANO_FORMAT is not set
 CONFIG_CONSOLE_UART_DEFAULT=y
 # CONFIG_CONSOLE_UART_CUSTOM is not set
 # CONFIG_CONSOLE_UART_NONE is not set
@@ -171,6 +172,8 @@ CONFIG_MBEDTLS_HARDWARE_AES=y
 CONFIG_MBEDTLS_HARDWARE_MPI=y
 CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y
 CONFIG_MBEDTLS_HARDWARE_SHA=y
+CONFIG_MBEDTLS_HAVE_TIME=y
+# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set
 
 #
 # SPI Flash driver