How to display free ram.

Gibbedy

Well-known member
With my arduino nano I'm using the following function to display free ram.
Code:
int freeRam ()
{
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

I don't know what/how that code works but would like an equivalent for teensy 3.2.
Does anyone know how?
Thanks.
 
This seems to work for me:

Code:
uint32_t FreeMem(){ // 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 (approximately) the free, available ram.
    return stackTop - heapTop;
}
 
sdfat library use this for ARM processor

Code:
extern "C" char* sbrk(int incr);
int SdFatUtil::FreeRam() {
  char top;
  return &top - reinterpret_cast<char*>(sbrk(0));
}

if you have sdfat library, and #include <SdFatUtil.h>
you can just call FreeRam();
 
sdfat library use this for ARM processor

Code:
extern "C" char* sbrk(int incr);
int SdFatUtil::FreeRam() {
  char top;
  return &top - reinterpret_cast<char*>(sbrk(0));
}

if you have sdfat library, and #include <SdFatUtil.h>
you can just call FreeRam();

This is so nice and tight! It doesn't seem to work for Teensy 4, however (I get negative values). Is there any easy mod to get it to work for both T3 and T4?

Chip
 
Try replacing "reinterpret_cast<char*>(sbrk(0))" with "(char *)&_heap_end".

EDIT: oh, wait... using "&top" is completely wrong for Teensy 4.x.

Try this instead:

Code:
extern unsigned long _heap_start;
extern unsigned long _heap_end;
extern char *__brkval;

int freeram() {
  return (char *)&_heap_end - __brkval;
}


void setup() {
  while (!Serial) ;
  Serial.print("freeram = ");
  Serial.println(freeram());
  volatile char *p = (char *)malloc(152000);
  *p = 0;
  Serial.print("freeram = ");
  Serial.println(freeram());
}

void loop() {
}
 
Last edited:
Is there a way to see RAM usage during program execution, in the similar way as repeatedly calling AudioProcessorUsage()? Thanks.
 
With the audio library, you can use AudioMemoryUsage() to see how much of the AudioMemory you reserved is actually in use. Details here:

https://www.pjrc.com/teensy/td_libs_AudioConnection.html

Using the more generic ways discussed on this thread won't work for the audio library. The purpose of using AudioMemory() at the beginning of your program is it takes a portion of Teensy's memory to be constantly allocated for the audio library. So if you use AudioMemory(50) to reserve 50 blocks of audio, to these more generic functions all 50 are always consumed, even if the audio library is only using a portion of them. This is one of the many things the audio library does to minimize the chance of non-audio activity glitching the audio. By keeping all of the audio memory allocated (with respect to the rest of the system) the audio library can manage that memory in a way that gives highly deterministic latency. The downside is all of that memory is dedicated to audio and other libraries can't use any of it, even if the audio library isn't making any use of some portion.

With regard to the non-audio memory, the code in msg #6 will tell you the portion of RAM2 which is still unused by malloc() & C++ new.

teensy41_memory.png


One way Teensy 4.x differs from earlier Teensy and many other microcontrollers is the local variables are in a physically different memory than the heap for malloc(). A more common arrangement is the heap starts right after the zeroed variable and grows upward in the same memory as the local variables which start at the top and grown downward.
 
Ok, I'm asking because I tried the code in #6 expecting to see the free RAM size change when called during execution, but it stays the same value. I think I'm misunderstanding something. I was hoping to see if available RAM runs out during execution.
 
Last edited:
If you run the code from msg #6 and change the number of bytes in the line with malloc(), you should see the reported memory usage change.
 
Am I right in thinking that using __brkval only shows you the "most ever" used heap memory, rather than some estimate of the "current state"? I'm interested in checking for the latter, to ensure (barring fragmentation issues) that a piece of code doesn't have a memory leak. I'm now using:
Code:
extern unsigned long _heap_end;
extern char *__brkval;
uint32_t FreeMem(){ // for Teensy 4.1
  char* p = (char*) malloc(10000); // size should be quite big, to avoid allocating fragment!
  free(p);
  return (char *)&_heap_end - p; // __brkval;
}
The 10000-byte allocation size ensures (in my use case) that p isn't inside the possibly-fragmented start of the heap, giving a misleading idea of the space remaining at the time of the call.

Cheers

Jonathan
 
Last edited:
With the audio library, you can use AudioMemoryUsage() to see how much of the AudioMemory you reserved is actually in use. Details here:

https://www.pjrc.com/teensy/td_libs_AudioConnection.html

Using the more generic ways discussed on this thread won't work for the audio library. The purpose of using AudioMemory() at the beginning of your program is it takes a portion of Teensy's memory to be constantly allocated for the audio library. So if you use AudioMemory(50) to reserve 50 blocks of audio, to these more generic functions all 50 are always consumed, even if the audio library is only using a portion of them. This is one of the many things the audio library does to minimize the chance of non-audio activity glitching the audio. By keeping all of the audio memory allocated (with respect to the rest of the system) the audio library can manage that memory in a way that gives highly deterministic latency. The downside is all of that memory is dedicated to audio and other libraries can't use any of it, even if the audio library isn't making any use of some portion.

With regard to the non-audio memory, the code in msg #6 will tell you the portion of RAM2 which is still unused by malloc() & C++ new.

teensy41_memory.png


One way Teensy 4.x differs from earlier Teensy and many other microcontrollers is the local variables are in a physically different memory than the heap for malloc(). A more common arrangement is the heap starts right after the zeroed variable and grows upward in the same memory as the local variables which start at the top and grown downward.

Can someone confirm that when you use AudioMemory you are essentially reserving a section of RAM2 for sole use by the audio library? So, for example, if I set AudioMemory(64) I'd be reserving 64 blocks, 16384 bytes in RAM2?
 
I don't know about audio, but you can find out where things live by comparing with the linker load map (tl062_t41.ld):

Code:
	ITCM (rwx):  ORIGIN = 0x00000000, LENGTH = 512K
	DTCM (rwx):  ORIGIN = 0x20000000, LENGTH = 512K
	RAM (rwx):   ORIGIN = 0x20200000, LENGTH = 512K
	FLASH (rwx): ORIGIN = 0x60000000, LENGTH = 7936K
	ERAM (rwx):  ORIGIN = 0x70000000, LENGTH = 16384K

(RAM is RAM2 in the image, ERAM is PSRAM in the image)
So if you have a pointer to something, print it out in hex and compare.
 
Seems as a macro the data will be a STATIC allocation, and then code to pass the resulting data pointer to the Audio system?

It does indeed do the alloc from DMAMEM/RAM2: {local install}\hardware\teensy\avr\cores\teensy4\AudioStream.h
Code:
#define AudioMemory(num) ({ \
	static DMAMEM audio_block_t data[num]; \
	AudioStream::initialize_memory(data, num); \
})

