HowTo : get malloc to work

To get dynamic memory allocation to work (malloc, free ...), means understanding the folowing 3 things :-
a) where's the heap
b) where's the stack
c) what's sbrk

I'm using an open-source tool-chain (codesourcery lite).

a) The heap is the region of RAM on your stellaris that isn't being using by your program.
So for example, on the LM3S6965 - 64k SRAM starts @2000 0000 and ends @ 2001 0000 (-1).
If you look in your map file, you'll find that you are only using say 2000 0000 to
2000 4000. This means that the RAM from 2000 4000 to 2001 0000 is unused (the heap) -
free for dynamic memory allocation. The heap grows upwards (increasing address).
You need to modify your linker script standalone.ld as follows :-
(only the last part of bss and the two provides are different from the standard)


Code:


 
MEMORY
{
    
FLASH (rx) : ORIGIN 0x00000000LENGTH 256K
    SRAM 
(rwx) : ORIGIN 0x20000000LENGTH 64K
}

SECTIONS
{
    .
text :
    {
        
KEEP(*(.isr_vector))
        *(.
text*)
        *(.
rodata*)
        
_etext = .;
    } > 
FLASH

    
.data AT (ADDR(.text) + SIZEOF(.text))
    {
        
_data = .;
        *(
vtable)
        *(.
data*)
        
_edata = .;
    } > 
SRAM

    
.bss :
    {
        
_bss = .;
        *(.
bss*)
        *(
COMMON)
        
_ebss = .;
        . = 
ALIGN (8);
       
_end = .;
    } > 
SRAM
}

/* end of allocated ram _end */
PROVIDE_HEAP_START _end );

/* end of the heap -> align 8 byte */ 
PROVIDE _HEAP_END ALIGN(ORIGIN(SRAM) + LENGTH(SRAM) - ,8) );









This gives us access to the start and end address of the heap - on any Stellaris.
Note that the start address can change each time you rebuild, but we have it.


b) The stack is the region of RAM where the system puts all the function call parameter data & local data.
If you look in your startup.c file, you'll see something like this :-


#ifndef STACK_SIZE
#define STACK_SIZE 512
#endif
static unsigned long pulStack[STACK_SIZE];

__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) =
{
(void (*)(void))((unsigned long)pulStack + sizeof(pulStack)), // The initial stack pointer

...

This is your stack start address and stack size. It's buried in your program's RAM.
You can find the real start address and end address in the map file if you want - but it's not important.
The stack grows downwards (decreasing address).
As long as the STACK_SIZE is big enough for your program, it will not overwrite your other RAM variables just preceeding it.
You could move your stack to the end of the heap and let it grow downwards - to one day meet your heap coming the other way,
but the above solution means that no collision is possible.


c) So, last time you tried to use malloc, it didn't compile because of an error - sbrk missing.

Malloc uses sbrk to get hold of the RAM addresses it will allocate back to you.
Note the use of our new PROVIDEs from the linker script, the memory addresses allocated can only be from our heap,
so there is no overlap with the program RAM. When all of the RAM is used, sbrk will tell malloc and malloc will tell you
- i.e. return NULL

Create a file called syscalls.c :-

Code:


 
#include 

// linker (standalone.ld) sets heap start and end
extern unsigned int  _HEAP_START;
extern unsigned int  _HEAP_END;

static 
caddr_t heap NULL;


