Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

STM32 hard faults when trying to printf numbers >= 10

Temporary Workaround

I worked around it with tiny printf:

  • http://www.sparetimelabs.com/tinyprintf/tinyprintf.php
  • https://github.com/cjlano/tinyprintf

Probably newlib printf is just taking too much memory. The PC changes to some strange things after this, which in theory should be the end of a array char[100].

cp = buf + BUF

Later it tries to execute

*--cp = something

and it crashes. Memory bug? There are many things I don't understand. For example, I am not sure whether the linker script is correct, or are the syscall functions. Before I can get further understanding, I have to stick with the tiny printf.


Original

I have an STM32F103RB board (Nucleo), and I just got USART1 working. Also, I tested Newlib functions, and puts() works as expected. However, when I try to use printf() for integers, like this:

printf("ADC: %d\r\n", test);

If test < 10 the program works, but if test >= 10 a hard fault is generated. After some GDB debugging I found that it generates from vfprintf:

#0  HardFault_Handler () at main.c:136
#1  <signal handler called>
#2  0x08005af4 in _vfprintf_r (data=<optimized out>, fp=0x20000384 <impure_data+852>, fmt0=fmt0@entry=0x20004f57 " \254\264", ap=...,
    ap@entry=...) at ../../../../../../newlib/libc/stdio/vfprintf.c:1601
#3  0x08004fd0 in printf (fmt=0x800b4ac "ADC: %d\n\r") at ../../../../../../newlib/libc/stdio/printf.c:52
#4  0x080004e8 in main () at main.c:168

I cannot think of any solutions. This is my _sbrk():

caddr_t _sbrk(int nbytes) {
  static caddr_t heap_ptr = NULL;
  caddr_t base;

  if (heap_ptr == NULL){
    heap_ptr = (caddr_t) &_end;
  }

  if ((caddr_t) _stackend > heap_ptr + nbytes) {
    base = heap_ptr;
    heap_ptr += nbytes;
    return (base);
  }
  else {
    errno = ENOMEM;
    return ((caddr_t) -1);
  }
}

My minimum stack size is configured to be 1024 bytes. I tried to increase until 10k, but it still generates this hard fault. I have no idea how to debug this or find the problem. How can I fix it?

I noticed one thing. The failure comes at vfprintf.c:1601, where I examined the pointer cp in GDB:

(gdb) x 0x20004f57
0x20004f57:     0x00b4ac20
(gdb) x 0x00b4ac20
0xb4ac20:       0x20004f80
(gdb) x 0x20004f58
0x20004f58:     0x0800b4ac
(gdb)

I don't know why the address 0x20004f57 points to a non-existent address.

Also, p _stackend gives

$6 = (caddr_t) 0xb33ea8a6 <error: Cannot access memory at address 0xb33ea8a6>

Which apparently does not exist

It seems that when _VFPRINTF_R() is executed, while the goto number is executed, the pointer cp is corrupted:

3: cp = <optimized out>
2: fmt = 0x800b453 "\n\r"
(gdb) n
1057                            base = DEC;
3: cp = <optimized out>
2: fmt = 0x800b453 "\n\r"
(gdb)
1400    number:                 if ((dprec = prec) >= 0)
3: cp = <optimized out>
2: fmt = 0x800b453 "\n\r"
(gdb)
1409                            if (_uquad != 0 || prec != 0) {
3: cp = 0x20004b58 "\245ۊ\256\211W{\325\326\377Y\352\224\t x\207\220\230&-\031\032~\337\032\371\024\254\"(\214\354\363\b\241\365\022\035\037\252\026\243\206\235P\005OZn\245c\n\352\244E^ά\246\301Ӕ\271L\264"
2: fmt = 0x800b453 "\n\r"
(gdb)

The Linker Script:

/*
Linker script for STM32F10x_128K_20K

modified from

http://www.codesourcery.com/archives/arm-gnu/msg02972.html
http://communities.mentor.com/community/cs/archives/arm-gnu/msg02972.html
*/

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

/* _Minimum_Stack_Size = 256; */
_Minimum_Stack_Size = 1024;

ENTRY(Reset_Handler)


/* Memory Spaces Definitions */

MEMORY
{
  RAM (rwx)  : ORIGIN = 0x20000000, LENGTH = 20K
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
}

__ram_start__ = ORIGIN(RAM);
__ram_size__  = LENGTH(RAM);
__ram_end__   = __ram_start__ + __ram_size__;
_estack = __ram_end__;
/* highest address of the user mode stack */



PROVIDE ( _Stack_Limit = _estack - _Minimum_Stack_Size );

/* Sections Definitions */

SECTIONS
{
    .text :
    {
        KEEP(*(.isr_vector))            /* Startup code */
        *(.text)                   /* code */
        *(.text.*)                 /* remaining code */
        *(.rodata)                 /* read-only data (constants) */
        *(.rodata.*)
        *(.glue_7)
        *(.glue_7t)
        *(.vfp11_veneer)
        *(.v4_bx)
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } >FLASH

    /* for exception handling/unwind - some Newlib functions (in
    common with C++ and STDC++) use this. */
    .ARM.extab :
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)

    } > FLASH

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

    . = ALIGN(4);
     _etext = .;
    /* This is used by the startup in order to initialize the .data secion
*/
    _sidata = _etext;

    /* This is the initialized 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  : AT ( _sidata )
    {
        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .data
secion */
        _sdata = . ;

        *(.data)
        *(.data.*)

        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .data
secion */
        _edata = . ;
    } >RAM


    /* This is the uninitialized data section */
    .bss :
    {
        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .bss
secion */
        _sbss = .;
    __bss_start__ = _sbss;
        *(.bss)
        *(.bss.*)
        *(COMMON)

        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .bss
secion */
        _ebss = . ;
    __bss_end__ = _ebss;
    } >RAM

    PROVIDE ( end = _ebss );
    PROVIDE ( _end = _ebss );
    PROVIDE ( _exit = _ebss );
    PROVIDE (_stackend = ORIGIN(RAM) + LENGTH(RAM) - _Minimum_Stack_Size);

    /* This is the user stack section
    This is just to check that there is enough RAM left for the User mode
stack
    It should generate an error if it's full.
     */
    ._usrstack :
    {
        . = ALIGN(4);
        _susrstack = . ;

        . = . + _Minimum_Stack_Size ;

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



    /* after that it's only debugging information. */

    /* 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) }
}

I didn't understand this linker script very well, and I modified the one that comes with "Discovering the STM32 Microcontroller".

like image 670
Carl Dong Avatar asked Oct 13 '14 02:10

Carl Dong


1 Answers

OK, after all sorts of testing, I found that it was the toolchain I used. I built the toolchain myself using GCC 4.8, and according to http://forum.chibios.org/phpbb/viewtopic.php?f=16&t=2241 , GCC 4.8 has some problems. I switched back to CodeSourcery toolchain and it works again.

like image 95
Carl Dong Avatar answered Oct 12 '22 00:10

Carl Dong