The Role of ROM, RAM, and Flash in Microcontrollers

Generally, ROM is used to store firmware, while RAM is used to hold data. Since FLASH ROM has faster read and write speeds and is easier to erase compared to ordinary ROM, it is typically used to store user programs and data that need to be permanently saved. For example, the core of a household electronic meter is a microcontroller, and the program for that microcontroller is stored in ROM. During operation, the meter needs to perform calculations, collect voltage and current, and calculate the power consumption based on voltage and current. The voltage and current are real-time data that the user does not care about; they are only used for calculating power consumption. Once the calculation is done, that collected data is no longer needed and is stored in RAM. However, the calculated power consumption needs to be permanently saved, so the microcontroller will periodically or at the moment of power failure save the power consumption data into FLASH. -- ROM stores instruction codes and some fixed values that cannot be modified after the program runs; RAM is used for random access to data during program execution, and data is lost when power is turned off.
Code refers to defining data in the ROM area, which has read-only attributes. For example, some LED display header data can be defined as code and stored in ROM.
ROM: (Read Only Memory) Program storage in microcontrollers used to store program data and constant or variable data. All code in C files and header files, global variables, local variables, constants defined with the 'const' qualifier, and code in startup.asm files (similar to bootloaders in ARM or BIOS in X86, but some low-end microcontrollers do not have this) are all stored in ROM. RAM: (Random Access Memory) Used to store variables used in the program. Any variable that needs to be rewritten during the program’s execution is stored in RAM. "Changed variables" include global variables, local variables, and stack segments.
After compilation, assembly, and linking, a hex file is generated. Using dedicated programming software and a programmer, the hex file is burned into the ROM (how is the hex file transferred to the MCU's internal ROM?). At this point, the ROM contains all program content: whether it is line-by-line program code, local variables used in functions, global variables declared in header files, or read-only constants declared with const, all are generated into binary data contained in the hex file and burned into ROM. The ROM now contains all the program information, which "guides" all actions of the CPU.
Some may wonder, if all data is in ROM, where does the data in RAM come from? When does the CPU load data into RAM? Could it be that during programming, the data that needs to be in RAM has already been burned into RAM?
To answer this question, one must first clarify a point: ROM is read-only memory, the CPU can only read data from it and cannot write data to it; the data remains in the storage after power off. RAM is random access memory, the CPU can read and write data to it, and the data is not preserved after power off. This is an eternal truth that must always be remembered.
With this understanding, it becomes easy to think that the data in RAM is not written during programming because after programming, when the power is removed and then restored, the CPU can still execute actions normally, and RAM still contains data. This indicates that the data in RAM was not written during programming, and it also shows that while the CPU is running, data has already been written into RAM. The key point is this: the data is not manually written; it is written by the CPU. So when does the CPU write it? Let me explain.
As mentioned, ROM contains all program content. When the MCU is powered on, the CPU starts executing instructions from the first line of code. This work prepares for the smooth operation of the entire program, or initializes RAM (note: ROM is read-only). The tasks include:
1. Allocating address space for global variables - if global variables have initial values, the initial values are copied from ROM to RAM; if not, the initial value at the address corresponding to the global variable is either 0 or undefined. Of course, if the variable's address space is already specified, it can be directly located to the corresponding address, and this task of allocating and locating addresses is done by the linker.
2. Setting the length and address of the stack segment - C language developed microcontroller programs generally do not involve setting the length of the stack segment, but that does not mean it is unnecessary. The stack segment is mainly used to "save the state" and "restore the state" during interrupt handling, and its importance is self-evident. Such important content is also included in the compiler's preset content, which indeed saves trouble, but it may not be worry-free. Why haven’t I noticed this before? Strange.
3. Allocating the starting addresses for the data segment, constant segment, and code segment. The addresses for the code and constant segments can be ignored; they are fixed in ROM, and their arrangement does not affect the program. However, the address of the data segment must be considered. The data in the data segment needs to be copied from ROM to RAM, and in RAM, there are both the data segment and the stack segment, as well as a general working register group. Typically, the addresses of the working register group are fixed, which requires that when addressing the data segment absolutely, the data segment does not cover the addresses of all working register groups. This must be taken seriously.
The "first line of code" here does not necessarily refer to the code you wrote; most of it is handled by the compiler or demo program files that come with the compiler. Because your own program (C language program) does not contain this content. For higher-end microcontrollers, this content is all included in the startup file. Reading it carefully is beneficial.
The usual practice is that when a standard flash MCU is powered on or reset, the PC pointer contains "0000", indicating that the CPU starts executing instructions from address 0000 in ROM, where a jump instruction is placed to jump to the _main function. Then, based on different instructions, it executes line by line. When an interrupt occurs (the number of interrupts is also limited, usually 2-5), the system allocates an interrupt vector table address, placing a jump instruction to the interrupt service routine in the interrupt vector. Thus, the entire program runs. The reason the CPU does this is due to the structure of this ROM.
In fact, the C language compiler does a lot of work here; you just don’t know it. If you carefully read the help files that come with the compiler, you will learn many things, which is the best way to understand the compiler.

Leave a Comment