cancel
Showing results for 
Search instead for 
Did you mean: 

OSPI external loader fails to read/write (linker issue?) on STM32H7A3 MCU

Emilmart
Senior

Hi everyone,

I’m working on a custom display board with a STM32H7A3 MCU and a MX66LM1G45G 128Mb OSPI flash. I have build a custom external loader following ST tutorial, with H7 params. I use JTAG to flash.

When it comes to use my .stldr custom loader in CubeIDE or CubeProgrammer (v2.7), it fails.

Here’s what’s CubeProgrammer says when I try to flash my .bin app (touchgfx project) using the external loader:

[…]

w ap 0 @0x90000000 0x000D083C bytes Data 0x00000000 // Error: failed to download Segment[1]

With CubeIDE using the external loader, it also fails at Segment[1]

(see full logs attached)

When I try to read a data at 0x90000000 with CubeProgrammer, it also fails (see file log_cubeprogrammer_ReadData.log attached)

I have successfully tested my OSPI functions in the main.c of the external loader (writing/reading/erasing) on my board (without the external loader linker modifications).

In my TouchGFX applicative project I implemented the same OSPI functions and was also able to read datas in memory mapped mode.

I’m not sure about the linker part in my external loader, and my guess is that the problem comes from there. I’m not sure at which adress I should store the external loader code, considering that my STM32H7A3 has some AXI_SRAM (0x24000000)/ AHB_SRAM (0x30000000) by default in STM32H7A3ZITX_FLASH.ld. The RAM is at 0x20000000 in this linker and is only 128KB (not enough for the external loader code), so in the end I followed the example for the H7 which says to store the loader code at 0x24000004 (same RAM origin and size than my TouchGFX project though).

With that linker config I’m able to go at the end of my system Initialisation (Init()) in Loader_Src.c in debug mode.

It may not be that but here are my linkers for the external loader and for the TouchGFX project:

external loader linker:

/* 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_D1) + LENGTH(RAM_D1);    /* end of RAM_D1 */
/* Generate a link error if heap and stack don't fit into RAM_D1 */
_Min_Heap_Size = 0x1000 ;      /* required amount of heap  */
_Min_Stack_Size = 0x1000 ; /* required amount of stack */
 
/* Specify the memory areas */
MEMORY
{
	RAM_D1 (xrw)      : ORIGIN = 0x24000004, LENGTH = 1024K
}								
 
/* Define output sections */
SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
  	. = . + 0x1FC;
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >RAM_D1 :Loader
 
 .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >RAM_D1
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >RAM_D1 :Loader
 
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >RAM_D1 :Loader
  
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >RAM_D1 :Loader
  
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >RAM_D1 :Loader
 
  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);
 
  /* Initialized data sections goes into RAM_D1, 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_D1 :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_D1 :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_D1 :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_D1 :Loader
  
  
  /* User_heap_stack section, used to check that there is enough RAM_D1 left */
  ._user_heap_stack :
  {
    . = ALIGN(4);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(4);
  } >RAM_D1 :Loader
 
  .ARM.attributes 0 : { *(.ARM.attributes) }
}

TouchGFX project linker:

ENTRY(Reset_Handler)
 
_estack = ORIGIN(RAM) + LENGTH(RAM);	/* end of "RAM" Ram type memory */
 
_Min_Heap_Size = 0x1000 ;	/* required amount of heap  */
_Min_Stack_Size = 0x1000 ;	/* required amount of stack */
 
MEMORY
{
  DTCMRAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 128K
  ITCMRAM    (xrw)    : ORIGIN = 0x00000000,   LENGTH = 64K
  RAM    (xrw)    : ORIGIN = 0x24000000,   LENGTH = 1024K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 2048K
  OCTOSPI (r)    : ORIGIN = 0x90000000, LENGTH = 128M
}
 
/* Sections */
SECTIONS
{
  /* The startup code into "FLASH" Rom type memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >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 */
  } >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   : {
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >FLASH
 
  .ARM : {
    . = ALIGN(4);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(4);
  } >FLASH
 
  .preinit_array     :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4);
  } >FLASH
 
  .init_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >FLASH
 
  .fini_array :
  {
    . = 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 */
 
    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
 
  } >RAM AT> FLASH
 
  . = 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 :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM
 
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }
 
  .ARM.attributes 0 : { *(.ARM.attributes) }
  ExtFlashSection :
  {
	*(ExtFlashSection ExtFlashSection.*)
	*(.gnu.linkonce.r.*)
    . = ALIGN(0x4);
  } >OCTOSPI
	
  FontFlashSection :
  {
	*(FontFlashSection FontFlashSection.*)
	*(.gnu.linkonce.r.*)
    . = ALIGN(0x4);
  } >OCTOSPI
 
  TextFlashSection :
  {
	*(TextFlashSection TextFlashSection.*)
	*(.gnu.linkonce.r.*)
    . = ALIGN(0x4);
   } >OCTOSPI
}

