The ARM Cortex-M0 is one of the smallest and lowest power microcontrollers in the Cortex-M series. With its limited flash memory and RAM, optimizing code and data size is critical for Cortex-M0 designs. This article provides an overview of techniques to reduce code and RAM usage when developing applications for the Cortex-M0.
Overview of Cortex-M0 Memory
The Cortex-M0 core is designed for microcontrollers with as little as 16KB of flash memory and 4KB of RAM. Typical Cortex-M0 chips have between 32KB and 256KB of flash and 8KB to 64KB of RAM. While this is sufficient for many simple applications, careful optimization is needed for more complex designs.
Code on the Cortex-M0 resides in flash memory. Both code and data, including global variables, live in RAM at runtime. With only 4-64KB of total RAM, it’s easy to run out of memory during development. Paying close attention to RAM usage is key for Cortex-M0 projects.
Techniques for Reducing Code Size
Here are some techniques to reduce code size on the Cortex-M0:
- Use C instead of C++. C++ adds overhead like exceptions and RTTI.
- Avoid floating point math. Use fixed point instead.
- Declare variables at the smallest scope possible.
- Use bitfields instead of integers when possible.
- Avoid function pointers. Use straight line code instead.
- Enable linker optimizations like garbage collection of unused code.
- Split code into multiple modules to allow the linker to eliminate dead code.
- Use C libraries designed for embedded systems instead of the full C standard library.
- Avoid recursive functions and complex control flows.
- Use efficient integer math optimizations provided by the compiler.
- Minimize string usage. Strings take up code space.
Choosing the right compiler and optimizer settings is also important:
- Use -Os compiler option for size optimization.
- Enable link time optimization to optimize across modules.
- Use shorter variable and function names.
- Remove debugging symbols before release builds.
With diligent coding practice and compiler settings, it’s often possible to fit a substantial application into 32KB or less of flash memory.
Techniques for Reducing RAM Usage
To reduce RAM usage on the Cortex-M0:
- Declare variables at the smallest scope possible.
- Use global and static variables sparingly.
- Allocate buffers and work areas dynamically instead of as globals.
- Use unions to overlay variables in memory.
- Use smaller data types like uint8_t instead of int.
- Reduce stack usage by optimizing function calls.
The linker can also help minimize RAM usage by:
- Placing variables into specific memory regions.
- Enabling garbage collection to remove unused variables.
- Using overlay memory regions to load variables as needed.
For strings and buffers, techniques like:
- Storing strings in flash instead of RAM.
- Using fixed size buffers instead of variable length buffers.
Greatly reduce RAM. With careful planning, it’s feasible to develop quite advanced applications in just 8-16KB of total RAM.
Data Structure Choices
Choosing optimal data structures is critical for optimizing Cortex-M0 memory usage. Examples include:
- Using bitfields/bitmasks instead of boolean flags.
- Storing state machines as integer enums rather than pointers.
- Using unions to store variants instead of objects.
- Using look-up tables for computations instead of formulas.
- Storing data in flash using custom file systems or data structures.
- Using statically allocated buffers instead of dynamic allocation.
- Grouping variables into structs/classes to use memory efficiently.
Common libraries like strings and hash tables can consume RAM quickly. Avoid using or optimize them to reduce overhead.
Peripheral Drivers and Libraries
The Cortex-M0 MCUs typically include a number of integrated peripherals like GPIO, timers, ADC, I2C, SPI etc. The drivers for these peripherals should be optimized for minimum size. Techniques include:
- Storing peripheral register definitions in flash instead of RAM.
- Using bit banding to access peripheral registers when supported.
- Minimizing driver overhead by using efficient C code.
- Providing multiple driver implementations optimized for size or speed.
- Optimizing drivers to enable compiler size optimizations.
Similarly, any middleware libraries used should also be optimized for the Cortex-M0 to minimize their overhead.
Summary
The ARM Cortex-M0’s limited memory makes code and data size optimization essential. With diligent use of C language optimizations, efficient data structures, compiler settings, linker optimizations and compact peripheral drivers, it’s possible to develop complex applications with only tens of KB of flash and RAM. Cortex-M0 optimization requires planning from the beginning of the design to minimize overhead.