Skip to content

Reducing Contiki OS firmware size

Benoît Thébaudeau edited this page Sep 23, 2019 · 10 revisions

TODO: This file needs many more additions. Feel free to add and edit.

Size optimizations in gcc

Optimised Makefile

Usually -Os is specified as a compiler switch on each module; this tells it to do size optimization, such as discarding unused functions, putting subroutines inline if they are called once, reusing common code such as exit routines, etc. Because of this mixing of routines, the linker is forced to include the entire module if any part of it is called.

This behaviour can be altered by using the -ffunction-sections switch to tell the compiler not to mix functions that could be referenced externally, along with the --gc-sections switch telling the linker to do garbage collection and discard unreferenced routines. This can save considerable space in modules containing routines that are not used in the current build, but may also result in worse size optimization. Note, the compiler can always optimize functions with the static attribute; when using --ffunction-sections, so make sure it is added wherever possible.

Add the following to your Makefile:

 CFLAGS += -ffunction-sections
 LDFLAGS += -Wl,--gc-sections,--undefined=_reset_vector__,--undefined=InterruptVectors,--undefined=_copy_data_init__,--undefined=_clear_bss_init__,--undefined=_end_of_init__

Optimised variable allocation

During startup gcc initializes variables located in RAM. Variables that are not declared with initial values (int foo;) are gathered into a section called .bss which is filled with zeros using a short loop. Initialized variables (int foo=1;) are stored in a .data section for copying to RAM; typically that storage reduces the amount of program flash memory available for code. Transferring variables to .bss and doing your own initialization can reduce the size, e.g. filling arrays with a constant value.

The objdump tool will show the size of the initialized RAM in the .data section, and the zeroed RAM variables in the .bss section. Toolchains may have their own tool, e.g. avr-objdump. Do these variables need 24 bytes of initialization? The MEMB() macro is convenient but if you absolutely need those bytes the static pointers could be used directly (also saving 24 bytes of RAM)

 $ objdump -t --section=.data udp-client.micaz
 00800100 l    d  .data  00000000 .data
 008001d0 l     O .data  00000008 packet_memb
 008001d8 l     O .data  00000008 metadata_memb
 008001c8 l     O .data  00000008 neighbor_memb
 ...

In the next example you can see that the variable s takes 34 bytes, does it really need to be static? Examine the map file to see where it is used. PROTIP: Searching for 1-letter variables is hard, os you should give longer names to your static variables!

 $ avr-objdump -t --section=.bss udp-client.micaz
 ...
 008002ce l     O .bss   00000002 outputfunc
 008002a3 l     O .bss   00000001 i.3761
 008002ac l     O .bss   00000022 s
 008002a4 l     O .bss   00000008 periodic
 ...

Reducing the Size of Contiki

There are many features within Contiki that can easily be excluded or reduced in size by setting the correct flags in the project-conf.h file that you should have in your application directory.

Processes headers contain pointers to ASCII strings that are used only for making out more user-friendly. You can use #define PROCESS_CONF_NO_PROCESS_NAMES 1 to disable the name strings from being stored, reducing both flash and RAM usage by the combined length of the strings and pointers, typical around 100 bytes.

Reducing uIP/IPv6 Stack Size

TCP and UDP can be separately enabled or disabled with #define UIP_CONF_TCP and #define UIP_CONF_UDP set to 0 or 1. Disabling TCP eliminates a MSS-sized buffer. Note, the TCP process needed for RPL-ROLL. Meshing works whether or not TCP is enabled.

Changing the radio duty cycling protocol

The RDC protocols require additional program and RAM to store neighbor information. Reduce the sequence number arrray size used for duplicate packet detection. Turn off phase optimization in contikimac to eliminate the neighbor wake time table. Use #define NETSTACK_CONF_RDC nullrdc_driver for the smallest size.

Excluding the uIP/IPv6 Stack

You can set CONTIKI_NO_NET=1 in the Makefile and exclude all the networking code. You can then add individual files or routines (e.g. checksum calculations) as needed. See Makefile.ravenusbstick and fakeuip.c for an example; by default the usb stick is just a network bridge with no uIP stack of its own, although the stack can be enabled to support an internal webserver.

Useful commands

$ size webserver6.elf
   text    data     bss     dec     hex filename
  58493     545   11314   70352   112d0 webserver6.elf

data is preinitialized RAM, bss is prezeroed RAM. You probably have an msp430-objdump tool but a vanilla objdump will do the job if you don't require disassembly

$ objdump -h webserver6.elf

webserver6.elf:     file format elf32-little

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .data         000001e8  00800100  0000e47a  0000e56e  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  1 .text         0000e47a  00000000  00000000  000000f4  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .bss          00002c32  008002e8  008002e8  0000e756  2**0
                  ALLOC
  3 .eeprom       00000036  00810000  00810000  0000e756  2**0
                  CONTENTS, ALLOC, LOAD, DATA
...

To drill down and see which variables are taking ram:

$ objdump -t --section=.data webserver6.elf
00800100 l    d  .data  00000000 .data
00800122 l     O .data  00000008 conns
0080012d l     O .data  00000006 file
00800133 l     O .data  00000006 tcp
...
$ objdump -t --section=.bss webserver6.elf
008002e8 l    d  .bss   00000000 .bss
008002e8 l     O .bss   00000002 filelength.3481
008002ec l     O .bss   00000015 scriptname.3479
Clone this wiki locally