Electronic – why trace_printf(“%f”,x); triggers a HARD FAULT on a stm32 MCU

floatinglinker-scriptsemihostingstm32f4

I'm using a stm32 F413ZH microncontroller with HAL libraries (mainly written to be used with c but can also be used with c++) in eclipse, actually I managed to completely configure eclipse including semihosting and debug mode(openOCD), the basic project files and also I managed to manually adapt the basic project files given by STM32CubeMX to work with c++. So… now I can use HAL libraries in a c++ eclipse environment and test my code via OpenOCD and trace_printf trace_puts using semihosting.

Now, again, after struggling too much setting up an stm32 environment, I find myself stuck, but this time is different.

These last 7 days I have been looking for a solution to my problem and I have tried many suggestions from similar issues online but none of them has solved my problem.

Well, the thing I'm facing is a HARD FAULT when using trace_printf() in semihosting while debugging, if I use this function to print via semihosting an integer (%d) everything is okay and I can read the printed value in the OpenOCD console but when I tried to print a value with the %f formater the supposedly printed data wasn't being shown in the OpenOCD console, then I read that in order to enable the printing of floating point values I needed to add -u _printf_float to the linker flags, so after adding the flag I tried to trace_printf() a floating value, an integer value or whatever data type but all of them using the %f formater but I keep getting a HARD FAULT using %f in trace_printf().

    [HardFault]
    Stack frame:
    R0 = 00666E69
    R1 = 2004FE78
    R2 = 2004FF00
    R3 = 00666E69
    R12 = F642D800
    LR = 08005DE7
    PC = 08006586
    PSR = 01000000
    FSR/FAR:
    CFSR = 00008200
    HFSR = 40000000
    DFSR = 0000000A
    AFSR = 00000000
    BFAR = 00666E69
    Misc
    LR/EXC_RETURN= FFFFFFF9

By debugging step by step the HARD FAULT handler is triggered after this function is called: vsnprintf()

I'm using this LINKER FLAGS -T mem.ld -T libs.ld -T sections.ld -Xlinker –gc-sections -L"../ldscripts" -Wl,-Map,"ThreePhaseSignals.map" –specs=nano.specs -u _printf_float

My project settings are:

Project toolchains

Target processor settings

C++ preprocessor

Linker settings and flags

My _sbrk.c is:
_sbrk.c

my linker files are:

mem.ld is:
mem.ld

and sections.ld is:

