What resides in the different memory types of a microcontroller?
.text

The .text segment contains the actual code, and is programmed into Flash memory for microcontrollers.  There may be more than one text segment when there are multiple, non-contiguous blocks of Flash memory; e.g. a start vector and interrupt vectors located at the top of memory, and code starting at 0; or separate sections for a bootstrap and main program.

.bss and .data

There are three types of data that can be allocated external to a function or procedure; the first is uninitialized data (historically called .bss, which also includes the 0 initialized data), and the second is initialized (non-bss), or .data. The name "bss" historically comes from "Block Started by Symbol", used in an assembler some 60 years ago.  Both of these areas areas are located in RAM.

As a program is compiled, variables will be allocated to one of these two general areas. During the linking stage, all of the data items will be collected together. All variables which need to be initialized will have a portion of the program memory set aside to hold the initial values, and just before main() is called, the variables will be initialized, typically by a module called crt0. The bss section is initialized to all zeros by the same startup code.

With a few microcontrollers, there are shorter instructions that allow access to the first page (first 256 locations, sometime called page 0) of RAM. The compiler for these processors may reserve a keyword like near to designate variables to be placed there. Similarly, there are also microcontrollers that can only reference certain areas via a pointer register (requiring extra instructions), and such variables are designated far. Finally, some processors can address a section of memory bit by bit and the compiler will have a way to specify that (such as the keyword bit).

So there might be additional segments like .nearbss and .neardata, etc., where these variables are collected.

.rodata

The third type of data external to a function or procedure is like the initialized variables, except it is read-only and cannot be modified by the program. In the C language, these variables are denoted using the const keyword. They are usually stored as part of the program flash memory.  Sometimes they are identified as part of a .rodata (read-only data) segment.  On microcontrollers using the Harvard architecture, the compiler must use special instructions to access these variables.

stack and heap

The stack and heap are both placed in RAM.  Depending on the architecture of the processor, the stack may grow up, or grow down. If it grows up, it will be placed at the bottom of RAM. If it grows down, it will be placed at the end of RAM. The heap will use the remaining RAM not allocated to variables, and grow the opposite direction of the stack.  The maximum size of the stack and heap can usually be specified as linker parameters.

Variables placed on the stack are any variables defined within a function or procedure without the keyword static.  They were once called automatic variables (auto keyword), but that keyword is not needed.  Historically, auto exists because it was part of the B language which preceded C, and there it was needed.  Function parameters are also placed on the stack.

Here is a typical layout for RAM (assuming no special page 0 section):



EEPROM, ROM, and NVRAM

Before Flash memory came along, EEPROM (electrically erasable programmable read-only memory) was used to store the program and const data (.text and .rodata segments).  Now there is just a small amount (e.g. 2KB to 8KB bytes) of EEPROM available, if any at all, and it is typically used for storing configuration data or other small amounts of data that need to be retained over a power-down power up cycle.  These are not declared as variables in the program, but instead are written to using special registers in the microcontroller.  EEPROM may also be implemented in a separate chip and accessed via an SPI or I²C bus.

ROM is essentially the same as Flash, except it is programmed at the factory (not programmable by the user).  It is used only for very high volume devices.

NVRAM (non-volatile RAM) is an alternative to EEPROM, and is usually implemented as an external IC.  Regular RAM may be considered non-volatile if it is battery-backed up; in that case no special access methods are needed.

Although data can be saved to Flash, Flash memory has a limited number of erase/program cycles (1000 to 10,000) so it's not really designed for that.  It also requires blocks of memory to be erased at once, so it's inconvenient to update just a few bytes.  It's intended for code and read-only variables.

EEPROM has much higher limits on erase/program cycles (100,000 to 1,000,000) so it is much better for this purpose.  If there is EEPROM available on the microcontroller and it's large enough, it's where you want to save non-volatile data.  However you will also have to erase in blocks first (typically 4KB) before writing.

If there is no EEPROM or it's too small, then an external chip is needed.  An 32KB EEPROM is only 66¢ and can be erased/written to 1,000,000 times.  An NVRAM with the same number of erase/program operations is much more expensive (x10)  NVRAMs are typically faster for reading than EEPROMs, but slower for writing.  They may be written to one byte at a time, or in blocks.

A better alternative to both of these is FRAM (ferroelectric RAM), which has essentially infinite write cycles (100 trillion) and no write delays.  It's about the same price as NVRAM, around $5 for 32KB.