Computing stuff tied to the physical world

ATmega memory use

In AVR, Software on May 22, 2011 at 00:01

Sometimes, it’s useful to find out how much memory a sketch uses.

Sometimes, it’s essential do so, i.e. when you’re reaching the limit. Because strange and totally unpredictable things happen once you run out of memory.

Running out of flash memory for the code is easy to avoid, as the Arduino IDE will tell you exactly how much is being used after each compile / upload:

Running out of EEPROM memory is harder, but usually not an issue, since very few sketches use substantial amounts of EEPROM, if any.

Running out of RAM space is the nasty one. Because it can happen at any time, not necessarily at startup, and not even predictably because interrupt routines can trigger the problem.

There are three areas in RAM:

  • static data, i.e. global variables and arrays … and strings !
  • the “heap”, which gets used if you call malloc() and free()
  • the “stack”, which is what gets consumed as one function calls another

The heap grows up, and is used in a fairly unpredictable manner. If you release areas, then they will lead to unused gaps in the heap, which get re-used by new calls to malloc() if the requested block fits in those gaps.

At any point in time, there is a highest point in RAM occupied by the heap. This value can be found in a system variable called __brkval.

The stack is located at the end of RAM, and expands and contracts down towards the heap area. Stack space gets allocated and released as needed by functions calling other functions. That’s where local variables get stored.

The trick is to keep RAM usage low, because it’s a scarce resource: an ATmega has a mere 2048 bytes of RAM.

Here’s a small utility function which determines how much RAM is currently unused:

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

And here’s a sketch using that code:

void setup () {
    Serial.begin(57600);
    Serial.println("\n[memCheck]");
    Serial.println(freeRam());
}

void loop () {}

The result will be:

[memCheck]
1846

Ok, so we have about 1.8 Kb free in a tiny sketch which does almost nothing. More precisely: a basic sketch uses 2048 – 1846 = 202 bytes for internal bookkeeping and stuff (128 bytes of which are needed for the hardware serial input buffer, BTW).

When that value drops to 0, your sketch will crash. This might show as an endless loop, strange calculations or output, or constant restarts. Don’t expect a nice error message!

Let’s make a tiny change:

Serial.println("\n[memCheck2]");

Output:

[memCheck2]
1844

Huh?

The first part of the explanation is that all C strings are also stored in RAM! This explains why adding a single character to the string reduced available memory.

The second part of the explanation, is that flash memory is organized in words. Therefore, sometimes when you expect a single-byte effect, this may get rounded up due to the way things are stored in flash memory.

And the third part of the explanation, is that all C strings also get stored in flash memory. The reason is that RAM contents is undefined on power-up, so one of the tasks performed by the C runtime startup code, is to copy all the strings from flash to RAM memory.

Moral of the story: be very careful when doing things with strings on an ATmega, because you may find that you quickly run out of memory space. Adding lots of verbose debugging print statements might cause more problems than you think!

Update – Here’s a great AVR RAM memory overview:

(from the avr-libc website)

  1. Great post JC. Sketches can do some really weird things when you run out of memory.

    Using tricks like PSTR(“My string”) to provide a pointer to the string stored in the sketch Eeprom memory instead of a RAM copy is a great trick you taught me – Tomorrow’s post maybe?

  2. It would be great if you explained the trick you used on the GLCD-Clock sketch here. Loading the string directly from the progmem. Maybe for the day after tomorrow?

    • Actually the GLCD-Clock sketch and PSTR trick are the same thing.

      They address the contents directly from the sketch/program Eeprom storage.

      The pgm_read functions (_byte, _word and _dword) allow you to read the contents of the sketch (or program) Eeprom at run time.

      PSTR is very similar except I think it is more of a compiler directive to not copy the string constant into RAM and just address it direct from the program Eeprom space instead.

  3. At your service: PSTR post coming up!

  4. Great, I quietly wished I had a free-memory function for quite a while now.

  5. Great timing. I actually used this to diagnose (and confirm my suspicions) that my arduino project was running out of ram.

    If you don’t mind, i’ll post this info on my site and cite you as a source.

  6. Future versions of Arduino will support strings in flash memory, and not burning ram using F(). It’ll look like this:

    Serial.println(F(“my string that does not consume any ram”));

Comments are closed.