2021-10-06 05:49 AM
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:
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
Solved! Go to Solution.
2021-10-12 09:37 AM
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
2021-10-06 05:58 AM
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
2021-10-06 05:59 AM
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:
Alternative:
Paul
2021-10-06 06:38 AM
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.
2021-10-06 06:42 AM
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 ?
2021-10-06 06:53 AM
Ah ! So my dedicated sections for RAM might need to be marked as NOLOAD as well, actually.
2021-10-06 07:40 AM
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"...
2021-10-12 09:37 AM
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
2021-10-12 09:45 AM
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