avr32 hello world

How I flashed an avr32 via DFU using GNU/Linux (debian) for the first time.

DFU is a USB device class, I think "data flash unit" or something. It's what
Atmel chose to put onto most of the avr32 as a factory preset: it is some
bootloader code that makes the chip register as a DFU device on any USB host,
and then that DFU device can be used to flash (reprogram) the avr32, without
the need for "relatively expensive" JTAG hardware.

Here is Atmel's datasheet on the DFU bootloader for my device:
AVR32784: AVR UC3 USB DFU Bootloader

Getting the first "hello world" program to run on my AT32UC3C1512C was quite a
challenge. After building this AVR USB dongle I expected the flashing to be
really easy and fast -- but it wasn't at all. Here is how it worked for me:


Get the avr32 toolchain.

There are links on atmel.com that ask for an email address. But direct links do work, for example: avr32-gnu-toolchain-3.4.2.435-linux.any.x86.tar.gz avr32-gnu-toolchain-3.4.2.435-linux.any.x86_64.tar.gz (I picked x86_64 because I have a 64bit box.) I also need the "Atmel AVR 8-bit and 32-bit Toolchain (3.4.2) 6.1.3.1475 - Header Files". http://www.atmel.com/Images/atmel-headers-6.1.3.1475.zip I extract them to ~/avr32/.

Get dfu-programmer

Get the latest from git clone https://github.com/dfu-programmer/dfu-programmer (builds really fast and easy, instructions in the README file.) If you prefer a release, try to get above 0.6.2 (if available) or at least 0.6.2. http://downloads.sourceforge.net/project/dfu-programmer/dfu-programmer/0.6.2/dfu-programmer-0.6.2.tar.gz

Write a simple program

I'm used to the 8-bit AVRs from ATMEL and expected this to be quite the same. Wrong again. It's not that easy to find out how to actually use a GPIO :) My dongle has a LED on PD27, so I longed for that pin to be driven HIGH: test.c
#include <avr32/io.h> int main(void) { AVR32_GPIO.port[3].gpers = 1 << 27; AVR32_GPIO.port[3].oders = 1 << 27; AVR32_GPIO.port[3].ovrs = 1 << 27; for (;;) {} return 0; }
avr32/io.h looks at the mpart passed to gcc (see Makefile below) and includes the correct definitions to access all the proper memory locations. It came in the atmel-headers zip file. Port D is AVR32_GPIO.port[3] -- 0=A, 1=B, 2=C, 3=go figure, that's what I thought at first, but the datasheet says something seemingly complex: Note that the PA, PB, PC, and PX ports do not necessarily directly correspond to the GPIO ports. To find the corresponding port and pin the following formulas can be used: GPIO port = floor((GPIO number) / 32), example: floor((36)/32) = 1 GPIO pin = GPIO number % 32, example: 36 % 32 = 4 A bit later I realize that there is a column labeled "GPIO" in the pin muxing section. What counts is that number. PD27 lists as GPIO 123. floor(123 / 32) = 3 123 % 32 = 27 So, in fact, with PD27 I end up at exactly the same numbers as I would have assumed from "D" and "27". I did check all pins on the AT32UC3C, and each and every GPIO number matches the schema perfectly. But they say that this is not guaranteed, maybe good to know for other devices. 'gpers' means "GPIO enable register, set only". In the old AVRs, to set a bit I would do: DDRD |= 1 << 7; On avr32, there is a dedicated memory address to do the "|=", so at the registers ending in 's' for "set", I can simply assign a value, and all zeros are ignored. There are others for "clear" and so on. See µC datasheet. (Mine is doc9166.pdf) 'oders' means "Output drive enable register, set". Drive the pin strongly, either to LOW or to HIGH. 'ovrs' means "Output value register, set", and with it I set pin PD27 to HIGH. Finally, the program enters an infinite loop, as in stops doing things.

Compile the program

To compile, I use a GNU Makefile, the avr32-* binaries and the avr headers. So I can't compile yet. First I need to setup things:

