<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><br class=""></div><div class="">Before:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">OVMS > module memory</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">============================</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">Free 8-bit 4291676/4507468, 32-bit 1608/16760, blocks dumped = 0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=no task         total=  38116      0      0      0 change= +38116     +0     +0     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=esp_timer       total=  49628      0    644      0 change= +49628     +0   +644     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=main            total=   9868      0      0  26924 change=  +9868     +0     +0 +26924</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=ipc0            total=  11096      0      0      0 change= +11096     +0     +0     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=Housekeeping    total=  44032      0      0     12 change= +44032     +0     +0    +12</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=ipc1            total=     12      0      0      0 change=    +12     +0     +0     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=Tmr Svc         total=     16      0      0      0 change=    +16     +0     +0     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=tiT             total=      0      0      0    128 change=     +0     +0     +0   +128</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=AsyncConsole    total=     20      0  14404  12000 change=    +20     +0 +14404 +12000</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">============================</span></font></div></div></blockquote><div class=""><br class=""></div><div class="">The change:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">class OvmsCommand</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">  {</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">  public:</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">    void* operator new(size_t sz) { return heap_caps_malloc(sz, MALLOC_CAP_SPIRAM); }</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">    void operator delete  (void* ptr) { return free(ptr); }</span></font></div></blockquote><div class=""><br class=""></div><div class="">After:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">OVMS > module memory</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">============================</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">Free 8-bit 4291676/4507468, 32-bit 1608/16760, blocks dumped = 0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=no task         total=  38116      0      0      0 change= +38116     +0     +0     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=esp_timer       total=  28708      0    644  20916 change= +28708     +0   +644 +20916</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=main            total=   9868      0      0  26924 change=  +9868     +0     +0 +26924</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=ipc0            total=  11096      0      0      0 change= +11096     +0     +0     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=Housekeeping    total=  37984      0      0   6060 change= +37984     +0     +0  +6060</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=ipc1            total=     12      0      0      0 change=    +12     +0     +0     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=Tmr Svc         total=     16      0      0      0 change=    +16     +0     +0     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=tiT             total=      0      0      0    128 change=     +0     +0     +0   +128</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">task=AsyncConsole    total=  12020      0  14404      0 change= +12020     +0 +14404     +0</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">============================</span></font></div></div></blockquote><div class=""><br class=""></div><div class="">So, simply moving OvmsCommand objects to SPI RAM saves us about 26KB of internal RAM.</div><div class=""><br class=""></div><div class="">Our goal would be to move as much of our infrequently accessed stuff as possible to SPI RAM. This would free up as much internal ram as possible for things like stacks, DMA, etc.</div><div class=""><br class=""></div><div class="">Here are the restrictions on SPI RAM that Espressif point out:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><dl class="docutils" style="box-sizing: border-box; margin: 0px 0px 24px; padding: 0px; list-style: none none; color: rgb(64, 64, 64); font-family: Lato, proxima-nova, "Helvetica Neue", Arial, sans-serif; font-size: 16px;"><dt style="box-sizing: border-box; font-weight: bold;" class="">The use of external RAM has a few restrictions:</dt><dd style="box-sizing: border-box; margin: 0px 0px 12px 24px;" class=""><ul class="first last simple" style="box-sizing: border-box; margin: 0px 0px 24px; padding: 0px; list-style-position: initial; list-style-image: initial; line-height: 24px;"><li style="box-sizing: border-box; list-style: disc; margin-left: 24px;" class="">When disabling flash cache (for example, because the flash is being written to), the external RAM also becomes inaccessible; any reads from or writes to it will lead to an illegal cache access exception. This is also the reason that ESP-IDF will never allocate a tasks stack in external RAM.</li><li style="box-sizing: border-box; list-style: disc; margin-left: 24px;" class="">External RAM cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into. Any buffers that will be used in combination with DMA must be allocated using <code class="literal docutils" style="box-sizing: border-box; font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; font-size: 12px; white-space: nowrap; max-width: 100%; background-color: rgb(255, 255, 255); border: 1px solid rgb(225, 228, 229); padding: 2px 5px; color: rgb(231, 76, 60); overflow-x: auto; background-position: initial initial; background-repeat: initial initial;"><span class="pre" style="box-sizing: border-box;">heap_caps_malloc(size,</span> <span class="pre" style="box-sizing: border-box;">MALLOC_CAP_DMA)</span></code> (and can be freed using a standard <code class="literal docutils" style="box-sizing: border-box; font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; font-size: 12px; white-space: nowrap; max-width: 100%; background-color: rgb(255, 255, 255); border: 1px solid rgb(225, 228, 229); padding: 2px 5px; color: rgb(231, 76, 60); overflow-x: auto; background-position: initial initial; background-repeat: initial initial;"><span class="pre" style="box-sizing: border-box;">free()</span></code> call.)</li><li style="box-sizing: border-box; list-style: disc; margin-left: 24px;" class="">External RAM uses the same cache region as the external flash. This means that often accessed variables in external RAM can be read and modified almost as quickly as in internal ram. However, when accessing large chunks of data (>32K), the cache can be insufficient and speeds will fall back to the access speed of the external RAM. Moreover, accessing large chunks of data can ‘push out’ cached flash, possibly making execution of code afterwards slower.</li><li style="box-sizing: border-box; list-style: disc; margin-left: 24px;" class="">External RAM cannot be used as task stack memory; because of this, xTaskCreate and similar functions will always allocate internal memory for stack and task TCBs and xTaskCreateStatic-type functions will check if the buffers passed are internal. However, for tasks not calling on code in ROM in any way, directly or indirectly, the menuconfig option <a class="internal reference" href="http://esp-idf.readthedocs.io/en/latest/api-reference/kconfig.html#config-spiram-allow-stack-external-memory" style="box-sizing: border-box; color: rgb(155, 89, 182); text-decoration: none; cursor: pointer;">SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY</a> will eliminate the check in xTaskCreateStatic, allowing task stack in external RAM. Using this is not advised, however.</li></ul></dd></dl><p style="box-sizing: border-box; line-height: 24px; margin: 0px 0px 24px; font-size: 16px; color: rgb(64, 64, 64); font-family: Lato, proxima-nova, "Helvetica Neue", Arial, sans-serif;" class="">Because there are a fair few situations that have a specific need for internal memory, but it is also possible to use malloc() to exhaust internal memory, there is a pool reserved specifically for requests that cannot be resolved from external memory; allocating task stack, DMA buffers and memory that stays accessible when cache is disabled is drawn from this pool. The size of this pool is configurable in menuconfig.</p></div></blockquote><div class=""><div class="">I’m not having much luck trying to globally override the new() and delete() memory allocators, so I’m now thinking of making a base class with new and delete operators, plus some other utility functions to make spi ram allocation easier. Then whenever we want to make one of our objects allocatable from spiram we simply derive from that base class. That class could pickup on the menuconfig hardware version (3.0 WROOM-32 vs 3.1 WROVER) and turn on/off SPIRAM appropriately.</div></div><div class=""><br class=""></div><div class="">Here is how Espressif do it:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">IRAM_ATTR void *heap_caps_malloc_default( size_t size )</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">{</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">    if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">        return heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">    } else {</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">        void *r;</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">        if (size <= malloc_alwaysinternal_limit) {</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">            r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">        } else {</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">            r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM );</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">        }</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">        if (r==NULL) {</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">            //try again while being less picky</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">            r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT );</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">        }</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">        return r;</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">    }</span></font></div><div class=""><font face="Andale Mono" class=""><span style="font-size: 14px;" class="">}</span></font></div></div></blockquote><div class=""><br class=""></div><div class="">We want to do the opposite - try to allocate from SPIRAM if it is available. There is a heap_caps_malloc_prefer that could do it for us.</div><div class=""><br class=""></div><div class="">Thoughts?</div><div class=""><br class=""></div><div class="">Regards, Mark.</div><div class=""><br class=""></div></body></html>