2025-09-18 5:57 AM
Hello Everyone,
I'm using an STM32L496 (1MB of flash) with an MT25QL128A external flash memory (16MB) for graphics assets in my TouchGFX application. I'm finding that I'm running out of code memory (the memory on board the STM32, not the memory on the external flash - that has plenty of room left). I've got all of my normal TouchGFX graphics assets (texts, fonts, graphics) going onto the external flash as per typical TouchGFX setup, but I've still at 93% usage on the STM's internal flash. I believe this is mostly due that high number of screens. Is there any way to move all or most of the GUI code (view and presenter code) into external memory as well?
2025-09-19 3:04 AM
Hello @BWKidd ,
Yes this is possible, you have to modify your linker script for that. I remember that we have shared something similar to this not so long ago on the forum but I couldn't find it. But here an example of how it could be managed (for gcc or STM32CubeIDE):
.text_tgfx :
{
*/TouchGFX/gui/*(.text)
*/TouchGFX/gui/*(.text*)
*/TouchGFX/generated/gui_generated/*(.text)
*/TouchGFX/generated/gui_generated/*(.text*)
} >EXTFLASH_CODE
2025-09-19 1:02 PM
@Osman SOYKURT Thanks so much for the quick response. I'll give this a go and either post what worked or ask a follow-up question or two if I can't get it going.
2025-09-22 8:22 AM
I've played around with this a little bit, first applying your example verbatim (other than changing ">EXTFLASH_CODE" to ">QUADSPI" since that's what my memory mapping to the external flash is called) as a staring point. This resulted in an immediate jump to hard fault when I tried to debug it in STM32CubeIDE 1.19.0. I then attempted to add exclusions for anything touchgfx that appeared in .init_array - not sure if this was what I was supposed to do, but based on chatpgt and google, it sounded like I don't want to put anything that is needed prior to main() running in external flash, since it won't be memory mapped yet. I've put my full linker file below.
Really I'm way out of my depth here, so if it looks like I have no idea what I'm doing, its because I don't!
/*
******************************************************************************
**
** @file : LinkerScript.ld
**
** @author : Auto-generated by STM32CubeIDE
**
** @brief : Linker script for STM32L496VGTx Device from STM32L4 series
** 1024KBytes FLASH
** 320KBytes RAM
** 64KBytes RAM2
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used
**
** Target : STMicroelectronics STM32
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
******************************************************************************
** @attention
**
** Copyright (c) 2024 STMicroelectronics.
** All rights reserved.
**
** This software is licensed under terms that can be found in the LICENSE file
** in the root directory of this software component.
** If no LICENSE file comes with this software, it is provided AS-IS.
**
******************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
_Min_Heap_Size = 0x1000; /* required amount of heap */
_Min_Stack_Size = 0x2000; /* required amount of stack */
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
RAM2 (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1020K
EEPROM_EMULATION (rx) : ORIGIN = 0x080FF000, LENGTH = 4K
QUADSPI (rx) : ORIGIN = 0x90000000, LENGTH = 16M
}
/* Sections */
SECTIONS
{
/* TouchGFX GUI code (.text) to QSPI, but keep ctor-providers internal */
.text_tgfx :
{
. = ALIGN(4);
__tgfx_text_start__ = .;
/* Exclude all TUs that appear in .init_array (ctors before main) */
EXCLUDE_FILE (
*/TouchGFX/generated/gui_generated/src/common/FrontendApplicationBase.o
*/TouchGFX/generated/texts/src/TypedTextDatabase.o
*/TouchGFX/target/generated/TouchGFXConfiguration.o
*/TouchGFX/target/generated/TouchGFXGeneratedHAL.o
*/TouchGFX/generated/gui_generated/src/*/*ViewBase.o
*/TouchGFX/generated/gui_generated/src/*/*PresenterBase.o
*/TouchGFX/gui/src/common/CustomKeyboard.o
*/TouchGFX/gui/src/containers/contKeyboardScreen.o
*/TouchGFX/gui/src/*/*Presenter.o
*/TouchGFX/gui/src/*/*View.o
) */TouchGFX/generated/gui_generated/src/*.o(.text .text.*)
EXCLUDE_FILE (
*/TouchGFX/generated/gui_generated/src/common/FrontendApplicationBase.o
*/TouchGFX/generated/texts/src/TypedTextDatabase.o
*/TouchGFX/target/generated/TouchGFXConfiguration.o
*/TouchGFX/target/generated/TouchGFXGeneratedHAL.o
*/TouchGFX/generated/gui_generated/src/*/*ViewBase.o
*/TouchGFX/generated/gui_generated/src/*/*PresenterBase.o
*/TouchGFX/gui/src/common/CustomKeyboard.o
*/TouchGFX/gui/src/containers/contKeyboardScreen.o
*/TouchGFX/gui/src/*/*Presenter.o
*/TouchGFX/gui/src/*/*View.o
) */TouchGFX/generated/gui_generated/src/*/*.o(.text .text.*)
EXCLUDE_FILE (
*/TouchGFX/generated/gui_generated/src/common/FrontendApplicationBase.o
*/TouchGFX/generated/texts/src/TypedTextDatabase.o
*/TouchGFX/target/generated/TouchGFXConfiguration.o
*/TouchGFX/target/generated/TouchGFXGeneratedHAL.o
*/TouchGFX/generated/gui_generated/src/*/*ViewBase.o
*/TouchGFX/generated/gui_generated/src/*/*PresenterBase.o
*/TouchGFX/gui/src/common/CustomKeyboard.o
*/TouchGFX/gui/src/containers/contKeyboardScreen.o
*/TouchGFX/gui/src/*/*Presenter.o
*/TouchGFX/gui/src/*/*View.o
) */TouchGFX/gui/src/*.o(.text .text.*)
EXCLUDE_FILE (
*/TouchGFX/generated/gui_generated/src/common/FrontendApplicationBase.o
*/TouchGFX/generated/texts/src/TypedTextDatabase.o
*/TouchGFX/target/generated/TouchGFXConfiguration.o
*/TouchGFX/target/generated/TouchGFXGeneratedHAL.o
*/TouchGFX/generated/gui_generated/src/*/*ViewBase.o
*/TouchGFX/generated/gui_generated/src/*/*PresenterBase.o
*/TouchGFX/gui/src/common/CustomKeyboard.o
*/TouchGFX/gui/src/containers/contKeyboardScreen.o
*/TouchGFX/gui/src/*/*Presenter.o
*/TouchGFX/gui/src/*/*View.o
) */TouchGFX/gui/src/*/*.o(.text .text.*)
. = ALIGN(4);
__tgfx_text_end__ = .;
} > QUADSPI
/* Add framebuffer section in RAM */
TouchGFX_Framebuffer (NOLOAD) :
{
. = ALIGN(4);
*(.TouchGFX_Framebuffer)
. = ALIGN(4);
} >RAM
/* The startup code into "FLASH" Rom type memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data into "FLASH" Rom 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)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data into "FLASH" Rom type memory */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(4);
} >FLASH
.ARM (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(4);
} >FLASH
.preinit_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
} >FLASH
.init_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
} >FLASH
.fini_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(4);
} >FLASH
/* Used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections into "RAM" Ram type memory */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.RamFunc) /* .RamFunc sections */
*(.RamFunc*) /* .RamFunc* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section into "RAM" 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
/* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the compiler libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
ExtFlashSection :
{
*(ExtFlashSection ExtFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >QUADSPI
FontFlashSection :
{
*(FontFlashSection FontFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >QUADSPI
TextFlashSection :
{
*(TextFlashSection TextFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >QUADSPI
}