Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 15 of 15

Thread: Get Free Memory for Teensy 3.0

  1. #1
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    955

    Get Free Memory for Teensy 3.0

    Hi All,

    I'm new to Teensy 3.0 and really love these little guys. I was wondering if anyone could point me in the direction on how to get the free sram while running a program. It would be nice to have this. I see that the Arduino IDE shows a "estimated memory use" but I would like to get it dynamically. I've looked at the K-20 data sheet but haven't really made heads or tails of how to get this. I see from page 90 the teensy sram memory address range is:
    SRAM_L: 1FFF,EFFF - 1FFF,FFFF
    SRAM_U: 2000,0000 - 2000,3FFF

    but not sure how to use this to get how much I'm using at any given time. I'm sure there alot more to this than I'm comprehending now but though I would see where I could get posting this to the forum.

    Thanks,
    duff

  2. #2
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    955
    So i just found that Bill Greiman has already tackled this in his sdFat library, just check out SdFatUtil.cpp in that library.

  3. #3
    As I'm working on a project which does a lot of dynamic memory allocation, it would indeed be quite useful to have a function to check the amount of free memory.

    I tried the function Duff recommended, from the most recent SdFat library (sdfatlib20130629.zip) the SdFatUtil.cpp file https://code.google.com/p/sdfatlib/downloads/list.

    After testing this function, I am not sure if it works correctly, or does what I want. (@Duff, does it work for you?)

    Initially I tried testing the function with the following, the first part is just a straight copy paste from the SdFatUtil.cpp file, after that I use the main method instead of the Arduino's setup & loop, as I compile using the makefile.
    Code:
    #include "WProgram.h"
    #include <stdlib.h>
    
    
    #ifdef __arm__
        // should use uinstd.h to define sbrk but Due causes a conflict
        extern "C" char* sbrk(int incr);
    #else  // __ARM__
        extern char *__brkval;
        extern char __bss_end;
    #endif  // __arm__
    
    // function from the sdFat library (SdFatUtil.cpp)
    // licensed under GPL v3
    // Full credit goes to William Greiman.
    int FreeRam() {
        char top;
        #ifdef __arm__
            return &top - reinterpret_cast<char*>(sbrk(0));
        #else  // __arm__
            return __brkval ? &top - __brkval : &top - &__bss_end;
        #endif  // __arm__
    }
    
    void* current;
    extern "C" int main(void) {
        Serial.begin(9600);
        delay(5000);
        Serial.println("Go");
        while (1){
            delay(10);
            current = malloc(1);               // allocate a byte
    
            Serial.print("Free ram:");Serial.print(FreeRam());
            Serial.print(","); Serial.println((uint32_t ) current); // print the pointer
            
        }
    }
    The output of this file is the following:
    Code:
    Go
    Free ram:8167,536866880
    Free ram:8167,536866896
    ...
    Free ram:8167,536870880
    Free ram:4071,536870896
    ...
    Free ram:4071,536874976
    Free ram:-25,536874992
    ...
    Free ram:-25,536878976
    Free ram:-25,536878992
    *stops printing
    The idea of the output is to print the output of the FreeRam function, as well as the address of the pointer. Now the number looks a bit large, I'm not sure what causes that, however, it can be seen that the pointer increments in 16 byte steps. So possibly malloc() always allocates 16 byte chunks.

    I went on to investigate what's happening, I came across http://playground.arduino.cc/Main/Co...unning_Arduino, https://en.wikipedia.org/wiki/Data_segment

    Which obtains information about the compiled code using 'avr-size', after some searching I found arm-none-eabi-size, which I guess is also used by the IDE to provide a memory indication

    Calling the arm-none-eabi-size command on my elf file
    Code:
    $ arm-none-eabi-size main.elf
    flash, block=0, bs=1024, auto=1
       text	   data	    bss	    dec	    hex	filename
      48532	   1628	   2252	  52412	   ccbc	main.elf
    The page from the Arduino playground states in the conclusion: for an approximation at compile time; SRAM as .data+.bss ( is already used)

    Subtracting the first and last pointer from the produced output we get:
    536878992-536866880 = 12112

    Now, adding the difference between the pointers, the bss block and the data block:
    12112+1628+2252 = 15992
    Which is pretty much the 16k the Teensy 3.0 has available.

    Initially I thought the function perhaps only measured the stack and therefore wasn't taking my malloc() data into account, however, replacing the main method with:
    Code:
    extern "C" int main(void) {
        Serial.begin(9600);
        delay(5000);
        Serial.println("Go");
    
        Serial.print("Free ram:"); Serial.print(FreeRam()); Serial.println(" Adding a char.");
        char foo = 1;
        Serial.print("Free ram:"); Serial.print(FreeRam()); Serial.println(" Adding an int16.");
        int16_t bar = 2;
        Serial.print("Free ram:"); Serial.print(FreeRam()); Serial.println(" Adding an int32.");
        int32_t baz = 3;
    
    }
    If I understand it all correctly, this places three variables onto the stack, printing the memory usage in between.
    Code:
    Go
    Free ram:8159 Adding a char.
    Free ram:8159 Adding an int16.
    Free ram:8159 Adding an int32.
    Where the free ram does not change at all. I'm not entirely sure what this means.

    The output of the FreeRam function only changes in blocks of 8167-4071 = 4096 bytes. I think this is due to something determining how a chunk of 4096 bytes of memory is used, whether it is used for the heap or for the stack. I'm not sure about this though.

    I see a workaround, somehow get the values of the .bss and .data block in the code, allocate a byte at the beginning of execution to get the location of the first byte of the heap, after which you can check how much is still free by locating another byte, subtracting them and adding the .data and .bss blocks to it. This provides the ram which is still available for allocation. However, I believe this does not take into account the size of the stack.

  4. #4
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    955
    Your pretty much spot on with what i have observed also. I really haven't looked into this so if you do find any thing out please share!

  5. #5
    A new version of the function can be found in post #9.

    Unfortunate to hear that you experienced this as well, I tried to dig a bit deeper and came up with several things which might be interesting.

    First of all, in the mk20dx128.ld file;
    Code:
    MEMORY
    {
    	FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K
    	RAM  (rwx) : ORIGIN = 0x1FFFE000, LENGTH = 16K
    }
    The beginning of the ram is defined as 0x1FFFE000 = 536862720 which I'll call ram0 for sake of simplicity.

    Made a new file for testing:main.cpp, which has the following sizes:
    Code:
    arm-none-eabi-size main.elf
    flash, block=4, bs=1024, auto=1
       text	   data	    bss	    dec	    hex	filename
      48852	   1628	   2248	  52728	   cdf8	main.elf
    The output the program produces is constant;
    Code:
    __bss_end: 536866596
    __exidx_start: 48844
    __exidx_end: 48852
    (uint32_t)__brkval: 536870912
    (unsigned long)&_estack: 536879104
    &top: 536879063
    foo (heap top?): 536866880
    Now, the value for __exidx_end is the same as the size of the text block.

    Subtracting the beginning of the ram from the other values;
    Code:
    (unsigned long)&_estack: 536879104 - ram0 = 16384 [Teensy ram size]
    __bss_end: 536866596 - ram0 = 3876 [data+bss size]
    (uint32_t)__brkval: 536870912 - ram0 = 8192 [No idea...]
    However, the trouble is that there still is no way to tell what the current stack size is, or how big the heap is. Somehow the pointer to foo, in the FreeRam() function didn't change, realizing that this is quite awkward, I decided to print the pointer returned by the malloc(10) in the loop. -> Suddenly both the pointer in the FreeRam() function as well as the pointer in the loop started changing. Perhaps the compiler stripped my allocation, because it was never used? main.cpp

    However, combining all this information does result in the following (A new version of the function can be found in post #9.):
    Code:
    extern unsigned long _estack; 
    uint32_t FreeRam() { // for Teensy 3.0
        uint32_t heapTop;
        void* foo = malloc(1);
        heapTop = (uint32_t) foo;
        free(foo);
        return (unsigned long)&_estack - heapTop;
    }
    Which works, more or less;main.cpp. Execution halts at 144 bytes remaining, but that might be due to the usb transfer which doesn't function with less. I guess the stack has a limited amount of space, which is pre-allocated before the heap.

    It was fun to figure this out, but I am not 100% sure whether this function is completely correct.
    Last edited by iwanders; 08-09-2013 at 06:22 PM.

  6. #6
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    955
    nice, i'll check it out. I use mostly the arduino ide for programming but I'm sure i can adapt it for that.

    i was wondering how safe it is to use malloc and calloc with teensy 3? i thought i read somewhere not to use it but it makes it nice when designing data buffers.

  7. #7
    I use mostly the arduino ide for programming but I'm sure i can adapt it for that.
    With or without the Arduino IDE, there is no difference for this function. In my code, you can consider the part in the while-loop as the loop() function you'd find in the Arduino IDE, and the part in the main function but before that loop the setup() function.

    i was wondering how safe it is to use malloc and calloc with teensy 3? i thought i read somewhere not to use it but it makes it nice when designing data buffers.
    In C++ there are the 'new' and 'delete' keywords, which can be used instead of free and malloc, I think they do exactly the same, but are perhaps preferred. I'm not sure why it would be unsafe to use them, but I do know it would be good practice to check whether malloc() didn't return a null pointer because it could not allocate the memory. Perhaps that's what they meant? Not using them means you cannot forget this (quite important) check.

  8. #8
    Senior Member
    Join Date
    Jun 2013
    Location
    So. Calif
    Posts
    2,828
    In any small RAM micro- malloc() is OK if the configured heap size supports the max.
    It is free() that kills you in small RAM micros.
    Best to not do it at all, or use only identically sized blocks.
    OOP's new() and delete() are murder on small RAM micros due to garbage collection issues and inablility to quickly conjoin free()'d blocks. People coming from OOP on PCs don't realize this.

    I've done my own malloc() and free() for some projects, and have code that manages like-sized blocks only, or uses one or two sizes that may be larger than requested in malloc(), so as to avoid the above issues with free().
    Have to have a simple/fast way to conjoin free'd blocks.

    there are a number of special malloc()/free() on the 'net. for microprocessors. Genreally though, embedded always/on system with small RAM don't use free()

    new() and delete() in C++ are just wrappers for the equivalent of, or actual use of ye ole malloc() and free(), unless a special version for embedded processors is done by you.
    Last edited by stevech; 07-12-2013 at 10:07 PM.

  9. #9
    Duff, I have an improvement for the FreeRam() function, the previous did not account for the stack, as I wrongly assumed that it had a predetermined size and location somewhere. Instead, it grows from the other side of the memory, opposite of the heap.

    I revisited the function with this new insight. Testing whether this new function accounted for the stack size turned out to be quite tricky, just creating variables did not work. In the end, I tested it by allocating an array on the stack of which the size depended on a value provided through the serial port.

    The new function becomes:
    Code:
    uint32_t FreeRam(){ // for Teensy 3.0
        uint32_t stackTop;
        uint32_t heapTop;
    
        // current position of the stack.
        stackTop = (uint32_t) &stackTop;
    
        // current position of heap.
        void* hTop = malloc(1);
        heapTop = (uint32_t) hTop;
        free(hTop);
    
        // The difference is the free, available ram.
        return stackTop - heapTop;
    }
    Last edited by iwanders; 08-11-2013 at 07:50 PM.

  10. #10
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    955
    ok i'll do some tests with this code I have looked into this a bit but still working out the specifics so i understand it also.

  11. #11
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    Your heap size measurement doesn't really work. If you are using free(), malloc(1) will generally return memory in the middle of the heap - so that address will be pretty useless.

    Even if you are not using free() yourself, some library may.

  12. #12
    That's a very good point tni, any suggestions how we could fix that? Perhaps we can somehow check which addresses are allocated on the heap and how big they each are? The question would be how to do this though, I could not figure out where the malloc function stores what is allocated and what isn't.

  13. #13
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    Well, you can either instrument the existing malloc, which for Teensy 3 seems to come from libc (you really need access to the internal data structure of the allocator, since free() doesn't tell you the amount of deallocated memory).

    Or - probably the most reasonable solution - you you can just try to allocate memory until allocation fails (malloc returns NULL) and then release it (say in a loop, allocating 200 byte blocks). At least on Teensy 2, malloc/free is intelligent enough not to overflow into the stack area and to shrink the heap as much as possible on deallocation, so it can be used by the stack again - the same is hopefully true for T3.

    On issue with counting free memory is fragmentation. If only small blocks are available on the heap, that's going to be useless if you need a larger block. So if you use a block size in the allocation loop mentioned above that matches your largest expected allocation size, you get a reasonably good conservative estimate...

  14. #14
    Senior Member
    Join Date
    Jun 2013
    Location
    So. Calif
    Posts
    2,828
    Here's what I have done in the past...
    For an embedded system, microprocessor with 32-64KB RAM
    needed dynamic allocation of buffers.
    Was not a C++ challenge of new and delete on small and variable sized objects.
    My approach was:
    At startup, use malloc() to create a single table of the addresses of 128, 512, and ethernet packet sized buffers, of fixed size. Number of buffers were chosen to cope with the expected worst case.
    During run time, my own buffer-get and buffer-release calls did get and release of these buffers, e.g., release the nth 512byte sized buffer. The app did a "get" for the appropriately sized buffer.
    For a "get" of a particular buffer size failed because all are in use, the next larger size was returned.
    The index / handle returned for a "get" is the index into the table mentioned above. A second table marks in-use vs. free.

    The appliocations tasks, which was a FreeRTOS program, by convention never called malloc() directly.
    The code for this strategy was simple and small.
    The purpose of this technique was to give buffers for in/out messages via 802.15.4, and in/out messages via IP packets.
    I also had code so the calling task / program could specify an option to "sleep" and retry, if/when all buffers >= the desired size were in use. During the sleep, another task would hopefully release a block.

    It avoided garbage collection as in big-computer object oriented programming, and it avoided the time delays and fragmentation problems of using free() for variable sized memory blocks.
    Last edited by stevech; 08-12-2013 at 03:22 PM.

  15. #15
    For my project I do not really need this function anymore, I decided it was easier to create classes which were platform independent, such that I can easily debug these classes with valgrind and gdb on my desktop. It is still an interesting topic however.

    Well, you can either instrument the existing malloc, which for Teensy 3 seems to come from libc (you really need access to the internal data structure of the allocator, since free() doesn't tell you the amount of deallocated memory).
    I looked into the various files relating to malloc() for the teensy, and I came accross the following file; *arduinodir*/hardware/tools/arm-none-eabi/arm-none-eabi/include/malloc.h. This file details the structure of mallinfo, which sounds like something we'd be after. After fixing some compile errors about _isatty and _fstat I got the following code, which I believe to accurately return the number of allocated bytes.
    Code:
    #ifdef __cplusplus
    extern "C" {
    #endif
    int _isatty (){
        return 0;
    }
    
    int _fstat (){
        return 1;
    }
    #ifdef __cplusplus
    }
    #endif
    #include "malloc.h"
    
    
    int heapSize(){
        return mallinfo().uordblks;
    }
    But even this number might not be useful, as tni mentioned the fragmentation which can occur. The whole concept of determining how much memory is available turned out to be a more complex and less useful than I initially thought.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •