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 with opcodes to set/clear the right LED or CS pins
- 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
hexlswill be in bytes but the data sheets often specifyBOOTSZin 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
RXpin that the bootloader was compiled with (typically, theRXpin of the processor's USART) needs to be connected to theTXpin (not theRXpin) 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
TXside 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
WDTOtime (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
CKDIV8fuse been programmed? Then the MCU runs slower by a factor of 8. - Does the
avrdudebaud rate match the bootloader's? Try an eighth of the nominal bootloader baud rate in caseCKDIV8is 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.hexsketch (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 |
| 510 | 512 | u8.1 | 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:
wbootloader providespgm_write_page(sram, flash)for the application atFLASHEND-4+1eEEPROM read/write supportUchecks whether flash pages need writing before doing sosuses skeleton of STK500v1 protocol (only u7.7);-c urclockand-c arduinoboth OKddual boot (over-the-air programming from external SPI flash)hhardware boot section: make sure fuses are set for reset to jump to boot sectionjvector bootloader: applications need to be patched externally, eg, usingavrdude -c urclockpbootloader protects itself from being overwrittenPvector bootloader only: protects itself and reset vector from being overwrittenrpreserves reset flags for the application in the register R2aautobaud detection (f_cpu/8n using discrete divisors, n = 1, 2, ..., 256)cbootloader 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.