LVGL: Avoid Ram overflow error

Description

So after some serious messing around I was able to finally get LVGL running on a custom Alientek board I have been using. The next issue I’m facing is related to my RAM and handling of the framebuffer.

http://www.lucadavidian.com/2017/10/02/stm32-using-the-ltdc-display-controller/

I followed this guide here and was capable of creating a blank, white image, frame buffer that allows me to draw and update the screen with lvgl but my only issue now is how to best implement this to avoid RAM overflow. Currently using ARGB8888 format with any resolution above around 500x300 causes this error.

LVGL_PORT.elf section .data' will not fit in region RAM_D1’

region `RAM_D1’ overflowed by 49936 bytes

Now to be specific I am not treating this framebuffer as a pointer, its being access from a .h file where its defined as a large array. I assume to make this work more efficiently I should probably be utilizing pointers so its maintained in the MCU memory rather then the RAM.

What MCU/Processor/Board and compiler are you using?

STM32H7

What LVGL version are you using?

Most recent

What do you want to achieve?

Not have my framebuffer overflow the ram constantly

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:

/*******************************************************************************
* image
* filename: unsaved
* name: framebufferDefault
*
* preset name: Color A8R8G8B8
* data block size: 32 bit(s), uint32_t
* RLE compression enabled: no
* conversion type: Color, not_used not_used
* bits per pixel: 24
*
* preprocess:
*  main scan direction: top_to_bottom
*  line scan direction: forward
*  inverse: no
*******************************************************************************/


 typedef struct {
     const uint32_t *data;
     uint16_t width;
     uint16_t height;
     uint8_t dataSize;
     } tImage;

#include <stdint.h>



uint32_t image_data_framebufferDefault[131250] = {   0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff.........}

const tImage framebufferDefault = { image_data_framebufferDefault, 500, 350,
    32 };

-----------------------------------------------------------------------
void put_px(uint16_t Xpos, uint16_t Ypos, uint32_t RGB_Code)
{
  /* Write data value to all SDRAM memory */
	image_data_framebufferDefault[Ypos * LayerWidth + Xpos] = RGB_Code;
}

Assuming you have external SDRAM (not internal RAM) available, you should just need to add an __attribute__((section(".sdram_section"))) to the beginning of the array declaration. .sdram_section needs to be replaced with whatever the correct linker section name is on your platform.

any idea where I can find that linker value? And I assume I just set the attribute in the .h file?

as I do have SDRAM

The attribute would be set on image_data_framebufferDefault.

Check for a file that ends with .ld or .lds in your project.

/**
 ******************************************************************************
 * @file      LinkerScript.ld
 * @author    Auto-generated by STM32CubeIDE
 * @brief     Linker script for STM32H743IITx Device from STM32H7 series
 *                      2048Kbytes FLASH
 *                      128Kbytes DTCMRAM
 *                      64Kbytes ITCMRAM
 *                      512Kbytes RAM_D1
 *                      288Kbytes RAM_D2
 *                      64Kbytes RAM_D3
 *
 *            Set heap size, stack size and stack location according
 *            to application requirements.
 *
 *            Set memory bank area and size if external memory is used
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
 * All rights reserved.</center></h2>
 *
 * This software component is licensed by ST under BSD 3-Clause license,
 * the "License"; You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                        opensource.org/licenses/BSD-3-Clause
 *
 ******************************************************************************
 */

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM_D1) + LENGTH(FLASH);	/* end of "RAM_D1" Ram type memory */

_Min_Heap_Size = 0x200;	/* required amount of heap  */
_Min_Stack_Size = 0x400;	/* required amount of stack */

/* Memories definition */
MEMORY
{
  DTCMRAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 128K
  ITCMRAM    (xrw)    : ORIGIN = 0x00000000,   LENGTH = 64K
  RAM_D1    (xrw)    : ORIGIN = 0x24000000,   LENGTH = 8000K
  RAM_D2    (xrw)    : ORIGIN = 0x30000000,   LENGTH = 288K
  RAM_D3    (xrw)    : ORIGIN = 0x38000000,   LENGTH = 64K
  FLASH     (rx)     : ORIGIN = 0x8000000,   LENGTH = 2048K
  BUFFERS   (rx)     : ORIGIN = 0xC0000000, LENGTH = 204K
}

/* Sections */
SECTIONS
{
  /* The startup code into "RAM_D1" Ram type memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >RAM_D1

  /* The program code and other data into "RAM_D1" Ram type memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >RAM_D1

  /* Constant data into "RAM_D1" Ram type memory */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >RAM_D1

  .ARM.extab   : {
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >RAM_D1

  .ARM : {
    . = ALIGN(4);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(4);
  } >RAM_D1

  .preinit_array     :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4);
  } >RAM_D1

  .init_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >RAM_D1

  .fini_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(4);
  } >RAM_D1

  /* Used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections into "RAM_D1" Ram type memory */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */

  } >RAM_D1

  /* Uninitialized data section into "RAM_D1" Ram type memory */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM_D3

  /* User_heap_stack section, used to check that there is enough "RAM_D1" Ram  type memory left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM_D1
  
 .buffers: {
    . = ALIGN(4);
    *(.buffers*)
	} >RAM_D3
  
  

  /* Remove information from the compiler libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

So I found this file, created a BUFFERS memory object and set it to RAM_D3 just for the purposes of testing.

__attribute__ ((section(".buffers"), used))

uint32_t image_data_framebufferDefault[112500] = {

Is this the correct method?

.buffers: {
    . = ALIGN(4);
    *(.buffers*)
	} >RAM_D3

I think you want >BUFFERS on that line. Aside from that this looks like the correct approach. Keep in mind you will need to call BSP_SDRAM_Init (or whatever the H7 function is called) before attempting to access this array, otherwise you will get a fault.

thanks for the help.

I just wanna say the forum support here has been fantastic.

1 Like

Thank you; it’s much appreciated! Glad I can help.

Also you’re completely correct I tried running the program and it hardfaulted after getting into the lvgl_task_handler(). I’ll let you know when I have the SDRAM_Init() figured. It should be generated by the STMCubeIDE correct?

The Cube projects are a bit strange; sometimes the IDE generates things automatically, and sometimes you have to call the function manually. I don’t usually use the STM32 IDE to build my projects so I’m afraid I can’t help much with that detail.

>  LVGL_PORT.elf section `.buffers' will not fit in region `RAM_D1'
> c:\st\stm32cubeide_1.5.0\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.5.0.202011040924\tools\arm-none-eabi\bin\ld.exe: LVGL_PORT.elf section `.buffers' will not fit in region `FLASH'
> c:\st\stm32cubeide_1.5.0\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.5.0.202011040924\tools\arm-none-eabi\bin\ld.exe: region `RAM_D1' overflowed by 1360936 bytes
> c:\st\stm32cubeide_1.5.0\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.5.0.202011040924\tools\arm-none-eabi\bin\ld.exe: region `FLASH' overflowed by 76036 bytes> Blockquote