You will find my full external loader project here:

https://github.com/pa0694/External_loader

Thanks for your support !

1 ACCEPTED SOLUTION

Accepted Solutions

Hi in the linker instead of using the Init as entry point like in the example I kept the Reset_Handler as entry point, with modifying the startup file for redirecting to the Init instead of the main

It seems that some peripheral aren't properly resetting but couldn't figure which ones. I met then other problems but l suggest you to use UART for debug purpose

View solution in original post

15 REPLIES 15
Emilmart
Senior

Well it seems that the cube programmer logs that are uploaded in the main post are not correct because I found out that I have a different log if I erase the chip fully (old flash code with test code was still there) before using the external loader in cubeprogrammer.

It appears that my Init function fail.

You'll find the correct log here :

16:47:04 : STM32CubeProgrammer API v2.7.0
16:47:15 : ST-LINK SN  : 50FF6E066675545734101487
16:47:15 : ST-LINK FW  : V2J37S7
16:47:15 : Board       : --
16:47:15 : Voltage     : 3.33V
16:47:15 : JTAG freq   : 9000 KHz
16:47:15 : Connect mode: Normal
16:47:15 : Reset mode  : Software reset
16:47:15 : Device ID   : 0x480
16:47:15 : Revision ID : --
16:47:15 : UPLOADING OPTION BYTES DATA ...
16:47:15 :   Bank          : 0x00
16:47:15 :   Address       : 0x5200201c
16:47:15 :   Size          : 308 Bytes
16:47:15 : UPLOADING ...
16:47:15 :   Size          : 1024 Bytes
16:47:15 :   Address       : 0x8000000
16:47:15 : Read progress:
16:47:15 : Data read successfully
16:47:15 : Time elapsed during the read operation is: 00:00:00.005
16:48:51:856 : Selected loader: C:\Program Files (x86)\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin/ExternalLoader/OSPI_flashloader_CSP.stldr
16:48:51:899 : External loader C:\Program Files (x86)\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin/ExternalLoader/OSPI_flashloader_CSP.stldr is loaded
16:48:57:954 : External loader sector number is too big, displaying only the first 4000 sectors
16:49:09:631 : Memory Programming ...
16:49:09:631 : Opening and parsing file: app.hex
16:49:09:654 :   File          : app.hex
16:49:09:654 :   Size          : 1213292 Bytes
16:49:09:654 :   Address       : 0x08000000 
16:49:09:655 : Erasing Segment <0> Address <0x08000000> Size <359216>Bytes
16:49:09:655 : Erasing memory corresponding to segment 0:
16:49:09:662 : Memory erase...
16:49:09:666 : halt ap 0 
16:49:09:666 : w ap 0 reg 15 PC   (0x24000000)  
16:49:09:666 : w ap 0 reg 17 MSP  (0x24000500)  
16:49:09:667 : w ap 0 reg 16 xPSR (0x01000000)  
16:49:09:668 : w ap 0 @0x24001140 0x00000200 bytes Data 0x00000000
16:49:09:668 : w ap 0 @0x24000000 0x00000004 bytes Data 0x0000BE00
16:49:09:685 : w ap 0 @0x24000004 0x00000D10 bytes Data 0x8F4FF3BF
16:49:09:685 : Erasing internal memory sectors [0 43]
16:49:09:685 : Init flashloader...
16:49:09:685 : halt ap 0 
16:49:09:685 : w ap 0 reg 0 R0   0x00000001
16:49:09:686 : w ap 0 reg 1 R1   0x00000000
16:49:09:688 : w ap 0 reg 2 R2   0x00000000
16:49:09:690 : w ap 0 reg 3 R3   0x00000000
16:49:09:690 : w ap 0 reg 4 R4   0x00000000
16:49:09:691 : w ap 0 reg 5 R5   0x00000000
16:49:09:691 : w ap 0 reg 6 R6   0x00000000
16:49:09:691 : w ap 0 reg 7 R7   0x00000000
16:49:09:691 : w ap 0 reg 8 R8   0x00000000
16:49:09:691 : w ap 0 reg 9 R9   0x00000000
16:49:09:692 : w ap 0 reg 10 R10  0x00000000
16:49:09:692 : w ap 0 reg 11 R11  0x00000000
16:49:09:692 : w ap 0 reg 12 R12  0x00000000
16:49:09:699 : w ap 0 reg 13 SP   0x00000000
16:49:09:699 : w ap 0 reg 14 LR   0x24000001
16:49:09:702 : w ap 0 reg 15 PC   0x2400000B
16:49:09:705 : w ap 0 reg 16 xPSR 0x01000000
16:49:09:705 : w ap 0 reg 17 MSP  0x24001110
16:49:09:706 : w ap 0 reg 18 PSP  0x00000000
16:49:09:706 : run ap 0 
16:49:09:707 : halt ap 0 
16:49:09:708 : r ap 0 reg 0 R0   0x00000001
16:49:09:708 : Loader sector erase...
16:49:09:708 : w ap 0 reg 0 R0   0x08000000
16:49:09:708 : w ap 0 reg 1 R1   0x08056000
16:49:09:709 : w ap 0 reg 2 R2   0x00000002
16:49:09:709 : w ap 0 reg 3 R3   0x00000000
16:49:09:709 : w ap 0 reg 4 R4   0x00000000
16:49:09:709 : w ap 0 reg 5 R5   0x00000000
16:49:09:709 : w ap 0 reg 6 R6   0x00000000
16:49:09:709 : w ap 0 reg 7 R7   0x00000000
16:49:09:709 : w ap 0 reg 8 R8   0x00000000
16:49:09:709 : w ap 0 reg 9 R9   0x00000000
16:49:09:709 : w ap 0 reg 10 R10  0x00000000
16:49:09:710 : w ap 0 reg 11 R11  0x00000000
16:49:09:710 : w ap 0 reg 12 R12  0x00000000
16:49:09:710 : w ap 0 reg 13 SP   0x00000000
16:49:09:710 : w ap 0 reg 14 LR   0x24000001
16:49:09:711 : w ap 0 reg 15 PC   0x2400086B
16:49:09:711 : w ap 0 reg 16 xPSR 0x01000000
16:49:09:712 : w ap 0 reg 17 MSP  0x24001110
16:49:09:712 : w ap 0 reg 18 PSP  0x00000000
16:49:09:712 : run ap 0 
16:49:09:816 : halt ap 0 
16:49:09:817 : r ap 0 reg 0 R0   0x00000001
16:49:09:817 : erase: 0152ms
16:49:09:817 : Erasing Segment <1> Address <0x90000000> Size <854076>Bytes
16:49:09:817 : Erasing memory corresponding to segment 1:
16:49:09:817 : Memory erase...
16:49:09:817 : halt ap 0 
16:49:09:818 : w ap 0 reg 15 PC   (0x24000000)  
16:49:09:818 : w ap 0 reg 17 MSP  (0x24000500)  
16:49:09:818 : w ap 0 reg 16 xPSR (0x01000000)  
16:49:09:818 : w ap 0 @0x24021FC0 0x00000200 bytes Data 0x00000000
16:49:09:818 : w ap 0 @0x24000000 0x00000004 bytes Data 0x0000BE00
16:49:10:728 : w ap 0 @0x24000004 0x00021B90 bytes Data 0x00000000
16:49:10:729 : Erasing external memory sectors [0 208]
16:49:10:729 : Init flashloader...
16:49:10:736 : halt ap 0 
16:49:10:744 : w ap 0 reg 0 R0   0x00000001
16:49:10:744 : w ap 0 reg 1 R1   0x00000000
16:49:10:745 : w ap 0 reg 2 R2   0x00000000
16:49:10:745 : w ap 0 reg 3 R3   0x00000000
16:49:10:745 : w ap 0 reg 4 R4   0x00000000
16:49:10:746 : w ap 0 reg 5 R5   0x00000000
16:49:10:746 : w ap 0 reg 6 R6   0x00000000
16:49:10:746 : w ap 0 reg 7 R7   0x00000000
16:49:10:746 : w ap 0 reg 8 R8   0x00000000
16:49:10:747 : w ap 0 reg 9 R9   0x00000000
16:49:10:747 : w ap 0 reg 10 R10  0x00000000
16:49:10:748 : w ap 0 reg 11 R11  0x00000000
16:49:10:748 : w ap 0 reg 12 R12  0x00000000
16:49:10:748 : w ap 0 reg 13 SP   0x00000000
16:49:10:748 : w ap 0 reg 14 LR   0x24000001
16:49:10:748 : w ap 0 reg 15 PC   0x2400057D
16:49:10:748 : w ap 0 reg 16 xPSR 0x01000000
16:49:10:748 : w ap 0 reg 17 MSP  0x24021F90
16:49:10:748 : w ap 0 reg 18 PSP  0x00000000
16:49:10:749 : run ap 0 
16:49:15:004 : halt ap 0 
16:49:15:004 : Init function fail with timeout
16:49:15:005 : r ap 0 reg 16 xPSR 0x21000000
16:49:15:005 : Loader sector erase...
16:49:15:005 : Error: failed to erase memory
16:49:15:030 : Error: failed to erase memory
16:49:15:030 : RUNNING Program ... 
16:49:15:030 :   Address:      : 0x08000000
16:49:15:030 : w ap 0 reg 15 PC   0xFFFFFFFF
16:49:15:031 : w ap 0 reg 17 MSP  0xFFFFFFFF
16:49:15:031 : w ap 0 reg 16 xPSR 0x01000000
16:49:15:032 : Warning: The core is locked up
16:49:15:041 : Start operation achieved successfully

