cancel
Showing results for 
Search instead for 
Did you mean: 

How to write custom (.ld) file?(External Flash Loader)

erkanc
Associate III

Hi everyone. I hope you are well.

I am researching how to write LD file. I listened external flash pins. I used an "External Flash Loader file" to program the external Flash IC and MCU. I used STLDR file that work. 

I will write stages step by step;

 

0x06 : Write Enable
0x05 : Read status, -> Response: 0x02
0xD8 : Block Erase
0x05 : Read status, -> Response: 0x03
...//Repeat 0x05 command
0x05 : Read status, -> Response: 0x00 // Block erased.
...
0x06 : Write Enable
0x05 : Read status, -> Response: 0x02
0x02 : Page Program
0x05 : Read status, -> Response: 0x03
...//Repeat 0x05 command
0x05 : Read status, -> Response: 0x00 // Page programmed.
...

 

I created a my custom STLDR file. Programmer(Stm32Cube) is "known" my STLDR file. But My MCU and Flash IC can not be programmed. I take error. And I listened again. My STLDR file use different procedure to program. Is not like above.

 

0x66 : Reset 0 
0x99 : Reset 1
0x06 : Write Enable
0x20 : Sector Erase
0x05 : Read status bla...bla...
...
//Finally Error

 

I researched my LD file. I understood "Init" function that is first line. But I can not find how to put other functions. I thought that I can correct the functions according to working STLDR file. 

My LD File:

 

/*
*****************************************************************************
**  File        : linker.ld
**
**                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
**
*****************************************************************************
*/

/* 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 */
/* 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_D1 (xrw)      : ORIGIN = 0x20000004, LENGTH = 144K-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_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, 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))
	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 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 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) }
}

 

 

I use MX25L6433F as external flash IC. And I find the driver following link;

https://controllerstech.com/w25q-flash-series-part-9-spi-flash-loader/

 

I wonder where is name of other functions in LD file. 

For example; the "Init" function is in <Loader_Src.c>.

 

/* Entry Point */
ENTRY(Init)

 

Loader_Src.c

 

#include "W25Qxx.h"
#include "main.h"
#include "gpio.h"
#include "spi.h"

#define LOADER_OK   0x1
#define LOADER_FAIL 0x0
extern void SystemClock_Config(void);

/**
 * @brief  System initialization.
 *   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_SPI2_FORCE_RESET();  //completely reset peripheral
    __HAL_RCC_SPI2_RELEASE_RESET();

    MX_SPI2_Init();

    flash_Reset();

    __set_PRIMASK(1); //disable interrupts
    return LOADER_OK;
}

/**
 * @brief   Program memory.
 *    Address: page address
 *    Size   : size of data
 *    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

    flash_WriteMemory(buffer, Address, Size);

    __set_PRIMASK(1); //disable interrupts
    return LOADER_OK;
}

int Read (uint32_t Address, uint32_t Size, uint8_t* buffer){
    __set_PRIMASK(0); //enable interrupts

	flash_ReadMemory(Address, Size, buffer);

    __set_PRIMASK(1); //disable interrupts
	return 1;
}

/**
 * @brief   Sector erase.
 *    EraseStartAddress :  erase start address
 *    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

    flash_SectorErase(EraseStartAddress, EraseEndAddress);

    __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

    flash_ChipErase();

    __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;

	uint8_t Buffer[2];
	uint32_t posBuf;

	checksum = CheckSum((uint32_t)MemoryAddr + (missalignement & 0xf), Size - ((missalignement >> 16) & 0xF), InitVal);

	while (Size>VerifiedData)
	{
		flash_ReadMemory(MemoryAddr+VerifiedData, 2, Buffer);

		posBuf=0;
		while ((Size>VerifiedData) && (posBuf<1024)) {
			if (Buffer[posBuf] != *((uint8_t*)RAMBufferAddr+VerifiedData))
			{
				__set_PRIMASK(1); //disable interrupts
				return ((checksum<<32) + MemoryAddr+VerifiedData);
			}
			posBuf++;
			VerifiedData++;
		}
	}
	__set_PRIMASK(1); //disable interrupts
	return (checksum<<32);
}

 

And other read, write, etc. functions are in "Loader_Src.c". But name of other functions are not in .LD file. 

Could you explain this routine?

Best regards.

 

1 ACCEPTED SOLUTION

Accepted Solutions

Hello @erkanc ,

I can see @KDJEM.1 has already provided excellent resources. 
I just wanted to point out that in the STM32 ST-LINK utility software description document, in section 3.9, there are some details about developing a customized external loader that might help you.  

Mohammad MORADI
ST Software Developer | TouchGFX

View solution in original post

5 REPLIES 5
KDJEM.1
ST Employee

Hello @erkanc ,

 

Please refer to these FAQs and follow the needed changes in the linker file:

 

I hope this help you.

Kaouthar

 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hello @erkanc ,

I can see @KDJEM.1 has already provided excellent resources. 
I just wanted to point out that in the STM32 ST-LINK utility software description document, in section 3.9, there are some details about developing a customized external loader that might help you.  

Mohammad MORADI
ST Software Developer | TouchGFX
erkanc
Associate III

Thank you @Mohammad MORADI ESFAHANIASL for document.

Thank you @KDJEM.1

I understood how to generate custom external loader. I use following example;

https://www.youtube.com/watch?v=KlG2doCkREM

Thank you Mauro De Vecchi - Bluewat.

I strive 3 weeks. Finally, I succeeded. 


I am going to share a document for this subject in Turkish language after apprehend well. 

You can follow on my website and on this community.

www.elektrocavus.com

KEEP() is the method to ensure entry points to the "DLL" are retained, and not dead code eliminated by the Linker because nothing internally calls them.

https://github.com/cturvey/stm32extldr/blob/main/ExternalLoader_G4.ld#L42

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

I'm very glad to hear you managed to make one :grinning_face:

Well done!

Mohammad MORADI
ST Software Developer | TouchGFX