Nucleo FR401RE heap size issue in latest mbed versions

20 Aug 2014

Hi all,

i updated mbed libs to latest and i saw my old application can't malloc more than 1KB.

The issue comes since mbed lib v87 and mbed-src v262

https://mbed.org/users/mbed_official/code/mbed-src/rev/85569914dbe0

TOOLCHAIN_ARM_MICRO/startup_stm32f401xe.s have been changed from Heap_Size EQU 0x00000000 to Heap_Size EQU 0x00000400

but now the heap can't grow outside that limit, while previously could grow untill overlapping the stack As workaround acutally i'm using latest mbed-src with that startup re-edited back to heapsize=0

Using an example given here to test the heap http://mbed.org/forum/bugs-suggestions/topic/4809/ the heap seems limited to 0x400

///// build 261 ok

Start
i is at 0x20017FE4
SP = 0x20017FE0
Mallocced 0x100 bytes at 0x200003B8, diff = 0x17C30
Mallocced 0x100 bytes at 0x200004C0, diff = 0x17B28
Mallocced 0x100 bytes at 0x200005C8, diff = 0x17A20
Mallocced 0x100 bytes at 0x200006D0, diff = 0x17918
Mallocced 0x100 bytes at 0x200007D8, diff = 0x17810
Mallocced 0x100 bytes at 0x200008E0, diff = 0x17708
Mallocced 0x100 bytes at 0x200009E8, diff = 0x17600
Mallocced 0x100 bytes at 0x20000AF0, diff = 0x174F8
Mallocced 0x100 bytes at 0x20000BF8, diff = 0x173F0
Mallocced 0x100 bytes at 0x20000D00, diff = 0x172E8
Mallocced 0x100 bytes at 0x20000E08, diff = 0x171E0
Mallocced 0x100 bytes at 0x20000F10, diff = 0x170D8
Mallocced 0x100 bytes at 0x20001018, diff = 0x16FD0
...
...
Mallocced 0x100 bytes at 0x20017C20, diff = 0x3C8
Mallocced 0x100 bytes at 0x20017D28, diff = 0x2C0
Mallocced 0x100 bytes at 0x20017E30, diff = 0x1B8
system hangs


//////// build 262+ broken

Start
i is at 0x20017FE4
SP = 0x20017FE0
Mallocced 0x100 bytes at 0x200003B8, diff = 0x17C30
Mallocced 0x100 bytes at 0x200004C0, diff = 0x17B28
Mallocced 0x100 bytes at 0x200005C8, diff = 0x17A20
Mallocced 0x100 bytes at 0x
system hangs

//////// build 262+ with heapsize 0x800 

Start
i is at 0x20017FE4
SP = 0x20017FE0
Mallocced 0x100 bytes at 0x200003B8, diff = 0x17C30
Mallocced 0x100 bytes at 0x200004C0, diff = 0x17B28
Mallocced 0x100 bytes at 0x200005C8, diff = 0x17A20
Mallocced 0x100 bytes at 0x200006D0, diff = 0x17918
Mallocced 0x100 bytes at 0x200007D8, diff = 0x17810
Mallocced 0x100 bytes at 0x200008E0, diff = 0x17708
Mallocced 0x100 bytes at 0x200009E8, diff = 0x17600
Mallocced 0x100 bytes at 0x       0, diff = 0x20017FE8
SP = 0x20017FE0
24 Aug 2014

Oh, i sorted it out, seems a known arm microlib limitation which must be took in account. http://www.keil.com/support/man/docs/armlib/armlib_chr1358938939726.htm

In mbed-src 262+ heap_size have been set to x400 (was 0 before), but according to keil, to let heap grow outside heap_size, the heap_limit must be set too.

heap_base, i think, it's set by compiler after it reserved initial ram for static/globals/ZI stuff, while heap_limit can be calced easily. Adding heap_limit calc to /TOOLCHAIN_ARM_MICRO/startup_stm32f401xe.s seems it did the trick.

; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000400

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
                EXPORT  __heap_base
                EXPORT  __heap_limit
                
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit	EQU (__initial_sp - Stack_Size) //just this

Simple code for testing, grabbed on the discussion i linked before

#include "mbed.h"
unsigned char a = 1;
Serial pc(USBTX, USBRX);
unsigned char globalbuff[0x80] =
{
    0x72, 0x68, 0x69, 0x73, 0x5F, 0x69, 0x73, 0x5F, 0x67, 0x6C, 0x61, 0x62, 0x61, 0x6C, 0x6C, 0x79, 
    0x5F, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5F, 0x69, 0x6E, 0x72, 0x61, 0x6D, 
    0x74, 0x68, 0x69, 0x73, 0x5F, 0x69, 0x73, 0x5F, 0x67, 0x6C, 0x61, 0x62, 0x61, 0x6C, 0x6C, 0x79, 
    0x5F, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5F, 0x69, 0x6E, 0x72, 0x61, 0x6D, 
    0x74, 0x68, 0x69, 0x73, 0x5F, 0x69, 0x73, 0x5F, 0x67, 0x6C, 0x61, 0x62, 0x61, 0x6C, 0x6C, 0x79, 
    0x5F, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5F, 0x69, 0x6E, 0x72, 0x61, 0x6D,
    0x74, 0x68, 0x69, 0x73, 0x5F, 0x69, 0x73, 0x5F, 0x67, 0x6C, 0x61, 0x62, 0x61, 0x6C, 0x6C, 0x79, 
    0x5F, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5F, 0x69, 0x6E, 0x72, 0x61, 0x6D, 
} ;
int main() {
    pc.baud (115200);
    pc.printf("Start\r\n");
    globalbuff[0]=0x74; //change a value otherwise compiler will place array in flash instead of ram
    int i = 0x100;
    pc.printf("i in stack at 0x%8X\r\n", &i);
    pc.printf("blogal buff at 0x%8X\r\n", &globalbuff);
    pc.printf("SP = 0x%8X\r\n", __current_sp());
    while(1) {
        void* test = malloc(i);
        int i2 = 2;
        if (test == NULL) break;
        pc.printf("Mallocced 0x%X bytes at 0x%8X, diff = 0x%X\r\n", i, test, (uint32_t)(&i2) - (uint32_t)test); 
        memset(test,a,0x100);
        a++;
    }
    pc.printf("End\r\n");
}

Result is fine, no hangs

i in stack at 0x20017FDC
blogal buff at 0x200001BD
SP = 0x20017FD8
Mallocced 0x100 bytes at 0x20000440, diff = 0x17BA0
Mallocced 0x100 bytes at 0x20000548, diff = 0x17A98
Mallocced 0x100 bytes at 0x20000650, diff = 0x17990
Mallocced 0x100 bytes at 0x20000758, diff = 0x17888
.......
.......
Mallocced 0x100 bytes at 0x20017990, diff = 0x650
Mallocced 0x100 bytes at 0x20017A98, diff = 0x548  
End

Dumped ram with ST-link utility and globals, ZI, stack, heap is all in place, no heap-stack overlapp (stack starts at 20017C00) and free() works too. /media/uploads/Geremia/no_overlap.png

Hope it helps somehow

24 Aug 2014

Nice job. I wondered in the first place why the heap couldn't be initial zero, but not really something I know alot about. A quick check makes me think other targets simply put heap limit at current_sp, but in any case I guess this needs a pull request on the mbed github (https://github.com/mbedmicro/mbed)

24 Aug 2014

As far as i saw, heap_size=0 with heap_limit not initialized (mbed-src <262), heap begins after ZI and..... overlaps stack ;) heap_size=x400 with heap_limit not initialized (mbed-src >=262, heap is limited to x400 size, so malloc, realloc, calloc, new are limited into that small space.

Quite all targets using arm micro toolchain have same issue i think.

btw, is there a way to force online compiler to use arm std toolchain?

24 Aug 2014

Don't think so, online compiler is set for one (I don't know either why large Nucleo targets are using micro instead of std).