Monday, November 19, 2012

Using Xboot with Calunium

Xboot is a bootloader for Atmel ATmega and Xmega microcontrollers and can be used as an alternative to the popular Optiboot. It's not quite as small as optiboot (4KB compared to 2KB) but it does offer one very useful advantage - it has an API which enables flash reprogramming by the application. Compiling the bootloader is mostly straightforward, follow the instructions included with Xboot. There are two additional points to watch. Make sure you copy the desired configuration file from the conf directory to the xboot directory. You may need to set the shell to bash when invoking make; I did this with the following command:

make SHELL=bash calunium_12MHz.conf.mk

The original source is available from Github, my fork contains configuration files for Calunium.

Using xboot with avrdude is easy, just be sure to select the avr109 bootloader protocol. You can also program from the Arduino IDE. You will need an entry in boards.txt like:

cal1284p_xboot_12.name=Calunium 1284P (PCB, 12MHz, XBoot, 115200 baud)
cal1284p_xboot_12.upload.protocol=avr109
cal1284p_xboot_12.upload.maximum_size=126976
cal1284p_xboot_12.upload.speed=115200
cal1284p_xboot_12.bootloader.low_fuses=0xFF
cal1284p_xboot_12.bootloader.high_fuses=0x92
cal1284p_xboot_12.bootloader.extended_fuses=0xFF
cal1284p_xboot_12.bootloader.path=xboot
cal1284p_xboot_12.bootloader.file=calunium_12MHz.hex
cal1284p_xboot_12.bootloader.unlock_bits=0x3F
cal1284p_xboot_12.bootloader.lock_bits=0x0F
cal1284p_xboot_12.build.mcu=atmega1284p
cal1284p_xboot_12.build.f_cpu=12000000L
cal1284p_xboot_12.build.core=arduino
cal1284p_xboot_12.build.variant=pcb

Application firmware updates


A security feature of the Atmel ATmega and Xmega processors is that an application cannot modify the flash program memory, only code executing from within the bootloader program space is permitted to do so. By compiling xboot with ENABLE_API set to yes the bootloader will export functions which can write to a temporary storage area located in the upper half of the user firmware area. The application can then call these bootloader functions to write a new firmware image to this temporary area of flash memory. On reboot xboot will copy the temporary firmware image to its correct location and then execute it. The process includes a cyclic redundancy check to prevent against writing corrupted firmware images. This approach is robust since flash reprogramming does not occur until the MCU has downloaded all of the new firmware. The disadvantage is that only half of the application space is available for the actual application; with the ATmega1284P this may not be a major problem.

I'm using the firmware update API to allow over-the-air updates to my AuroraWatchNet magnetometer and it works perfectly. The new firmware is copied in blocks of 128 bytes inside a message which has been signed with a HMAC-MD5 authentication code. After each second block has been received the 256 bytes are copied to one page in the temporary flash area. I'll probably add an option to perform firmware updates from the microSD card which is included on Calunium v2.

The only important points to note are that the CRC passed to xboot must be the one computed from the firmware image sent (not the one received!), and that xboot calculates the CRC over the entire temporary area. If the new image is smaller than the temporary area you'll need to erase the temporary area before copying so that the remaining space is in a known state. Unprogrammed bytes will be 0xFF. Be sure to calculate the CRC including the effect of these unprogrammed bytes otherwise the CRC xboot calculates will not match and it won't copy the firmware.

Bootloader of choice


I'm really pleased with how well xboot seems to work and I like how easy it is to add new configurations. I've just started using it with the internal RC oscillator running uncalibrated; I had to reduced the baud rate to 38400 where the frequency error is much less (see datasheet). Based on my experience so far this is definitely my recommended bootloader for the ATmega1284P.