Setup environment to use the avr32-* binaries

I added this line to my ~/.bashrc: export PATH="$HOME/avr32/avr32-gnu-toolchain-linux_x86_64/bin:$PATH" After running above command, 'which' should show an avr32-gcc inside my path: $ which avr32-gcc ~/avr32/avr32-gnu-toolchain-linux_x86_64/bin/avr32-gcc

Have a basic Makefile

Makefile
INCLUDE = -I$(HOME)/avr32/atmel-headers-6.1.3.1475 MPART = -mpart=uc3c1512c TARGET = at32uc3c1512 default: test.hex %.elf: %.c trampoline.o avr32-gcc $(INCLUDE) -Wl,-e,_trampoline,-Map,$*.map $(MPART) trampoline.o $*.c -o $*.elf %.o: %.s avr32-gcc -x assembler-with-cpp $(INCLUDE) $(MPART) -c $*.s %.hex: %.elf avr32-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $*.elf $*.hex clean: rm *.hex *.elf *.o *.map 2>/dev/null || true test: dfu-programmer $(TARGET) get bootloader-version .PHONY: %.up %.up: %.hex dfu-programmer $(TARGET) erase dfu-programmer $(TARGET) flash --suppress-bootloader-mem $*.hex dfu-programmer $(TARGET) launch #`reset` for older dfu-programmer # vim: noexpandtab
This Makefile is a compressed result of my struggling for two days to get things right. It needs a lot of explaining.

-mpart and TARGET

avr32-gcc and dfu-programmer need different identifiers for the same device. I have an AT32UC3C1512C. avr32-gcc hence needs -mpart=uc3c1512c and, though a precise fit is not found, dfu-programmer needs at32uc3c1512 as first argument. (It lacks the final 'C' in the name.)

INCLUDE

With -I/path/to/atmel/headers I make avr32-gcc look for and find that avr32/io.h header file at the path I extracted the headers zip file to.

trampoline