// low level bulk memory allocator - used by malloc
caddr_t _sbrk int increment ) {
 
    
caddr_t prevHeap;
    
caddr_t nextHeap;
    
    if (
heap == NULL) {
        
// first allocation
        
heap = (caddr_t)&_HEAP_START;
    }

    
prevHeap heap;
            
    
// Always return data aligned on a 8 byte boundary 
    
nextHeap = (caddr_t)(((unsigned int)(heap increment) + 7) & ~7);        

    
// get current stack pointer 
    
register caddr_t stackPtr asm ("sp");
    
    
// Check enough space and there is no collision with stack coming the other way
    // if stack is above start of heap
    
if ( (((caddr_t)&_HEAP_START stackPtr) && (nextHeap stackPtr)) || 
         (
nextHeap >= (caddr_t)&_HEAP_END)) {    
        return 
NULL// error - no more memory 
    
} else {
        
heap nextHeap;
        return (
caddr_tprevHeap;    
    }    
}










Ready to go ...

22 Replies

  • dereksoftstuff wrote:
    c) what's sbrk

    So, just out of idle curiosity, where does the name "sbrk" come from...?
  • This is really a good post. Awhile back I was getting "_sbrk" errors and had no clue what this was. I searched for hours online with no resolution in sight. I finally figured out it was used by malloc, but I still didn't know what this function was supposed to do until someone in another forum provided a generic example. This one is just drop-in-and-go.

    As a matter of fact, I think I'm going to use it. Mine doesn't check if I'm running off the RAM memory map.....
  • Hello JM

    I just don't know how we can get by without dynamic memory ...

    I've seen some of your posts on your project, are you going to share some of the good bits of code with us?

    I'm putting together some building blocks that I'll post soon. There doesn't seem to be much about regarding simple generic code utilities etc.

    The driverlib doesn't go far enough, and noone seems to be posting what they have done - is everyone building the same wheel? Or is everyone too shy ...

    I'd like to see more development within the forum, so that newcomers can start off with some good stuff.
    And we can create some solid flexible utilities.

    Anyone else out there? or shall I continue making my own square wheel ...
  • I have a routine that sets up PWM and SPI to generate signals for an audio DAC for 9 common sample frequencies, and code that processes Shoutcast audio streams....that one is still kinda beta. Useful to the right person, maybe.

    I thought DriverLib is fairly decent at what it is intended to do, but sometimes, like with other things, something isn't obvious. Like how to set/clear a digital output. It took a few days to figure out why the outputs I was trying to control didn't work.
  • dereksoftstuff wrote:
    I just don't know how we can get by without dynamic memory ...
    For embedded stuff - especially small embedded stuff - avoiding dynamic allocation is more the norm than the exception!

    The thing is: by the time you've done all the work to determine how big your heap needs to be, and what to do if an out-of-memory happens, you might just as well have spent that time working out a suitable static allocation scheme...
  • Hello AN

    Yes I used to use PIC's as well (PIC18) - great stuff, but those days are gone now. It had 3 or 4k RAM and not much point in using dynamic memory.
    The current range of LMI M3 chips have 64k RAM - lets just say that again "64k RAM" for small embedded systems - new world ...

    You probably will not be interested in my upcoming posts regading utilities and dynamic memory, but if you get the chance give it a try, you might be surprised.
  • I've been meaning to post this for awhile, but I am having an odd problem with the _sbrk function. It is a problem with the part of the code that checks if I'm exceeding available RAM.

    With this code in place, I was having a problem where malloc was not allocating RAM, even though I knew I had enough. When I removed this code, everything worked fine. So I did some debugging....Turns out, even though I was requesting 20-some KB from malloc, the number passed to the _sbrk function was over 23 or 24K!

    For now I just removed the check and make sure I have enough before asking for memory. But any ideas what is going on with this?
  • Nice spot, the last chunk of RAM isn't allocated.

    The RAM check is OK - infact only needed if your stack starts at top of heap.

    The problem is that the end of heap is at 2000fff8 and malloc wants to go to 20010000.

    Therefore 2 mods :-
    a) linker (remove -8)

    Code:


     
    PROVIDE 
    _HEAP_END ALIGN(ORIGIN(SRAM) + LENGTH(SRAM) ,8) );






    b) sbrk
    >= to >
    Code:


     

    ... (nextHeap > (caddr_t)&_HEAP_END)){    
        return 
    NULL// error - no more memory 






    I think that there is not necessarily a 1:1 relationship between malloc calls and sbrk calls, that depends on the malloc algorithm. It might get a bit more than you requested and try and service your next request with what's left ...

    Also the malloc algorithm itself will use some of the RAM to link the chunks etc.


    I use the following simple utility :-
    systemRAM.h & .c



    Code:


     
    #ifndef SYSTEM_RAM_H_
    #define SYSTEM_RAM_H_

    #include 


    #ifdef __cplusplus
    extern "C"
    {
    #endif


    typedef struct dynamicRAMInfoType {
        
    UINT size;
        
    UINT startAddr;
        
    UINT endAddr;
    };

    typedef voidRAM_ADDRESS;

    struct dynamicRAMInfoType sizeofDynamicRAM(void);

    boolean validHeapAddress(RAM_ADDRESS addr);

    RAM_ADDRESS freeMemory(RAM_ADDRESS addr);


    #ifdef __cplusplus
    }
    #endif

    #endif // SYSTEM_RAM_H_








    Code:


     

    #include 
    #include 
    #include 

    // linker (standalone.ld) sets heap start and end
    extern unsigned int  _HEAP_START;
    extern unsigned int  _HEAP_END;


    #ifdef __cplusplus
    extern "C"
    {
    #endif


    struct dynamicRAMInfoType sizeofDynamicRAM(void) {
        
    struct dynamicRAMInfoType info;
        
        
    info.startAddr = (UINT)&_HEAP_START;
        
    info.endAddr = (UINT)&_HEAP_END;
        
    info.size info.endAddr info.startAddr;
        
        return 
    info;
    }

    boolean validHeapAddress(RAM_ADDRESS addr) {
        if (((
    UINT)addr >= (UINT)&_HEAP_START) && 
            ((
    UINT)addr UINT)&_HEAP_END)) {
            return 
    TRUE;
        } else {
            return 
    FALSE;
        }
    }

    RAM_ADDRESS freeMemory(RAM_ADDRESS addr) {
        
    free(addr);
        return 
    NULL;
    }


    #ifdef __cplusplus
    }
    #endif





  • Hi!

    New to this forum so let me introduce myself briefly:
    20 odd years of C, 15 odd years of C++, 7 years of Atmel AVR 8-bit microcontrollers in the luggage.

    I recently got hold of the EKT-LM3S6965 Evaluation Kit, and am using the Code Red IDE. It rests on GCC and Eclipse just as the CodeSourcery does. (Difference is that the free version of Code Red has no size limitations, but is tied to the Ev Kit.) I am using it to evaluate programming in C++ for CortexM3's.

    I've been struggling with the new-operator for more than a week now, having dug to the point where I understood that it had to do with sbrk(). I tried several sbrk() suggestions found on the net. I also tried to nick the sbrk() that is suggested in the newlib documentation. All to no avail. Upon entering sbrk() the increment was always ridicolously high (think megabytes) and have struggled on under the assumption that there was some kind of type mismatch in the call chain down to sbrk(). Not until today did I realize that LM had this forum set up, and found this thread. It took me much less than an hour to start afresh and test the suggestion here by "dereksoftstuff" and it seems to work like a charm!

    Details on my implementation for Code Red:
    I am setting up a C++ project thus like so:
    1) File, New, C++ Project
    2) In the C++ Project wizard, select Executable and then LMI C++ Project
    You now have a C++ project, and can implement sbrk(). I chose to simply add it to min_cpp.c. In its default state this file implements dummy versions of malloc() and free(), where malloc() returns 0 to indicate that it failed to allocate dynamic memory. The file contains two "blocks" mutually exclusive by a #ifdef preprocessor directive.

    3) Comment out the definition of CPP_NO_HEAP. This will eliminate the dummy definitions of malloc() and free() so that the linker instead will get the "real" ones out of newlib/libc.

    4) In the #else block of the file, implement sbrk() as adviced by Derek. Dont forget extern [...] HEAP_START, extern [...] _HEAP_END and static [...] heap.

    5) Change the linker script as adviced by Derek. For the project set up as abobe the linker script is named LM3S6965.ld .

    You should now be able to test the functionality of malloc() and ultimately of the new-operator.

    Great post, Derek! Thank you!

    Aside: I have also wondered about the meaning of "sbrk" and have found a small tidbit on this. "brk", short for "break", was the name of the end of the heap in Unix. The "s" is not clear to me, but I'm speculation that "Set Break" (ie. change the size of the memory region holding the heap) was shortened to "sbrk".
  • @johanekdahl-

    Great post - welcome aboard - believe that you will love these LMI devices.

    Agree - derek is terrific - we owe him - thanks derek...