8000 Too little DMA memory (or free memory block) is available for the eps32s3 (or other esp32s) · Issue #12075 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

Too little DMA memory (or free memory block) is available for the eps32s3 (or other esp32s) #12075

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
nspsck opened this issue Jul 23, 2023 · 5 comments

Comments

@nspsck
Copy link
Contributor
nspsck commented Jul 23, 2023

Hi, I am having trouble to get the maximum 256KB DMA block using the latest release. After some hardcoded debug print in a C-Module, I found out that the maximum allocatable memory block is roughly at 247KB. This is the code I've used:

STATIC void frame_buffer_alloc(mp_lcd_rm67162_obj_t *self, int len) {
    self->frame_buffer_size = len;
    size_t free_size = heap_caps_get_largest_free_block(MALLOC_CAP_DMA);
    self->frame_buffer = heap_caps_malloc(self->frame_buffer_size, MALLOC_CAP_DMA);
    int threshold = free_size / 1024;

    if (self->frame_buffer == NULL) {
        if (threshold <= 247) {
            mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to allocate DMA'able framebuffer, 
                                           Maiximum available DMA size: less than 247KB"));
        }
        if (threshold <= 248) {
            mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to allocate DMA'able framebuffer, 
                                           Maiximum available DMA size: less than 248KB"));
        }
        if (threshold <= 249) {
            mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to allocate DMA'able framebuffer, 
                                           Maiximum available DMA size: less than 249KB"));
        } else {
            mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to allocate DMA'able framebuffer, 
                                           Maiximum available DMA size: more than 249KB"));
        }
    }
    memset(self->frame_buffer, 0, self->frame_buffer_size);
}

Now I don't know if this is a bug... But, if uPy is using roughly 16KB of RAM as it advertised (which I highly doubt on the esp32 port, because of wifi and stuff), and freeRTOS taking at most 10KB according to this (that being all the ROM has been loaded into the RAM), why isn't it possible to allocate 256KB of continous block? Is this caused by some bug or have I underestimate the memory usage of uPy by a huge margine?

@nspsck nspsck added the bug label Jul 23, 2023
@mattytrentini
Copy link
Contributor

Related to #11853? (That issue is high on the priority list and is a consequence of the recent move to IDF v5.0.2)

@nspsck
Copy link
Contributor Author
nspsck commented Jul 24, 2023

I highly doubt that would be the case. I also tried to modify the main.c file but it had no effect on this issue. To be precise, I have modified this line to / 3 / 4 / 6, It had no effect on this problem.

Further I tracked down the change log of the main.c file (I searched for main.c on that site to locate the file) and it seems like the micropython heap is allocated the same way. In the older version, line 140, 146 and 147 are effectively excuted for the esp32s3 and in the newer version, these are the same 3 lines. The main difference between newer and older version is, that the newer Version uses heap_caps_malloc() and the older version just uses malloc().

So I took my next approach. First of all, I changed the heap_caps_malloc() to malloc(), then I modified the following lines in sdkconfig using menuconfig, note CONFIG_SPIRAM_USE_MALLOC=y most likely implies CONFIG_SPIRAM_USE_CAPS_ALLOC=y, since it is called in the menuconfig: "make memory also allocatable using malloc()."

CONFIG_SOC_SPIRAM_SUPPORTED=y
CONFIG_SPIRAM=y
# CONFIG_SPIRAM_MODE_QUAD is not set
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_TYPE_AUTO=y
# CONFIG_SPIRAM_TYPE_ESPPSRAM64 is not set
CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_CLK_IO=30
CONFIG_SPIRAM_CS_IO=26
# CONFIG_SPIRAM_FETCH_INSTRUCTIONS is not set
# CONFIG_SPIRAM_RODATA is not set
CONFIG_SPIRAM_SPEED_80M=y
# CONFIG_SPIRAM_SPEED_40M is not set
CONFIG_SPIRAM_SPEED=80
CONFIG_SPIRAM_BOOT_INIT=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
# CONFIG_SPIRAM_USE_MEMMAP is not set
# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_MEMTEST=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
# CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP is not set
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=262144
# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set
# CONFIG_SPIRAM_ECC_ENABLE is not set
CONFIG_ESP32S3_SPIRAM_SUPPORT=y

It built and booted, but the same problem is still present. So I assume that this problem is not the new idf version related... But there is a fairly chance that I am wrong since I am a beginner and know very little about this whole project.

EDIT: of cause I assumed that there are no significant changes to the heap_caps_malloc() related function in the esp-idf. And because there are indeed roughly 247KB internal RAM block available no matter of the divider, I assume that divider only limits what uPy can see and use, not the C-Modules. Which leads me to believe that this issue has something to do with freeRTOS or how uPy handles C-Modules or RAM in general. But again, those are just my speculations.

@shariltumin
Copy link
$ diff main.c main.c-ORIG
109,110c109
< // void *mp_task_heap = heap_caps_malloc(mp_task_heap_size, caps);
< void *mp_task_heap = heap_caps_malloc(2000*1024, caps)
---
> void *mp_task_heap = heap_caps_malloc(mp_task_heap_size, caps);

set the MP heap hardcoded to 2000*1024=2048000. We should get about 2000KB MP heap.

>>>
>>> gc.mem_free()
4273360

Still gc.mem_free() shows about half of the 8MB PSRAM free.

>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 6338304, 6291456, 6338304), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16864, 13312, 13496), (8132, 7752, 7680, 7752)]
>>>

But esp32.idf_heap_info() reports 'correctly' - (8388608, 6338304, 6291456, 6338304) the 6338304 is about 6MB since MP was set to 2000KB.

gc.mem_free() uses py/gc.c gc_info(), maybe gc.c is broken?

