Skip to main content
Associate
May 14, 2026
Question

SPI External Loader Error for STM32G491KCU6

  • May 14, 2026
  • 1 reply
  • 119 views

I need to use an external loader via single SPI for a project. Despite performing all the necessary steps. I'm getting an error through CubeProgrammmer.

/* Entry Point */
ENTRY(Init)

/* Generate 2 segment for Loader code and device info */
PHDRS {Loader PT_LOAD ; SgInfo PT_LOAD ; }

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000004, LENGTH = 112K-4
}

/* Define output sections */
SECTIONS
{
 /* The startup code goes first into FLASH */
 
 .isr_vector :
 {
 	. = . + 0x1FC;
 . = ALIGN(4);
 KEEP(*(.isr_vector)) /* Startup code */
 . = ALIGN(4);
 } >RAM :Loader
 
 

 .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >RAM
 .ARM : {
 __exidx_start = .;
 *(.ARM.exidx*)
 __exidx_end = .;
 } >RAM :Loader

 .preinit_array :
 {
 PROVIDE_HIDDEN (__preinit_array_start = .);
 KEEP (*(.preinit_array*))
 PROVIDE_HIDDEN (__preinit_array_end = .);
 } >RAM :Loader
 
 .init_array :
 {
 PROVIDE_HIDDEN (__init_array_start = .);
 KEEP (*(SORT(.init_array.*)))
 KEEP (*(.init_array*))
 PROVIDE_HIDDEN (__init_array_end = .);
 } >RAM :Loader
 
 .fini_array :
 {
 PROVIDE_HIDDEN (__fini_array_start = .);
 KEEP (*(SORT(.fini_array.*)))
 KEEP (*(.fini_array*))
 PROVIDE_HIDDEN (__fini_array_end = .);
 } >RAM :Loader

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

 /* Initialized data sections goes into RAM, load LMA copy after code */
 .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 :Loader

 
 /* Uninitialized data section */
 . = ALIGN(4);
 .bss :
 {
 /* This is used by the startup in order to initialize the .bss secion */
 _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 :Loader

 /* The program code and other data goes into FLASH */
 .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 */
 } >RAM :Loader
 
 .Dev_info :
 {
	KEEP(*Dev_Inf.o ( .rodata* ))
 } :SgInfo
 
 
 /* Constant data goes into FLASH */
 .rodata :
 {
 . = ALIGN(4);
 *(.rodata) /* .rodata sections (constants, strings, etc.) */
 *(.rodata*) /* .rodata* sections (constants, strings, etc.) */
 . = ALIGN(4);
 } >RAM :Loader
 
 
 /* User_heap_stack section, used to check that there is enough RAM left */
 ._user_heap_stack :
 {
 . = ALIGN(4);
 PROVIDE ( end = . );
 PROVIDE ( _end = . );
 . = . + _Min_Heap_Size;
 . = . + _Min_Stack_Size;
 . = ALIGN(4);
 } >RAM :Loader

 


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

My linker code is above. I also generate .stldr file.

#include "Dev_Inf.h"

/* This structure containes information used by ST-LINK Utility to program and erase the device */
#if defined (__ICCARM__)
__root struct StorageInfo const StorageInfo = {
#else
struct StorageInfo __attribute__((section(".Dev_info"))) /*const*/ StorageInfo = {
#endif
 "W25Q64JVZPIQ-STM32G491-EXT-FLASH", 	 	// Device Name + EVAL Borad name
 SPI_FLASH, 					// Device Type
 0x00000000, 						// Device Start Address
 0x00800000, 						// Device Size in Bytes (8MBytes)
 0x100, 						 // Programming Page Size 256Bytes
 0xFF, 						// Initial Content of Erased Memory
// Specify Size and Address of Sectors (view example below)
 {
 {0x00000800, 0x00001000}, 		 // Sector Num : 2048 ,Sector Size: 4KBytes
 {0x00000000, 0x00000000}
 }
}; 


My dev_inf function is as shown above. I'm using W25Q64 as an external flash. The flash drive is working correctly without external loader. It has been tested before.

int Init(void)
{

 SystemInit();
 /* ADAPTATION TO THE DEVICE
 *
 * change VTOR setting for H7 device
 * SCB->VTOR = 0x24000000 | 0x200;
 *
 * change VTOR setting for other devices
 * SCB->VTOR = 0x20000000 | 0x200;
 *
 * */
 SCB->VTOR = 0x20000000 | 0x200;



 HAL_DeInit();
 HAL_Init();

 /* Configure the system clock */
 SystemClock_Config();

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_SPI3_Init();


 __HAL_SPI_ENABLE(&hspi3);

 __set_PRIMASK(0); //enable interrupts
 __set_PRIMASK(1); //disable interrupts

 return LOADER_OK;
}

I'm trying to initialize an external loader using the Init() function above. I have also read, write, sector erase and other functions. 

atilkicioglu_0-1778732932630.png

I can't read and write to start address. As you see at image, I have an error. Loader was selected by the way. 

int Read (uint32_t Address, uint32_t Size, uint8_t* Buffer)
{
// sFLASH_ReadBuffer(Buffer, Address, Size);

 return 1;
}

In addition, I tried Read() function above. This function at above only return 1 so, There shouldn't be any errors. But I'm still getting errors.

I need to write an external loader for my processor. I've tried many things but haven't succeeded. I would appreciate your help. 

 




1 reply

ST Employee
May 14, 2026

Hello @atilkicioglu ,

Thanks for sharing the details. Based on what you posted, the W25Q64 itself is probably not the problem, since your SPI flash works outside the loader. The issue is more likely with the external loader setup, especially the linker script and how the `StorageInfo` structure is placed. For ST external loaders, the code must run entirely from SRAM, the device information must be exposed in a proper `.Dev_info` section, and the memory map should follow ST’s expected layout. In particular, using `0x20000004` as the RAM origin is unusual, and I would strongly recommend using 0x20000000 unless you have a specific reason not to.

Also, make sure the loader functions return the values expected by ST’s loader template, since returning 1 may be interpreted as an error depending on the implementation. Your Init() routine should stay simple: configure clocks, GPIO, SPI, and then return success. In short, CubeProgrammer can see the loader, but it likely cannot correctly use it because the loader metadata or memory placement does not fully match ST’s external loader guidelines.

Kind regards, 

DHIF Khaled 

"Please mark my answer as best by clicking on the “Accept as solution"" button if it fully answered your question. This will help other users find this solution faster.​"
Associate
May 18, 2026

Hello @Khaled_DHIF ,

Thanks for your reply. I check many examples in order to complete external loader. Here are two exapmle links for the RAM address issue you mentioned earlier.

https://github.com/STMicroelectronics/stm32-memory-loaders/blob/main/STM32G4x_boards/MT25QL512ABB_STM32G474E-EVAL/Project/EWARM/Target.icf 

https://community.st.com/t5/stm32-mcus/how-to-add-your-spi-flash-into-the-stm32cubeprogrammer-s/ta-p/49392 

As you can see at links, RAM starts 0x20000004 address for STM32G series.

Associate
May 18, 2026

I built the code using the IAR Embedded Workbench for generating .stldr file. Added the following lines to Init() function at top:

char* startadd = __section_begin(".bss");
uint32_t size = __section_size(".bss");
memset(startadd,0,size);

Now I can read starter address but, I cannot write any data on the address. When start the write section, I have not a error. 

atilkicioglu_0-1779113604136.png
As you can see, External loader can go Init() function but I cannot write at starter address.