How to write custom (.ld) file?(External Flash Loader)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-07-08 11:07 PM - edited ‎2024-07-08 11:09 PM
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.
Solved! Go to Solution.
- Labels:
-
STM32CubeMX
-
TouchGFX
Accepted Solutions
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-07-09 7:59 AM
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.
ST Software Developer | TouchGFX
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-07-09 3:46 AM
Hello @erkanc ,
Please refer to these FAQs and follow the needed changes in the linker file:
- How to add your SPI flash into the STM32CubeProgrammer’s external loader Part 1
- How to add your spi flash into the STM32CubeProgrammer's External Loader Part 2
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-07-09 7:59 AM
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.
ST Software Developer | TouchGFX
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-07-10 6:40 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-07-10 7:40 AM
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
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-07-12 2:02 AM
I'm very glad to hear you managed to make one :grinning_face:
Well done!
ST Software Developer | TouchGFX
