8

I have got some difficulties understanding the memory management.

Arduino documentation says, it is possible to keep constants like strings or whatever I don't want to change during runtime in program memory. I think of it as embedded somewhere in the code segment, which must be fairly possible inside a von-Neumann-architecture. I want to make use from that to make my UI menu on a LCD possible.

But I'm bewildered by those instructions to just read and print data from program memory:

strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy. 
    Serial.println( buffer );

Why on earth do I have to copy the damn content to RAM before accessing it? And if this is true, what happens to all the code then? Is it also loaded to RAM before execution? How is the code (32kiB) handled then with only 2kiB RAM? Where are those little goblins carrying floppy disks?

And even more interesting: What happens to literal constants like in this expression:

a = 5*(10+7)

are 5, 10 and 7 really copied to RAM before loading them into registers? I just can't believe that.

  • A global variable is loaded into memory, and is never released from it. The code above only copies the data to memory when needed, and releases it when done. Also note that the code above only reads one byte from the string_table array. That array might be 20KB, and would never fit in memory (even temporarily). You can however load just one index using the above method. – Gerben Oct 12 '14 at 18:55
  • @Gerben: This is a real drawback on global variables, I did not take this into account yet. I get some headache now. And the code snippet was just an example from the documentation. I refrained to program sth. myself before having clarification about the concepts. But I've got some insight now. Thanks! – Ariser Oct 12 '14 at 19:07
  • I found the documentation somewhat confusing when I first read it. Try looking at some real life examples too (like e.g. a library). – Gerben Oct 13 '14 at 18:50
10

AVR is a modified Harvard architecture family, so code is stored in flash only, whereas data exists primarily in RAM when being manipulated.

With that in mind, let's address your questions.

Why on earth do I have to copy the damn content to RAM before accessing it?

You don't need to per se, but by default code assumes that data is in RAM unless the code is modified to specifically look in flash for it (such as with strcpy_P()).

And if this is true, what happens to all the code then? Is it also loaded to RAM before execution?

Nope. Harvard architecture. See the Wikipedia page for the full details.

How is the code (32kiB) handled then with only 2kiB RAM?

The preamble generated by the compiler copies the data that should be modifiable/modified into SRAM before running the actual program.

Where are those little goblins carrying floppy disks?

Dunno. But if you happen to see them then there's nothing I can do to help.

... are 5, 10 and 7 really copied to RAM before loading them into registers?

Nah. The compiler evaluates the expression at compile time. Whatever else happens depends on the lines of code around it.

  • Ok, I did not know AVR was harvard. But I'm familiar with that concept. The goblins aside, I think I know when to use those copy functions now. I have to restrict use of PROGMEM to data which is rarely used to save CPU cycles. – Ariser Oct 12 '14 at 18:57
  • Or modify your code to use it directly from flash. – Ignacio Vazquez-Abrams Oct 12 '14 at 19:13
  • But how would this code look like? let's say I have several arrays of uint8_t representing strings I want to put onto a LCD display via SPI. const uint8_t test1[5]= { 0x54, 0x65, 0x73, 0x74, 0x31 }; const uint8_t bla[9]= { 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x62 }; const uint8_t Menu[4]= { 0x3d, 0x65, 0x6e, 0x75}; how do I bring this data to flash and later on into the SPI.transfer() function, which takes one uint8_t per call. – Ariser Oct 12 '14 at 21:37
  • 2
8

This is how Print::print prints from program memory in the Arduino library:

size_t Print::print(const __FlashStringHelper *ifsh)
{
  const char PROGMEM *p = (const char PROGMEM *)ifsh;
  size_t n = 0;
  while (1) {
    unsigned char c = pgm_read_byte(p++);
    if (c == 0) break;
    n += write(c);
  }
  return n;
}