And that will alloc 260 bytes per usable block?
Code:
#define AUDIO_BLOCK_SAMPLES  128
...
typedef struct audio_block_struct {
	uint8_t  ref_count;
	uint8_t  reserved1;
	uint16_t memory_pool_index;
	int16_t  data[AUDIO_BLOCK_SAMPLES];
} audio_block_t;
 
Here's some code to get to grips with memory layout and use (for the Teensy 4.1):

Code:
void memInfo () {
    constexpr auto RAM_BASE   = 0x2020'0000;
    constexpr auto RAM_SIZE   = 512 << 10;
    constexpr auto FLASH_BASE = 0x6000'0000;
    constexpr auto FLASH_SIZE = 8 << 20;

    extern char _stext[],_etext[],_sbss[],_ebss[],_sdata[],_edata[],_estack[],
            _heap_start[],_heap_end[],_extram_start[],_extram_end[],*__brkval;

    char* sp = __builtin_frame_address(0);

    Serial.printf("_stext        %08x\n",      _stext);
    Serial.printf("_etext        %08x +%db\n", _etext, _etext-_stext);
    Serial.printf("_sdata        %08x\n",      _sdata);
    Serial.printf("_edata        %08x +%db\n", _edata, _edata-_sdata);
    Serial.printf("_sbss         %08x\n",      _sbss);
    Serial.printf("_ebss         %08x +%db\n", _ebss, _ebss-_sbss);
    Serial.printf("curr stack    %08x +%db\n", sp, sp-_ebss);
    Serial.printf("_estack       %08x +%db\n", _estack, _estack-sp);
    Serial.printf("_heap_start   %08x\n",      _heap_start);
    Serial.printf("__brkval      %08x +%db\n", __brkval, __brkval-_heap_start);
    Serial.printf("_heap_end     %08x +%db\n", _heap_end, _heap_end-__brkval);
    Serial.printf("_extram_start %08x\n",      _extram_start);
    Serial.printf("_extram_end   %08x +%db\n", _extram_end,
                                                _extram_end-_extram_start);
    Serial.printf("\n");

    Serial.printf("<ITCM>  %08x .. %08x\n",
            _stext, _stext + ((int) _itcm_block_count << 15) - 1);
    Serial.printf("<DTCM>  %08x .. %08x\n",
            _sdata, _estack - 1);
    Serial.printf("<RAM>   %08x .. %08x\n",
            RAM_BASE, RAM_BASE + RAM_SIZE - 1);
    Serial.printf("<FLASH> %08x .. %08x\n",
            FLASH_BASE, FLASH_BASE + FLASH_SIZE - 1);
    Serial.printf("<PSRAM> %08x .. %08x\n",
            _extram_start, _extram_start + (external_psram_size<<20) - 1);
    Serial.printf("\n");

    auto stack = sp-_ebss,
         heap = _heap_end-__brkval,
         psram = _extram_start + (external_psram_size<<20) - _extram_end;
    Serial.printf("avail STACK %8d b %5d kb\n", stack, stack>>10);
    Serial.printf("avail HEAP  %8d b %5d kb\n", heap, heap>>10);
    Serial.printf("avail PSRAM %8d b %5d kb\n", psram, psram>>10);
}

