Saturday, June 28, 2014

Atmel AVR Toolchain 3.4.4

Since I started developing on AVR MCUs, I've used avr-gcc, which includes avr-libc.  A few months ago I found out Atmel maintains a version of avr-libc separate from the Savannah project.  It's part of the Atmel AVR Toolchain.  I've wanted install avr-gcc 4.8 on my CentOS server, however I could only find rpms for 4.7.  The Atmel toolchain version 3.4.4 includes avr-gcc 4.8.1, so I decided to try it out.

To download the binary package I had to out an annoying form, before I could get access to the direct download link.  The binary is distribution agnostic, and can be installed in a user directory without root access.  After downloading just run tar xzf to extract the files, then update your path.  For bash, I added the following two lines to my .bashrc:
PATH=$PATH:~/avr8-gnu-toolchain-linux_x86/bin
export PATH

The AVR toolchain consists of three main parts:

  1. Compiler (gcc)
  2. GNU binutils (objdump, ld, nm, ...)
  3. avr-libc (including headers for various AVR MCUs)
As of May 2014, when AVR toolchain version 3.4.4 was released, the latest version of the gcc 4.8 branch was 4.8.2.  The toolchain includes binutils 2.24, which is the latest version.  With avr-libc it gets a bit confusing.  The latest avr-libc release on savannah.gnu.org is 1.8.0 from December 2011.  In a discussion on AVRfreaks, I found out Atmel maintains a separate branch of avr-libc, which seems to be based on the latest avr-libc from savannah plus additional fixes and MCU headers.  I checked for avr-libc bug #41519, which I submitted on 2014/02/07 and was fixed on 2014/03/18.  The fix is included in the toolchain 3.4.4.

I've previously blogged about link-time optimization, so I wanted to check the code generated by the AVR toolchain.  I used the following test program:
#include <avr/io.h>
#include <util/delay.h>

void toggle(unsigned char pin)
{
    PINB = (1<<pin);
}

#define LEDPIN 1
void main()
{
  toggle(LEDPIN);
  while (1) {
    toggle(LEDPIN);
    _delay_ms(500);
  }
}

I compiled it with -Os and -flto.  As expected, the toggle function was inlined in main() when I looked at the code using avr-objdump -D:
0000004c <main>:
  4c:   82 e0           ldi     r24, 0x02       ; 2
  4e:   83 b9           out     0x03, r24       ; 3
  50:   83 b9           out     0x03, r24       ; 3
  52:   2f ef           ldi     r18, 0xFF       ; 255
  54:   34 e3           ldi     r19, 0x34       ; 52
  56:   9c e0           ldi     r25, 0x0C       ; 12
  58:   21 50           subi    r18, 0x01       ; 1
  5a:   30 40           sbci    r19, 0x00       ; 0
  5c:   90 40           sbci    r25, 0x00       ; 0
  5e:   e1 f7           brne    .-8             ; 0x58 <main+0xc>
  60:   00 c0           rjmp    .+0             ; 0x62 <main+0x16>
  62:   00 00           nop
  64:   f5 cf           rjmp    .-22            ; 0x50 <main+0x4>

However the toggle function was still included in the binary.  After trying different compiler options, I found that adding -fwhole-program will not included the unused toggle function.  When testing a gcc-4.8.0 MinGW, I didn't have to use the -fwhole-program option.  As well, the gcc docs state, "This option should not be used in combination with -flto. Instead relying on a linker plugin should provide safer and more precise information."  In the description of -flto it also says, "When the linker plugin is not available, -fwhole-program should be used..."

When I compared the build specs (gcc -dumpspecs) between the AVR toolchain build and the MinGW build, I found the following string in the AVR toolchain: "fuse-linker-plugin:    %e-fuse-linker-plugin is not supported in this configuration"  Although there doesn't seem to be anything wrong with using -fwhole-program instead of the linker plugin, at least for simplicity of build options, the linker plugin is better.

No comments:

Post a Comment