Skip to main content
Visitor II
April 22, 2020
Solved

STM32F7: USBH_malloc fails (despite 8 KB heap)

  • April 22, 2020
  • 2 replies
  • 1930 views

In my project, which uses FatFs and a couple of peripherals, I always used a heap size of 0x100 bytes, with no apparent problems.

After activating the USB Host for HID Class, I increased the heap size to 0x2000 bytes (8 KB), but still the USBH_malloc call for 52 bytes in USBH_HID_InterfaceInit() fails, i.e., returns NULL.

USBH_malloc seems defined as plain malloc, but alas, I cannot debug into the malloc function.

The heap section in the linker file is

_Min_Heap_Size = 0x2000; /* required amount of heap */
...
/* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
 ._user_heap_stack :
 {
 . = ALIGN(8);
 PROVIDE ( end = . );
 PROVIDE ( _end = . );
 . = . + _Min_Heap_Size;
 . = ALIGN(8);
 } >RAM

at the end of the declarations. The map file shows the position of the heap as intended:

...
 0x0000000020019848 __bss_end__ = _ebss
 
._user_heap_stack
 0x0000000020019848 0x2000 load address 0x0000000000217308
 0x0000000020019848 . = ALIGN (0x8)
 0x0000000020019848 PROVIDE (end, .)
 [!provide] PROVIDE (_end, .)
 0x000000002001b848 . = (. + _Min_Heap_Size)
 *fill* 0x0000000020019848 0x2000 
 0x000000002001b848 . = ALIGN (0x8)

What can cause malloc to fail here? Is 8 KB still not enough for 1 USB class/interface/endpoint/configuration each?

    This topic has been closed for replies.
    Best answer by Lars Beiderbecke

    Clive, thanks for your answer!

    I actually found out what is wrong. I read that _sbrk() in syscalls.c is somehow is involved in malloc(), and sure enough:

    caddr_t _sbrk(int incr)
    {
    	extern char end asm("end");
    	static char *heap_end;
     
     // ...
    	if (heap_end + incr > stack_ptr)
    	{
    		errno = ENOMEM;
    		return (caddr_t) -1;
    	}
     // ...
    }

    Now what I didn't write (isn't it always in the details you don't mention?) is that I relocated my stack to DTCM, but heap is in RAM1. Thus, that check will be always false!

    After replacing stack_ptr with 0x2003c000, everything works fine.

    2 replies

    Graduate II
    April 22, 2020

    a) failure to release

    b) fragmentation

    c) reentry/thread safe issues

    Might want to walk the heap and see what the state is at failure. Instrument the malloc/free to track symmetry, and detect double free, or orphans.

    Not using GCC, but would expect linked list hidden in front of the allocation. Check the allocator source.

    Lars BeiderbeckeAuthorAnswer
    Visitor II
    April 22, 2020

    Clive, thanks for your answer!

    I actually found out what is wrong. I read that _sbrk() in syscalls.c is somehow is involved in malloc(), and sure enough:

    caddr_t _sbrk(int incr)
    {
    	extern char end asm("end");
    	static char *heap_end;
     
     // ...
    	if (heap_end + incr > stack_ptr)
    	{
    		errno = ENOMEM;
    		return (caddr_t) -1;
    	}
     // ...
    }

    Now what I didn't write (isn't it always in the details you don't mention?) is that I relocated my stack to DTCM, but heap is in RAM1. Thus, that check will be always false!

    After replacing stack_ptr with 0x2003c000, everything works fine.

    Graduate II
    April 22, 2020

    Yes this depressing piece of code has propagated everywhere, big headaches with STM32 with multiple RAM regions.

    Why ST can't use linker symbols properly for both heap and stack initialization and limit checking is beyond me.

    That and the use of symbols to set SCB->VTOR to what the linker built...

    Super User
    April 22, 2020

    > Why ST can't use linker symbols properly for both heap and stack initialization and limit checking is beyond me.

    Not to compete with makers of proper tools and libraries for their hardware? For demonstration and examples this simplistic code is enough...

    -- pa