Sample output with PSRAM fitted:

Code:
_stext        00000000
_etext        00006128 +24872b
_sdata        20000000
_edata        2000133c +4924b
_sbss         2000133c
_ebss         200032e0 +8100b
curr stack    20077fd0 +478448b
_estack       20078000 +48b
_heap_start   20203060
__brkval      2021d000 +106400b
_heap_end     20280000 +405504b
_extram_start 70000000
_extram_end   70000000 +0b

<ITCM>  00000000 .. 00007fff
<DTCM>  20000000 .. 20077fff
<RAM>   20200000 .. 2027ffff
<FLASH> 60000000 .. 607fffff
<PSRAM> 70000000 .. 707fffff

avail STACK   478448 b   467 kb
avail HEAP    405504 b   396 kb
avail PSRAM  8388608 b  8192 kb

A bit over the top perhaps, but at least it helped me better understand how things are laid out.
I didn't find an easy way to get at the PSRAM high-water mark in the SMalloc code.
 
Last edited:
Thanks JCW and defragster, starting to understand how and where the memory goes now which is great. I'm suffering random crashes on a Teensy 4.1 so wanting to understand exactly what's where so I can diagnose the issue.
 
@jcw - made sketch below and it hits CrashReport - and the ELF search worked to show line #47 - in RED. And printing stop short of that line?
Running on a T_4.1 with twin PSRAM's.

@Paul: Good work on the CrashReport output message! It found the line and 'addr2line' even in my path.

Code:
Added this line to see the value of _itcm_block_count and it faults on this line:
  [B]Serial.printf("_itcm_block_count %08x\n",      [COLOR="#FF0000"]_itcm_block_count[/COLOR]);[/B]

Code:
T:\TEMP\arduino_build_T4MemInfo.ino>addr2line -e T4MemInfo.ino.elf 0x166
T:\tCode\Memory\T4MemInfo/T4MemInfo.ino:47