So apparently its still attempting to write .buffers to RAM_D1. Or at least appears to be. See the reason this is a strange error to me is I’m pretty sure the FB is being allocated to the buffer memory block as LVGL is still hardfaulting like one would expect from that but not sure.

It’s also trying to put .buffers into FLASH at the same time. :confused:

How are you able to run the program and see the hardfault if it’s failing to link?

I was using a smaller “image” as my initial framebuffer. If I decrease it I suspect ill be able to make it hard fault again.

attribute ((section(".buffers"), used))
uint32_t image_data_framebufferDefault[459000] = {

current “image” size

Are you able to print to the debug console from the board? Can you try printf("%08x\n", &image_data_framebufferDefault[0]);? I’d like to see what address it ends up being put at with the smaller size.

When I don’t assign it to buffer and it actually renders that’s the value.

After reassigning the .buffers attribute in the definition. HardFaults.

I’m 99% sure 0x24000000 is internal RAM, not external SDRAM. It looks like something is still wrong with your linker script.

Can you send your linker script in its current state?

/**
 ******************************************************************************
 * @file      LinkerScript.ld
 * @author    Auto-generated by STM32CubeIDE
 * @brief     Linker script for STM32H743IITx Device from STM32H7 series
 *                      2048Kbytes FLASH
 *                      128Kbytes DTCMRAM
 *                      64Kbytes ITCMRAM
 *                      512Kbytes RAM_D1
 *                      288Kbytes RAM_D2
 *                      64Kbytes RAM_D3
 *
 *            Set heap size, stack size and stack location according
 *            to application requirements.
 *
 *            Set memory bank area and size if external memory is used
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
 * All rights reserved.</center></h2>
 *
 * This software component is licensed by ST under BSD 3-Clause license,
 * the "License"; You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                        opensource.org/licenses/BSD-3-Clause
 *
 ******************************************************************************
 */

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1);	/* end of "RAM_D1" Ram type memory */

_Min_Heap_Size = 0x200;	/* required amount of heap  */
_Min_Stack_Size = 0x400;	/* required amount of stack */

/* Memories definition */
MEMORY
{
  DTCMRAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 128K
  ITCMRAM    (xrw)    : ORIGIN = 0x00000000,   LENGTH = 64K
  RAM_D1    (xrw)    : ORIGIN = 0x24000000,   LENGTH = 512k
  RAM_D2    (xrw)    : ORIGIN = 0x30000000,   LENGTH = 288K
  RAM_D3    (xrw)    : ORIGIN = 0x38000000,   LENGTH = 64K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 2048K
  BUFFERS (xrw)    : ORIGIN = 0xC000000, LENGTH=64k
}

/* Sections */
SECTIONS
{
  /* The startup code into "RAM_D1" Ram type memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >RAM_D1

  /* The program code and other data into "RAM_D1" Ram type memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >RAM_D1

  /* Constant data into "RAM_D1" Ram type memory */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >RAM_D1

  .ARM.extab   : {
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >RAM_D1

  .ARM : {
    . = ALIGN(4);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(4);
  } >RAM_D1

  .preinit_array     :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4);
  } >RAM_D1

  .init_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >RAM_D1

  .fini_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(4);
  } >RAM_D1

  /* Used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections into "RAM_D1" Ram type memory */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */

  } >RAM_D1

  /* Uninitialized data section into "RAM_D1" Ram type memory */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM_D1

  /* User_heap_stack section, used to check that there is enough "RAM_D1" Ram  type memory left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM_D1
  
 .buffers: {
    . = ALIGN(4);
    *(.buffers*)
	} >BUFFERS
  
  

  /* Remove information from the compiler libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

You’re missing a zero at the end of the BUFFERS address. You’ll probably also want to increase the size because I doubt 64K will be enough for your framebuffer.