Simplifying Embedded System Development
with Cost-Effective Bootloading Using I2C/SMBus Interfaces
By Evan Schulz, Applications Engineer, Embedded Mixed-Signal Products, Silicon Labs
Adding bootloading capabilities to embedded applications provides the framework to update firmware running on a microcontroller (MCU) at any time. This capability is beneficial if the final firmware image contains a bug, if the firmware image needs to be programmed into an MCU after the final product is assembled, or if an application’s firmware needs to be updated in the field. Any communication protocol can be used for bootloading as long as the MCU has a means of communicating using the chosen protocol and enough free code space to store the bootloader firmware.
The Inter-Integrated Circuit (I2C) or System Management Bus (SMBus) protocols are commonly used by MCUs, require only two wires for communication, and can be implemented in a small amount of firmware, making these protocols ideal candidates to use in a bootloader. Updated bootloader-ready firmware images can be sent to the target device by a separate MCU or a fixed-function communication bridge connected to a PC.
Let’s examine general bootloader design considerations, as well as I2C/SMBus-specific implementation techniques for embedded MCU applications. First, we will cover some basic information of the I2C protocol, including hardware and firmware considerations.
The I2C protocol requires two signals – serial data (SDA) and serial clock (SCL) -- for communication with other integrated circuits in a system. Both bidirectional lines require a pull-up resistor, typically in the 1k-ohm to 4.7k-ohm range and are configured in open-drain mode. In this configuration, the device driving the line can pull the line low or release the line (which will result in the external resistor pulling the signal high). I2C devices are “hot-swappable,” which means that devices can be added and removed freely from the bus. This can be particularly useful if the I2C bus is being shared between the bootloader and other integrated circuits within a system. For example, if two devices in an embedded system are communicating on the I2C bus, an external bootloading device can be attached to the bus to communicate with the MCU at the same time.
Figure 1. Example of bootloader traffic.
One drawback of implementing a shared bus is that traffic on the bus (as shown in Figure 1) can increase the amount of time necessary to bootload a device. Additional items that can affect the time required to bootload a device include the duration of a flash page erase, flash byte program and communication protocol speed. The flash page erase and byte program time are set parameters that cannot be changed. The clock frequency of the bootloader’s communication protocol should be considered, as it will directly affect the amount of time required to send a new firmware image to the target MCU. The majority of I2C devices support up to 400 KHz clock frequencies (fast-mode), although some devices support up to 2 MHz clock frequencies (high-speed mode).
Another benefit of the I2C protocol is that devices with different I/O voltages can communicate with each other, provided that all of the pins on the I2C bus are tolerant of the voltage the bus is being pulled up to. This enables a wide variety of devices to communicate on the same bus. Each device on the bus is preconfigured with a unique slave address that supports communication with “master” devices. The master device initiates all data transfers on the bus, and multiple masters can exist on the same bus. The protocol employs an arbitration scheme that provides a deterministic way to resolve two or more masters transmitting at the same time, as well as a method of flow control to allow devices with slower system clock frequencies to communicate with devices operating at faster system clock frequencies. Using two pins on a device and requiring two passive external components make the I2C bus inexpensive from a hardware perspective.
From a firmware perspective, adding I2C bootloading capabilities to an MCU will increase the overall application’s code size. Firmware tasks that need to be completed include:
· Flash erase routines
· Flash write routines
· Polled-mode communication interface implementation
· Interrupt vector redirection
· Bootloading communication protocol
· Bootload enable pin
· CRC calculation to verify the update code image.
A flash erase routine is necessary to erase the application’s code during the bootload process and a write routine is necessary to write bytes that are received by the target MCU during the bootload process. To reduce the chance of any sort of flash corruption in the bootloader application space, both routines should have boundary checks to insure that locations of flash outside the application space are not erased or written. This is very important to the system because the communication interface implementation will reside in the protected bootload area of code.
Firmware on the device manages the protection of the bootload area of code by preventing erases or writes in that area of code space. Although an interrupt-driven I2C communication interface is a valid option, a polled-mode I2C communication interface is sufficient for the system and is much simpler. If the bootloader code resides in the lowest page(s) of flash memory, the interrupts need to be redirected to the application firmware space. Compilers for 8051-compatible MCUs will generate assembly code that places interrupt vectors starting at address 0x0003, but can be configured to instead locate LJMP instructions in place of the interrupt vector table starting at location 0x0003. This enables the interrupt vectors in the application firmware space to be updated when the MCU firmware is bootloaded.
A bootloader communication protocol is needed to define the structure of bytes sent between the target MCU and the bootloader device. For example, the protocol should specify a write command that the bootloader device can send to the target MCU to initiate a firmware update. Figure 2 shows an example of a packet of bytes sent to a target MCU from a bootloader device.
Figure 2. Packet of bytes sent to target MCU from a bootloader device.
The first byte transmitted on the bus is the I2C address of the target MCU. After the target MCU acknowledges its own I2C address, a bootload write command is transmitted to inform the target MCU what data to expect from the bootloader device. Next, the starting location to write the updated code is sent on the bus, followed by the flash keys (if required by the MCU) and the updated code image. The target MCU will need a buffer in data or external data (xdata) space to store incoming bytes. As the target MCU receives a byte on the I2C bus, the target MCU will send an acknowledgement on the bus, which allows the bootloader device to send another byte. This sequence will continue as long as there is room in the target MCU’s buffer. Finally, a cyclic redundancy check (CRC) is sent to validate the updated firmware image. After the target MCU’s buffer is full and the data in the target MCU’s buffer is validated using the CRC, the target MCU can begin writing the buffer to flash, and the bootloader MCU should stop sending data.
In Figure 2, the flash keys are sent by the bootloader device to the target MCU. Although this method requires additional communication overhead, it is safer than having the flash keys hard-coded in firmware running on the target MCU. If flash keys are hard-coded on the target MCU, the chance of flash corruption on the target MCU increases. After the flash write completes on the target MCU, the target MCU should send a predefined packet back to the bootloader MCU to indicate that more bytes can be sent. The bootloader communication protocol and I2C firmware also will need to handle I2C error conditions, such as arbitration lost errors or negative-acknowledgments (NAKs). If SCL low timeouts are enabled (which is an SMBus-specific timeout condition), it will need to be handled by the protocol and firmware as well. The bootloader communication protocol and firmware will handle any sort of communication error, but will not handle manually entering bootload mode or invalid firmware images.
A bootload pin should be created to provide a fail-safe method of entering bootload mode. This can be implemented with a general-purpose input/output (GPIO) pin. For example, when the target MCU is reset, the first condition that should be checked is the state of the bootload enable pin. Depending on that state of the pin, the firmware will enter bootload mode or enter application mode. This provides a fail-safe way to enter bootload mode and can be used to recover from firmware errors. A CRC or signature check also should be checked after reset to validate the entire application image before running the application firmware in case of any sort of corruption. Before entering application mode, it is important to verify that the application image is valid. If the application image is not valid, the firmware running on the device could be harmful to the system. The image can be verified by running a CRC on the application space or by checking a signature byte located at a specific location in code space. It is recommended to use both methods to reduce the risk of any invalid firmware being executed.
The last aspect of the bootload process is determining how to send the updated firmware to the target MCU. If an I2C bootloader is selected, a general-purpose MCU or a fixed-function communication bridge can be used as the bootloader device. With the general-purpose MCU, firmware will need to be developed to handle communicating with the target MCU. In addition, the developer will need a way to pass the updated firmware from a computer to the MCU, which will require more code development. This option provides the most flexibility, but also requires the most development time. A fixed-function communication bridge can be used in place of an MCU and will not require any firmware development. For example, if an HID-to-I2C or HID-to-SMBus communication bridge is used, firmware development and driver installation would not be required. A host-side HID application would be necessary to communicate with the bridge to send updated firmware to the target MCU. Figure 3 shows an example of a system using a fixed-function communication bridge.
Figure 3. Fixed-function communication bridge example.
In any embedded system, having the ability to update firmware on an MCU provides flexibility to the developer. If an application image contains a bug, if the firmware needs to be programmed into an MCU after the final product is assembled or if an application’s firmware needs to be updated in the field, a bootloader will provide a convenient development tool.
I2C or SMBus bootloaders are excellent options for developers. The I2C and SMBus protocols require two signals and two passive external components (resistors) and can be implemented in a small amount of firmware. Updated bootloader-ready firmware images can be sent to the target device by a separate MCU or a fixed-function communication bridge connected to a computer. I2C and SMBus communication interfaces are economical, ubiquitous peripherals that are commonly used on MCUs, making them ideal candidates for use as a bootloader communication interface.