on 2022-06-30 12:43 AM
int Write(uint32_t Address, uint32_t Size, uint8_t *buffer); int Read(uint32_t Address, uint32_t Size, uint8_t *Buffer); int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress); int MassErase(void);You can add these following functions too, but they are optional:
uint32_t CheckSum(uint32_t StartAddress, uint32_t Size, uint32_t InitVal); int Verify (uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size);The return values of the functions need to be “0” to represent an operation failed or “1” when it succeeded, and you can create these defines to help you to manage that:
#define LOADER_OK 0x1 #define LOADER_FAIL 0x0We need to include the HAL drivers and some initialization functions to manage the peripherals, so add the following lines to the code:
#include "main.h" #include "gpio.h" extern void SystemClock_Config(void); extern void MX_SPI2_Init(void); extern void MX_GPIO_Init(void);The remaining function that we need to create in this file is the Init function that will be called by the Entry Point mentioned in the linker script step by step:
int Init(void) { SystemInit(); SCB->VTOR = 0x20000000 | 0x200; HAL_DeInit(); HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_SPI2_Init(); __set_PRIMASK(0); //enable interrupts W25qxx_Init(); //External memory function initialization __set_PRIMASK(1); //disable interrupts return LOADER_OK; }The other two files that we need to create are “Dev_Info.c” and “Dev_Info.h”. These files will manage the header memory information used by STM32CubeProgrammer to read, program and erase the device – there are examples available in the STM32CubeProgrammer installation folder and also referred in the UM2237. So, let’s start creating these files from scratch. Create “Dev_Inf.h” in Core - > Inc folder, and “Dev_Inf.c” in Core -> Src.
#define MCU_FLASH 1 #define NAND_FLASH 2 #define NOR_FLASH 3 #define SRAM 4 #define PSRAM 5 #define PC_CARD 6 #define SPI_FLASH 7 #define I2C_FLASH 8 #define SDRAM 9 #define I2C_EEPROM 10 #define SECTOR_NUM 10 // Max Number of Sector types struct DeviceSectors { unsigned long SectorNum; // Number of Sectors unsigned long SectorSize; // Sector Size in Bytes }; struct StorageInfo { char DeviceName[100]; // Device Name and Description unsigned short DeviceType; // Device Type: ONCHIP, EXT8BIT, EXT16BIT, ... unsigned long DeviceStartAddress; // Default Device Start Address unsigned long DeviceSize; // Total Size of Device unsigned long PageSize; // Programming Page Size unsigned char EraseValue; // Content of Erased Memory struct DeviceSectors sectors[SECTOR_NUM]; };In “Dev_Inf.c” we only need to declare and initialize that struct and store it on the .Dev_info section of the RAM (for that, an __attribute__(section(“.Dev_Info”)) is placed in the struct declaration). The variable data of the struct should be changed to be compatible with the memory.
/* * Dev_Inf.c * */ #include "Dev_Inf.h" /* This structure contains 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 "W25Q64_STM32G070R", // Device Name + version number SPI_FLASH, // Device Type 0x00000000, // Device Start Address MEMORY_FLASH_SIZE, // Device Size in Bytes MEMORY_PAGE_SIZE, // Programming Page Size 0xFF, // Initial Content of Erased Memory // Specify Size and Address of Sectors (view example below) { { 0x80, // Sector Numbers, 0x1000 }, //Sector Size { 0x00000000, 0x00000000 } } };The struct data is shown by Cube Programmer when the External Loader is present on that.
cp "${BuildArtifactFileBaseName}.elf" "../STM32G070_W25Q64.stldr"Build the application and check in the project files if the STM32G070_W25Q64.stldr file was created (you can change the name if you want in the command text).
“C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader”(this path can change depending of your installation path, another way to reach on that is clicking with right mouse button on the STM32CubeProgrammer desktop shortcut and going to “Open file location” and search for the ExternalLoader folder on the opened location).
/* 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" Ram type memory */ _Min_Heap_Size = 0x200 ; /* required amount of heap */ _Min_Stack_Size = 0x400 ; /* required amount of stack */ /* Memories definition */ MEMORY { RAM_D1 (xrw) : ORIGIN = 0x20000004, LENGTH = 36K-4 } /* Sections */ SECTIONS { .isr_vector : { . = . + 0x1FC; /* ISR vector offset */ . = 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 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_D1 :Loader /* Uninitialized data section */ . = 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 :Loader /* The program code and other data into 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) KEEP (*(.init)) KEEP (*(.fini)) KEEP (*Loader_Src.o ( .text* )) . = ALIGN(4); _etext = .; /* define a global symbols at end of code */ } >RAM_D1 : Loader .Dev_Info : { __Dev_info_START = .; *(.Dev_info*) KEEP(*(.Dev_info)) __Dev_info_END = .; } >RAM_D1 :SgInfo /* Constant data into "FLASH" Rom type memory */ .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" Ram type memory left */ ._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM_D1 :Loader /* Remove information from the compiler libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } .ARM.attributes 0 : { *(.ARM.attributes) } }
Hi @Laura C. , thank you for such a perfect article!
Yet the *.stldr file I obtained worked perfectly with CubeProgrammer v2.4.0, but stopped working with newer versions.
Is there any reason for that?
The issue is posted here, could you please check it?
Hello @AZyma.1 ,
The article was made using the STM32CubeProg 2.8 or 2.9 (newer than 2.4), so we are reviewing your case internally.
Great Knowledge article and excellent examples.
I am using a STM32F030C8 with an external flash W25Q128 connected via SPI port, this processor does not have a VTOR so can't run code from RAM.
Is it possible to create an external loader for my CPU with this code running from FLASH ?
So if im not using interrupts I suspect I don't need VTOR, so Let me re-phrase that question.
To use the STM32cubeprogrammer for extern SPI flash loading, does the STMcubeprogrammer ONLY allow RAM based code to run on CPU. Or can the STMcubeprog load in a cpu FLASH based program to do the SPI writing and then replace the cpu flash code with the real cpu program later in the cycle ?
Here you specify the loader may provide the function Verify:
int Verify (uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size);
But in the example provided by ST on ttps://github.com/STMicroelectronics/stm32-external-loader
The function is:
uint64_t Verify (uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement)
There is one more parameter: missalignement,
and the return type is not the same: int vs uint64_t
1) Which is the right function?
2) Can you give me some information on the value and the usage of the parameter missalignement?
3 Where can I get this project source? The linker scrip was very beneficial, but I would like to see a working Loader_Src.c.
Thank you.
Hello,
I get the code downloaded using the steps, but after power up microcontroller nucleo board does nothing, any way to test it via stm32cubeIDE?? I try to compile and download and get error every time.
Thanks.
It should be noted that the value of SECTOR_NUM must be 10: otherwise the external loader is rejected by the STM32CubeProgrammer >= V1.12
Adding this precision in the code would avoid many errors...
Hi,
Assuming we have an external loader and we have programmed the external flash properly.
Are we able to debug the code residing in external flash by using the loader that we have used to program the external flash?
In my case I have flashed an external QSPI memory by using an external loader.(The main application resides in external QSPI Memory)
Another application runs on the internal flash inits clocks, QSPI and some GPIOs and then it brings the controller into memory mapped mode and jumps to main application which resides on external QSPI flash.
After the jump the debugger looses its connection with the new location.
I can verify that that the code in the external flash is running by blinking some LEDs, toggling them etc.
But when I start to debug in only debugs the code in the internal flash. As soon as it jumps to external code I can not debug.
Can you suggest a way of debugging the code in external flash.
PS:I have an external RAM on the custom board as well. Is it feasible to load the code in external flash into external RAM for debugging purposes or should the external loader written for loading the application to the flash memory may also be used for debugging.