/*
 * Default linker script for Cortex-M (it includes specifics for 
 STM32F[34]xx).
 * 
 * To make use of the multi-region initialisations, define
 * OS_INCLUDE_STARTUP_INIT_MULTIPLE_RAM_SECTIONS for the _startup.c file.
 */

  /*
   * The '__stack' definition is required by crt0, do not remove it.
   */
    __stack = ORIGIN(RAM) + LENGTH(RAM);

    _estack = __stack;  /* STM specific definition */

   /*
   * Default stack sizes.
   * These are used by the startup in order to allocate stacks 
     * for the different modes.
     */

   __Main_Stack_Size = 1024 ;

   PROVIDE ( _Main_Stack_Size = __Main_Stack_Size ) ;

  __Main_Stack_Limit = __stack  - __Main_Stack_Size ;

   /* "PROVIDE" allows to easily override these values from an 
     * object file or the command line. */
    PROVIDE ( _Main_Stack_Limit = __Main_Stack_Limit ) ;

      /*
      * There will be a link error if there is not this amount of 
      * RAM free at the end. 
      */
      _Minimum_Stack_Size = 256 ;

     /*
      * Default heap definitions.
      * The heap start immediately after the last statically allocated 
      * .sbss/.noinit section, and extends up to the main stack limit.
      */
       PROVIDE ( _Heap_Begin = _end_noinit ) ;
       PROVIDE ( _Heap_Limit = __stack - __Main_Stack_Size ) ;

      /* 
       * The entry point is informative, for debuggers and simulators,
       * since the Cortex-M vector points to it anyway.
       */
       ENTRY(_start)


      /* Sections Definitions */

     SECTIONS
    {
/*
 * For Cortex-M devices, the beginning of the startup code is stored in
 * the .isr_vector section, which goes to FLASH. 
 */
.isr_vector : ALIGN(4)
{
    FILL(0xFF)

    __vectors_start = ABSOLUTE(.) ;
    __vectors_start__ = ABSOLUTE(.) ; /* STM specific definition */
    KEEP(*(.isr_vector))        /* Interrupt vectors */

    KEEP(*(.cfmconfig))         /* Freescale configuration words */   

    /* 
     * This section is here for convenience, to store the
     * startup code at the beginning of the flash area, hoping that
     * this will increase the readability of the listing.
     */
    *(.after_vectors .after_vectors.*)  /* Startup code and ISR */

} >FLASH

.inits : ALIGN(4)
{
    /* 
     * Memory regions initialisation arrays.
     *
     * Thee are two kinds of arrays for each RAM region, one for 
     * data and one for bss. Each is iterrated at startup and the   
     * region initialisation is performed.
     * 
     * The data array includes:
     * - from (LOADADDR())
     * - region_begin (ADDR())
     * - region_end (ADDR()+SIZEOF())
     *
     * The bss array includes:
     * - region_begin (ADDR())
     * - region_end (ADDR()+SIZEOF())
     *
     * WARNING: It is mandatory that the regions are word aligned, 
     * since the initialisation code works only on words.
     */

    __data_regions_array_start = .;

    LONG(LOADADDR(.data));
    LONG(ADDR(.data));
    LONG(ADDR(.data)+SIZEOF(.data));

    LONG(LOADADDR(.data_CCMRAM));
    LONG(ADDR(.data_CCMRAM));
    LONG(ADDR(.data_CCMRAM)+SIZEOF(.data_CCMRAM));

    __data_regions_array_end = .;

    __bss_regions_array_start = .;

    LONG(ADDR(.bss));
    LONG(ADDR(.bss)+SIZEOF(.bss));

    LONG(ADDR(.bss_CCMRAM));
    LONG(ADDR(.bss_CCMRAM)+SIZEOF(.bss_CCMRAM));

    __bss_regions_array_end = .;

    /* End of memory regions initialisation arrays. */

    /*
     * These are the old initialisation sections, intended to contain
     * naked code, with the prologue/epilogue added by crti.o/crtn.o
     * when linking with startup files. The standalone startup code
     * currently does not run these, better use the init arrays below.
     */
    KEEP(*(.init))
    KEEP(*(.fini))

    . = ALIGN(4);

    /*
     * The preinit code, i.e. an array of pointers to initialisation 
     * functions to be performed before constructors.
     */
    PROVIDE_HIDDEN (__preinit_array_start = .);

    /*
     * Used to run the SystemInit() before anything else.
     */
    KEEP(*(.preinit_array_sysinit .preinit_array_sysinit.*))

    /* 
     * Used for other platform inits.
     */
    KEEP(*(.preinit_array_platform .preinit_array_platform.*))

    /*
     * The application inits. If you need to enforce some order in 
     * execution, create new sections, as before.
     */
    KEEP(*(.preinit_array .preinit_array.*))

    PROVIDE_HIDDEN (__preinit_array_end = .);

    . = ALIGN(4);

    /*
     * The init code, i.e. an array of pointers to static constructors.
     */
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP(*(SORT(.init_array.*)))
    KEEP(*(.init_array))
    PROVIDE_HIDDEN (__init_array_end = .);

    . = ALIGN(4);

    /*
     * The fini code, i.e. an array of pointers to static destructors.
     */
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP(*(SORT(.fini_array.*)))
    KEEP(*(.fini_array))
    PROVIDE_HIDDEN (__fini_array_end = .);

} >FLASH

/*
 * For some STRx devices, the beginning of the startup code
 * is stored in the .flashtext section, which goes to FLASH.
 */
.flashtext : ALIGN(4)
{
    *(.flashtext .flashtext.*)  /* Startup code */
} >FLASH


/*
 * The program code is stored in the .text section, 
 * which goes to FLASH.
 */
.text : ALIGN(4)
{
    *(.text .text.*)            /* all remaining code */

    /* read-only data (constants) */
    *(.rodata .rodata.* .constdata .constdata.*)        

    *(vtable)                   /* C++ virtual tables */

    KEEP(*(.eh_frame*))

    /*
     * Stub sections generated by the linker, to glue together 
     * ARM and Thumb code. .glue_7 is used for ARM code calling 
     * Thumb code, and .glue_7t is used for Thumb code calling 
     * ARM code. Apparently always generated by the linker, for some
     * architectures, so better leave them here.
     */
    *(.glue_7)
    *(.glue_7t)

} >FLASH

/* ARM magic sections */
.ARM.extab : ALIGN(4)
{
   *(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH

. = ALIGN(4);
__exidx_start = .;      
.ARM.exidx : ALIGN(4)
{
   *(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;

. = ALIGN(4);
_etext = .;
__etext = .;

/* MEMORY_ARRAY */
/*
.ROarraySection :
{
    *(.ROarraySection .ROarraySection.*)                          
} >MEMORY_ARRAY
*/

/*
 * The secondary initialised data section.
 */
.data_CCMRAM : ALIGN(4)
{
   FILL(0xFF)
   *(.data.CCMRAM .data.CCMRAM.*)
   . = ALIGN(4) ;
} > CCMRAM AT>FLASH

/* 
 * This address is used by the startup code to 
 * initialise the .data section.
 */
_sidata = LOADADDR(.data);

/*
 * The initialised data section.
 *
 * The program executes knowing that the data is in the RAM
 * but the loader puts the initial values in the FLASH (inidata).
 * It is one task of the startup to copy the initial values from 
 * FLASH to RAM.
 */
.data : ALIGN(4)
{
    FILL(0xFF)
    /* This is used by the startup code to initialise the .data section */
    _sdata = . ;            /* STM specific definition */
    __data_start__ = . ;
    *(.data_begin .data_begin.*)

    *(.data .data.*)

    *(.data_end .data_end.*)
    . = ALIGN(4);

    /* This is used by the startup code to initialise the .data section */
    _edata = . ;            /* STM specific definition */
    __data_end__ = . ;

} >RAM AT>FLASH

/*
 * The uninitialised data sections. NOLOAD is used to avoid
 * the "section `.bss' type changed to PROGBITS" warning
 */

/* The secondary uninitialised data section. */
.bss_CCMRAM (NOLOAD) : ALIGN(4)
{
    *(.bss.CCMRAM .bss.CCMRAM.*)
} > CCMRAM

/* The primary uninitialised data section. */
.bss (NOLOAD) : ALIGN(4)
{
    __bss_start__ = .;      /* standard newlib definition */
    _sbss = .;              /* STM specific definition */
    *(.bss_begin .bss_begin.*)

    *(.bss .bss.*)
    *(COMMON)

    *(.bss_end .bss_end.*)
    . = ALIGN(4);
    __bss_end__ = .;        /* standard newlib definition */
    _ebss = . ;             /* STM specific definition */
} >RAM

.noinit_CCMRAM (NOLOAD) : ALIGN(4)
{
    *(.noinit.CCMRAM .noinit.CCMRAM.*)         
} > CCMRAM

.noinit (NOLOAD) : ALIGN(4)
{
    _noinit = .;

    *(.noinit .noinit.*) 

     . = ALIGN(4) ;
    _end_noinit = .;   
} > RAM

/* Mandatory to be word aligned, _sbrk assumes this */
PROVIDE ( end = _end_noinit ); /* was _ebss */
PROVIDE ( _end = _end_noinit );
PROVIDE ( __end = _end_noinit );
PROVIDE ( __end__ = _end_noinit );

/*
 * Used for validation only, do not allocate anything here!
 *
 * This is just to check that there is enough RAM left for the Main
 * stack. It should generate an error if it's full.
 */
._check_stack : ALIGN(4)
{
    . = . + _Minimum_Stack_Size ;
} >RAM

/*
 * The FLASH Bank1.
 * The C or assembly source must explicitly place the code 
 * or data there using the "section" attribute.
 */
.b1text : ALIGN(4)
{
    *(.b1text)                   /* remaining code */
    *(.b1rodata)                 /* read-only data (constants) */
    *(.b1rodata.*)
} >FLASHB1

/*
 * The EXTMEM.
 * The C or assembly source must explicitly place the code or data there
 * using the "section" attribute.
 */

/* EXTMEM Bank0 */
.eb0text : ALIGN(4)
{
    *(.eb0text)                   /* remaining code */
    *(.eb0rodata)                 /* read-only data (constants) */
    *(.eb0rodata.*)
} >EXTMEMB0

/* EXTMEM Bank1 */
.eb1text : ALIGN(4)
{
    *(.eb1text)                   /* remaining code */
    *(.eb1rodata)                 /* read-only data (constants) */
    *(.eb1rodata.*)
} >EXTMEMB1

/* EXTMEM Bank2 */
.eb2text : ALIGN(4)
{
    *(.eb2text)                   /* remaining code */
    *(.eb2rodata)                 /* read-only data (constants) */
    *(.eb2rodata.*)
} >EXTMEMB2

/* EXTMEM Bank0 */
.eb3text : ALIGN(4)
{
    *(.eb3text)                   /* remaining code */
    *(.eb3rodata)                 /* read-only data (constants) */
    *(.eb3rodata.*)
} >EXTMEMB3


/* After that there are only debugging sections. */

/* This can remove the debugging information from the standard libraries */    
/* 
DISCARD :
{
 libc.a ( * )
 libm.a ( * )
 libgcc.a ( * )
 }
 */

/* Stabs debugging sections.  */
.stab          0 : { *(.stab) }
.stabstr       0 : { *(.stabstr) }
.stab.excl     0 : { *(.stab.excl) }
.stab.exclstr  0 : { *(.stab.exclstr) }
.stab.index    0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment       0 : { *(.comment) }
/*
 * DWARF debug sections.
 * Symbols in the DWARF debugging sections are relative to the beginning
 * of the section so we begin them at 0.  
 */
/* DWARF 1 */
.debug          0 : { *(.debug) }
.line           0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo  0 : { *(.debug_srcinfo) }
.debug_sfnames  0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges  0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev   0 : { *(.debug_abbrev) }
.debug_line     0 : { *(.debug_line) }
.debug_frame    0 : { *(.debug_frame) }
.debug_str      0 : { *(.debug_str) }
.debug_loc      0 : { *(.debug_loc) }
.debug_macinfo  0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames  0 : { *(.debug_varnames) }    
 }

On internet many people says that this is because printing floats consumes a lot of memory and probable the MCU is crashing due to memory overflow, so many suggestions aim to modify the linker script where stack and heap assignations are made, some others say that this hard faut is related to_sbrk.c in newlib.

I tried to adapt these solutions to my particular case but till now my problem still is not solved. I don't know if I'm badly implementing the suggestions or simply my problem is different.

Can someone help me with this?

Best Answer

solved. Follow this video and if it doesn't work follow the full series of this tutorial. https://www.youtube.com/watch?v=vSy4QgOP8t8&list=PLQpFqqFXr8MCxMUzun2nnqMwohhxNXWVj&index=4

  • I was using a bad linker file

  • I was not correctly using syscalls.c

  • I was using OpenOCD, use Segger. Trust me, it's much better.

It was a hard work to find the solution. I hope this helps someone.