cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 .bin file is enormous

TGeof.2
Associate II

Hi !

I'm used to working with F4 and F7 devices, and I've always made '.bin' files to distribute the firmware upgrades, which can be flashed via a custom bootloader, directly on the board, from an SD card.

With my current project however, on a STM32H743BI, the generated '.bin' file is almost 700MiB in size!

My two questions are:

  1. How come this is so big ? Does this have to do with the H7 memory mapping ?
  2. What reasonable options do I have ? Can '.hex' files be as easily read and written to FLASH memory ? Should I rather generate several '.bin' files to split sections ?

Here's my linkerscript for reference:

ENTRY(Reset_Handler)
STACK_SIZE = 0x8000;
 
/* Non cacheable RAM for DMA
   This can be adjusted as needed
*/
DMA_RAM_SIZE = 32k;
MIN_HEAP_SIZE = 0x2000;      /* required amount of heap  */
BOOTLOADER_SIZE = 256k;
FLASH_ORIGIN = 0x08000000;
 
/* Specify the memory areas */
MEMORY
{
bootloader (rx) : ORIGIN = FLASH_ORIGIN, LENGTH = BOOTLOADER_SIZE
FLASH (rx)      : ORIGIN = FLASH_ORIGIN + BOOTLOADER_SIZE, LENGTH = 2048K - BOOTLOADER_SIZE
DTCMRAM (xrw)   : ORIGIN = 0x20000000, LENGTH = 128K
DMA_RAM (xrw)   : ORIGIN = 0x24000000, LENGTH = DMA_RAM_SIZE
RAM (xrw)       : ORIGIN = 0x24000000 + DMA_RAM_SIZE, LENGTH = 512K - DMA_RAM_SIZE
RAM_D2 (xrw)    : ORIGIN = 0x30000000, LENGTH = 288K
RAM_D3 (xrw)    : ORIGIN = 0x38000000, LENGTH = 64K
ITCMRAM (xrw)   : ORIGIN = 0x00000000, LENGTH = 64K
SDRAM (xrw)     : ORIGIN = 0xC0000000, LENGTH = 32M
}
 
/* Define output sections */
SECTIONS
{
  /* created for EXTERNAL SDRAM allocation */
  .external_ram (NOLOAD) :
  {
    . = ALIGN(4);
    *(.external_ram)
    *(.external_ram*)
    . = ALIGN(4);
  } >SDRAM
 
  .dma_ram :
  {
    . = ALIGN(4);
    *(.)        
    *(.dma_ram*)
    . = ALIGN(4);
  } >DMA_RAM
 
  .ram_d2 :
  {
    . = ALIGN(4);
    *(.ram_d2)
    *(.ram_d2*)
    . = ALIGN(4);
  } >RAM_D2
 
  .ram_d3 :
  {
    . = ALIGN(4);
    *(.ram_d3)
    *(.ram_d3*)
    . = ALIGN(4);
  } >RAM_D3
 
  .dtcmram :
  {
    . = ALIGN(4);
    *(.dtcmram)
    *(.dtcmram*)
    . = ALIGN(4);
  } >DTCMRAM
 
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH
 
  /* 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 */
  } >FLASH
 
  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH
 
  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >FLASH
 
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH
 
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH
 
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH
 
  /* 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 AT> FLASH
  
 
  /* Copy specific fast-executing code to ITCM RAM */
  itcm_data = LOADADDR(.itcm_text);
  .itcm_text :
  {
    . = ALIGN(4);
    itcm_text_start = .;
    *(.itcm_text)
    *(.itcm_text*)
    . = ALIGN(4);
    itcm_text_end = .;
  } >ITCMRAM AT> FLASH 
 
 
  /* 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
 
  /* Stack section in RAM, it must be 8-byte aligned. */
  .stack (NOLOAD):
  {
      . = ALIGN(8);
      _sstack = .;
      . = . + STACK_SIZE;
      . = ALIGN(8);
      _estack = .;
  } > DTCMRAM
 
  /* User_heap section, used to check that there is enough RAM left */
  ._user_heap :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + MIN_HEAP_SIZE;
    . = ALIGN(8);
  } >RAM
 
 
  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }
 
  .ARM.attributes 0 : { *(.ARM.attributes) }
}

The 'bin' file is made with the simplest command:

arm-none-eabi-objcopy -O binary firmware.elf firmware.bin

Thanks for your time and help

1 ACCEPTED SOLUTION

Accepted Solutions
TGeof.2
Associate II

For reference, I found the underlying issue. The sections I created to use the different RAM regions should have been marked NOLOAD.

I changed the linker script following this SO reply: https://stackoverflow.com/a/65577906/6386478

Now my sections look like this, for example:

/* .dtcmram contains value-initialized data, so we need to store that init data in flash at address _si_dtcmram */
  .dtcmram (NOLOAD) :
  {
    . = ALIGN(4);
    _s_dtcmram = .;
    KEEP(*(.dtcmram))
    . = ALIGN(4);
    _e_dtcmram = .;
  } >DTCMRAM AT> FLASH
  _si_dtcmram = LOADADDR (.dtcmram);
 
