Feature-rich small AVR bootloader using urprotocol
- Tight code: most bootloaders fit into
- 256 bytes albeit normally without EEPROM read/write support
- 384 bytes with EEPROM read/write support
- 512 bytes with dual-boot, EEPROM read/write support and Chip Erase capability
- Highly modular feature set:
- Dedicated protocol between programmer and bootloader (urprotocol)
- EEPROM read/write support
- Vector bootloader for devices without a boot section or to save space on devices with one
- Software UART: no hardware UART required, but also useful when the chip's CPU frequency and the bootloader's desired baud rate are incompatible (eg, 8 MHz and 115,200 baud)
- Subroutine
pgm_write_page(sram, progmem)
for applications so they can change themselves: on many MCUs the SPM write flash only works when called from the bootloader section (default on) - Dual programming from external SPI flash memory for over-the-air programming
- Template bootloader with nops that can be replaced at bootloader-burning time on the fly with code to operate the right LED/TX/RX/CS pins (see accompanying urloader sketch once published)
- Saves the reset flags in R2 for inspection by the application via the .init0 section (default on)
- Bootloader protects itself from overwriting (default on)
- Automatic host baud rate detection
- Chip erase in bootloader (faster than -c urclock emulation)
- Update flash feature that tries to wear out flash less (and is faster)
- Avrdude (from v7.1 onwards) supports urboot bootloaders with
-c urclock
Urboot bootloaders can be hundreds of bytes shorter than optiboot with the same functionality, see below.
Background. More information on how bootloaders work with tips, tricks and limitations here.
Compiling an urboot bootloader. The small sizes of urboot
bootloaders depend on many a trick
of the trade, most of which only work with the avr-gcc toolchain used for compilation. It turns out
that two older avr-toolchains, version 4.8.1 and 5.4.0, produce pretty tight code. Linux binaries of
these are located in the src/avr-toolchain
directory. This project uses helper programs in perl:
an avr-gcc wrapper urboot-gcc
and hexls
that shows stats of the produced .hex
files. So, all
things considered, compiling this project is easiest on Linux with perl installed (and some
required perl libraries, too; install them with cpan
). All going well make all
should produce
.hex
, .elf
and .lst
files of a number of bootloaders. The created .hex files should coincide
with the corresponding ones in the directory
src/all
. Once all these hurdles are
taken, it is easy to create own bootloaders with commands such as
$ make MCU=atmega328p AUTOBAUD=1 VBL=1 AUTOFRILLS=5..10 NAME=atmega328p_a
Alternatively, the Dockerfile may be built and used to build binaries on other systems:
$ docker run --platform linux/amd64 -v "$(pwd)/src":/src --rm -it $(docker build -q .) \
MCU=atmega328p AUTOBAUD=1 VBL=1 AUTOFRILLS=5..10 NAME=atmega328p_a
More detailed information here: make
options
Pre-compiled bootloaders. If compiling bootloaders is not feasible, feel free to try out one of
the 1,283,806 different pre-compiled bootloaders in the directory tree for MCU parts
. You can find bootloaders for
popular boards in a corresponding directory for
boards, for example, autobaud
bootloaders for the venerable ATmega328P based
Uno
with a LED on PB5, the Arduino Pro
Mini
(same bootloaders), the
Jeenode
with a low-active LED on PB1, the ATmega1284P based Moteino
Mega
and the ATmega2560 Mega R3
board;
or, eg, some 16 MHz and 115,200 baud bootloaders for the ATtiny167 based Digispark
Pro,
the ATtiny85 based
Disgispark
and the ATtiny84 based
Luminet.
Finally, there are pre-compiled urboot bootloaders for popular Arduino cores in the
cores directory of the
urboot.hex repository.
How to select a bootloader. Tips, tricks and some background here.
Usage. As with all bootloaders, one needs to first program them onto the board/chip using a (physical) programmer and an uploader program, eg, through
avrdude -c usbasp -p m328p -U flash:w:bootloader.hex:i
If the part does not have hardware support for bootloaders (eg,
ATtiny167
), then this is all that is needed. However, if your part has
hardware support for bootloaders (eg, ATmega328p
), then particular
attention is needed as to whether the bootloader
- Is a vector bootloader (
j
), in which case the fuses need to be programmed so that on reset the MCU jumps to the reset vector at address 0. Ensure the lock bits do not preclude writing to any of the boot sections (otherwise the space freed up by vector bootloaders cannot be used). - Assumes hardware support (
h
) and sits in a dedicated HW boot section, in which case the fuses need to be set to ensure that on reset the MCU jumps to the correct bootloader start; the boot section size fuse bits need to match the actual bootloader size (consult the data sheet of the part); the lock bits should protect the boot section from being written to.
Bootloader | FUSE_BOOTRST |
FUSE_BOOTSZ |
Lock bits for boot section |
---|---|---|---|
j |
Reset to memory start | Don't care | Read/write to everywhere |
h |
Reset to boot section | Match with usage in words | Protect boot section |
Strictly speaking vector bootloaders also need a jmp
or rjmp
from the reset vector at address 0
to the bootloader. However, if the chip was erased before putting the bootloader on, then this is
not necessary: Erased words read 0xffff
, and although not an official opcode, behave as sbrs r31,7
(skip one instruction if bit 7 in R31 is set). A reset to address 0 on an otherwise erased
flash will therefore eventually run into the start of the bootloader. Uploading the first
application with avrdude -c urclock
will set the reset vector correctly to jump to the
bootloader.
Some parts also need the SELFPRGEN
fuse set allowing the vital spm
opcode (store to program
memory) to be executed without which bootloaders cannot write to flash.
Once the board has its bootloader the board can be directly connected to and programmed from the host/laptop/PC (without a physical programmer), eg, through
$ avrdude -c urclock -p m328p -b 115200 -P /dev/ttyUSB0 -U flash:w:mysketch.hex:i
$ avrdude -c urclock -p m328p -b 115200 -P /dev/ttyUSB0 -U eeprom:r:backup.eep:I
$ avrdude -c urclock -p m328p -b 115200 -P /dev/ttyUSB0 -t
Voila!
Fun facts. avrdude -c urclock
can keep bootloaders in terminal mode -t
without the
bootloader resetting itself through the watchdog timer. avrdude -c urclock
keeps metadata of the
uploaded program, which are displayed with avrdude -c urclock -xshowall
. Urboot bootloaders tell
avrdude exactly which part they were compiled for; hence, -c urclock
is the only programmer
for which the part does not need specifying via -p
.
Trouble shooting. So, you have selected the right bootloader for your board and followed above but programming does not work as expected?
- Carefully double check all fuse settings for your part; for example, if the bootloader requires
hardware support then the bootloader size as given by command line tools or
hexls
will be in bytes but the data sheets often specifyBOOTSZ
in words (note the factor of 2). - Check the lock settings for your part; for example, if the bootloader is a vector bootloader then the lock bits should enable to write to everywhere, even the hardware bootloader section. Urboot bootloaders are sometimes smaller than the smallest hardware boot sections and they protect themselves from being overwritten; if the hardware lock for a large hardware boot section is enabled then this space cannot be used for applications.
- Check the logical connections. If using a separate USB/serial converter check whether the
correct I/O pins are hooked up: The
RX
pin that the bootloader was compiled with (typically, theRX
pin of the processor's USART) needs to be connected to theTX
pin (not theRX
pin) of the USB/serial converter chip and vice versa. If the board has a USB/serial converter, check to which processor pins it is connected and verify these are the RX/TX pins that the bootloader was compiled with. - Check the electrical connections. Some USB/serial converters require a hardware pullup on the
TX
side of the board. Some boards run at a different voltage than 5V USB; they may need their own power supply (in which case connect GND, but do not connect the different Vcc); they may need voltage level shifters. - Check the mechanical connections. Do the contacts fit tight?
- Reset the board manually. Some boards have a hardware mechanism that allows avrdude to reset the
board by plucking the RTS or the DTR line for a short while. Avrdude does both before starting
communication, but if the board lacks the hardware for that, it needs to be reset manually
before programming: First press the reset button of the board and release it; then start
avrdude by pressing enter of the previously typed command line within the
WDTO
time (typically one second). - Ensure neither the actual F_CPU nor the used baud rate deviates by more than 1%
- Does the bootloader match the actual F_CPU frequency? Check the crystal on the board and/or
the board documentation. Has the
CKDIV8
fuse been programmed? Then the MCU runs slower by a factor of 8. - Does the
avrdude
baud rate match the bootloader's? Try an eighth of the nominal bootloader baud rate in caseCKDIV8
is programmed. Autobaud bootloaders only recognise 256 different baud rates: F_CPU/8n where n is 1..256; is the used baud rate close to an auto-detectable one? - Measure the F_CPU of the board, eg, by measuring the blink frequency of the
blink.hex
sketch (uploaded with a physical programmer)
- Does the bootloader match the actual F_CPU frequency? Check the crystal on the board and/or
the board documentation. Has the
- Swap out the USB/serial cable if there is no USB/serial chip on board
Comparison. The table below lists a sample of urboot
bootloaders and their features alongside
vanilla optiboot and its larger bigboot variant for EEPROM access. They are all for a 16 MHz MCU
and 115,200 baud serial communication speed, except for the autobaud ones, which should work
for all F_CPU
and a range of associated baudrates.
Size | Usage | Version | Features | Hex file |
---|---|---|---|---|
190 | 192 | u8.0 | ----jpr-- |
attiny2313_min.hex |
248 | 256 | u8.0 | w---jPr-- |
urboot_m328p_1s_x16m0_115k2_uart0_rxd0_txd1_led+b5_pr.hex |
256 | 256 | u8.0 | we--jpr-- |
attiny2313_emin.hex |
384 | 384 | u8.0 | weU-jPrac |
urboot_m328p_1s_autobaud_uart0_rxd0_txd1_led+b5_pr_ee_ce.hex |
454 | 512 | u7.7 | wes-hprac |
urboot_m328p_1s_autobaud_uart0_rxd0_txd1_led+b5_ee_ce_hw_stk500.hex |
474 | 512 | o8.3 | --s-h-r-- |
optiboot_atmega328.hex from the optiboot project |
504 | 512 | u8.0 | weUdhprac |
urboot_m328p_1s_autobaud_uart0_rxd0_txd1_led+d5_csb0_dual_ee_ce_hw.hex |
512 | 512 | u8.0 | weUdhprac |
atmega328p_ad.hex |
710 | 1024 | o8.3 | -es-h-r-- |
bigboot_328.hex from the optiboot project |
- Size: Bootloader code size including small table at top end
- Usage: How many bytes of flash are needed, ie, HW boot section or a multiple of the page size
- Version: For example, u7.6 is an urboot version, o5.2 is an optiboot version
- Features:
w
bootloader providespgm_write_page(sram, flash)
for the application atFLASHEND-4+1
e
EEPROM read/write supportU
checks whether flash pages need writing before doing sos
uses skeleton of STK500v1 protocol (only u7.7);-c urclock
and-c arduino
both OKd
dual boot (over-the-air programming from external SPI flash)h
hardware boot section: make sure fuses are set for reset to jump to boot sectionj
vector bootloader: applications need to be patched externally, eg, usingavrdude -c urclock
p
bootloader protects itself from being overwrittenP
vector bootloader only: protects itself and reset vector from being overwrittenr
preserves reset flags for the application in the register R2a
autobaud detection (f_cpu/8n using discrete divisors, n = 1, 2, ..., 256)c
bootloader provides chip erase functionality (only recommended for large MCUs)-
corresponding feature not present
The naming convention for the .hex
files is given in the larger comparison table covering a range of MCUs.