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:

Smartphone / Tablet / Ultrabook Holder

I spent a bit too much time playing video games the past few weeks, so my “you need to be productive” meter finally tripped, and I spent a few hours making a “smart device” holder to make up for it.

It was actually not nearly as hard as I thought it would be, but there’s a lot of magic in the design work and design software that I will have to absorb before I’m nearly as good as one of my coworkers (to be fair, she’s a MechE):

smartphone tablet ultrabook holder

U-Boot fw_env.config for Riotboard

For all you U-Boot users out there, if you set bootdelay=0 on a Riotboard, you will have problems if you ever want to get back into the U-Boot configuration menu.

To erase or modify the U-Boot environment settings, you need to create an /etc/fw_env.config that the U-Boot userspace tools can use.

tl;dr

This is what the fw_env.config file should look like on a Riotboard (at least, when using a build of Freescale’s General Availability kernels):

# Device        Env. Offset   Env. Size    Flash Sector Size
/dev/mmcblk3    0x60000       0x2000       0x1000

The U-Boot environment is saved to /dev/mmcblk3, which is tricky, because the partition table explicitly defines an unused area at the beginning of the eMMC storage device.

Once you’ve got the configuration file in place, running fw_printenv should show something like:

# fw_printenv
baudrate=115200
boot_a_script=load ${devtype} ${devnum}:${bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_extlinux=sysboot ${devtype} ${devnum}:${bootpart} any ${scriptaddr} ${prefix}extlinux/extlinux.conf
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_targets=mmc0 mmc1 mmc2 usb0 pxe dhcp 
bootcmd=run distro_bootcmd
bootcmd_dhcp=usb start; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi
bootcmd_mmc0=setenv devnum 0; run mmc_boot
bootcmd_mmc1=setenv devnum 1; run mmc_boot
bootcmd_mmc2=setenv devnum 2; run mmc_boot
bootcmd_pxe=usb start; dhcp; if pxe get; then pxe boot; fi
bootcmd_usb0=setenv devnum 0; run usb_boot
bootdelay=0
bootm_size=0x10000000
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
ethact=FEC
ethprime=FEC
fdt_addr_r=0x13000000
fdtfile=imx6dl-riotboard.dtb
kernel_addr_r=0x12000000
loadaddr=0x12000000
mmc_boot=if mmc dev ${devnum}; then setenv devtype mmc; run scan_dev_for_boot_part; fi
pxefile_addr_r=0x13200000
ramdisk_addr_r=0x13300000
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${bootpart} bootfstype; then rune
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${bootpart} ${prefix}extlinux/extlinux.conf; then echo Found ${prefix}extlinux/extlinux.conf; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: cone
scriptaddr=0x13100000
stderr=serial
stdin=serial
stdout=serial
usb_boot=usb start; if usb dev ${devnum}; then setenv devtype usb; run scan_dev_for_boot_part; fi

You can now use the fw_setenv command to reset the bootdelay parameter.

Figuring Out The Right Configuration Parameters

If you’re using the U-Boot riotboard_defconfig, you’ll see:

#define CONFIG_SYS_EXTRA_OPTIONS "IMX_CONFIG=board/boundary/nitrogen6x/nitrogen6s1g.cfg,MX6S,DDR_MB=1024,ENV_IS_IN_MMC"

If you then look at include/configs/embestmx6boards.h, you’ll see the configuration used to store the environment:

/* Environment organization */
#define CONFIG_ENV_SIZE			(8 * 1024)

#if defined(CONFIG_ENV_IS_IN_MMC)
/* RiOTboard */
#define CONFIG_FDTFILE	"imx6dl-riotboard.dtb"
#define CONFIG_SYS_FSL_USDHC_NUM	3
#define CONFIG_SYS_MMC_ENV_DEV		2	/* SDHC4 */
#define CONFIG_ENV_OFFSET		(6 * 64 * 1024)
#define CONFIG_SUPPORT_EMMC_BOOT /* eMMC specific */
#elif defined(CONFIG_ENV_IS_IN_SPI_FLASH)
...
#endif

Kind of a pain in the ass that no one at embest thought it would be useful to document this information.