Memory
This is a basic overview of the memory model in Mbed OS.
+---------------------+ Last address of RAM
| Scheduler/ISR stack |
+---------------------+
| ^ |
| | |
| |
| Heap cont. |
|---------------------|
| User thread n stack |
|---------------------|
| User thread 2 stack |
|---------------------|
| User thread 1 stack |
|---------------------|
| ^ |
| | |
| |
| Heap |
+---------------------+
| |
| ZI: Global data |
| |
+---------------------+
| ZI: Idle stack |
+---------------------+
| ZI: Timer stack |
+---------------------+
| ZI: Main stack |
+---------------------+
| |
| ZI: Global data |
| |
+---------------------+
| Optional |
| Crash-Data-RAM |
| region |
+---------------------+
| RW: Vector table |
+=====================+ First address of RAM
| | Last address of flash
| |
| Application |
| |
| |
+---------------------+
| |
| Optional bootloader |
| |
+---------------------+
| RO: Vector table |
+---------------------+ First address of flash
There are at least two kinds of memory in the system: flash and RAM.
RAM
Inside RAM, you can distinguish two logical types: static and dynamic memory. Static memory is allocated at compile time and, consequently, does not change size during runtime. Dynamic memory is allocated at runtime. For example, program memory use grows and shrinks as you fork and join threads, and construct and destruct objects. The system uses each of them in different ways:
- Static:
- Vector table (read and write).
- Crash data RAM. (Please see the error handling documentation for more information on this region.)
- Global data.
- Static data.
- Stacks for default threads (main, timer, idle and scheduler/ISR).
- Dynamic:
- Heap (dynamic data).
- Stacks for user threads. Mbed OS dynamically allocates memory on heap for user thread stacks.
Stack checking is turned on for all threads, and the kernel errors if it detects an overflow condition.
Flash
Flash is a read-only memory (ROM) that contains:
- Vector table (read only).
- Application code.
- Application data.
- Optional bootloader.
Static memory optimization
Removing unused modules
For a simple program like Blinky, a program that flashes an LED, typical memory usage is split among the following modules:
+---------------------+-------+-------+-------+
| Module | .text | .data | .bss |
+---------------------+-------+-------+-------+
| Fill | 132 | 4 | 2377 |
| Misc | 28807 | 2216 | 88 |
| features/frameworks | 4236 | 52 | 744 |
| hal/common | 2745 | 4 | 325 |
| hal/targets | 12172 | 12 | 200 |
| rtos/rtos | 119 | 4 | 0 |
| rtos/rtx | 5721 | 20 | 6786 |
| Subtotals | 53932 | 2312 | 10520 |
+---------------------+-------+-------+-------+
The features/frameworks
module includes the Mbed OS test tools, even if you are no longer testing your program. Because of this, you are building one of our test harnesses into every binary. Removing this module saves a significant amount of RAM and flash memory.
Printf
and UART
The linker can also remove other modules that your program does not use. For example, Blinky's main
program doesn't use printf
or UART drivers. However, every Mbed OS module handles traces and assertions by redirecting their error messages to printf
on serial output. This forces the printf
and UART drivers to compile and requires a large amount of flash memory.
To disable error logging to serial output, set the NDEBUG
macro and the following configuration parameter in your program's mbed_app.json
file:
{
"macros": [
"NDEBUG=1"
],
"target_overrides": {
"*": {
"platform.stdio-flush-at-exit": false
}
}
}
Note: Different compilers yield different memory savings.
Embedded targets
You can also take advantage of the fact that these programs only run on embedded targets. When you run a C++ application on a desktop computer, the operating system constructs every global C++ object before calling main
. It also registers a handle to destroy these objects when the program ends. The code the compiler injects has some implications for the application:
- The code that the compiler injects consumes memory.
- It implies dynamic memory allocation and thus requires the binary to include
malloc
, even when the application does not use it.
When you run an application on an embedded device, you don't need handlers to destroy objects when the program exits, because the application will never end. To save more RAM and flash memory, remove destructor registration on application startup and eliminate the code to destruct objects when the operating system calls exit()
at runtime.
Note: Mbed OS 5.2.0+ enables removing destructor registration by default.