This repository should work out of the box on a typical PC under Linux. Just cd src
and type
make atmega328p_autobaud_ur
to test whether compilation works in principle. If successful, a
bootloader atmega328p_autobaud_ur.hex
should have been generated. The
system needs perl
installed and quite possibly the following perl
modules:
File::Basename
Capture::Tiny
POSIX
Scalar::Util
Getopt::Long
Number::Range
They can normally be installed using cpan install <module>
. On the first run, cpan
may need
to be configured. Follow the recommendations there; my personal choice for the way cpan installs
perl modules is via sudo
, but it should not matter what is chosen.
Urboot bootloaders have many configuration options for avr-gcc. With the notable exception of
-DSTART
and -DRJMPWP
(which are set by the avr-gcc
wrapper urboot-gcc
, see below) almost
all bootloader generation options can be put on the command line for make without the -D
. For
example, issuing the command
$ make MCU=attiny45 F_CPU=123456L SWIO=1 RX=AtmelPB0 TX=AtmelPB1 NAME=myboard
creates a bootloader file myboard.hex
for a board with the MCU ATtiny45 and the CPU frequency
1.23456 MHz using software I/O on the pins PB0
and PB1
for uploading new applications.
Below is a list of options that are currently supported by the urboot project;
unless an option can only be issued to avr-gcc
this help file will leave the leading -D
away.
-
MCU=<mcu>
This selects the AVR part, eg, atmega328p (the default
make
option), for which the bootloader is compiled. The name must be in all lower case. -
F_CPU=<num>L
This option selects the CPU frequency of the destination board in Hertz as a long constant number, eg,
F_CPU=16000000L
for 16 MHz, which is also the defaultmake
option. Unless the optionAUTOBAUD
is selected, the bootloader code needs to know the CPU frequency as the serial communication baud rate is derived from it. -
NAME=<name>
This is a make-only option that determines the base name of the three files that
make
creates:<name>.hex
is an Intel hex file of the bootloader<name>.elf
is the executable and linkable format file<name>.lst
is a crude ASCII listing of an assembler file
In the absence of this option
make
invents its own name that may or may not be agreeable. For backward compatibilityMOVETO=<name>
does the same thing.
-
UARTNUM=<n> UARTALT=<n> UART2X=<0|1> AUTOBAUD=<0|1> RX=...
-
UARTNUM=<n> UARTALT=<n> UART2X=<0|1|2> BAUD_RATE=<baud>
-
SWIO=<0|1> RX=... TX=... BAUD_RATE=<baud>
These three different settings determine how the serial communication is implemented. When the MCU has a hardware UART using that will normally create shorter code, and this is the default. In case the MCU has more than one UART, then
UARTNUM
specifies which one to use (0, 1, ...). Note thatUARTNUM
always counts from 0, irrespective of whether the only UART is actually calledUSART1
or whether the UARTs have a different naming scheme as with the XMEGA UARTs that are named after the port, eg,USARTC0
.UARTALT
, if given, specifies an alternative pin assignment for RX/TX. It is usually a small number that aligns with the available alternative assignments lined out in the data sheet.If
AUTOBAUD
is set then the generated bootloader will try to initialise the UART with the host communication speed that the bootloader augurs from the first byte that is expected from the host at runtime; the code utilises that the lower 5 bit of that byte (0x30
=STK_GET_SYNC
) are0x10
.AUTOBAUD
is a bit of a misnomer, as urboot only offers 256 different baud rates. Moreover, these must be compatible withF_CPU
, namelyF_CPU/8
,F_CPU/16
,F_CPU/24
, ...,F_CPU/2048
. The set of compatible baud rates comes with a narrow tolerance band of +/-1.5%. As a consequence, a 16 MHz MCU can only safely communicate in the following 33 baud rate intervals (the last one is the union of 224 overlapping individual baud rate intervals):[1970000, 2030000] = [0.985*F_CPU/(8*1), 1.015*F_CPU/(8*1)], F_CPU = 16 MHz [ 985000, 1015000] = [0.985*F_CPU/(8*2), 1.015*F_CPU/(8*2)] [ 656667, 676667] [ 492500, 507500] [ 394000, 406000] [ 328333, 338333] [ 281429, 290000] [ 246250, 253750] [ 218889, 225556] [ 197000, 203000] [ 179091, 184545] [ 164167, 169167] [ 151538, 156154] [ 140714, 145000] [ 131333, 135333] [ 123125, 126875] [ 115882, 119412] <-- 115200 baud is out of specs for a 16 MHz MCU [ 109444, 112778] [ 103684, 106842] [ 98500, 101500] [ 93810, 96667] [ 89545, 92273] [ 85652, 88261] [ 82083, 84583] [ 78800, 81200] [ 75769, 78077] [ 72963, 75185] [ 70357, 72500] [ 67931, 70000] [ 65667, 67667] [ 63548, 65484] [ 61563, 63437] [ 7695, 61515] = [0.985*F_CPU/(8*256), 1.015*F_CPU/(8*33)]
Notice that the ubiquitous 115,200 baud is actually out of specs for popular 16 MHz MCU builds. In practice, tolerances of up to 2.5% may actually work, though that is not recommended. Also signal noise and cable length play a big role in whether communication is stable. The maximum permitted baud rate of
F_CPU/8
may not always work. In general, lower baud rates are expected to work better, at least they will exhibit a lower quantisation error. If the chosen oscillator source has an unknown but high deviation from the nominal frequency (as is the case for the internal 8 MHz oscillators of a typical MCU) then it is safer to choose a host computer baud rate in the last interval of the above table, ie, a baud rate of less thanF_CPU/260
. In the case of an 8 MHz internal oscillator with an autobaud bootloader this would be, eg, 28,800 baud, though 57,600 baud are likely to work and 115,200 baud might work. External oscillators usually have a good tolerance, even a resonator with a 2000 ppm error will only produce an additional deviation error of 0.2% in addition to the quantisation error caused by limited baud rate division choices for autobaud bootloaders.The above set of baud rates is generated by setting
UART2X
to 1, the default whenAUTOBAUD
has been set. SettingUART2X=0
saves 4 bytes of code and has a higher tolerance of 2% error but will restrict available baud rates to half of the above baud rates:F_CPU/16
,F_CPU/32
,F_CPU/48
, ...,F_CPU/4096
.In order for the
AUTOBAUD
feature to work, urboot will need to (and usually does) know the rx line of the UART so it can measure the host's baud rate. In addition, the rx line needs to reside on a port that is within the bit-addressable bytes of the MCU, which are typically located in [0x20, 0x3f]. Therefore UARTs for which the rx pin is located on PortH
and above are not available forAUTOBAUD
, eg, UART2 and UART3 of the ATmega2560.If
AUTOBAUD
is not set or if it is set to 0, the code automatically selects reasonably good settings to match the desired fixed baud rateBAUD_RATE
. In the absence ofAUTOBAUD
ifUART2X
is set to 1 or unset, then the code decides about whether or not to switch to the higher baud rate set based on the respective error rates. SettingUART2X
to 2 will always utilise the set of higher baud rates only, setting it to 0 will always utilise the set of lower baud rates to match the desired BAUD_RATE.Some classic parts, eg the ATmega103, do not have a way to set double UART speed. Setting
UART2X
to 1 has no effect for these, and the set of availableAUTOBAUD
baud rates is as ifUART2X
had been set to 0.A dozen or so AVR parts support the automotive Local Interconnect Network that can be used as ordinary UART. This includes the LIN/UARTs of ATtiny167/87. They allow the setting the number of samples per transferred bit to 8..63 in addition to the baud rate divisor. Most classic UARTs only can set the number of samples to 16 or 8 (the latter is actually what
UART2x=1
does). As a consequence, LIN/UARTs can create almost all baud rates well within 1% of quantisation error with far fewer blind spots. Urboot bootloaders for these parts make use of that automagically. The setting ofUART2X
takes no effect for these parts when working with fixed baud rates.AUTOBAUD
though works exactly as it does for normal USARTs of other classic parts, ie, the number of samples will be either set to 8 or 16 depending on the setting ofUART2X
as described above.SWIO=1
creates code for software I/O. That is not only useful for those MCUs that don't have a UART, but also for those combinations ofF_CPU
andBAUD_RATE
where a UART would create too large quantisation errors for the baud rate. It might also be necessary to resort to software I/O when the board uses the MCU's rx and tx lines otherwise. The optionsRX
andTX
specify which pins are used for software I/O. The syntax is the same as withLED
andSFMCS
: Use any pin descriptor in Atmel format such asAtmelPB2
or as Arduino pin number as inTX=ArduinoPin2
. The default baud rate depends on the CPU frequency of the board and is 115,200 for 8 MHz or above. Again, the ports of the rx and tx line need to be below PortH
for software I/O to work.Admissible baud rates also depend on the CPU frequency. The table below details the approximate boundaries as implemented in urboot bootloaders:
Baud rate Lowest Highest Min @ FCPU = 16 MHz Max @ FCPU = 16 MHz Classic UART2X=0
FCPU/65536 FCPU/16 244 baud 1,000,000 baud Classic UART2X=1
FCPU/65536 FCPU/8 244 baud 2,000,000 baud Classic UART2X=2
FCPU/32768 FCPU/8 488 baud 2,000,000 baud LIN UART FCPU/16384 FCPU/8 980 baud 2,000,000 baud AUTOBAUD
UART2x=0
FCPU/4096 FCPU/16 3,900 baud 1,000,000 baud AUTOBAUD
UART2x=1
FCPU/2048 FCPU/8 7,800 baud 2,000,000 baud SWIO=1
(16-bit PC)FCPU/1560 FCPU/23 10,300 baud 700,000 baud SWIO=1
(22-bit PC)FCPU/1560 FCPU/27 10,300 baud 600,000 baud Note that the highest values are theoretical limits that are normally quite difficult to achieve in practice owing to the fact that the bootloader needs to do work in between receiving bytes, owing to typically higher quantisation errors at the high end of baud rates and owing to physical limitations given by cable length and line noise. Even within those boundaries, some baud rates are out of specs, particularly with classic UARTs as detailed above.
-
URPROTOCOL=<1|0>
From urboot u8.0, this option is no longer available and the urprotocol is deemed to be switched on.
When set to 0, this option used to implement a skeleton of the STK500v1 protocol that generated extra code needed for avrdude's arduino programmer to work with the bootloader. The generated code at places did not do anything useful except keeping the STK500 v1 protocol going for the
avrdude -c arduino
programmer, which would issue a couple of requests to set the device that are not needed. WithURPROTOCOL=0
the generated code gracefully accepted these requests but ignored them or, worse, returned a value that just was not true: for example, the so generated bootloader pretended the fuses are0xff
.URPROTOCOL=1
works with a version of avrdude that offers an urclock programmer. The protocol is different to STK500v1, so that an MCU id and a few other features of this bootloader are sent to avrdude. GenerallyURPROTOCOL=1
requires the bootloader to do much less work and shifts some of the work to the programmer. As Avrdude v7.1 or above with its urclock programmer is reasonably well distributed, urboot u8.0+ bootloaders only ever use urprotocol. This simplifiesurboot.c
and its maintenance. -
WDTO=<timeout>
(watchdog timeout)Timeout for watchdog that governs how long after reset urboot waits for characters from the programmer before starting the application. Possible values are:
16MS
,32MS
,64MS
,125MS
,250MS
,500MS
,1S
and2S
. Most MCUs also provide4S
and8S
. When the device has a reset circuit that can be triggered into reset by pulling the DTR or RTS line of the associated USB-to-serial converter chip then small times such as500MS
(the default in theurboot.c
file) or1S
(the default in the Makefile) are useful. If there is no such reset circuit and upload involves manual synchronisation then higher values such as2S
are recommended. Very small values up to125MS
are unlikely to provide enough time for establishing a handshake with the uploading program. -
EEPROM=<1|0>
With
EEPROM=1
, avrdude's option-U eeprom:r:file.eep:i
saves the eeprom contents of the MCU onto a filefile.eep
and-U eeprom:w:file.eep:i
will write the file contents to the MCU's eeprom. It is a useful property to be able to manage non-volatile data and programs independently. The default for this option isEEPROM=1
. -
VBL=<0|1|2|3> [VBL_VECT_NUM=<num>]
This option is valid for parts that do not have a
UPDI
interface. It determines whether or not a vector bootloader is generated. The default for this option isVBL=0
for devices that have hardware boot section support. As planned, from urboot u8.0VBL=2
orVBL=3
are no longer available.VBL=1
creates a vector bootloader: The AVR interrupt vector table consists of 4-bytejmp
instructions or 2-byterjmp
instructions (if the MCU has no more than 8 kB flash it is always arjmp
). The reset vector is the first in the table, and contains a jump instruction to the start of the application. A vector bootloader is a hook between the reset vector and the application, where the reset vector has been made to point to the bootloader, and the bootloader — once finished — jumps to a dedicated but otherwise unused interrupt vector that has been made to point to the application. This trick provides a bootloader for boards or MCUs without boot section support (think Trinket, Digispark, ATmega48, ...). On MCUs with boot section support one can utilise a vector bootloader, too, by changing the fuses to jump to0x0000
on external reset. This way the boot section can start at any multiple ofSPM_PAGESIZE
in high flash space, which can reduce the space taken by the bootloader. For example, an ATmega328P system can then use 384 bytes (3 pages) rather than the otherwise minimum 512 bytes boot section. Likewise, those MCUs with 1 kB minimum boot size (ATmega1284P, eg) might save considerably more space that would otherwise needlessly be used for the bootloader.By default, a vector bootloader loses one interrupt vector that no program ever can use on that device. Urboot chooses a "rare" vector of the target MCU to store a jump instruction to the application start address. The default for that rarely used interrupt vector is
SPM_RDY
or, if that does not exist,EE_RDY
, but it can be set byVBL_VECT_NUM=<n>
with = 1, 2, 3, ...If reserving an interrupt vector for the bootloader is out of question, there is also the option of enlarging the processor's interrupt vector table by one additional slot. This can be done in the Arduino IDE through adding the following to the end of the
hardware/arduino/avr/cores/arduino/main.cpp
source that will then automatically be included into every application:// Additional vector in ISR vector table used by the urboot bootloader uint8_t __attribute__((used)) __attribute__((section(".vectors"))) _vbl_jmp_to_application[FLASHEND+1UL > 8192? 4: 2] = {0x0C, 0x94};
The initialisation of the vector with the lower half of the 4-byte
jmp
instruction matters for MCUs with flash memory between more than 8k and 128k inclusively as urbootVBL=2
or3
only patches the destination address in the missing upper half of the 4-bytejmp
. This increases the size of each sketch by 4 or 2 bytes, respectively. Compiling the bootloader with the optionVBL_VECT_NUM=-1
tells it to use this additional vector (which, eg, would translate to 26 for the ATmega328P; 35 for the ATmega1284P; 57 for the ATmega2560; 15 for the ATtiny85 and 20 for the ATtiny167).VBL=1
creates a vector bootloader with otherwise zero bytes overhead; the only change in the bootloader is that it jumps to the application through the designated interrupt vector rather than the reset vector. Note that the interrupt vector table of every application burned with this bootloader needs to be patched before being uploaded: the designated interrupt vector needs to be a copy of the original reset vector with the application start, and the reset vector then needs to be modified to jump to the start of the bootloader. In the case of relative jumps (for MCUs with up to 8 kB flash) the relative destination needs adapting, too. Patching the application for upload can be done separately as an extra step between compiling and uploading. Alternatively, avrdude's urclock programmer will patch the application automatically. It knows about this bootloader, which encodes its version number and main capabilities in a table at the top of the boot section (see below), so thatavrdude -c urclock
can and will apply the patch for upload and verification auto-magically.Earlier urboot versions up to u7.7 could compile a bootloader with
VBL=2
or3
, so the bootloader itself patches the vectors at the cost of enlarged code size. As avrdude v7.1 or higher with its urclock programmer is now reasonably well distributed, optionsVBL=2
andVBL=3
are no longer relevant: patching the application vectors in every bootloader is not a good use of flash if that can be done by the uploader program. Removing these options has simplified the source codeurboot.c
.Burning a VBL-enabled urboot onto an MCU should in theory also require the jump to boot section be burned to the reset vector. The accompanying
urloader
sketch does that. However, one will get away with not burning the reset jump address into the vector table, if the MCU flash is erased otherwise: erased words read0xffff
, and although this is not an official opcode, it behaves assbrs r31, 7
(skip one instruction if bit 7 in R31 is set). Any reset to0x0000
on an otherwise erased flash will therefore eventually run into the start of the bootloader. -
DUAL=<0|1> [SFMCS=<AtmelPxx|ArduinoPinX> | TEMPLATE=1]
DUAL=1
provides dual-boot functionality: When the boot sector is entered through a Watchdog timer (WDT) reset, a dual-boot enabled urboot reads the first page of an attached external SPI flash memory. If that is compatible with ajmp
/rjmp
vector table as judged by the first few bytes, it is assumed that the SPI flash memory contains a new program to be burned onto the MCU. As there is no size associated with the copy of the program the maximum number of flash pages is being copied onto the MCU right up to START, where the bootloader sits. It then runs the new application. This is a crucial stepping stone for "over the air" programming: The application program, when run normally, recognises "new program" packets that it gets from somewhere (radio, WiFi, magic, ...) and drops them onto the start of external memory. When finished, the application triggers a watchdog reset, urboot burns the new program, and the application has just reprogrammed itself: Voila! Useful for those wireless projects that are installed where they are kind of hard to get to. The default isDUAL=0
.The
SFMCS=<AtmelPxx|ArduinoPinX>
option encodes the pin number of the SPI flash memory chip select line: either use a constant such asAtmelPB0
(port B, pin 0) or, eg,ArduinoPin8
, which uses the Arduino Pin numbers that are available for some boards/MCUs.TEMPLATE=1
creates a template bootloader with different nop opcodes (mov rN,rN
) where theSFMCS
pin needs to be manipulated. These template bootloaders cannot be used as they are, but the accompanying urloader sketch (to burn a template bootloader) allows the user to select the CS pin of the flash memory. This will then replace themov rN, rN
opcodes with the rightsbi
/cbi
opcodes in the right places on the fly when burning the bootloader. -
PROTECTME=<1|0>
Setting
PROTECTME=1
prevents the bootloader from overwriting itself. This can only in rare circumstances be safely switched off, eg, when nopgm_write_page()
function is exported, the bootloader uses hardware boot section support and the lock bits are set to protect the bootloader. As the protection code is only between 4 and 10 bytes and the user might forget protecting the hardware-supported boot section through lockbits, this protection can no longer be switched off from urboot u7.7 onwards. -
PROTECTRESET=<1|0>
Setting
PROTECTRESET=1
protects a vector bootloader from overwriting the reset vector. Overwriting the reset vector has the potential to "brick" the bootloader. Programming withavrdude -c urclock
looks after the reset vector, so incidents where it can be overwritten are rare even without protection: the principal cause of this happening is when the user calls thepgm_write_page(sram, flash)
function from the application and overwrites the reset vector at flash address zero; another cause can be a user overwriting the bootloader size with a wrong number using the extended avrdude option-xbootsize=<n>
when uploading a file. SettingPROTECTRESET=1
will ensure that a backwardrjmp
from address 0 to the bootloader will be written to flash whenever the user writes the first flash memory page. As an aside, whenever the functionpgm_write_page(sram, flash)
is called by the user forflash=0
then the first two bytes of the memory that the pointer sram points to are also altered with therjmp
to the bootloader. IfPROTECTRESET
is unset, ie, neither 0 or 1, then this option will be set if there is still space in the bootloader to accommodate the protection code. -
PGMWRITEPAGE=<1|0>
PGMWRITEPAGE=1
makes available a bootloader functionvoid pgm_write_page(void *sram, progmem_t pgm);
to applications. This function writes
SPM_PAGESIZE
bytes from SRAM memory into flash at locationpgm
. When the device has more than 64 kB flash, and henceRAMPZ
exists, thenprogmem_t
is a type that holds 32-bit addresses, elseprogmem_t
is a 16-bit address(void *)
. The caller has to take care thatpgm
is aligned with memory pages ofSPM_PAGESIZE
bytes.sram
is always 16-bit. Normally, application code cannot write to flash, as this has to be done from the boot section, so this option provides applications with the opportunity to treat unused flash a bit like EEPROM. There are library functionsurstoreWrite()
andurstoreRead()
that make use of this interface. As exporting this function in the bootloader comes at a small cost of some 4-6 bytes overhead to call the anyway neededwritebuffer()
function, the recommendation is to always set this option. From a security point of view, there are certain risks involved, as this option allows the application to modify itself. If you are concerned about this, I recommend using external programming using a programmer instead of using any bootloader at all (rendering urboot moot) and/or setting the fuses very restrictively (which renders this option obsolete). Applications call the urboot functionpgm_write_page()
normally by((void (*)(void *, progmem_t))((FLASHEND+1UL-4UL)/2))(sram, pgm);
Division by 2 reflects the fact that avr-gcc has PC word addresses.
pgm
needs to be a 32-bit type if the MCU has more that 64k flash, a 16-bit type otherwise. Here is a full definition of thepgm_write_page()
function to be used in the application sketch (cut and paste or use theur/libraries/urboot
library):#if FLASHEND > 0xFFFFUL typedef const uint_farptr_t progmem_t; #else typedef const void *progmem_t; #endif /* * Copy a single page from SRAM to page-aligned PROGMEM utilising smr's urboot * - Takes ca 8.7 ms on an 16 MHz ATmega328p * - Remember to flush serial buffers before calling * - The bootloader alters the first bytes of where sram points to if it has * reset protection and Page 0 is overwritten * - See https://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html re EIND, RAMPZ */ void urbootPageWrite(void *sram, progmem_t pgm) { cli(); #if FLASHEND > 0x1FFFFUL // Set the correct memory segment for indirect jump EIND = ((FLASHEND+1UL-4UL)/2) >> 16; #endif // Call the bootloader routine pgm_write_page(sram, pgm) ((void (*)(void *sram, progmem_t pgm))((FLASHEND+1UL-4UL)/2))(sram, pgm); #if FLASHEND > 0xFFFFUL RAMPZ = 0; #endif #if FLASHEND > 0x1FFFFUL EIND = 0; #endif sei(); }
This function and other utility functions are available in
urboot-utils.cpp
in the directoryurboot-utils
The options below are frills, ie, not really essential for the functionality of the bootloader.
-
CHIP_ERASE=<0|1>
With this option set to 1, an STK_CHIP_ERASE command will erase all flash below the bootloader; otherwise,
STK_CHIP_ERASE
commands are ignored. If theURPROTOCOL
option is set then this bootloader will informavrdude -c urclock
whether or not it has the ability to perform a chip erase, so that avrdude can emulate a chip erase if needed by erasing unset bytes in the input file for flash to0xff
. Avrdude's chip erase emulation is much slower than the bootloader's chip erase, as it carries the overhead of sending0xff
bytes over the serial interface. Setting this option will cost around 16-38 bytes code. -
UPDATE_FL=<0|1|2|3|4>
(skip unnecessary page erases or writes)This option is new in urboot u8.0. It allows various degrees of updating flash rather than writing to it unconditionally. Flash memory of AVR microprocessors is physically organised in pages that need erasing (all bytes set to
0xff
) before writing new content to them. Write and, in particular, erase cycles reduce the endurance of flash memory to the extent that the data sheet only warrants a certain number of them, typically 100,000.UPDATE_FL=1
adds code to the write page routine that checks first whether flash would be changed by the new page contents and only writes to flash if necessary.UPDATE_FL=2
does the same as1
and, in addition, compiles code into the chip erase routine that skips erasing a flash page if it was already all 0xff.UPDATE_FL=3
does the same as2
but in addition checks within the write page routine whether, once a flash page needs modifying, it needs erasing first; some content changes only transition selected bits from1
to0
for which a page erase is unnecessary.UPDATE_FL=4
does the same as3
but in addition compile in checks for whether the new page is empty, for which after a page erase no further page write is necessary. Note that the extra code for level2
is only generated if there is a chip erase routine in the bootloader, ie,CHIP_ERASE=1
. And the extra code in levels3
and4
is only generated when the bootloader write page routine normally erases a page before writing it; this is so if the bootloader provides a page erase function for the user, ie,PGMWRITEPAGE=1
, or if the bootloader has no other means of erasing a chip, ie,CHIP_ERASE=0
. Level1
costs roughly 20 bytes code, level2
another 20 bytes and level3
and4
add 10 bytes code each. Of course, a fully kitted outUPDATE_FL=4
level also makes the bootloader the fastest it can be. -
RETSWVERS=<0|1>
(return correct software version)From urboot u8.0 this option has been deprecated as it no longer has an effect.
Option
RETSWVERS=1
used to create code to tell the avrdude or otherwise programmer truthfully the current software version. The space used for this code was never worthwhile. Without it, urboot pretended to be on software versionX.X
and hardware versionX
, whereX
is the major software version.RETSWVERS=0
used to save 28 bytes forURPROTOCOL=0
(STK500 compatibility), which is no longer supported in u8.0. WithURPROTOCOL=1
this option had no effect, as the bootloader did not expect, nor respond to,STK_SW_MAJOR/MINOR
requests. -
EXITFE=<0|1|2>
(exit on frame errors)Option
EXITFE=2
creates code that exits on frame errors in the serial communication. The rationale is that frame errors indicate a wrong bit-rate, which might be owing to an external device wanting to communicate with the application. Setting EXITFE to 1 cares about frame errors in the sense that the watchdog timer is only reset on bytes that have no frame error. Not resetting the watchdog timer hastens the bootloader's reset to the application. Setting this option to 0 ignores frame errors, which ultimately saves a few bytes of code and delays the inevitable exit from the bootloader that will be caused anyway through protocol errors. -
QEXITERR=<0|1>
(quick exit on error)Option QEXITERR=1 creates code so that when there is a protocol error during the bootloader communication, the application is started within 16 ms as opposed to the default WDT timeout. If QEXITERR is set, then other exit on error situations also make use of this code for quick exit.
-
BLINK=<0|1> [LED=<pin> [LEDPOLARITY=<1|-1>] | TEMPLATE=1]
Light effects:
BLINK=1
toggles the activity LED during serial data I/O. The OptionLED=<pin>
sets the LED pin in terms of Atmel port and bit number as in, eg,AtmelPB1
orArduinoPin9
.LEDPOLARITY
specifies whether the LED is assumed to be low active (LEDPOLARITY=-1
) or high active otherwise (LEDPOLARITY
undefined or set to 1). UsingTEMPLATE=1
is an alternative to specifying the LED pin at compile time. This creates a template bootloader with different nop opcodes (mov rN,rN
) instead of code that operates the LED. The urloader sketch to burn a template bootloader lets the user select/change the LED pin and polarity. This will then replace themov rN, rN
opcodes with the rightsbi
/cbi
opcodes in the right places on the fly when burning the bootloader. -
QEXITEND=<0|1>
(quick exit on end)Option
QEXITEND=1
creates code so that when finished programming the application is started within 16 ms as opposed to the default WDT timeout. -
FRILLS=<level>
Shortcut for setting defaults for some of the options; the default level is 0; if the below options are undefined they will be defined as
Option default UPDATE_FL=4
FRILLS >= 10
UPDATE_FL=3
FRILLS >= 9
UPDATE_FL=2
FRILLS >= 8
CHIP_ERASE
FRILLS >= 7
UPDATE_FL=1
FRILLS >= 6
QEXITEND
FRILLS >= 5
QEXITERR
FRILLS >= 4
EXITFE
FRILLS >= 3? 2: FRILLS >= 2
BLINK
FRILLS >= 1
-
AUTOFRILLS=<level-list>
In absence of
FRILLS=<level>
, this option asksurboot-gcc
to try out the first<level>
of the comma-separated<level-list>
ofFRILLS
levels, to then determine the the highestFRILLS
level of the list that fits into the usage occupied by the first level. For exampleAUTOFRILLS=0,7,6,1,2,3,4
first determines the space usage of a bootloader withFRILLS=0
and then figures out which of the otherFRILLS
levels also fit into that space. The finally compiled bootloader uses the biggest of these. This option is only seen by theavr-gcc
wrapperurboot-gcc
and not passed on to the actual compiler. From urboot u8.0 the range operator..
can be used as well: above AUTOFRILLS could have been specified with0,7,6,1..4
.
-
DEBUG_FREQ=<fq> [FREQ_PIN=<pin> [FREQ_POLARITY=<1|-1>]] [EXACT_DF=<0|1>] [DEBUG_CYCLES=<n>]
If
DEBUG_FREQ
is set, the bootloader initially swings a square wave ofDEBUG_CYCLES
periods (default 5) on pinFREQ_PIN
with a frequency offq
Hz, wherefq
can be a real number.FREQ_PIN
can be the same as the LED pin, and in fact defaults to theLED
pin if the latter is defined and the former is not. This option is useful of one wants to measure the frequencyF_CPU
of the MCU on the FREQ_PIN (using another MCU or a scope). This is not meant for production, only for debugging. 50 Hz is a good frequency as this delays bootloader comms by only 100 ms. Any lower frequency might be too much delay for default synchronisation with avrdude, thoughavrdude -c urclock
has a-xdelay=<ms>
parameter that can be used to compensate for the extra startup time of this option.FREQ_POLARITY=1
starts the square wave with a high level after reset, and-1
with a low level after reset. After then
periods the signal onFREQ_PIN
stays low, irrespective ofFREQ_POLARITY
. The duration of one period of the square wave might be too short by up to 5 clock cycles, which is normally of no concern. If precision of this debug square wave is of importance, thenEXACT_DF
can be set to1
, in which case the quantisation error of one period is at most 1 clock cycle at the expense of up to 4 bytes extra code. -
FLASHWRAPS=<1|0>
This option informs
urboot.c
whether or not flash memory wraps around. When the bootloader finally starts the application it will jump to a location in the vector table (either the reset vector or, if it is a vector bootloader, to the dedicated vector). If flash memory wraps around, then the bootloader can execute a short 2-byterjmp
over the end of flash to the vector table rather than a absolutejmp
thus saving 2 bytes of code. The default isFLASHWRAPS=1
iff the size of flash memory is a power of 2, but can be overwritten should this assumption be wrong for a particular MCU. This option has no effect in MCUs with up to 8k flash as they always userjmp
.
urboot-gcc
is an avr-gcc
wrapper for urboot bootloader compilation; it computes the correct start
address for the bootloader and the correct rjmp
opcode to the pgm_write_page(sram, flash)
function
in the bootloader and injects both as -DSTART=<addr>
and -DRJMPWP=<opcode>
, respectively, when
calling avr-gcc
.
-
-DSTART=<address>
There are several places in the code of the bootloader where it needs to know its eventual place in flash memory, eg, for code to protect overwriting the bootloader itself, for patching applications in vector bootloaders (see below) and for copying the right amount of external SPI flash memory to microprocessor flash in dual boot. Bootloaders normally sit in high flash memory (except in AVR parts with
UPDI
interface). The-DSTART=0x...UL
option informsurboot.c
about the byte address where the linker will be told to put the bootloader. Coincidently,FLASHEND-START+1UL
is the size of the bootloader.START
is actually also the address of the main function but using the symbol main instead of the START compile-time constant enlarges the code without need. The accompanying perl scripturboot-gcc
acts asavr-gcc
wrapper; it knows a lot of MCUs, can compute the "right" START and auto-magically runs the avr-gcc appropriately with the correct-DSTART-...
option. -
-DRJMPWP=0xCxxx
The compiler option
-DRJMPWP=0xCxxx
stores therjmp pgm_write_page
opcode just below the two version bytes, so that this function can be called at the addressFLASHEND+1UL-4
. The accompanying perl compiler wrapperurboot-gcc
computesRJMPWP
automatically (iff thepgm_write_page
andmain
symbols both exist in the .elf file) and passes this option on toavr-gcc
. In case thatPGMWRITEPAGE=0
or that the-DRJMPWP=..
. opcode has not been supplied tourboot.c
byurboot-gcc
or the user then theret
opcode will be stored instead of therjmp
, so that calling this interface returns empty-handed. Use as follows in your application:
$ urboot-gcc -\?
Syntax: urboot-gcc [-toolchain=<v>] {avr-gcc opts} urboot.c
Function: compiles urboot with the right addresses until compilation converges
Option:
-toolchain=<v> uses ./avr-toolchain/<v>/bin
or avr-gcc version in $PATH on -toolchain=system or -toolchain=
or GCCROOT environment variable
or ./avr-toolchain/4.8.1/bin
(show available list with -toolchain=?)
Finding the right address for the bootloader is a two-step, sometimes three-step process: On first compilation a START address is used that works with the target mcu, but will be too low. Once the bootloader is complied and linked, urboot-gcc compiles again using size info and urboot version bits from the newly created .elf file. The bootloader placement depends on the type of bootloader support that the mcu offers, and on whether the bootloader is a vector bootloader (VBL - as encoded in the version bits), which requires no bootloader support by the mcu. For example, you might have compiled a bootloader for the ATmega1284P with VBL support that fits into 512 bytes. This will be placed in the topmost 512 bytes of the flash space. However, if compiled as normal bootloader without VBL support, it must be placed in the top 1024 bytes, because that's the smallest boot section that this mcu offers. urboot-gcc aligns the bootloader with the correct SPM page size and/or boot section size. In a slight complication of things, it may happen that the size of the bootloader changes after the second step. This is because the bootloader address is used as compile-time constant, which when changed, can be used in a different way by the code or optimiser. This is normally not a problem, but when the new size ticks over the boot section or page size limit, this will trigger a linker error after the second step. To resolve this, urboot-gcc must compile and link a third time, this time adding a few bytes of slack to the size. In case you requested the export of pgm_write_page() in the bootloader source, urboot-gcc computes the appropriate 2-byte rjmp call and places it 4 bytes under the end of the bootloader, ie, at the end of flash just before the two version bytes.
In addition to figuring out the correct start address of the bootloader and the correct rjmp to the
pgm_write_page(sram, flash) bootloader routine, urboot-gcc also runs avr-gcc one more final time
with -D_urboot_AVAILABLE=<num_bytes>
to indicate how many free bytes are available for desirable,
but not essential features. Currently, urboot.c uses this feature for the generation of reset
vector protection code for vector bootloaders.
hexls
is a perl script that lists urboot bootloader hex files with size, version, features and
name.
$ hexls -\?
Syntax: hexls {<bootloader>.hex}
Function: list urboot bootloader hex files with size, version, features and name
Options:
-version Print version (1.80) and exit
-pre=<text> Print <text> as preamble
-mcu=<name> Assume the hex files were compiled for MCU <name>
-md=url Print neat markdown table with header (files use url)
-check Check rjmp op code at end (if pgm_page_write-enabled and .lst file there)
-sort Sort rows according to use, size, version, features, name
-use Print number of bytes occupied by bootloader and exit
Example:
$ hexls -sort -md *.hex
Size | Usage | Version | Features | Hex file |
---|---|---|---|---|
256 | 256 | u8.0 | w---jPr-- |
atmega328p_min.hex |
256 | 256 | u8.0 | w---jPra- |
atmega328p_amin.hex |
366 | 384 | u8.0 | weU-jPr-- |
atmega328p_8000khz_swio.hex |
384 | 384 | u8.0 | weU-jPr-c |
atmega328p_led9_50Hz_fp9.hex |
384 | 384 | u8.0 | weU-jPrac |
atmega328p_a.hex |
406 | 512 | u8.0 | weU-hprac |
atmega328p_h.hex |
512 | 512 | u8.0 | weUdhprac |
atmega328p_ad.hex |
- 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 sod
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 (recommended for large MCUs).
unable to tell from .hex file whether this feature is present-
corresponding feature not present
- Hex file: often qualified by the MCU name and/or configuration
swio
software I/O (not UART)led9
toggles an LED on, in this example, Arduino pin 9fp9
for debugging the bootloader uses, eg, Arduino pin 9 to swing a signal of low frequency, eg, 50 Hzd
dual bootmin
most feature-rich bootloader for smallest achievable flash usage (impliesur
)
-
TOOLVER=<4.8.1|4.9.2|5.4.0|7.3.0|system>
This selects the version of the avr toolchain that should be used for compilation. Older toolchains tended to produce slightly smaller code. From urboot u8.0 onwards, the sweetspot seems to be the now default
TOOLVER=7.3.0
-
GCCROOT=<dir>
Alternatively,
GCCROOT
, the directories with the binaries of the toolchain, can be specified directly; the default is./avr-toolchain/$(TOOLVER)/bin/
.
make MCU=attiny2313 EEPROM=0 VBL=1 FRILLS=0 PGMWRITEPAGE=0 VBL_VECT_NUM=EEPROM_READY_vect_num NAME=attiny2313_min
make MCU=atmega328p AUTOBAUD=1 VBL=1 AUTOFRILLS=5..10 NAME=atmega328p_a
make MCU=atmega328p AUTOBAUD=1 FRILLS=10 NAME=atmega328p_h
make MCU=atmega328p AUTOBAUD=1 VBL=1 SWIO=1 RX=ArduinoPin0 TX=ArduinoPin1 AUTOFRILLS=5..10 NAME=atmega328p_swio
./urboot-gcc -toolchain=7.3.0 -g -Wundef -Wall -Os -fno-split-wide-types -mrelax -mmcu=attiny2313 -DF_CPU=16000000L -Wno-clobbered -DWDTO=1S -DAUTOBAUD=0 -DDUAL=0 -DEEPROM=0 -DVBL=1 -DVBL_VECT_NUM=EEPROM_READY_vect_num -DPGMWRITEPAGE=0 -DFRILLS=0 -Wl,--relax -nostartfiles -nostdlib -o attiny2313_min.elf urboot.c
./avr-toolchain/7.3.0/bin/avr-objcopy -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex attiny2313_min.elf attiny2313_min.hex
./avr-toolchain/7.3.0/bin/avr-objdump -h -S attiny2313_min.elf > attiny2313_min.lst
./urboot-gcc -toolchain=7.3.0 -g -Wundef -Wall -Os -fno-split-wide-types -mrelax -mmcu=atmega328p -DF_CPU=16000000L -Wno-clobbered -DWDTO=1S -DAUTOBAUD=1 -DDUAL=0 -DEEPROM=1 -DVBL=1 -DPGMWRITEPAGE=1 -DAUTOFRILLS=5..10 -Wl,--relax -nostartfiles -nostdlib -o atmega328p_a.elf urboot.c
./avr-toolchain/7.3.0/bin/avr-objcopy -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex atmega328p_a.elf atmega328p_a.hex
./avr-toolchain/7.3.0/bin/avr-objdump -h -S atmega328p_a.elf > atmega328p_a.lst
./avr-toolchain/7.3.0/bin/avr-gcc -DSTART=0x740UL -DRJMPWP=0x9508 -Wl,--section-start=.text=0x740 -Wl,--section-start=.version=0x7fa -DFRILLS=0 -D_urboot_AVAILABLE=2 -g -Wundef -Wall -Os -fno-split-wide-types -mrelax -mmcu=attiny2313 -DF_CPU=16000000L -Wno-clobbered -DWDTO=1S -DAUTOBAUD=0 -DDUAL=0 -DEEPROM=0 -DVBL=1 -DVBL_VECT_NUM=EEPROM_READY_vect_num -DPGMWRITEPAGE=0 -Wl,--relax -nostartfiles -nostdlib -o attiny2313_min.elf urboot.c
./avr-toolchain/7.3.0/bin/avr-objcopy -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex attiny2313_min.elf attiny2313_min.hex
./avr-toolchain/7.3.0/bin/avr-objdump -h -S attiny2313_min.elf > attiny2313_min.lst
./avr-toolchain/7.3.0/bin/avr-gcc -DSTART=0x7e80UL -DRJMPWP=0xcfcd -Wl,--section-start=.text=0x7e80 -Wl,--section-start=.version=0x7ffa -DFRILLS=7 -D_urboot_AVAILABLE=14 -g -Wundef -Wall -Os -fno-split-wide-types -mrelax -mmcu=atmega328p -DF_CPU=16000000L -Wno-clobbered -DWDTO=1S -DAUTOBAUD=1 -DDUAL=0 -DEEPROM=1 -DVBL=1 -DPGMWRITEPAGE=1 -Wl,--relax -nostartfiles -nostdlib -o atmega328p_a.elf urboot.c
./avr-toolchain/7.3.0/bin/avr-objcopy -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex atmega328p_a.elf atmega328p_a.hex
./avr-toolchain/7.3.0/bin/avr-objdump -h -S atmega328p_a.elf > atmega328p_a.lst