Skip to main content
Visitor II
December 28, 2020
Question

Generating external Loader (.stldr) for specific hardware

  • December 28, 2020
  • 14 replies
  • 21104 views

In the last weeks, I was working on running W25Q256 nor flash chip from vendor Winbond with STM32F746 MCU on a custom board. My guideline in this road is ST's how to run QSPI flash video tutorial (5-part video) and of course some genius guys in the community who's helped me a lot to rewrite the functions of QSPI flash (Init, Erase, Write, Memory-mapped). So far functions tested in the Main.c and I can read the pre-written data on the memory. The next step is to generate (.stldr) external loader file and modify Flash.ld file.

according to the video tutorial, I added Loader_Src.c, dev_inf.c, and dev_inf.h files to the workspace but there is a little confusion about flash.ld file that contains memory definitions and all other data and text stuff to assign to specific memory (>FLASH, >RAM, >QUADSPI).

for the point of concern, I use STM32F746 with IS42S32400F SD-RAM connected with 2-banks to MCU and of-course W25Q256 NOR-FLASH.

  1. how to modify flash.ld file to my specific condition and generate .stldr file using loader files added?
  2. I generated an external loader file before, but when I added the loader file to the CUBEPROGRAMMER directory, it didn't recognize by the programmer and got an error with blank fields in EL section of the programmer!

here I uploaded relevant files for check

STM32F746BGTX_FLASH.ld

