[Ovmsdev] Playing with SPI RAM

Michael Balzer dexter at expeedo.de
Sat Jan 13 02:30:12 HKT 2018


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
>             <http://esp-idf.readthedocs.io/en/latest/api-reference/kconfig.html#config-spiram-allow-stack-external-memory> will eliminate the check in
>             xTaskCreateStatic, allowing task stack in external RAM. Using this is not advised, however.
>
>     Because there are a fair few situations that have a specific need for internal memory, but it is also possible to use malloc() to exhaust internal memory,
>     there is a pool reserved specifically for requests that cannot be resolved from external memory; allocating task stack, DMA buffers and memory that stays
>     accessible when cache is disabled is drawn from this pool. The size of this pool is configurable in menuconfig.
>
> 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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openvehicles.com/pipermail/ovmsdev/attachments/20180112/4d6558ed/attachment.htm>


More information about the OvmsDev mailing list