I also joined the external_loader.elf (rename "DOT" by "." otherwise I can't upload it) with the linker

It doesn't work concurrently with your TouchGFX application, the flashing tools load it into RAM at 0x24000004 for the express purpose of accessing the OSPI memory (ie set up pins, interface, use of appropriate commands). The loader code itself only needs to occupy a handful of KB (128KB being more than enough), HAL/Cube code is bloated, but the .ELF contains a lot of linker/debugger noise, and hopefully the code sections are actually relatively small.

You might however want to test it thoroughly as a stand-alone application, ie your OSPI BSP, where you exercise the erase (mass, sector) and your ability to write data to the NOR Flash, and recover that data. You could use test patterns as those are easier to generate than finding MBs of data to use.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Hi @Community member​ and thanks for your answer and clarification.

I have succesfully tested all my OSPI BSP function when the entry of the linker is Reset_Handler (main). I then set the Entry point to Init (but still with the original linker & ram + flash use), and it seems that I have found the culprit -but I have yet not been able to solve it. It appears that my HAL_Delay gets stucks in the while. (it stops the debug when it comes at this point "Break at address "0xd20645d8" with no debug information available, or outside of program code.").

I was using an HSE oscillator with timebase source as TIM6, so I set it to HSI and SysTick source, but the problem is still the same. I can avoid to use Hal_Delay in my BSP code but some of the HAL OSPI functions seems to use that tick so I'm a bit stuck.

It's strange that Hal_Delay is working when my entry point is set to Reset_Handler (main) but not when it's set to Init (Loader_Src.c), because in both I call the same HAL_Init / SystemClock_Config functions

GChau.2
Associate II

Hi @Emilmart​ ,

did you solved your issue?

I have the same issue with with STM32H7B3ZITXQ. I have followed the same tutorial that you mentionned. I have tried TIM6 and SysTick as clock source but same result.

Hi in the linker instead of using the Init as entry point like in the example I kept the Reset_Handler as entry point, with modifying the startup file for redirecting to the Init instead of the main

It seems that some peripheral aren't properly resetting but couldn't figure which ones. I met then other problems but l suggest you to use UART for debug purpose

OSPI and OCTOSPIM

ST's incessant switching from mapped to direct mode can be problematic.

Use of interrupts, generally, and for timing is also problematic. With thought can be avoided

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
DMari.3
Associate

Hi @Emilmart​,

I have the same issue with with STM32H7A3VI. Could you show me how you did it?

"Hi in the linker instead of using the Init as entry point like in the example I kept the Reset_Handler as entry point, with modifying the startup file for redirecting to the Init instead of the main"

in your linker.ld file, keep:

/* Entry Point */

ENTRY(Reset_Handler)

then in your startup file.s ( startup_stm32h7a3zitx.s for me), set

/* Call the application's entry point.*/
  bl  Init
  bx  lr

DMari.3
Associate

Did not work @Emilmart​ . When I use the STM32CubeProgrammer to write the custom loader from memory position 0x90000000, I see on the oscilloscope that the Init function is executed (MX_OCTOSPI1_Init, Reset and MemoryMappedMode), then the Write function is executed. But at no time the SectorErase or MassErase function is executed and then the following error happens.

0693W00000Bd3knQAB.pngCan anyone explain to me what is happening?