The start of the flash holds the bootloader, i.e. the code that connects to my PC as a DFU device and allows me to flash the chip. Luckily, that section is protected and I can't write over it without a JTAG, so I'm safe there. The thing is, any bytes written there will be lost anyway, and obviously my own program has to start after that section. With my device, the at32uc3c1512c, that happens to be at address 0x80002000. 0x80000000 is the start of the flash, and 0x2000 later, that's where the DFU bootloader jumps to start my program, so that's where my program should go. But at first my main() was at 0x8000010c. It could not possibly work. A lot of searching later, I found that I can define a trampoline section in assembler code. It appears this code actually does things (not much, it jumps to the program start), but as those bytes will never get written via my DFU method, I don't care *what* it does. For me, the most important things are that it takes up an address range of 0x2000 and that it comes before the main() function, or, more precisely, the program start. When using a JTAG, it's possible to overwrite that bootloader section (the first 0x2000 addresses), and it apparently makes sense to have code there, then, so that the same .hex can be used for both JTAG and DFU. I haven't really understood the depths of that, though. I guess this trampoline code would aptly trample over the DFU bootloader when programmed via JTAG, but since I don't have a JTAG, who cares anyway... ;) Here is the trampoline code: I got this file from some archive somewhere, and it seems to come from Atmel's demo projects. It included a "conf_isp.h" file, but I just copied the PROGRAM_START_OFFSET definition over from there, that was all that was used from that include. Apparently it is sufficient to say 0x2000 here, and the 0x80000000 comes from telling things to go into the flash section somehow, -ish, kinda. trampoline.s
/* Derived from 'trampoline.S' seen in Atmel example code, hence the following * license information has to remain in here, and regarding 3., I hereby * explicitly do not use said 5 letter name to say that this modified file * works for me: * * * Copyright (c) 2007, Atmel Corporation All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name of ATMEL may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define PROGRAM_START_OFFSET 0x00002000 // This must be linked @ 0x80000000 if it is to be run upon reset. .section .reset, "ax", @progbits .global _trampoline .type _trampoline, @function _trampoline: // Jump to program start. rjmp program_start .org PROGRAM_START_OFFSET program_start: // Jump to the C runtime startup routine. lda.w pc, _stext
To have this trampoline in the resulting hex, it has to be included somehow. An #include didn't work :) So I compile it to an .o and add it in the final compilation (linking) step. compile trampoline.s to trampoline.o: avr32-gcc -x assembler-with-cpp $(INCLUDE) $(MPART) -c $*.s add the trampoline.o to the .elf: avr32-gcc $(INCLUDE) $(MPART) trampoline.o $*.c -o $*.elf But that's not enough yet, I also need to tell the linker to start off with the trampoline instead of main, which is done by avr32-ld --entry=name (or -e name) 'name' here is the symbol of the trampoline start, being _trampoline as seen in the .s file. avr32-gcc -Wl,arg1,arg2 passes arguments on to 'avr32-ld', hence: avr32-gcc $(INCLUDE) -Wl,-e,_trampoline $(MPART) trampoline.o $*.c -o $*.elf

use the Makefile

Things I do: $ make clean removes all .o, .hex, .map, .elf files, so I can be sure all will be rebuilt. $ make or $ make test.hex .hex depends on .elf, so the makefile compiles the trampoline.o, then test.elf, then runs objcopy to convert the elf to a hex. A hex is what can be uploaded. $ make test.elf keeps an elf around, and thus I can see whether main starts after 0x80002000. At first I saw this troublesome output: $ make test.elf $ avr-nm test.elf | grep main 8000010c T main And with the trampoline I see: $ avr-nm test.elf | grep main 80002114 T main (I would have expected 8000210c, but whatever.) $ make test.up %.up is a phony target, it just attempts to upload a .hex file of the same name. It feeds the .hex file to dfu-programmer.

flashing as non-root user

Wait, what, Permission Denied? That's right, on most machines, the USB DFU device is not available to all users. It can be made available by letting your system know about it (recommended). On debian linux, you can use the following script to install a udev rule that makes the AVR32 DFU available to all users. When done, run /etc/init.d/udev restart and re-plug the avr32 device. create-avr32-dfu-rule.sh
#!/bin/sh # make all users able to access AVR32 DFU on USB tmp="$(mktemp)"
cat > "$tmp" <<END # AVR32 DFU programmer (bootloader on shipped devices) ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2feb", GROUP="plugdev", MODE="0660" END sudo cp "$tmp" /etc/udev/rules.d/90-avr32dfu.rules
If you can't get this to work, you may use `sudo make test.up' (not recommended as it implies a security risk on your system).

dfu-programmer

A note on --suppress-bootloader-mem: If I don't have this option, I get an error: $ make test.up [...] dfu-programmer at32uc3c1512 flash test.hex Bootloader and code overlap. Use --suppress-bootloader-mem to ignore make: *** [test.up] Error 1 The --suppress... option (I think) makes it so that the bootloader bytes are not overwritten (or it just writes them anyway and the chip itself ignores the bytes, not sure). So only the bytes from offset 0x2000 on actually end up in the flash on the µC. It's not possible to erase the DFU bootloader like this; without a JTAG around, I was a bit anxious trying that the first time, phew :)

enable/disable DFU on startup

When the device powers up, it either provides a DFU device on USB that lets you send a program to the avr32, or it runs your program immediately. While developing, it makes sense to always launch the DFU on HW restart. But in a finished product, you want your own program to run immediately, of course. In short, on my device, the AT32UC3C1512C, GPIO14 (PA14) decides whether to run the program or DFU, upon a device reset: PA14 lo (GND) = launch DFU PA14 hi (VDDANA) = launch your program If you leave the pin floating, i.e. unconnected, it will randomly start into DFU or your program, depending on the electromagically induced potential on the pin. Physically, this setup makes sense: A 10k to 100k resistor connects PA14 to VDDANA (pull-up), and A push-button connects, when pressed, PA14 to GND. Whenever you push the button while rebooting the device, DFU will launch. Another possibility is to use a jumper or a three-legged switch to connect PA14 either to GND or VDDANA. Why VDDANA? Because PA14 is powered by VDDANA. That's not ideal if you want to keep as much noise out of the ADC as possible -- if you use another pin, you can use a VDDIOn instead. It is possible to change the pin used for this decision by writing the last two words in the flash memory to specific values, as described in the DFU bootloader datasheet. If you don't get the checksums right, it'll be PA14. (Check out dfu-programmer: it has some command line options to write the bootloader config. Careful though, it might be possible to put your device in a state where the bootloader never comes up again.) Note: Even though the AT32UC3C datasheet refers to "PB0" being the indicator to select DFU or your program, it is actually referring to Push Button 0 of that evaluation board mentioned there. When you check the schematics, you will see PB0 is connected to PA14. Also, the default settings for the DFU spell out to PA14. Plus I successfully verified that it is really PA14. That was a bit confusing at first, as there is a pin on the AVR32 called "PB0" as well.

dfu-programmer start vs. reset

This paragraph is a thing of the past. At this moment, the "launch" command is the one to call; "start" and "reset" are gone, and "launch" does what "reset" used to do. You may still read the old grievance in case you have an older version: Whenever I start a program via `dfu-programmer start`, my program is running slower than expected. When the device resets and runs my program immediately, the clock runs at the full expected speed. So, for a while, I was manually resetting the device after every reprogramming, which is quite cumbersome during development. But eventually, I found something: when my program launches after a DFU device has run on the USB port, the AVR32_PM.cpusel register is 0x80, even though the datasheet says its reset state is 0. With 0x80, the CPUSEL.cpudiv bit is 1, resulting in a half-speed CPU clock. So what is this, a register is not in its reset state? Seems that there is still state from the DFU bootloader left in the registers. After a long while, I noticed the 'reset' command in the dfu-programmer manpage. At first that puzzled me -- why reset anyway? Shouldn't the chip simply go back to DFU programmer mode? Turns out that this restarts the device using a watchdog-timer-reset, after which the DFU bootloader does not kick in. It is a sort of lesser reset, less "drastic" than pulling the RESET pin low or repowering the device. So this is a way to start my own program from the DFU bootloader, but first resetting all registers! It fixes all clock slews. If you still have 'dfu-programmer start' in your makefile, replace it with 'dfu-programmer reset', or you will suffer weird behavior, with clocks and possibly other things.

dfu-programmer erase problem

Before a recent reinstallation of my host system, I was seeing odd behavior with dfu-programmer 0.6.2 that I needed to work around. The behavior has since gone away, and the "erase" works as expected. You may still read about the old problem in case you see something similar: The 'erase' command always exited with return code 1 for me. It left the device in a state where any following dfu-programmer command would claim that there is no device. Screenshot of odd behavior
$ sudo -s # dfu-programmer at32uc3c1512 erase # echo $? # <--- FYI 1 # dfu-programmer at32uc3c1512 erase dfu-programmer: no device present. # ignore this # dfu-programmer at32uc3c1512 flash --suppress-bootloader-mem test.hex Validating... 14640 bytes used (2.84%) # dfu-programmer at32uc3c1512 launch
The fix was to call the 'erase' command twice and ignore the return values: Makefile (extract)
%.up: %.hex -dfu-programmer $(TARGET) erase -dfu-programmer $(TARGET) erase dfu-programmer $(TARGET) flash --suppress-bootloader-mem $*.hex dfu-programmer $(TARGET) launch #`reset` for older dfu-programmer

kthx

I'm happy to have found a sane way to program my avr32. I can skip all that gory Java stuff Atmel is distributing for DFU programming (i.e. batchisp and flip). Thank you for your attention. If you have any comments or questions, have found typos or untruths, please contact me immediately! :) ...also take a look at what a basic DFU capable setup looks like in hardware, in pcb making, a report of how I built a simplistic avr32 dongle.
login 2014-07-25 01:14