/**
 ******************************************************************************
 * @file LinkerScript.ld
 * @author Auto-generated by STM32CubeIDE
 * @brief Linker script for STM32F746BGTx Device from STM32F7 series
 * 1024Kbytes FLASH
 * 320Kbytes RAM
 *
 * 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) + LENGTH(RAM);	/* 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 (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
 FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
}
 
/* Sections */
SECTIONS
{
 /* 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 : { 
 . = 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
 
 /* 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) } 
}

 Dev_inf.c

/*
 * Dev_Inf.c
 *
 */
#include "Dev_Inf.h"
#include "quadspi.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 const StorageInfo = {
#endif
 "QSPI_flashloader_CSP", // Device Name + version number
 NOR_FLASH, // Device Type
 0x90000000, // 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)
 { {
 (MEMORY_FLASH_SIZE / MEMORY_SECTOR_SIZE), // Sector Numbers,
 (uint32_t) MEMORY_SECTOR_SIZE
 }, //Sector Size
 
 { 0x00000000, 0x00000000 }
 }
};

    This topic has been closed for replies.

    14 replies

    Visitor II
    December 28, 2020

    Loader_Src.c

    #include "quadspi.h"
    #include "main.h"
    #include "gpio.h"
     
    #define LOADER_OK 0x1
    #define LOADER_FAIL 0x0
    extern void SystemClock_Config(void);
     
     
    int Init(void);
    int Write(uint32_t Address, uint32_t Size, uint8_t* buffer);
    int SectorErase(uint32_t EraseStartAddress, uint32_t EraseEndAddress);
    int MassErase(void);
    uint32_t CheckSum(uint32_t StartAddress, uint32_t Size, uint32_t InitVal);
    uint64_t Verify(uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement);
     
     
    /**
     * @brief System initialization.
     * @param None
     * @retval LOADER_OK = 1 : Operation succeeded
     * @retval LOADER_FAIL = 0 : Operation failed
     */
    int Init(void) {
     
     *(uint32_t*)0xE000EDF0 = 0xA05F0000; //enable interrupts in debug
     
     
     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;
     
     __set_PRIMASK(0); //enable interrupts
     
     HAL_Init();
     
     SystemClock_Config();
     
     MX_GPIO_Init();
     
     __HAL_RCC_QSPI_FORCE_RESET(); //completely reset peripheral
     __HAL_RCC_QSPI_RELEASE_RESET();
     
     if (CSP_QUADSPI_Init() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     __set_PRIMASK(1); //disable interrupts
     return LOADER_OK;
    }
     
    /**
     * @brief Program memory.
     * @param Address: page address
     * @param Size : size of data
     * @param buffer : pointer to data buffer
     * @retval LOADER_OK = 1 : Operation succeeded
     * @retval LOADER_FAIL = 0 : Operation failed
     */
    int Write(uint32_t Address, uint32_t Size, uint8_t* buffer) {
     
     __set_PRIMASK(0); //enable interrupts
     
     if (HAL_QSPI_Abort(&hqspi) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_WriteMemory((uint8_t*) buffer, (Address & (0x0fffffff)), Size) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     __set_PRIMASK(1); //disable interrupts
     return LOADER_OK;
    }
     
    /**
     * @brief Sector erase.
     * @param EraseStartAddress : erase start address
     * @param EraseEndAddress : erase end address
     * @retval LOADER_OK = 1 : Operation succeeded
     * @retval LOADER_FAIL = 0 : Operation failed
     */
    int SectorErase(uint32_t EraseStartAddress, uint32_t EraseEndAddress) {
     
     __set_PRIMASK(0); //enable interrupts
     
     if (HAL_QSPI_Abort(&hqspi) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_EraseSector(EraseStartAddress, EraseEndAddress) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     __set_PRIMASK(1); //disable interrupts
     return LOADER_OK;
    }
     
    /**
     * Description :
     * Mass erase of external flash area
     * Optional command - delete in case usage of mass erase is not planed
     * Inputs :
     * none
     * outputs :
     * none
     * Note: Optional for all types of device
     */
    int MassErase(void) {
     
     __set_PRIMASK(0); //enable interrupts
     
     if (HAL_QSPI_Abort(&hqspi) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_Erase_Chip() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     __set_PRIMASK(1); //disable interrupts
     return LOADER_OK;
    }
     
    /**
     * Description :
     * Calculates checksum value of the memory zone
     * Inputs :
     * StartAddress : Flash start address
     * Size : Size (in WORD)
     * InitVal : Initial CRC value
     * outputs :
     * R0 : Checksum value
     * Note: Optional for all types of device
     */
    uint32_t CheckSum(uint32_t StartAddress, uint32_t Size, uint32_t InitVal) {
     uint8_t missalignementAddress = StartAddress % 4;
     uint8_t missalignementSize = Size;
     int cnt;
     uint32_t Val;
     
     StartAddress -= StartAddress % 4;
     Size += (Size % 4 == 0) ? 0 : 4 - (Size % 4);
     
     for (cnt = 0; cnt < Size; cnt += 4) {
     Val = *(uint32_t*) StartAddress;
     if (missalignementAddress) {
     switch (missalignementAddress) {
     case 1:
     InitVal += (uint8_t) (Val >> 8 & 0xff);
     InitVal += (uint8_t) (Val >> 16 & 0xff);
     InitVal += (uint8_t) (Val >> 24 & 0xff);
     missalignementAddress -= 1;
     break;
     case 2:
     InitVal += (uint8_t) (Val >> 16 & 0xff);
     InitVal += (uint8_t) (Val >> 24 & 0xff);
     missalignementAddress -= 2;
     break;
     case 3:
     InitVal += (uint8_t) (Val >> 24 & 0xff);
     missalignementAddress -= 3;
     break;
     }
     } else if ((Size - missalignementSize) % 4 && (Size - cnt) <= 4) {
     switch (Size - missalignementSize) {
     case 1:
     InitVal += (uint8_t) Val;
     InitVal += (uint8_t) (Val >> 8 & 0xff);
     InitVal += (uint8_t) (Val >> 16 & 0xff);
     missalignementSize -= 1;
     break;
     case 2:
     InitVal += (uint8_t) Val;
     InitVal += (uint8_t) (Val >> 8 & 0xff);
     missalignementSize -= 2;
     break;
     case 3:
     InitVal += (uint8_t) Val;
     missalignementSize -= 3;
     break;
     }
     } else {
     InitVal += (uint8_t) Val;
     InitVal += (uint8_t) (Val >> 8 & 0xff);
     InitVal += (uint8_t) (Val >> 16 & 0xff);
     InitVal += (uint8_t) (Val >> 24 & 0xff);
     }
     StartAddress += 4;
     }
     
     return (InitVal);
    }
     
    /**
     * Description :
     * Verify flash memory with RAM buffer and calculates checksum value of
     * the programmed memory
     * Inputs :
     * FlashAddr : Flash address
     * RAMBufferAddr : RAM buffer address
     * Size : Size (in WORD)
     * InitVal : Initial CRC value
     * outputs :
     * R0 : Operation failed (address of failure)
     * R1 : Checksum value
     * Note: Optional for all types of device
     */
    uint64_t Verify(uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement) {
     
     __set_PRIMASK(0); //enable interrupts
     uint32_t VerifiedData = 0, InitVal = 0;
     uint64_t checksum;
     Size *= 4;
     
     if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     checksum = CheckSum((uint32_t) MemoryAddr + (missalignement & 0xf),
     Size - ((missalignement >> 16) & 0xF), InitVal);
     while (Size > VerifiedData) {
     if (*(uint8_t*) MemoryAddr++
     != *((uint8_t*) RAMBufferAddr + VerifiedData)) {
     __set_PRIMASK(1); //disable interrupts
     return ((checksum << 32) + (MemoryAddr + VerifiedData));
     }
     VerifiedData++;
     }
     
     __set_PRIMASK(1); //disable interrupts
     return (checksum << 32);
    }

    Visitor II
    December 28, 2020

    I start a new project from scratch by just configuring QSPI and nothing else, added loader related files, and built successfully. starting CUBEPROGRAMMER and external loader appears with correct values in the list (EL section) just OK along with the tutorial video. but when trying to load sample code has done with success but no changes in address 0x90000000! what is the problem?

    external loader generation could have been easier, can't understand why ST doesn't take care of it!:unamused_face:

    Visitor II
    January 1, 2021

    @Community member​ would you please give me a hand to solve it, I saw your earlier posts about generating stldr file but currently, I am in big confusion!

    1.The ST tutorial said that we have to change the default linker script (generated by CubeIDE) with the one that the tutorial suggests, But in the suggested linker file, All the sections are assigned to RAM :fearful_face:.

    2.I have the driver implementations, completely fine and running in main.c but when it comes to stldr generation, All suddenly not working at all :pouting_face:.

    So please help me out getting through this issue, would you?

    Graduate II
    January 1, 2021

    The loader is loaded into SRAM by the programmer, and executes from there.

    The linker script needs to output two sections, one with the StorageInfo structure where the programmer pulls the device geometry, name and statistics from, and a program section.

    I'm in the process of porting an example to GNU/GCC only, will attach the script here shortly.

    Visitor II
    January 2, 2021

    @Community member​ I wish you the bests for 2021 and thank you for your reply in these early days of the year

    I include your linker to my project and change the RAM address as you mentioned. firstly I got some errors about location counters (_end, e_stack, ...) in sysmem and startup files. I will try to fix them and get back soon.

    Graduate II
    January 2, 2021

    Thanks.

    The loaders shouldn't need startup.s or main(), the Init() function should clear any critical globals or BSS space.

    Visitor II
    January 3, 2021

    @Community member​ with your linker provided, I'm able to read the external memory just for once (I mean, when I loaded generated stldr to CubeProgmrammer and connect, when hit the Read button, the memory reads properly at 0x90000000 but when hit it again...Read Error). I enable Verbosity level 3 and see that the error is because of the Init function timeout (screenshot attached). It seems the overall functions are OK but need minor adjustments. Would you please provide me a Loader_Src file OR help me fix mine:beaming_face_with_smiling_eyes:

    I'm looking for your attack after you finished your porting project.

    Loader_Src.c

    #include "quadspi.h"
    #include "main.h"
    #include "gpio.h"
     
    #define LOADER_OK 0x1
    #define LOADER_FAIL 0x0
    extern void SystemClock_Config(void);
     
    /**
     * @brief System initialization.
     * @param None
     * @retval LOADER_OK = 1 : Operation succeeded
     * @retval LOADER_FAIL = 0 : Operation failed
     */
    int
    Init(void) {
     
     *(uint32_t*)0xE000EDF0 = 0xA05F0000; //enable interrupts in debug
     
     
     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;
     
     __set_PRIMASK(0); //enable interrupts
     
     HAL_Init();
     
     SystemClock_Config();
     
     MX_GPIO_Init();
     
     __HAL_RCC_QSPI_FORCE_RESET(); //completely reset peripheral
     __HAL_RCC_QSPI_RELEASE_RESET();
     
     if (CSP_QUADSPI_Init() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     __set_PRIMASK(1); //disable interrupts
     return LOADER_OK;
    }
     
    /**
     * @brief Program memory.
     * @param Address: page address
     * @param Size : size of data
     * @param buffer : pointer to data buffer
     * @retval LOADER_OK = 1 : Operation succeeded
     * @retval LOADER_FAIL = 0 : Operation failed
     */
    int
    Write(uint32_t Address, uint32_t Size, uint8_t* buffer) {
     
     __set_PRIMASK(0); //enable interrupts
     
     if (HAL_QSPI_Abort(&hqspi) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_WriteMemory((uint8_t*) buffer, (Address & (0x0fffffff)), Size) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     __set_PRIMASK(1); //disable interrupts
     return LOADER_OK;
    }
     
    /**
     * @brief Sector erase.
     * @param EraseStartAddress : erase start address
     * @param EraseEndAddress : erase end address
     * @retval LOADER_OK = 1 : Operation succeeded
     * @retval LOADER_FAIL = 0 : Operation failed
     */
    int
    SectorErase(uint32_t EraseStartAddress, uint32_t EraseEndAddress) {
     
     __set_PRIMASK(0); //enable interrupts
     
     if (HAL_QSPI_Abort(&hqspi) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_EraseSector(EraseStartAddress, EraseEndAddress) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     __set_PRIMASK(1); //disable interrupts
     return LOADER_OK;
    }
     
    /**
     * Description :
     * Mass erase of external flash area
     * Optional command - delete in case usage of mass erase is not planed
     * Inputs :
     * none
     * outputs :
     * none
     * Note: Optional for all types of device
     */
    int
    MassErase(void) {
     
     __set_PRIMASK(0); //enable interrupts
     
     if (HAL_QSPI_Abort(&hqspi) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_Erase_Chip() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     __set_PRIMASK(1); //disable interrupts
     return LOADER_OK;
    }
     
    /**
     * Description :
     * Calculates checksum value of the memory zone
     * Inputs :
     * StartAddress : Flash start address
     * Size : Size (in WORD)
     * InitVal : Initial CRC value
     * outputs :
     * R0 : Checksum value
     * Note: Optional for all types of device
     */
    uint32_t
    CheckSum(uint32_t StartAddress, uint32_t Size, uint32_t InitVal) {
     uint8_t missalignementAddress = StartAddress % 4;
     uint8_t missalignementSize = Size;
     int cnt;
     uint32_t Val;
     
     StartAddress -= StartAddress % 4;
     Size += (Size % 4 == 0) ? 0 : 4 - (Size % 4);
     
     for (cnt = 0; cnt < Size; cnt += 4) {
     Val = *(uint32_t*) StartAddress;
     if (missalignementAddress) {
     switch (missalignementAddress) {
     case 1:
     InitVal += (uint8_t) (Val >> 8 & 0xff);
     InitVal += (uint8_t) (Val >> 16 & 0xff);
     InitVal += (uint8_t) (Val >> 24 & 0xff);
     missalignementAddress -= 1;
     break;
     case 2:
     InitVal += (uint8_t) (Val >> 16 & 0xff);
     InitVal += (uint8_t) (Val >> 24 & 0xff);
     missalignementAddress -= 2;
     break;
     case 3:
     InitVal += (uint8_t) (Val >> 24 & 0xff);
     missalignementAddress -= 3;
     break;
     }
     } else if ((Size - missalignementSize) % 4 && (Size - cnt) <= 4) {
     switch (Size - missalignementSize) {
     case 1:
     InitVal += (uint8_t) Val;
     InitVal += (uint8_t) (Val >> 8 & 0xff);
     InitVal += (uint8_t) (Val >> 16 & 0xff);
     missalignementSize -= 1;
     break;
     case 2:
     InitVal += (uint8_t) Val;
     InitVal += (uint8_t) (Val >> 8 & 0xff);
     missalignementSize -= 2;
     break;
     case 3:
     InitVal += (uint8_t) Val;
     missalignementSize -= 3;
     break;
     }
     } else {
     InitVal += (uint8_t) Val;
     InitVal += (uint8_t) (Val >> 8 & 0xff);
     InitVal += (uint8_t) (Val >> 16 & 0xff);
     InitVal += (uint8_t) (Val >> 24 & 0xff);
     }
     StartAddress += 4;
     }
     
     return (InitVal);
    }
     
    /**
     * Description :
     * Verify flash memory with RAM buffer and calculates checksum value of
     * the programmed memory
     * Inputs :
     * FlashAddr : Flash address
     * RAMBufferAddr : RAM buffer address
     * Size : Size (in WORD)
     * InitVal : Initial CRC value
     * outputs :
     * R0 : Operation failed (address of failure)
     * R1 : Checksum value
     * Note: Optional for all types of device
     */
    uint64_t
    Verify(uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement) {
     
     __set_PRIMASK(0); //enable interrupts
     uint32_t VerifiedData = 0, InitVal = 0;
     uint64_t checksum;
     Size *= 4;
     
     if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     checksum = CheckSum((uint32_t) MemoryAddr + (missalignement & 0xf),
     Size - ((missalignement >> 16) & 0xF), InitVal);
     while (Size > VerifiedData) {
     if (*(uint8_t*) MemoryAddr++
     != *((uint8_t*) RAMBufferAddr + VerifiedData)) {
     __set_PRIMASK(1); //disable interrupts
     return ((checksum << 32) + (MemoryAddr + VerifiedData));
     }
     VerifiedData++;
     }
     
     __set_PRIMASK(1); //disable interrupts
     return (checksum << 32);
    }

    0693W000006HL2LQAW.png

    Visitor II
    January 5, 2021

    I made some changes in the Init function and finally, it is OK, I checked the loader in st-link utility and there is no error when reading and initializing. But now when sector-erase, it actually erases the coresponding sector but with fail message! I think the next challenge would be making erase functions work. @Community member​ 

    this is the working Init function (at least for me):

    int Init(void) {
     
    	*(uint32_t*)0xE000EDF0=0xA05F0000;
     
    	SystemInit();
     SCB->VTOR = 0x20000000 | 0x200;
     
     __set_PRIMASK(0); //enable interrupts
     
    	HAL_Init();
     SystemClock_Config();
     
     
     __HAL_RCC_GPIOF_CLK_ENABLE();
     __HAL_RCC_GPIOH_CLK_ENABLE();
     __HAL_RCC_GPIOB_CLK_ENABLE();
     __HAL_RCC_GPIOA_CLK_ENABLE();
     
     MX_GPIO_Init();
     if(A == 0)
     {
     	MX_QUADSPI_Init();
     }
     QSPI_ResetChip();
     /* CSP_QUADSPI_Init();
     CSP_QSPI_EnableMemoryMappedMode();*/
     
     __HAL_RCC_QSPI_FORCE_RESET(); //completely reset peripheral
     __HAL_RCC_QSPI_RELEASE_RESET();
     
     if (CSP_QUADSPI_Init() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     __set_PRIMASK(1); //disable interrupts
     
     A++;
     
     return LOADER_OK;
    }

    and this is the erase which has an error but actually working!

    int SectorErase(uint32_t EraseStartAddress, uint32_t EraseEndAddress) {
     
     __set_PRIMASK(0); //enable interrupts
     
     if (HAL_QSPI_Abort(&hqspi) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     
     if (CSP_QSPI_EraseSector(EraseStartAddress, EraseEndAddress) != HAL_OK) {
     __set_PRIMASK(1); //disable interrupts
     return LOADER_FAIL;
     }
     
     __set_PRIMASK(1); //disable interrupts
     return LOADER_OK;
    }

    And if you curious about CSP_QSPI_EraseSector :

    uint8_t
    CSP_QSPI_EraseSector(uint32_t EraseStartAddress, uint32_t EraseEndAddress) {
     
     QSPI_CommandTypeDef sCommand;
     
     EraseStartAddress = EraseStartAddress
     - EraseStartAddress % MEMORY_SECTOR_SIZE;
     
     /* Erasing Sequence -------------------------------------------------- */
     sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
     sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
     sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
     sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
     sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
     sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
     sCommand.Instruction = SECTOR_ERASE_CMD;
     sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
     
     sCommand.DataMode = QSPI_DATA_NONE;
     sCommand.DummyCycles = 0;
     
     while (EraseEndAddress >= EraseStartAddress) {
     sCommand.Address = (EraseStartAddress & 0x0FFFFFFF);
     
     if (QSPI_WriteEnable() != HAL_OK) {
     return HAL_ERROR;
     }
     
     if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
     != HAL_OK) {
     return HAL_ERROR;
     }
     EraseStartAddress += MEMORY_SECTOR_SIZE;
     
     if (QSPI_AutoPollingMemReady() != HAL_OK) {
     return HAL_ERROR;
     }
     }
     
     return HAL_OK;
    }

    Visitor II
    January 8, 2021

    @Community member​ 

    Dear Clive, would you please make an external loader for me? cause it becomes really really annoying to me:confounded_face:.

    My flash chip: W25Q256FV

    Pinouts:

      PF6   ------> QUADSPI_BK1_IO3

      PF7   ------> QUADSPI_BK1_IO2

      PF8   ------> QUADSPI_BK1_IO0

      PF9   ------> QUADSPI_BK1_IO1

      PB2   ------> QUADSPI_CLK

      PB6   ------> QUADSPI_BK1_NCS

    it would really helps.

    Note: this is a commercial project and not a hobby, So I'm running out of time. please let me know if a refund needed;)

    Graduate II
    January 8, 2021

    I have a W25Q64/128 one built (3-Byte Addressing), I'll need to finish porting the 256 (4-Byte)

    The initial load on the H7 is still broken in Cube Programmer, so a disconnect/connect is required before it runs the STLDR/ELF Init function properly

    Visitor II
    July 23, 2021

    Thank you so much for the hard work @Community member​ . I am currently trying to make a flash loader for an H7 for a W25Q128, unfortunately yours doesn't work on my case probably because of the many variations of W25Q128 ( I own an W25Q128JVSIQ and I guess you own a FV due to the naming convention on the next file). Do you happen to have the .c and .h driver files for the stldr ? I guess I will have to do some very small modifications. Thank you in advance.

    Spyros

    Visitor II
    August 17, 2021

    The screen I made with Touchgfx works just fine. But Flash Memory is at 82% and will be full soon. So I need to implement QSPI. I have W25Q128JVSQ Flash and 176 pin STM32H743IITx board. Could you please prepare an MX file (with .ioc extension) for me? I work in a company that carries out an R&D project and I need to finish this job as soon as possible. Otherwise it won't be good for me.

    Visitor II
    August 17, 2021

    0693W00000D2h1JQAR.jpg0693W00000D2h6hQAB.jpg0693W00000D2h6SQAR.jpg

    Visitor II
    August 17, 2021

    These (4 pictures) are the images of the product, can you please help me urgently?