>>> import micropython as mp
>>> mp.mem_info()
stack: 704 out of 15360
GC: total: 4274752, used: 2688, free: 4272064
 No. of 1-blocks: 52, 2-blocks: 14, max blk sz: 18, max free sz: 266968

mem_info() also reports a total of 4274752, but it should be 2048000.

@dpgeorge
Copy link
Member

@shariltumin you need to change the size_t mp_task_heap_size variable.

@shariltumin
Copy link

@dpgeorge Thanks! My mistake.

With 8MB PSRAM I managed to build two firmwares v1.20.0-283-g7d66ae603 esp-idf 5.0.2 on ESP32S3-N16R8 board: 1) 2/6 MP/IDF and 2) 6/2 MP/IDF

Below is a somewhat lengthy report of the results of my tests.

The msgpipe is a C module that allocates/deallocates memory to/from a ring buffer structure using malloc()/free().

#Firmware 2/6 test.

Source modification:

$ diff main.c main.c-ORIG 
108,109c108
<     // size_t mp_task_heap_size = MIN(heap_caps_get_largest_free_block(caps), heap_total / 2);
<     size_t mp_task_heap_size = 2000*1024;
---
>     size_t mp_task_heap_size = MIN(heap_caps_get_largest_free_block(caps), heap_total / 2);

REPL run:

MicroPython v1.20.0-283-g7d66ae603 on 2023-07-25; ESP32S3-LUAT-CORE N16R8 UART (KAKI5) with ESP32-S3
Type "help()" for more information.
>>> gc.mem_free()
1999680
>>> import esp32
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 6338304, 6291456, 6338304), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16864, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> from msgpipe import pipe
>>> a ='a'*1999000
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError: memory allocation failed, allocating 1999001 bytes
>>> gc.mem_free()
1999376
>>> a = 'a'*1990000
>>> gc.mem_free()
9024
>>> mp.mem_info()
stack: 704 out of 15360
GC: total: 2001088, used: 1992688, free: 8400
 No. of 1-blocks: 59, 2-blocks: 15, max blk sz: 124376, max free sz: 480
>>> p = pipe(4*1024*1024)
>>> p.size()
4194304
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 2143996, 2097152, 2143996), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16776, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> p.resize(0)
0
>>> p = pipe(6291456)
>>> p.size()
-1
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 2143996, 2097152, 2143996), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16600, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> p.resize(0)
0
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 2143996, 2097152, 2143996), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16600, 13312, 13496), (8132, 7752, 7680, 7752)]

>>> import machine
>>> machine.reset()

>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 6338304, 6291456, 2143992), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16776, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> p = pipe(6*1024*1024)
>>> p.size()
6291456
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 46844, 46080, 46844), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16688, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> p.resize(0)
0
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 6338304, 6291456, 46844), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16688, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> p = pipe(6*1024*1024)
>>> p.size()
6291456
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 46844, 46080, 46844), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16600, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> 
MPY: soft reboot
MicroPython v1.20.0-283-g7d66ae603 on 2023-07-25; ESP32S3-LUAT-CORE N16R8 UART (KAKI5) with ESP32-S3
Type "help()" for more information.
>>> import esp32
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 46844, 46080, 46844), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16600, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> dir()
['__name__', 'os', 'gc', 'bdev', 'esp32']

The idf heap is not deallocated during a soft reboot.


#Firmware 6/2 test.

Source modification:

$ diff main.c main.c-ORIG 
108,109c108
<     // size_t mp_task_heap_size = MIN(heap_caps_get_largest_free_block(caps), heap_total / 2);
<     size_t mp_task_heap_size = 6000*1024;
---
>     size_t mp_task_heap_size = MIN(heap_caps_get_largest_free_block(caps), heap_total / 2);

REPL run:

>>> gc.mem_free()
6001872
>>> a = 'a'*6000000
>>> len(a)
6000000
>>> gc.mem_free()
1328
>>> import esp32
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 2242304, 2228224, 2242304), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16864, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> from msgpipe import pipe
MemoryError: memory allocation failed, allocating 512 bytes
>>> del a
>>> gc.collect()
>>> gc.mem_free()
6001664
>>> from msgpipe import pipe
>>> gc.mem_free()
6001312
>>> a = 'a'*6000000
>>> gc.mem_free()
1808
>>> p = pipe(2228000)
>>> p.size()
2228000
>>> esp32.idf_heap_info(esp32.HEAP_DATA)
[(32767, 32031, 31744, 32015), (8388608, 14300, 14080, 14300), (265392, 230676, 229376, 230676), (22308, 64, 56, 28), (32768, 16776, 13312, 13496), (8132, 7752, 7680, 7752)]
>>> a[0]
'a'
>>> a[6000000-1]
'a'

ESP-WARNING "ESP32 has a few hundred kilobytes of internal RAM, residing on the same die as the rest of the chip components. It can be insufficient for some purposes, so ESP32 has the ability to use up to 4 MB of virtual addresses for external PSRAM (Psuedostatic RAM) memory. The external memory is incorporated in the memory map and, with certain restrictions, is usable in the same way as internal data RAM."

Despite the above warning, I manage to allocate more than 4MB of memory block. I do not know the impact on system stability, perhaps more knowledgeable can explain.

This is my SPIRAM configuration file in the sdkconfig.board file.

# SPI RAM config
#
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_TYPE_AUTO=y
CONFIG_DEFAULT_PSRAM_CLK_IO=30
CONFIG_DEFAULT_PSRAM_CS_IO=26
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_BOOT_INIT=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
CONFIG_SPIRAM_SIZE=-1
CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_MEMTEST=y
CONFIG_SPIRAM_CACHE_WORKAROUND=y

Sorry for taking up so much space, but I hope it is useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants
0