printf() isn’t always your friend

One thing you can say about embedded microprocessors: Running out of stack, heap, and flash memory is actually possible, and a difference of 8 kilobytes can actually make or break your code.

In these cases of memory pressure, you need to wring out all of the most dubious code in your codebase. Usually, this takes the form of removing parts of the C library or other transitively-included libraries that add bulk but are not strictly necessary.

Here’s a an example project that overran the runtime boundaries on a Nordic nRF51 Development Kit. Note that the total flash size is approaching the maximum-available user program storage space. Looking at the linker script, it’s apparent that there’s a total of 148KB available for programs and 20744 bytes of available for RAM:

On the particular chip I’m using, there’s a total of 256KB flash and 32KB of RAM. The first ~16KB are chewed up by a bootloader, the next ~110KB are chewed up by the SoftDevice code. So that leaves around 148KB free for user code. But this may not be a hard rule. The minute I crossed the ~120KB code-size mark, I started getting hard faults at runtime:

When the chip jumps into a situation of hard error, the kit will start flashing its LEDs in a criss-cross repeating pattern, like so:

If you’re watching the serial port, the error string Operator new out of memory may appear.

Remove printf()

Turns out that printf() on an ARM Cortex-M0 adds 8 kilobytes to the program size, which is about 1/16th of the usable flash memory on the chip.

Removing all instances of printf() can help:

Out of Memory

The bigger problem appears to be the fact that the program runs out of memory. Examining the compiler output, it’s clear that the Total RAM memory in use is, in both of the above cases, dangerously close to the total RAM limit stated in the Linker script.

And in fact, in a different program that I’ve been working on, even though the total Flash memory is even smaller than the above, the total RAM memory in use is enough to throw the whole program over the edge:

What else can be thrown out? Stay tuned.

Turning on the verbose compile options, one can see a ton of compiler defines, of which some might be removed to disable certain pieces of code in the underlying architecture: