This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

HowTo : get malloc to work

Other Parts Discussed in Thread: LM3S6965To 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 ...
  • 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...
  • Hi All

    Since there are obviously knowledgeable people involved in this thread I would like to try to fill in a couple of gaps.

    Can someone explain the following?
    1) __HEAPSIZE__ = 1024; // just 1k defined for heap use in the linker script file
    2) When malloc(1) is called, _sbrk() is called with a value of 0x20. On first call the heap location is returned (in the test case 0x200007c4)
    3) _srbrk() is then called again (still from initial malloc()) with the value 0x81c which seems to be setting the top of heap to the next 4k boundary(?) 0x20001000, which is however above the allocated heap range (__heap_end__ is 0x20000bc4 in the test case). This causes a NULL to be returned.
    4) following mallocs() then cause _sbrk() to be called with 0x1000 and always returns a NULL
    5) repeating with 4k HEAP size defined, the second _sbrk() call is successful and then the test sequence of a few malloc() and free() works normally. _sbrk() is not called any more.

    Does this mean that at least 4k of heap space is required for the malloc() to guaranty that it can be used at all?

    Background:
    a) the system this is used in has two memory management systems at the same time. A dynamic system with no overhead (this not supporting free()) which is used for main work. This avoids management overhead for most efficient storage (1 byte can be allocated without taking more that 1 byte from heap. It also has controllable alignment which is useful for drivers which need special hardware alignment.
    b) optionally malloc() can be used from the library for code which needs malloc()/free() type operation.
    c) The memory layout is seen here memory layout and the HEAP size allocated to malloc() needs fixed boundaries and can not grow up to the stack space.

    The following is the _sbrk() implementation:
    Code:


     extern int  __heap_start__;
    extern int  __heap_end__;

    extern void *_sbrk(int incr)
    {
        static 
    unsigned char *heap NULL;
        
    unsigned char *prev_heap;

        if (
    heap == NULL) {
            
    heap = (unsigned char *)&__heap_start__;
        }
        
    prev_heap heap;

        if ((
    heap incr) >= (unsigned char *)&__heap_end__) {
            return 
    0;
        }
        
    heap += incr;
        return (
    void *)prev_heap;
    }






    One would expect that it is quite easy to find GCC malloc() source code to study but this has turned out to be rather a challenge. Moreover it seems that there are various malloc() implementations and it then depends on the package being used as to which one is actually integrated. For example, previous versions of Codesourcery G++ work differently and don't need the user to supply the _sbrk() stub at all...

    All in all rather confusing. Can anyone explain the exact operation as detailed from experimental results above and confirm the 4k limitation?

    Regards

    Mark
  • Hi

    I would like to add some more experimental results which may be relevant.

    a) as noted before, the first call of sbrk() with parameter zero is used to find out the start location of heap.
    b) the second, as also noted before, requests the heap size to be increased to the next 4k boundary (why 4k boundary?). This means that the initial heap size varies somewhere between a small value and almost 4k
    c) If I now try to allocate memory that doesn't fit into the available heap space, _sbrk() is called again to increase the space. It is always called with an increment value of 4k. If there is 3.9k of physical memory from the present top of heap to the end of the available space (however calculated) it will thus fail.
    d) the consequence that I see is that the point at which it fails depends also on the heap start location (due to the strange first increment value). If a _sbrk() call is just successful (just enough remaining to allocate the next 4k block) all is well and in fact there may be then well over 3k of free heap left (if the last malloc() call only uses a small value). If variables in the system are added/removed it will reposition the starting location of heap and possibly change this so that the final 4k block request will suddenly fail (eg. when there is only 3.99k free), even if the malloc() caller only wants 1 byte form heap.

    My present conclusion is that the malloc(), _sbrk() technique used by this GCC implementation requires a certain amount of margin (certainly over 4k and preferably at least 8k) to ensure that it can tolerate changes in memory use without sudden failure. A minimum heap size of 4k is required to guaranty that the first heap size increment will work. A minimum heap size of 8k is required to guaranty that the second heap size increment will always work. The second heap size increment will, in the worst case, allocate only around 4k of actual heap for use so a 4k overhead should be available. Due to some management overhead (which is also on heap) the values should be increased also by at least 20 bytes plus around 16 per actual malloc() chunk on heap.

    Have I understood this correctly???

    Regards

    Mark

    www.uTasker.com
  • If you are linking with newlib then the source for malloc ought to be at the RedHat site (http://sourceware.org/newlib/).

    If you download a snapshot then the path, including tarballs and stuff, is:

    newlib-1.17.0.tar.gz/newlib-1.17.0.tar/newlib-1.17.0/newlib/libc/stdlib/malloc.c

    Post edited by: johanekdahl, at: 2009/01/26 03:53
    Forum software seems to dislike backslashes - replaced by forward slashes.

    Post edited by: johanekdahl, at: 2009/01/26 03:55
  • Hello Mark

    a) It's important to understand the interface relationship :-

    User -> malloc -> sbrk

    Analogy

    Customer -> shop -> Warehouse

    sbrk is a BULK memory allocator, so it allocates BIG chunks of the heap to malloc (not the User).

    It seems that the default pagesize is 4k bytes, so this is a chunk.

    b) The only important thing to the User is the HEAP SIZE (= HEAP END - HEAP START)

    The utility posted here will give you all 3 of these values.
    The HEAP START is set by the linker, and so will vary from build to build.


    c) malloc, realloc ... need to know where to start their memory allocation from.
    So at initialisation, at least 2 calls to sbrk are needed - first to find the HEAP START, and next to find the first chunk (This will be on a 4k boundary).
    If the HEAP SIZE is
    Future calls to sbrk will return NULL to malloc. No more Heap!

    malloc allocates memory to the user, so when we call malloc(n) n = num bytes, malloc will try to service our request from its own resources.
    If it can not it will call sbrk for more BULK memory.

    Therefore, just because sbrk says to malloc that there is no more BULK memory, does not mean that malloc can not fulfill our next call to it.
    That will depend on 'n' and the resources malloc already has.

    Obviously, if the system has 10k HEAP SIZE, and we malloc with n = 11k, no joy, and malloc will return NULL to User.
    We will not be able to use every byte of the Heap, beacuse malloc will have an overhead.
    From initial testing this seems to be ~ 1 -> 1.5k bytes.
    If the system has 3k HEAP SIZE, and we malloc with n = 1800 ok, n = 1850 fails.

    malloc does all the memory management including updating it's map for User Free()'s etc, so the memory available at any given time will vary.

    d) a simple test program

    Put a print in sbrk for nextHeap returned, and match to succesive malloc calls in test app - separate with a few seconds delay so that you can see the behaviour between malloc and sbrk.

    e) details of malloc & mallocr (reentrant) are in the libc (newlib) sources from codesourcery g++.
    I don't think you can configure anything externally, so a new build of libc would have to be done.

    Hope this helps ...
  • Hi Derek

    Thanks. I think that that corresponds well with my test results.

    I have made the following recommendation to GCC users of the project and feedback has been positive until now.

    http://www.utasker.com/forum/index.php?topic=489.msg2060#msg2060

    The thing that made me worried was the fact that if I set my heap size to say 4.5k (0x1200) and want to allocate 1k from it it sometimes worked and sometimes failed. It depends on where the boundary of the heap start is!

    a) HEAP start address 0x20000ef0
    First chunk size is 0x110 in size and so a second chunk is requested. This is 4k and so the total heap is 0x1100 - the malloc works (0x400 from 0x1100 allocated)

    b) HEAP start address 0x20000c10
    First chunk size is 0x3f0 in size and so a second chunk is requested. The requested 4k is not available (0xe10 is less that 4k [0x1000]) and malloc fails to allocate 1k, although 4.5k is theoretically available.

    The 4k values is a bit large for small embedded systems so it is therefore up to the user to (be able to) accept the safety margin overhead or else rebuild the library with personal settings.

    Regards

    Mark

    www.uTasker.com
  • OK, I'm no expert on linker scripts (quite the opposite, actually) and it's friday afternoon. Still I'm looking at this again, preparing a lab on a course so I can't just be satified with it working. This time around I need to understand what goes on.

    I understand the chain talked about above. The portions of memory that sbrk() returns at each call I refer to as "chunks" (as I seem to recall that term from somewhere). The area that sbrk() takes these chunks from I call "the area usable for heap", or just the "heap area" for short. We could argue over the correctness of these terms, but I had to choose some terminology in order to ask the questions below clearly:

    1) The things we do in the linker script, ending up with two PROVIDE(...) seems to indicate that we treat all the RAM after .bss as the area usable for the heap. If I understad things correctly the stack grows from end of RAM towards lower addresses, so the stack and the heap could collide. Correct?

    (This seems to be the case as there are provisions in the sbrk() implementation provided by Derek for not letting the heap overrun the stack. I realize that it would be much harder, and expensive, to test the opposite, eg probing for the end of the heap before pushing eg a call frame on the stack).

    2) I ddon't understand exactlyl what the linker script PROVIDEs. It seems these end up as something very similaar to variables. In Dereks implementation of sbrk() we have eg
    Code:


     extern unsigned int _HEAP_END;




    Later we see things like
    Code:


     nextHeap > (caddr_t)&HEAP_END




    What is that '&' doing there? This seems to imply that _HEAP_END does not hold the end address of the heap area, but is located at the point whick is the end of the heap area. Correct?
    Had it held the address of the end of the heap area then I would have expected
    Code:


     nextHeap > (caddr_t)_HEAP_END




    and then the extern declarations could have used the caddr_t type. Correct?
    If so, could we have used any type in those extern declarations? Or does PROVIDE in the linker script provide "symbols of the type unsigned int"? (Yeah, I know. I'm on my way to the binutils/ld documentation RSN..)

    3. Let's say that I know by experimentation, some marker method, or pure magic, that my stack will be 4K at the max. I also know that after .bss I have 12K of RAM left. In order to avoid that the stack overruns the heap, I'd like to restrict the heap size to 8K. Could I do this in the linker script
    Code:


     
        PROVIDE
    _HEAP_START _end);
        
    PROVIDE_HEAP_END _HEAP_START 81928)); // 8K heap




    ?


    With thanks for all the elightenment in this thread so far, and with hopes for some more of the same stuff!

    Cheers!
    --Johan

    Post edited by: johanekdahl, at: 2009/01/30 08:25

    Post edited by: johanekdahl, at: 2009/01/30 08:26
  • Hi Johan

    I think that all remarks are correct:

    1) An _sbrk() implementation which limits the heap size according to the present stack pointer value (with an additional stack margin) is only accurate to the chunk resolution (4k as the library seems to be compiled with). Also the user has to be sure that the stack margin value is realistic - if a routine happens to use 6k of stack whereas the user believes a maximum of 2k is adequate (and sets this accordingly) this routine is a potential time-bomb in the program... Therefore the user also has to adequately understand his/her program operation (both use of heap and use of stack) to be sure.

    !! Warning: inline optimization can cause stack size to be rather larger than expected. Consider the following test code.

    void fnDoSomethingOnStack(void)
    {
    unsigned char ucTest[1024];
    memset(ucTest, 0x55, sizeof(ucTest));
    }

    void fnTest(void)
    {
    fnDoSomethingOnStack();
    fnDoSomethingOnStack();
    fnDoSomethingOnStack();
    }


    Question - how much stack does fnTest() require?
    Answer (depending on compiler of course)
    a) 1k bytes plus a few for register saving. Very true when in-line optimization is disabled
    b) 3k plus a few bytes for register saving. Alsopossibly true with in-line compiling enabled, for some compilers.

    Although a stupid example, in-lining can result in the compiler creating the following equivalent:

    void fnTest(void)
    {
    unsigned char ucTest1[1024];
    unsigned char ucTest2[1024];
    unsigned char ucTest3[1024];
    memset(ucTest1, 0x55, sizeof(ucTest1));
    memset(ucTest2, 0x55, sizeof(ucTest2));
    memset(ucTest3, 0x55, sizeof(ucTest3));
    }


    Disabling in-lining ensures it doesn't and also defining
    static unsigned char ucTest[1024]; in the sub-routine ensures that the array is only available once (but then it also exists forever).

    2) _HEAP_END is a variable that the GCC linker puts at a defined location. It has no defined content value since the value is not relevant. Only its address is relevant and that is indeed why &_HEAP_END is useful.

    caddr_t type is an old UNIX type. I understand that today it is more 'modern' to use void instead. Since the comparisons are essentially just 'simple' address comparisons void * or unsigned char * are probably just as justified - there will be no difference in the result.

    3) by restricting the heap to 8k you are ensuring that the HEAP doesn't grow any larger than that. Just remember the chunk size of 4k means that you are only guarantying that you can really use around 4k of it (including heap overhead)...

    Regards

    Mark

    www.uTasker.com

    Post edited by: mjbcswitzerland, at: 2009/01/30 09:00
  • Hello Johan

    It is your choice where to put the STACK.

    It can be put at the end of RAM 0x20010000 and grow downwards, in which case there is a danger that a collison could take place with the heap coming the other way. That is why there is a check in the sbrk code.

    Code:


     
    ((caddr_t)&_HEAP_START stackPtr)






    Or you can have the stack below the heap in memory in which case it is impossible to have a collision, since they are growing in opposite directions.

    The latter seems to be the default way, although if you do exceed the max stack size, then you'll be overwriting whatever data is just below it in the memory map.

    The second approach can be done in at least 2 ways
    a) leave it to the linker to bury it with the rest of your data
    b) specify where you want it in the linker script - something like this :

    Code:


     
        
    .bss :
        {
            
    _bss = .;
            *(.
    bss*)
            *(
    COMMON)
            
    _ebss = .;
            . = 
    ALIGN (8);
           
    _end = .;
        } > 
    SRAM
            
        STACK_SIZE 
    4096
          
        .
    stack : {
            
    PROVIDE_STACK_END = . );
            . += 
    STACK_SIZE;
            . = 
    ALIGN (8);
           
    PROVIDE_STACK_START = . );
        } >
    SRAM   
        
    PROVIDE
    _HEAP_START _STACK_START );
    PROVIDE _HEAP_END ALIGN(ORIGIN(SRAM) + LENGTH(SRAM) ,8) ); 







    the PROVIDEs are just symbols that can be referenced as extern in your code to get the memory addresses.

    This ties up with the startup.c which will have something like :-

    Code:


     

    extern unsigned long _STACK_START
    ;

    __attribute__ ((section(".isr_vector")))
    void (* const g_pfnVectors[])(void) =
    {
        (
    void (*)(void))&_STACK_START,   // The initial stack pointer

        
    ResetISR,                               // The reset handler
        
    NmiSR,                                  // The NMI handler
        
    FaultISR,                               // The hard fault handler 
    ...








    Just have a play with your linker script, and look at the .map generated to see whether it's what you want - and works ...

    I think Mark has a different problem in that he's trying to specify a fixed heap size starting from 'somewhere' within a 4k chunk. This means that he can not know how big the first chunk is, and the next chunk (which will be 4k) may or may not be within the fixed size, so it may not be obtained. Whereas the approach adopted here, grabs all the heap it can to the end of RAM (a 4k boundary). It may be better to start the heap on a boundary and specify the size in 4k blocks for certainty, if you go that way.
  • Hey guys,

    does anyone of you know, what I have to do change in my linker script, that the malloc call will allocate the memory in an external RAM (connected via EPI, Host Bus, 8 bit)?

    I am sorry that I have no idea about the linker script myself :(

  • If you don't like the default 4K chunk size newlib uses, then rebuild newlib with SMALL_MEMORY defined. Just do an export CFLAGS=-DSMALL_MEMORY (do this also for CXXFLAGS and CPPFLAGS to be sure) before configure, make and make install. Malloc will then use 128bytes instead of 4K. You may also need to rebuild gcc from source. The steps from http://www.kunen.org/uC/gnu_tool.html worked for me using mingw.

  • HI guys.

    First I've to explain myself that i'm the beginner in embedded and c. So, Could someone explain to me about this condition "((caddr_t)&_HEAP_START stackPtr)"

    dereksoftstuff said:
    Code:

        if ( (((caddr_t)&_HEAP_START stackPtr) && (nextHeap stackPtr)) || 
             (
    nextHeap >= (caddr_t)&_HEAP_END)) {    
            return 
    NULL// error - no more memory 
        
    }

    Because, when i used codesourcery lite to compile it, the error is occured.

    syscalls.c:96:30: error: expected ')' before 'stackPtr'

    syscalls.c:96:7: warning: the address of '_HEAP_START' will always evaluate as 'true'

     

    Thanks

  • I think it should look like

    ...

    ..._HEAP_START < stackPtr ...

    ...

    regards,

    Stefan