__FlashStringHelper* is an empty class that allows for overloaded functions like print to differentiate a pointer to program memory from one to normal memory, as both are seen as const char* by the compiler (see https://stackoverflow.com/questions/16597437/arduino-f-what-does-it-actually-do)

So you could overload the print function for your LCD display so that it takes a __FlashStringHelper* argument, lets call it LCD::print, and then use lcd.print(F("this is a string in progmem"));' to call it.F()` is a macro that ensures the string is in program memory.

To predefine the string (to be compatible with built-in Arduino print) I have used:

const char firmware_version_s[] PROGMEM = {"1.0.2"};
__FlashStringHelper* firmware_version = (__FlashStringHelper*) firmware_version_s;
...
Serial.println(firmware_version);

I think an alternative would be something like

size_t LCD::print_from_flash(const char *pgms)
{
  const char PROGMEM *p = (const char PROGMEM *) pgms;
  size_t n = 0;
  while (1) {
    unsigned char c = pgm_read_byte(p++);
    if (c == 0) break;
    n += write(c);
  }
  return n;
}

which would avoid the __FlashStringHelper cast.

2

Arduino documentation says, it is possible to keep constants like strings or whatever I don't want to change during runtime in program memory.

All constants are initially in program memory. Where else would they be when the power is off?

I think of it as embedded somewhere in the code segment, which must be fairly possible inside a von-Neumann-architecture.

It's actually Harvard architecture.

Why on earth do I have to copy the damn content to RAM before accessing it?

You don't. In fact there is a hardware instruction (LPM - Load Program Memory) which moves data directly from program memory into a register.

I have an example of this technique in Arduino Uno output to VGA monitor. In that code there is a bitmapped font stored in program memory. It is read from that on-the-fly and copied to the output like this:

  // blit pixel data to screen    
  while (i--)
    UDR0 = pgm_read_byte (linePtr + (* messagePtr++));

A disassembly of those lines shows (in part):

  f1a:  e4 91           lpm r30, Z+
  f1c:  e0 93 c6 00     sts 0x00C6, r30

You can see that a byte of program memory was copied into R30, and then immediately stored into the USART register UDR0. No RAM involved.


However there is a complexity. For normal strings the compiler expects to find data in RAM not PROGMEM. They are different address spaces, and therefore 0x200 in RAM is something different from 0x200 in PROGMEM. Thus the compiler goes to the trouble of copying constants (like strings) into RAM at program startup, so it doesn't have to worry about knowing the difference later on.

How is the code (32kiB) handled then with only 2kiB RAM?

Good question. You won't get away with having more than 2 KB of constant strings, because there won't be room to copy them all.

That's why people who are writing things like menus and other wordy stuff, take extra steps to give the strings the PROGMEM attribute, which disables them being copied into RAM.

But I'm bewildered by those instructions to just read and print data from program memory:

If you add the PROGMEM attribute you have to take steps to let the compiler know that these strings are in a different address space. Making a complete (temporary) copy is one way. Or just print directly from PROGMEM, a byte at a time. An example of that is:

// Print a string from Program Memory directly to save RAM 
void printProgStr (const char * str)
{
  char c;
  if (!str) 
    return;
  while ((c = pgm_read_byte(str++)))
    Serial.print (c);
} // end of printProgStr

If you pass this function a pointer to a string in PROGMEM, it does the "special read" (pgm_read_byte) to pull the data from PROGMEM rather than RAM, and the prints it. Note that this takes one additional clock cycle, per byte.

And even more interesting: What happens to literal constants like in this expression a = 5*(10+7) are 5, 10 and 7 really copied to RAM before loading them into registers? I just can't believe that.

No, because they don't have to be. That would compile into a "load literal into register" instruction. That instruction is already in PROGMEM, so the literal is now dealt with. No need to copy it to RAM and then read it back.


I have a lengthy description of these things on the page Putting constant data into program memory (PROGMEM). That has example code for setting up strings, and arrays of strings, reasonably easily.

It also mentions the F() macro which is an easy way of simply printing from PROGMEM:

Serial.println (F("Hello, world"));

A bit of preprocessor complexity lets that compile into a helper function which pulls the bytes in the string from PROGMEM a byte at a time. No intermediate use of RAM is required.

It is easy enough to use that technique for things other than Serial (eg. your LCD) by deriving the LCD printing from the Print class.

As an example, in one of the LCD libraries I wrote, I did exactly that:

class I2C_graphical_LCD_display : public Print
{
...
    size_t write(uint8_t c);
};

The key point here is to derive from Print, and override the "write" function. Now your overridden function does whatever it needs to output a character. Since it is derived from Print you can now use the F() macro. eg.

lcd.println (F("Hello, world"));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.