Code:
T:\tCode\Memory\T4MemInfo\T4MemInfo.ino Jul 29 2021 12:59:38
CrashReport:
  A problem occurred at (system time) 11:59:45
  Code was executing from address 0x166
  CFSR: 82
	(DACCVIOL) Data Access Violation
	(MMARVALID) Accessed Address: 0x1 (nullptr)
	  Check code at 0x166 - very likely a bug!
	  Run "addr2line -e mysketch.ino.elf 0x166" for filename & line number.
  Temperature inside the chip was 48.33 °C
  Startup CPU clock speed is 600MHz
  Reboot was caused by auto reboot after fault or bad interrupt detected
_stext        00000000
_etext        00006198 +24984b
_sdata        20000000
_edata        20001818 +6168b
_sbss         20001818
_ebss         200042c0 +10920b
curr stack    20077fd0 +474384b
_estack       20078000 +48b
_heap_start   20203060
__brkval      20203060 +0b
_heap_end     20280000 +511904b
_extram_start 70000000
_extram_end   70000000 +0b

[B][U]Fault 1[/U][/B]


Code:
void setup() {
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  while (!Serial);
  Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  if ( CrashReport ) Serial.print ( CrashReport );
  memInfo();
}


void loop() {
  // put your main code here, to run repeatedly:

}

extern unsigned long _itcm_block_count;
extern uint32_t external_psram_size;

void memInfo () {
  constexpr auto RAM_BASE   = 0x20200000;
  constexpr auto RAM_SIZE   = 512 << 10;
  constexpr auto FLASH_BASE = 0x60000000;
  constexpr auto FLASH_SIZE = 8 << 20;

  extern char _stext[], _etext[], _sbss[], _ebss[], _sdata[], _edata[], _estack[],
         _heap_start[], _heap_end[], _extram_start[], _extram_end[], *__brkval;

  char* sp = __builtin_frame_address(0);

  Serial.printf("_stext        %08x\n",      _stext);
  Serial.printf("_etext        %08x +%db\n", _etext, _etext - _stext);
  Serial.printf("_sdata        %08x\n",      _sdata);
  Serial.printf("_edata        %08x +%db\n", _edata, _edata - _sdata);
  Serial.printf("_sbss         %08x\n",      _sbss);
  Serial.printf("_ebss         %08x +%db\n", _ebss, _ebss - _sbss);
  Serial.printf("curr stack    %08x +%db\n", sp, sp - _ebss);
  Serial.printf("_estack       %08x +%db\n", _estack, _estack - sp);
  Serial.printf("_heap_start   %08x\n",      _heap_start);
  Serial.printf("__brkval      %08x +%db\n", __brkval, __brkval - _heap_start);
  Serial.printf("_heap_end     %08x +%db\n", _heap_end, _heap_end - __brkval);
  Serial.printf("_extram_start %08x\n",      _extram_start);
  Serial.printf("_extram_end   %08x +%db\n", _extram_end, _extram_end - _extram_start);
  Serial.printf("\n");

  Serial.printf("Fault 1\n");
  Serial.flush(); delay(10);
[B][COLOR="#FF0000"]  Serial.printf("<ITCM>  %08x .. %08x\n", _stext, _stext + ((int) _itcm_block_count << 15) - 1);[/COLOR][/B] [U]//line 47[/U]
  Serial.printf("Fault 2\n");
  Serial.flush(); delay(10);
  Serial.printf("<DTCM>  %08x .. %08x\n",
                _sdata, _estack - 1);
  Serial.printf("<RAM>   %08x .. %08x\n",
                RAM_BASE, RAM_BASE + RAM_SIZE - 1);
  Serial.printf("<FLASH> %08x .. %08x\n",
                FLASH_BASE, FLASH_BASE + FLASH_SIZE - 1);
  Serial.printf("<PSRAM> %08x .. %08x\n",
                _extram_start, _extram_start + (external_psram_size << 20) - 1);
  Serial.printf("\n");

  auto stack = sp - _ebss,
       heap = _heap_end - __brkval,
       psram = _extram_start + (external_psram_size << 20) - _extram_end;
  Serial.printf("avail STACK %8d b %5d kb\n", stack, stack >> 10);
  Serial.printf("avail HEAP  %8d b %5d kb\n", heap, heap >> 10);
  Serial.printf("avail PSRAM %8d b %5d kb\n", psram, psram >> 10);
}
 
