[Ovmsdev] Playing with SPI RAM
Mark Webb-Johnson
mark at webb-johnson.net
Fri Jan 12 10:09:45 HKT 2018
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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openvehicles.com/pipermail/ovmsdev/attachments/20180112/9efe88b8/attachment.htm>
More information about the OvmsDev
mailing list