-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Reducing Contiki OS firmware size
TODO: This file needs many more additions. Feel free to add and edit.
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__
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
...
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.
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.
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.
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.
$ 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