Yeah, my bad - I left out some important definitions. Change the definition to:

Code:
extern char _itcm_block_count [];
.

This is nasty linker stuff. The address is the value, it's not a variable, i.e. it's not a memory location.
Also: "external_psram_size" is a byte, not a long ... I think.

Update - I've placed a better version at https://gist.github.com/jcw/73b2c29fe0172ae8938fb577035d248d.
 
Last edited:
Copied new memInfo () code and it still faulting here ???

Indeed :: extern "C" uint8_t external_psram_size;
 
Great - That works without problem.

Code:
T:\tCode\Memory\T4MemInfo\T4MemInfo.ino Jul 29 2021 14:45:17
_stext        00000000
_etext        00006158 +24920b
_sdata        20000000
_edata        20001800 +6144b
_sbss         20001800
_ebss         200042c0 +10944b
curr stack    20077fd0 +474384b
_estack       20078000 +48b
_heap_start   20203060
__brkval      20203060 +0b
_heap_end     20280000 +511904b
_extram_start 70000000
_extram_end   70000000 +0b

<ITCM>  00000000 .. 00007fff
<DTCM>  20000000 .. 20077fff
<RAM>   20200000 .. 2027ffff
<FLASH> 60000000 .. 607fffff
<PSRAM> 70000000 .. 70ffffff

avail STACK   474384 b   463 kb
avail HEAP    511904 b   499 kb
avail PSRAM 16777216 b 16384 kb
 
This from last edit 6 months back - this works when getFreeITCM() called from the linked setup() to Set a pointer to FREE RAM in the ITCM section - and get the size of that 'orphaned' area.
Code:
[B]uint32_t *ptrFreeITCM;  // Set to Usable ITCM free RAM
uint32_t  sizeofFreeITCM; // sizeof free RAM in uint32_t units.[/B]
uint32_t  SizeLeft_etext;
extern unsigned long _stext;
extern unsigned long _etext;
FLASHMEM
void   getFreeITCM() { // end of CODE ITCM, skip full 32 bits
  Serial.println("\n\n++++++++++++++++++++++");
  SizeLeft_etext = (32 * 1024) - (((uint32_t)&_etext - (uint32_t)&_stext) % (32 * 1024));
  sizeofFreeITCM = SizeLeft_etext - 4;
  sizeofFreeITCM /= sizeof(ptrFreeITCM[0]);
  ptrFreeITCM = (uint32_t *) ( (uint32_t)&_stext + (uint32_t)&_etext + 4 );
  printf( "Size of Free ITCM in Bytes = %u\n", sizeofFreeITCM * sizeof(ptrFreeITCM[0]) );
  printf( "Start of Free ITCM = %u [%X] \n", ptrFreeITCM, ptrFreeITCM);
  printf( "End of Free ITCM = %u [%X] \n", ptrFreeITCM + sizeofFreeITCM, ptrFreeITCM + sizeofFreeITCM);
[B]  for ( uint ii = 0; ii < sizeofFreeITCM; ii++) ptrFreeITCM[ii] = 1;
  uint jj = 0;
  for ( uint ii = 0; ii < sizeofFreeITCM; ii++) jj += ptrFreeITCM[ii];
  printf( "ITCM DWORD cnt = %u [#bytes=%u] \n", jj, jj*4);
[/B]}

Check just now it was still writeable - with the added for() loops at the end of getFreeITCM().
 
I've pasted the code at https://gist.github.com/jcw/73b2c29fe0172ae8938fb577035d248d
That way it can be edited/fixed/tweaked without cluttering up this forum with buggy code.

Does anyone happen to have this code still? The link seems to have expired. I'm running into a nasty bug and all I'm able to glean from the build is that I should have a ton of headroom:

teensy_size: Memory Usage on Teensy 4.1:
teensy_size: FLASH: code:95992, data:21764, headers:8192 free for files:8000516
teensy_size: RAM1: variables:18816, code:93064, padding:5240 free for local variables:407168
teensy_size: RAM2: variables:12384 free for malloc/new:511904

All my strings are F()ed up and everything. Grr! A Crash Report would be amazing!
 
Back
Top