[Ovmsdev] Playing with SPI RAM

Mark Webb-Johnson mark at webb-johnson.net
Sat Jan 13 12:57:13 HKT 2018


We can menuconfig to change malloc to use spiram, but that is not really practical. Too inflexible. We really need a simple way to say allocate this in spiram if possible.

In a few months, it will be easier (as the IDF better supports explicit declarations for internal memory stuff).

The std allocator method may be a cleaner approach than a derived base class. I will look into it.

Regards, Mark

> On 13 Jan 2018, at 2:30 AM, Michael Balzer <dexter at expeedo.de> wrote:
> 
> Mark,
> 
> a base class can also provide an malloc() override.
> 
> Additionally, all standard containers can be instantiated with a custom Allocator. Default is std::allocator.
> 
> http://en.cppreference.com/w/cpp/concept/Allocator
> 
> So with an SPIAllocator we can put the container management overhead     into SPI RAM as well very easily. But that may affect performance badly on central lists & maps under load, we should be careful with that option.
> 
> Regards,
> Michael
> 
> 
>> Am 12.01.2018 um 03:09 schrieb Mark Webb-Johnson:
>> 
>> Before:
>> 
>> OVMS > module memory
>> ============================
>> Free 8-bit 4291676/4507468, 32-bit 1608/16760, blocks dumped = 0
>> task=no task         total=  38116      0      0      0 change= +38116     +0     +0     +0
>> task=esp_timer       total=  49628      0    644      0 change= +49628     +0   +644     +0
>> task=main            total=   9868      0      0  26924 change=  +9868     +0     +0 +26924
>> task=ipc0            total=  11096      0      0      0 change= +11096     +0     +0     +0
>> task=Housekeeping    total=  44032      0      0     12 change= +44032     +0     +0    +12
>> task=ipc1            total=     12      0      0      0 change=    +12                     +0     +0     +0
>> task=Tmr Svc         total=     16      0      0      0 change=    +16     +0     +0     +0
>> task=tiT             total=      0      0      0    128 change=     +0     +0     +0   +128
>> task=AsyncConsole    total=     20      0  14404  12000 change=    +20     +0 +14404 +12000
>> ============================
>> 
>> The change:
>> 
>> class OvmsCommand
>>   {
>>   public:
>>     void* operator new(size_t sz) { return heap_caps_malloc(sz, MALLOC_CAP_SPIRAM); }
>>     void operator delete  (void* ptr) { return free(ptr); }
>> 
>> After:
>> 
>> OVMS > module memory
>> ============================
>> Free 8-bit 4291676/4507468, 32-bit 1608/16760, blocks dumped = 0
>> task=no task         total=  38116      0      0      0 change= +38116     +0     +0     +0
>> task=esp_timer       total=  28708      0    644  20916 change= +28708     +0   +644 +20916
>> task=main            total=   9868      0      0  26924 change=  +9868     +0     +0 +26924
>> task=ipc0            total=  11096      0      0      0 change= +11096     +0     +0     +0
>> task=Housekeeping    total=  37984      0      0   6060 change= +37984     +0     +0  +6060
>> task=ipc1            total=     12      0      0      0 change=    +12                     +0     +0     +0
>> task=Tmr Svc         total=     16      0      0      0 change=    +16     +0     +0     +0
>> task=tiT             total=      0      0      0    128 change=     +0     +0     +0   +128
>> task=AsyncConsole    total=  12020      0  14404      0 change= +12020     +0 +14404     +0
>> ============================
>> 
>> So, simply moving OvmsCommand objects to SPI RAM saves us about 26KB of internal RAM.
>> 
>> 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.
>> 
>> Here are the restrictions on SPI RAM that Espressif point out:
>> 
>> The use of external RAM has a few restrictions:
>> When disabling flash cache (for example, because the flash is being written to), the external RAM also becomes inaccessible; any reads from or writes to it will lead to an illegal cache access exception. This is also the reason that ESP-IDF will never allocate a tasks stack in external RAM.
>> External RAM cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into. Any buffers that will be used in combination with DMA must be allocated using heap_caps_malloc(size, MALLOC_CAP_DMA) (and can be freed using a standard free() call.)
>> External RAM uses the same cache region as the external flash. This means that often accessed variables in external RAM can be read and modified almost as quickly as in internal ram. However, when accessing large chunks of data (>32K), the cache can be insufficient and speeds will fall back to the access speed of the external RAM. Moreover, accessing large chunks of data can ‘push out’ cached flash, possibly making execution of code afterwards slower.
>> External RAM cannot be used as task stack memory; because of this, xTaskCreate and similar functions will always allocate internal memory for stack and task TCBs and xTaskCreateStatic-type functions will check if the buffers passed are internal. However, for tasks not calling on code in ROM in any way, directly or                   indirectly, the menuconfig option SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY will eliminate the check in xTaskCreateStatic, allowing task stack in external RAM. Using this is not advised, however.
>> Because there are a fair few situations that have a specific need for internal memory, but it is also possible to use malloc() to exhaust internal memory, there is a pool reserved specifically for requests that cannot be resolved from external memory; allocating task stack, DMA buffers and memory that stays accessible when cache is disabled is drawn from this pool. The size of this pool is configurable in menuconfig.
>> 
>> 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.
>> 
>> Here is how Espressif do it:
>> 
>> IRAM_ATTR void *heap_caps_malloc_default( size_t size )
>> {
>>     if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {
>>         return heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
>>     } else {
>>         void *r;
>>         if (size <= malloc_alwaysinternal_limit) {
>>             r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
>>         } else {
>>             r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM );
>>         }
>>         if (r==NULL) {
>>             //try again while being less picky
>>             r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT );
>>         }
>>         return r;
>>     }
>> }
>> 
>> 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.
>> 
>> Thoughts?
>> 
>> Regards, Mark.
>> 
>> 
>> 
>> _______________________________________________
>> OvmsDev mailing list
>> OvmsDev at lists.teslaclub.hk
>> http://lists.teslaclub.hk/mailman/listinfo/ovmsdev
> 
> -- 
> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal
> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
> _______________________________________________
> OvmsDev mailing list
> OvmsDev at lists.teslaclub.hk
> http://lists.teslaclub.hk/mailman/listinfo/ovmsdev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.teslaclub.hk/pipermail/ovmsdev/attachments/20180113/caf12070/attachment-0001.html>


More information about the OvmsDev mailing list