Pow ! Memory issue…

Does this story sounds familiar to you:

You write a nice piece of code which compiles like a charm and fails to operate properly. Systematically or randomly, arduino will produce erratic data or commands to external devices and will probably freeze (so does your spinal). Then you take a breath, review each line of code, double check the wirings, wherever applicable, upload the code once again… and it still fails to operate. This is probably the “Pow” syndrome! In other words, you may be facing a memory issue.

This post will guide you trough the memory architecture of an Arduino board.

First of all, what is the memory used for ? It serves 3 purposes:

  • store the application (compiled code) that you upload to the Arduino board with the Arduino IDE
  • store the current state of the application (the values of the variables, pieces of code being executed, etc.). This data will be discarded on reboot.
  • store persistent data which will not be discarded on reboot

Arduino UNO and MEGA boards are fitted with microcontrollers which make room for each of these memory compartments:

For example, an Arduino UNO board  fitted with a ATMEGA 328P has the following specifications:

  • 32 kB of flash memory
  • 2 kB of SRAM memory
  • 1 kB of EEPROM memory

By the way, why do not we use the same type of technology for memory? The reasons are cost, speed and durability.

  • Flash memory: is reasonably fast, can handle up to 1 000 000 write cycles and an unlimited number of read cycles. It is cheap (same technology as in the SD card).
  • SRAM memory: is real fast, can handle an unlimited number of write and read cycles. It is not cheap.
  • EEPROM memory: is rather slow, can handle up to 100 000 write cycles and an unlimited number of read cycles. It is cheap.

Ok, the flash memory is used by the Arduino IDE to store a program, but how do I access the other memories ?

  • The EEPROM memory is available for read and write operations on user request. The microcontroller does not use this memory for itself (e.g. for recording temporary data). To use the EEPROM memory, you can either use the Arduino EEPROM library or the PlainEEPROM library from us. You can access this memory space by specifying the address in the EEPROM you want to read from or write to. Its usage is straightforward, if you provide valid addresses (i.e. ranging from 0 up to 1023 on an Arduino UNO), you are not going into any troubles with this memory. However, care shall be taken when recording data in formats other than bytes.
  • On the other hand, the microcontroller makes a plentiful use of the SRAM for storing executed pieces of code and for storing the values of variables. For example:
uint8_t foo; /* use 1 byte in SRAM */
foo = 4; /* assign the value "4" to the reserved memory space "foo" (in SRAM) */
PlainDSP DSP; /* use the necessary space by a PlainDSP object in the SRAM (16 bytes in fact) */

Each time a variable is created, it is placed in the SRAM.

  • if the variable is global, it is stored at a fixed address in the SRAM memory, this memory space is called the static data. This memory space has a fixed size.
  • If a variable is created inside a function, it is stored in a memory space called the stack in the SRAM. This memory space is called in this way because it contains a set of data and accessing them is only made by accessing the top of the stack. As a stack of plates, you can put a plate onto the stack or remove the plate which is on the top but you can’t pick a random plate in the stack (I am not challenging you!). The stack is a fundamental principle in most of modern languages including C/C++. It is a convenient way for representing the nested function calls. This principle was introduced by… Alan Turing. The essential thing to know if that the stack size may vary during the application execution : it grows when entering in a function and it shrinks when leaving a function. If you want to learn more about the stacks, here is a link to this subject on Wikipedia.

The static data and the stack are two special memory spaces in the SRAM. The data into these spaces are said statically allocated: the compiler knows the size of each variable. On the other hand, the SRAM contains another special memory space, the heap, which contains dynamically allocated data. It is used for the variables which size may vary during the application execution. For example, PlainDSP make an intensive use of this space: a PlainDSP object needs two large vectors of data : the real and the imaginary values used by the FFT algorithm. When initializing the PlainDSP object with the following function:

DSP.SetAcquisitionEngine(_defAdcChannel, _refVoltage,  _samplingFrequency, _samples, _options);

This function internally makes a dynamic allocation of memory thanks to the malloc() function (Memory ALLOCation):

_vData = (float*)malloc(_samples * sizeof(float)); /* Create data vector */
_vImag = (float*)malloc(_samples * sizeof(float)); /* Create imaginary vector */

Here, the malloc() function allocates (i.e. reserves) some contiguous space in the heap (therefore, in the SRAM). This function returns the address where the allocated space begins so that _vData and _vImag refer to memory locations in the heap. Their size has been calculated to contain the desired number of samples, each sample is coded on a floating point value ( 4 bytes).

If you no longer need the PlainDSP object and you want to free some memory for another usage, you can do it with the PlainDSP ReleaseVector() function. This function internally calls the C function free() function which deallocate the previously allocated memory:

void PlainDSP::ReleaseVector(uint8_t dataType) 
/* Delete a vector of data and release memory space */
{
	switch(dataType) {
	case DSP_DAT_TYP_IMAG:
		free(_vImag);
		_vImag = NULL;
		break;
	case DSP_DAT_TYP_REAL:
		free(_vData);
		_vData = NULL;
		break;
	}
}

After the memory has been freed,  the _vImag and _vData pointers are set to NULL. this means that they point to the address “0”. This is a commonly used practice to say that the pointers do not point to any dynamically allocated memory in the heap. In this way, the application can check whether the variable is set or not. Note that the heap size may vary during the application execution: it grows when malloc() is called, it shrinks when free() is called. malloc() and free() are the main functions related to the dynamic allocation in C. You may have seen new and delete functions in pure C++ programs. For some reasons, Arduino is said to be programmed in C++ language and it does use these functions…

The SRAM, as every memory types, is a single-dimension vector of bytes. The static data, the heap and the stack are arranged as follows (in a slightly simplified schema):

memory2

The arrows indicate in which directions the heap and the stack grow. Ah! here we are, here comes the trouble… What if the heap and the stack overlap ? Pow ! The data may be corrupted (e.g. writing to the stack overwrites data in the heap) or the malloc() may fail, in this case, it allocates nothing and returns NULL.

With the 1.5.6 beta version of the Arduino IDE, you can see how much memory is used in the static data memory space. Regardless of the dynamically allocated memory, three outputs are possible:

1) All is fine:

compil1

2) Be careful, errors may occur:

compil2

3) The application needs too much memory, it can not work:

compil3

This gives you an indication of the available memory space for the stack and heap. The compiler cannot estimate the memory space used by the heap and the stack, it is the developer’s responsibility to make sure that the dynamically allocated memory will not overlap the stack. Keeping the example of the PlainDSP library,  the number of samples must be carefully chosen: for each sample, 8 bytes in SRAM are allocated (two vectors of 4-bytes floating point numbers). Using 128 samples takes 1kB in SRAM, it is acceptable with a moderate amount of global variables. Using 256 samples would take 2kB in SRAM, so as to say, all the SRAM for an Arduino UNO, this will fatally lead to memory issues.

The link to the Arduino guide in the last screenshot gives wise tips on reducing the memory footprint in the flash memory. In order to reduce the memory footprint in the SRAM, you could also put global variables in the EEPROM, and in this way save up to 1KB of precious SRAM. Arduinoos contains some posts (here is the first one) about the EEPROM usage. Note that the Arduino UNO board contains a built-in EEPROM but you can also use external EEPROMs.

 

One thought on “Pow ! Memory issue…

Leave a Reply