This is an old revision of the document!
XBoot Readme
1. Introduction
XBoot is an extensible, modular bootloader for the ATMEL AVR processor series, compatible with both AVR ATMEGA and AVR XMEGA devices with sufficient memory. It is compatible with the AVR109 (butterfly) bootloader protocol with a few XMEGA specific extensions for access to the user and production signature rows. XBoot includes several advanced features including multiple serial busses and an API providing the ability for the running application to update itself without the need for external programming hardware. Many bootloaders only support RS232 for programming from a PC, but XBoot's modularity allows it to support the exact same set of commands over any hardware serial port. Currently, I²C, RS485, and parallel FIFO support have been incorporated. This allows for easy in-system reconfiguration of XBoot equipped chips with little additional time investment. Also, XBoot includes support for I²C address autonegotiation for when multiple, identically configured processors sit on the same I²C bus.
Thanks for using XBoot!
Alex Forencich
1.1 Compatibility List
Currently, XBoot should work on any XMEGA processor and any ATMEGA with sufficient memory (4K boot NRWW block). The following list of processors are currently supported. An asterisk denotes the MCU has been tested and confirmed XBoot compatible.
- XMEGA
- atxmega16a4 *
- atxmega32a4 *
- atxmega64a1
- atxmega64a3 *
- atxmega64a4
- atxmega128a1 *
- atxmega128a3
- atxmega128a4
- atxmega192a1
- atxmega192a3
- atxmega256a1
- atxmega256a3b
- atxmega256a3 *
- atxmega16d4
- atxmega32d4
- atxmega64d3
- atxmega64d4
- atxmega128d3
- atxmega128d4
- atxmega192d3
- atxmega256d3
- atxmega16a4u
- atxmega32a4u
- atxmega64a3u
- atxmega64a4u
- atxmega128a3u
- atxmega128a4u
- atxmega192a3u
- atxmega256a3u
- atxmega256a3bu
- atxmega64b1
- atxmega64b3
- atxmega128b1
- atxmega128b3
- ATMEGA
- atmega328p *
- atmega1284p *
2. Using XBoot
2.1 Configure
Before building XBoot, please configure it so it will interface properly with your system. This will involve selecting a .conf.mk file in the conf directory and then editing some parameters. The main parameters that need to be set in the .conf.mk file are the target chip (MCU) and the frequency (F_CPU). For ATMEGA chips, the boot size (BOOTSZ) must also be set, generally to the largest setting. All you need to do is make sure the only line that's not commented out is the one for your chip and the proper frequency. For the simplest bootloader configuration on the XMEGA, you may only choose 2000000 and 32000000 for the clock speed, corresponding to the two internal RC oscillator frequencies. For ATMEGA chips, the specific start up clock speed (based on the fuse settings and/or external crystal or other clock source) must be specifically entered manually. For the rest of the configuration, see the section 3, “Configuring XBoot”.
2.2 Build XBoot and Program to Chip
To build XBoot, select a .conf.mk file from the conf directory and make sure the settings are appropriate for your chip, programmer, and configuration. Then type “make [file].conf.mk”. This will copy [file].conf.mk to config.mk, generate config.h, compile the whole package, and generate xboot.hex, which can be downloaded with any programming cable capable of programming AVR chips. If you have an XMEGA and want to save some time and just program the boot section, type “make xboot-boot.hex” and then write the new file xboot-boot.hex to the boot section directly with avrdude. The makefile includes built-in support for avrdude, so all you need to do is modify the avrdude parameters in the .conf.mk file for the programmer you have and type “make program”.
Note that after typing “make [file].conf.mk”, the settings from [file].conf.mk will remain active until either a new .conf.mk file is selected or “make clean” is run.
2.3 Write Main Application Program
To write a program to a device with XBoot installed, use a command like this:
avrdude -p atxmega64a3 -P /dev/ttyUSB0 -c avr109 -b 115200 -U flash:w:main.hex
Or for windows:
avrdude -p atxmega64a3 -P com1 -c avr109 -b 115200 -U flash:w:main.hex
Also, feel free to re-use XBoot's makefile for your own code. Like XBoot, it is reconfigurable and can be used to compile most projects, just make sure to turn off the MAKE_BOOTLOADER flag for regular programs. It also has the configuration options for XBoot as a target built in, all you need to do is switch a couple of comments around.
NOTE: At this time, avrdude (currently 5.10) does NOT support programming the XMEGA flash boot section (see https://savannah.nongnu.org/bugs/?28744) due to the fact that a different programming command must be sent to the chip to write flash pages in the boot section. If you want to use avrdude, you will need to compile it from source with one of the patches listed on the bug report.
2.4 Notes for Main Application
Here are a few tips for your main application that will make using XBoot a much more pleasant experience.
2.4.1 Catch the "Enter Bootloader" command
When AVRDude starts programming the chip, the first character sent out is the the sync command; the “Escape” character, 0x1B. If your program transmits ASCII, or only transmits Binary during certain program states, you can monitor the UART for the escape character and cause a software reset to enter the bootloader, as shown in the following snippet on an XMEGA:
if (rx_byte == 0x1B) { CCPWrite( &RST.CTRL, RST_SWRST_bm ); }
Or, if CCPWrite is not available, this should also work:
if (rx_byte == 0x1B) { CCP = CCP_IOREG_gc; RST.CTRL = RST_SWRST_bm; }
In many cases, this allows you to use the AVRDude program command without having to manually reset the AVR. Alternatively, the API call xboot_reset()
will have the same effect.
3. Configuring XBoot
XBoot is designed to be reconfigured to suit specific needs. Out of the box, all of the standard features are turned on. Turning off features and reassigning pins is easy, just pick a .conf.mk file in the .conf folder, make a copy of it, and edit the appropriate parameters. Then build xboot with your parameters by running “make [file].conf.mk”.
Recommended configuration:
# Entry USE_ENTER_DELAY = yes USE_ENTER_UART = yes # Communication USE_LED = yes USE_UART = yes # Bootloader Features ENABLE_BLOCK_SUPPORT = yes ENABLE_FLASH_BYTE_SUPPORT = yes ENABLE_EEPROM_BYTE_SUPPORT = yes ENABLE_LOCK_BITS = yes ENABLE_FUSE_BITS = yes
This configuration will make the bootloader work similarly to an Arduino. It will blink its light a few times, polling for a character. If none is received, it starts the application. If one shows up, it enters the bootloader and processes it.
In fact, the file arduino328p.conf.mk can be used to build XBoot for use on an atmega328p based Arduino.
3.1 Bootloader clock options
3.1.1 USE_DFLL
This will turn on the DFLL for the selected oscillator, improving its accuracy. Recommended for high serial baud rates. This option only applies to XMEGA processors.
3.1.2 USE_32MHZ_RC
This will switch to the 32MHz RC oscillator on start. In the default configuration of xboot.h, this will be defined automatically when F_CPU
is set to 32000000. This option only applies to XMEGA processors.
3.2 AVR 1008 fixes
If you're using a device affected by AVR1008, then you may need to enable these for the bootloader to successfully program the chip. Affected chips are the ATXMEGA256A3 rev A, ATXMEGA256A3B rev B, ATXMEGA256A3 rev B, and possibly the ATXMEGA192A3.
3.2.1 USE_AVR1008_EEPROM
This enables the AVR1008 fix for the EEPROM controller. This option only applies to XMEGA processors.
3.3 Bootloader entrance options
3.3.1 USE_ENTER_DELAY
If this is defined, XBoot will run a loop, specified with the ENTER_BLINK_*
variables, and check for an entry condition. If none is found, it jumps into the main code. (BTW, they're called ENTER_BLINK_*
because they assume USE_LED
is defined. If it isn't, it will still work, but the variable names don't make a whole lot of sense…)
Options
ENTER_BLINK_COUNT
defines the number of times to blink the LED, e.g. 3ENTER_BLINK_WAIT
defines the number of loops to make between blinks, e.g. 30000
3.3.2 USE_ENTER_PIN
If this is defined, XBoot will check the state of a pin, specified with the ENTRY_PORT
and ENTRY_PIN_*
variables, when it starts (and possibly throughout the startup delay loop) to determine if it should start or just jump into the main program.
Options
ENTER_PORT
defines the port that the in is in, e.g.PORTC
ENTER_PIN
defines the pin in the port, an integer from 0 to 7ENTER_PIN_CTRL
defines thePINnCTRL
register for the pin, e.g.ENTER_PORT.PIN0CTRL
ENTER_PIN_STATE
defines the “asserted” state of the pin, 0 or 1ENTER_PIN_PUEN
enables a pull-up resistor on the pin if nonzero
3.3.3 USE_ENTER_UART
If this is defined, XBoot will poll for received characters over the UART. If one is received, it will enter the bootloader code. USE_UART
must be defined. ENTER_UART_NEED_SYNC
can also be defined to require the sync command (0x1B) in order to enter the bootloader. This can help prevent the bootloader from being started accidentally.
3.3.4 USE_ENTER_I2C
If this is defined, XBoot will poll for received characters over the I2C
interface. If one is received, it will enter the bootloader code. USE_I2C
must be defined.
3.3.5 USE_ENTER_FIFO
If this is defined, XBoot will poll for received characters over the FIFO. If one is received, it will enter the bootloader code. USE_FIFO
must be defined. ENTER_FIFO_NEED_SYNC
can also be defined to require the sync command (0x1B) in order to enter the bootloader. This can help prevent the bootloader from being started accidentally.
3.4 Bootloader exit options
3.4.1 LOCK_SPM_ON_EXIT
If this is defined, SPM instructions will be locked on bootloader exit.
3.5 Bootloader communication
3.5.1 USE_LED
If this is defined, XBoot will use an LED for feedback, specified by the LED_*
variables.
Options
LED_PORT_NAME
defines the port, e.g.A
forPORTA
LED_PIN
defines the pin, e.g. 0LED_INV
inverts the LED state if nonzero
3.5.2 USE_UART
If this is defined, XBoot will configure and use a UART for communication.
Options
UART_BAUD_RATE
defines the baud rate of the UART, e.g. 19200UART_PORT_NAME
defines the port that the UART is connected to, e.g.D
- Note: this only applies to XMEGA devices
UART_NUMBER
defines number of the UART device on the port, e.g. 1 for USARTD1 (or USART1 for ATMEGA)UART_U2X
turns on the double-rate BRG in ATMEGA parts- Note: this only applies to ATMEGA devices
3.5.3 USE_UART_EN_PIN
If this is defined along with USE_UART
, XBoot will configure and use a transmit
enable pin. This allows configuration over a half-duplex RS485 connection.
Options
UART_EN_PORT_NAME
defines the port, e.g.C
forPORTC
UART_EN_PIN
defines the pin, e.g. 4UART_EN_INV
inverts the EN pin output state if nonzero
3.5.4 USE_I2C
If this is defined, XBoot will configure and use an I2C/TWI controller in slave mode for communication.
Note: Currently only implemented on XMEGA.
Options
I2C_DEVICE_PORT
defines the port the I2C interface is on, e.g.E
for TWIE
I2C_MATCH_ANY
will enable the I2C controller promiscuous mode (match any address) if nonzeroI2C_ADDRESS
defines the default I2C address 0x10I2C_GC_ENABLE
enables the I2C bus general call capability (address 0) if nonzero
3.5.5 USE_I2C_ADDRESS_NEGOTIATION
Enables I2C address autonegotiation if defined. Requires USE_I2C
.
Note: Currently only implemented on XMEGA due to the presence of the Production Signature Row with a signature unique to each chip produced. A suitable workaround for ATMEGA has not yet been implemented.
Options
I2C_AUTONEG_DIS_PROMISC
will disable I2C promiscuous mode after completion of autonegotiation routine if nonzeroI2C_AUTONEG_DIS_GC
will disable I2C general call detection after completion of autonegotiation routine if nonzeroI2C_AUTONEG_PORT_NAME
defines the port in which the autonegotiation pin is located, e.g.A
I2C_AUTONEG_PIN
defines the pin, e.g. 2
3.5.6 USE_ATTACH_LED
Enables the autonegotiation code to turn on a light when a new I2C address is received.
Options
ATTACH_LED_PORT_NAME
defines the port, e.g.A
forPORTA
ATTACH_LED_PIN
defines the pin, e.g. 1ATTACH_LED_INV
inverts the LED state if nonzero
3.5.7 USE_FIFO
If this is defined, XBoot will talk to an external parallel FIFO for communication.
Options
FIFO_DATA_PORT_NAME
defines the FIFO data port, e.g.C
forPORTC
FIFO_CTL_PORT_NAME
defines the FIFO control port, e.g.D
forPORTD
FIFO_RXF_N_bm
defines the receive flag pin mask on the control port, e.g.(1 << 3)
for pin 3FIFO_TXE_N_bm
defines the transmit enable pin mask on the control portFIFO_RD_N_bm
defines the read strobe pin mask on the control portFIFO_WR_N_bm
defines the write strobe pin mask on the control portFIFO_BIT_REVERSE
will reverse the data bits
3.6 General Options
3.6.1 USE_INTERRUPTS
Defining this will configure XBoot to use interrupts instead of polled I/O for serial communications. This will increase code size and won't offer much advantage at the time being, so only use if you know what you're doing.
3.6.2 USE_WATCHDOG
Defining this will enable the watchdog timer during operation of the bootloader. This can reduce the overhead caused by failed programming attempts by resetting the chip if the bootloader and host get out of sync.
Options
WATCHDOG_TIMEOUT
determines the watchdog timeout period; leave only one of the listed lines uncommented (see XMEGA A series datasheet for details)
3.7 Bootloader features
Generally, these are all enabled, but they can be disabled to save code space.
3.7.1 ENABLE_BLOCK_SUPPORT
Enables flash block access support
3.7.2 ENABLE_FLASH_BYTE_SUPPORT
Enables flash byte access support
3.7.3 ENABLE_EEPROM_BYTE_SUPPORT
Enables EEPROM byte access support
3.7.4 ENABLE_LOCK_BITS
Enables lock bit read and write support (note: cannot clear lock bits to 1, complete chip erase from external programmer needed to do that)
Note: only supported on XMEGA
3.7.5 ENABLE_FUSE_BITS
Enables fuse bit read support (cannot write fuse bits outside of hardware programming)
Note: only supported on XMEGA
3.7.6 ENABLE_FLASH_ERASE_WRITE
Erase each page before writing. This allows the device to be reprogrammed without a complete erase sequence.
3.7.7 ENABLE_CRC_SUPPORT
Enables commands for computing the CRC of various sections of Flash memory.
3.8 API Support
3.8.1 ENABLE_API
Enable API functionality. This functionality can be completely disabled to save space.
3.8.2 USE_API_VERSION
Select API version to implement. Currently the only legal value is 1.
3.8.3 ENABLE_API_LOW_LEVEL_FLASH
Enable low level flash access APIs. Turns on the following API calls:
xboot_spm_wrapper
(can be separately disabled)xboot_erase_application_page
xboot_write_application_page
xboot_write_user_signature_row
(XMEGA specific)
3.8.4 ENABLE_API_SPM_WRAPPER
Enable SPM wrapper API. Requires ENABLE_API_LOW_LEVEL_FLASH
to be defined.
3.8.5 ENABLE_API_FIRMWARE_UPDATE
Enable firmware update APIs. Turns on the following API calls in addition to enabling the firmware upgrade code in xboot:
xboot_app_temp_erase
xboot_app_temp_write_page
3.9 Code Protection
The code protection features built into xboot keep your code safe from reverse engineering. Protected areas will read the same as unprogrammed flash or EEPROM (0xff).
3.9.1 ENABLE_CODE_PROTECTION
Enable basic code protection. Code protection prevents reading of the flash memory via xboot's interface. Code protection is temporarily disabled by an erase command, allowing verification of newly written firmware. Code protection does not disable CRC commands.
3.9.2 ENABLE_EEPROM_PROTECTION
Enable EEPROM protection. EEPROM protection prevents reading of the EEPROM memory via xboot's interface. Like code protection, EEPROM protection is temporarily disabled by an erase command, allowing verification of newly written firmware.
3.9.3 ENABLE_BOOTLOADER_PROTECTION
Enable bootloader protection. Bootloader protection prevents reading of the boot block via xboot's interface. Unlike code protection, bootloader protection is not disabled by an erase command.
4. XBoot API
XBoot provides several hooks to enable flash reprogramming by the application. Since the SPM
instruction is disabled when executing in the application section (Read-While-Write, RWW), it is not possible for an application to write to any location in flash memory without being able to use code located in the boot (Non-Read-While-Write, NRWW) section.
XBoot provides three different types of API calls. The first type are informational and are required for operation of the API. The second type are for low-level Flash programming access. The third are for controlled firmware updating. The low-level Flash programming calls and the firmware update calls can be selectively disabled separately.
Note: XBoot automatically disables interrupts during Flash programming operations. This is necessary because, despite the name, RWW flash cannot be read and written at the same time (NRWW can be read while RWW is written, though). The interrupt bit will be automatically restored on return from an API call.
4.1 Using the API
The API requires some loader code to find and execute the APIs. This code is fully contained within xbootapi.c
and xbootapi.h
. Include these files in your application to use all of the xboot API calls.
The loader must also know the size of the boot section on the chip in order to calculate all of its offsets and find the jump table. This is defined in the header files for XMEGA chips as BOOT_SECTION_SIZE
since it is constant. Since ATMEGA chips are generally reconfigurable, it is not constant and therefore must be defined manually in xbootapi.h or passed to avr-gcc with -DBOOT_SECTION_SIZE=0x…
. The xboot makefile does this automatically when it builds xboot for many ATMEGA chips and the makefile can be freely reused for your application, simplifying this process.
Note: All the page-based flash access commands work on Flash pages SPM_PAGESIZE
bytes in size, located at addresses of multiples of SPM_PAGESIZE
.
4.2 Return Values
All of the API calls except for xboot_reset
return a value indicating success or an error code, defined in xbootapi.h
.
#define XB_SUCCESS 0 #define XB_ERR_NO_API 1 #define XB_ERR_NOT_FOUND 2 #define XB_INVALID_ADDRESS 3
XB_SUCCESS
is returned when the call succeedsXB_ERR_NO_API
is returned when the loader cannot find the API calls in xboot (either the APIs are disabled, xboot is not installed, or the loader is not looking at the right address)XB_ERR_NOT_FOUND
is returned when the particular API call is not found because it is disabled in xbootXB_INVALID_ADDRESS
is returned when an invalid address is passed to an API call
4.3 General API Functions
The general API functions are informational only.
uint8_t xboot_get_version(uint16_t *ver); uint8_t xboot_get_api_version(uint8_t *ver);
4.3.1 xboot_get_version
uint8_t xboot_get_version(uint16_t *ver);
Returns the version of xboot in ver
, MSB is major version and LSB is minor version.
4.3.2 xboot_get_api_version
uint8_t xboot_get_api_version(uint8_t *ver);
Returns the API version in ver
. Currently the only legal value is 1.
4.4 Low-level Flash Programming API
The low-level Flash programming API provides low-level access to the Flash memory. Can be disabled in xboot via ENABLE_API_LOW_LEVEL_FLASH
uint8_t xboot_spm_wrapper(void); uint8_t xboot_erase_application_page(uint32_t address); uint8_t xboot_write_application_page(uint32_t address, uint8_t *data, uint8_t erase); uint8_t xboot_write_user_signature_row(uint8_t *data);
4.4.1 xboot_spm_wrapper
uint8_t xboot_spm_wrapper(void);
Not currently implemented. Will eventually be used to execute any valid SPM command. Use with caution. Can be independently disabled in xboot via ENABLE_API_SPM_WRAPPER
4.4.2 xboot_erase_application_page
uint8_t xboot_erase_application_page(uint32_t address);
Erase the page in application memory (SPM_PAGESIZE
bytes) pointed to by address
.
4.4.3 xboot_write_application_page
uint8_t xboot_write_application_page(uint32_t address, uint8_t *data, uint8_t erase);
Write SPM_PAGESIZE
bytes of data
to the page in application memory pointed to by address
, erasing before hand if erase
is nonzero.
4.4.4 xboot_write_user_signature_row
uint8_t xboot_write_user_signature_row(uint8_t *data);
XMEGA only. Write data
to the user signature row, automatically erasing it beforehand.
4.5 Firmware Update API
The firmware update API allows client firmware to update itself atomically while making it difficult for the application to accidentally overwrite itself. The loader can call the underlying low level calls if these higher level calls are disabled, however, the firmware may not be updated after a reset if the actual firmware update capability in the bootloader is disabled.
Out of all the API calls listed here, the only actual API calls are xboot_app_temp_erase
and xboot_app_temp_write_page
. The rest do not require code running in the bootloader space, aside from xboot_install_firmware
which calls xboot_app_temp_write_page
internally, and so are implemented directly in xbootapi.c
.
Note that for all the app_temp
calls, addr = 0
is not the beggining of application flash but the beginning of the application temporary section, generally about the halfway point of the chip's memory. Hence, when using these calls, it is impossible to overwrite the application section directly.
uint8_t xboot_app_temp_erase(void); uint8_t xboot_app_temp_write_page(uint32_t addr, uint8_t *data, uint8_t erase); uint8_t xboot_app_temp_crc16_block(uint32_t start, uint32_t length, uint16_t *crc); uint8_t xboot_app_temp_crc16(uint16_t *crc); uint8_t xboot_app_crc16_block(uint32_t start, uint32_t length, uint16_t *crc); uint8_t xboot_app_crc16(uint16_t *crc); uint8_t xboot_install_firmware(uint16_t crc); void __attribute__ ((noreturn)) xboot_reset(void);
4.5.1 xboot_app_temp_erase
uint8_t xboot_app_temp_erase(void);
Erase the application temporary section
4.5.2 xboot_app_temp_write_page
uint8_t xboot_app_temp_write_page(uint32_t addr, uint8_t *data, uint8_t erase);
Write SPM_PAGESIZE
bytes of data
to page in temporary section pointed to by addr
, erasing beforehand if erase
is nonzero. Note that addr = 0
is not the beggining of application flash but the beginning of the application temporary section.
Equivalent to:
xboot_write_application_page(address + XB_APP_TEMP_START, data, erase);
4.5.3 xboot_app_temp_crc16_block
uint8_t xboot_app_temp_crc16_block(uint32_t start, uint32_t length, uint16_t *crc);
Compute the crc hash of length
bytes, starting at start
in the application temporary section and return in crc
. Note that start = 0
is not the beggining of application flash but the beginning of the application temporary section.
Equivalent to:
xboot_app_crc16_block(start + XB_APP_TEMP_START, length, crc);
4.5.4 xboot_app_temp_crc16
uint8_t xboot_app_temp_crc16(uint16_t *crc);
Computer the crc hash of the complete application temporary section and return in crc
.
Equivalent to:
xboot_app_temp_crc16_block(0, XB_APP_TEMP_SIZE, crc);
4.5.5 xboot_app_crc16_block
uint8_t xboot_app_crc16_block(uint32_t start, uint32_t length, uint16_t *crc);
Compute the crc hash of length
bytes, starting at start
in the application section and return in crc
. Note that start = 0
is actually address 0 in flash, the beginning of the application section.
xboot_app_crc16_block
uses _crc16_update
from avr-libc <util/crc16.h> with an initial value of 0 internally to compute the crc.
4.5.6 xboot_app_crc16
uint8_t xboot_app_crc16(uint16_t *crc);
Computer the crc hash of the complete application section and return in crc
.
Equivalent to:
xboot_app_crc16_block(0, XB_APP_SIZE, crc);
4.5.7 xboot_install_firmware
uint8_t xboot_install_firmware(uint16_t crc);
Write the install firmware command to the end of application temporary flash, along with the crc of the section passed in crc
. This crc must be calculated when the firmware update is built to ensure its correctness, not calculated with xboot_app_temp_crc16
on the fly.
This command does not actually install the firmware. It simply goes to the highest location in application flash and writes “XBIF” followed by the CRC. After XBoot starts (and if firmware update is enabled), it will look for this sequence, calculate the crc, install the firmware (if the crc matches), and erase the application temporary section. Since the command is stored in the flash memory, if XBoot is interrupted by a reset during the copy operation, it will simply restart the copy operation from the beginning when it starts up again, making the update operation atomic.
As this command will return, it is possible to perform clean-up and perhaps post a message that the device is going down for a firmware update before the actual update occurs. To reset the chip for the update, call xboot_reset
. Note that the firmware install process can be cancelled by erasing the last page of the application temporary section (or the whole section) before resetting the chip.
The crc functions all use _crc16_update
from avr-libc <util/crc16.h> with an initial crc of 0. The equivalent C code is:
uint16_t crc16_update(uint16_t crc, uint8_t a) { int i; crc ^= a; for (i = 0; i < 8; ++i) { if (crc & 1) crc = (crc >> 1) ^ 0xA001; else crc = (crc >> 1); } return crc; }
Calculating the crc for a new firmware must be done before it is sent to the chip for an update. As the crc passed to xboot_install_firmware
must match the crc that XBoot calculates over the entire application temporary section, the firmware must be padded to the size of the application temporary section (XB_APP_TEMP_SIZE
) with 0xff when the crc is calculated beforehand.
This checksum will be the same as the one calculated by xboot_app_temp_crc16
. The temptation to simply pass the output of this function back to xboot_install_firmware
is great. However, this is where a device can be bricked with ease if the firmware is copied into the application section incorrectly. Therefore, make sure the precalculated crc is the one passed to xboot_install_firmware
.
4.5.8 xboot_reset
void __attribute__ ((noreturn)) xboot_reset(void);
This call will trigger a device reset and will not return. In XMEGA devices, it is done via the RST.CTRL
register. In ATMEGA devices, it uses the watchdog timer, which XBoot will disable automatically after the reset.
4.6 Offsets defined in xbootapi.h
Several offsets and addresses are defined in xbootapi.h
. They are detailed in the following table, along with example values (XMEGA with 64K application flash and 8K boot flash, 72K total flash):
Name | Value | Description |
---|---|---|
PROGMEM_SIZE | 0x012000 | Size of entire program memory |
BOOT_SECTION_START | 0x010000 | Offset of boot section |
APP_SECTION_START | 0x000000 | Offset of entire application section |
APP_SECTION_SIZE | 0x010000 | Size of entire application section |
APP_SECTION_END | 0x00FFFF | End address of entire application section |
JUMP_TABLE_LOCATION | 0x0101E8 | Location of jump table in bootloader |
XB_APP_START | 0x000000 | Offset of application section for firmware updates |
XB_APP_SIZE | 0x008000 | Size of application section for firmware updates |
XB_APP_END | 0x007FFF | End address of application section for firmware updates |
XB_APP_TEMP_START | 0x008000 | Offset of application temporary section for firmware updates |
XB_APP_TEMP_SIZE | 0x008000 | Size of application temporary section for firmware updates |
XB_APP_TEMP_END | 0x00FFFF | End address of application temporary section for firmware updates |
4.7 Using the Firmware Update API
The firmware update API allows client firmware to update itself. The process is not entirely foolproof, but with sufficient testing it is very difficult to 'brick' a device with the firmware upgrade API.
The memory map of a device using the firmware update API looks something like the following (XMEGA with 64K application flash and 8K boot flash, 72K total flash):
Section | Address |
---|---|
Application 32K | 0x000000 XB_APP_START XB_APP_END 0x007FFF |
Temporary Storage 32K | 0x008000 XB_APP_TEMP_START XB_APP_TEMP_END 0x00FFFF |
XBoot 8K | 0x010000 0x011FFF |
To successfully use the firmware update API, the application must fit completely inside the application section.
Updating is performed by writing the firmware one page of SPM_PAGESIZE
bytes at a time to the temporary storage section with xboot_app_temp_write_page
, then calling xboot_install_firmware
to schedule the firmware update, and finally calling xboot_reset
to enter XBoot to reset the chip and actually install the firmware.
When the firmware is not being updated, the temporary storage section can be used for storing application data.
4.7.1 Firmware update example
The follwing example assumes get_new_firmware_crc
, read_new_firmware
, and cleanup
are functions with appropriate parameters defined elsewhere in the client firmware. This routine calls get_new_firmware_crc
to get the crc of the firmware update followed by a call to xboot_app_temp_erase
to erase the temporary section. Then it calls read_new_firmware
to read bytes into read_data, writing accumulated pages to the temporary section until it has no more bytes to read. The last page is padded with 0xff so as to not affect the crc calculation. Then it computes the crc of the application temporary section. If it matches the crc it fetched earlier, it initializes the firmware install process, cleans up, and resets the chip.
void upgrade_firmware() { uint8_t page_buffer[SPM_PAGESIZE]; uint8_t read_data[1024]; uint8_t *read_ptr; uint32_t addr = 0; uint16_t page_addr = 0; uint16_t read_bytes; uint16_t target_crc = get_new_firmware_crc(); uint16_t crc; if (xboot_app_temp_erase() != XB_SUCCESS) return; while (read_bytes = read_new_firmware(read_data, 1024)) { read_ptr = read_data; while (read_bytes > 0) { page_buffer[page_addr++] = *read_ptr++; read_bytes--; if (page_addr >= SPM_PAGESIZE) { if (xboot_app_temp_write_page(addr, page_buffer, 0) != XB_SUCCESS) return; addr += SPM_PAGESIZE; page_addr = 0; } } } if (page_addr > 0) { while (page_addr < SPM_PAGESIZE) { page_buffer[page_addr++] = 0xff; } if (xboot_app_temp_write_page(addr, page_buffer, 0) != XB_SUCCESS) return; } xboot_app_temp_crc16(&crc); if (crc != target_crc) return; if (xboot_install_firmware(target_crc) != XB_SUCCESS) return; cleanup(); xboot_reset(); }