/* .ram_d2 only contains data that needs to be zero-initialized, no need for flash storage */
  .ram_d2 (NOLOAD) :
  {
    . = ALIGN(4);
    _s_ram_d2 = .;
    KEEP(*(.ram_d2))
    . = ALIGN(4);
    _e_ram_d2 = .;
  } >RAM_D2

Then, following this guide: https://www.youtube.com/watch?v=7stymN3eYw0

I migrated the startup code from ASM to C, which made it way easier to initialize my custom sections, either with zeros or with init data stored in flash, for example:

void Reset_Handler(void)
{
  //...
 
  // Initialise .dtcmram section with data stored in flash
  uint32_t* dtcmram_init = &_si_dtcmram;
  uint32_t* _dtcmram = &_s_dtcmram;
  while(_dtcmram < &_e_dtcmram) *_dtcmram++ = *dtcmram_init++;
 
  //...
}

If that reply can save someone the few days I spent on this, it'll be worth it.

Cheers

View solution in original post

8 REPLIES 8
Peter BENSCH
ST Employee

If you create a HEX, it will be considerably smaller because it can contain several blocks, each with a start address.

BIN files must contain everything in one block so that every memory hole in the address space is also mapped in the BIN.

Yes, HEX can be easily written to the Flash memory, e.g. using the STM32CubeProgrammer.

Regards

/Peter

In order 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.
Paul1
Lead

Hi,

Have had this too with some projects for STM32H743 and STM32L476.

Quick answer - Use the ELF or Hex files instead of BIN file (I prefer ELF, but your choice, extra debug data may be in ELF).

Long answer:

  • It has something to do with extra sections defined in linker file
  • We added some user defined sections for FLASH and RAM for "non-volatile" parameters for our design.
  • Moving these sections to end of linker file fixed our STM32L476 BIN, but only made our STM32H743 BIN slightly less insane.
  • I expect if you juggle the order of any sections you added to the default linker files it may fix your problem
  • The BIN file generation seems to mistakenly be adding RAM areas to BIN according to order in linker file.

Alternative:

  • don't define the extra sections in linker, hard code them as pointers instead (yuck!)
  • share the ELF or HEX files instead of BIN files.

Paul

TDK
Guru

Looks like you're only using FLASH to store data, as SDRAM is marked as NOLOAD. You should be able to create a BIN file for the new firmware that only contains contents for FLASH.

Look at the map file and figure out what data is being placed elsewhere and correct that.

If you feel a post has answered your question, please click "Accept as Solution".
TGeof.2
Associate II

Thanks for your replies, @Peter BENSCH​ & @Community member​ !

Using .hex seems like a reasonable solution, but would require that I write a hex parser to program the flash from the bootloader...

But it also seems like I could generate several .bin files for several sections. Do you have any clue as to which sections should be kept, and which should be dropped ?

Ah ! So my dedicated sections for RAM might need to be marked as NOLOAD as well, actually.

Shuffling the sections around, I was indeed able to shave 200MiB off, but the file is still 450MiB, so, as you said it's only "slightly less insane"...

TGeof.2
Associate II

For reference, I found the underlying issue. The sections I created to use the different RAM regions should have been marked NOLOAD.

I changed the linker script following this SO reply: https://stackoverflow.com/a/65577906/6386478

Now my sections look like this, for example:

/* .dtcmram contains value-initialized data, so we need to store that init data in flash at address _si_dtcmram */
  .dtcmram (NOLOAD) :
  {
    . = ALIGN(4);
    _s_dtcmram = .;
    KEEP(*(.dtcmram))
    . = ALIGN(4);
    _e_dtcmram = .;
  } >DTCMRAM AT> FLASH
  _si_dtcmram = LOADADDR (.dtcmram);
 
/* .ram_d2 only contains data that needs to be zero-initialized, no need for flash storage */
  .ram_d2 (NOLOAD) :
  {
    . = ALIGN(4);
    _s_ram_d2 = .;
    KEEP(*(.ram_d2))
    . = ALIGN(4);
    _e_ram_d2 = .;
  } >RAM_D2

Then, following this guide: https://www.youtube.com/watch?v=7stymN3eYw0

I migrated the startup code from ASM to C, which made it way easier to initialize my custom sections, either with zeros or with init data stored in flash, for example:

void Reset_Handler(void)
{
  //...
 
  // Initialise .dtcmram section with data stored in flash
  uint32_t* dtcmram_init = &_si_dtcmram;
  uint32_t* _dtcmram = &_s_dtcmram;
  while(_dtcmram < &_e_dtcmram) *_dtcmram++ = *dtcmram_init++;
 
  //...
}

If that reply can save someone the few days I spent on this, it'll be worth it.

Cheers

See also

https://community.st.com/s/question/0D53W00000PpuUgSAJ/split-bin-in-multiple-regions

.HEX files tend to be 2.5x the size of data described by separate binaries. They are however not unduly difficult to build a loader around

.DFU files allow for multiple regions to be described in a binary object format, without all the clutter usually carted around by .ELF/.AXF formats

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