Advanced Computer Systems Course - CC2650 FW update feature

Led by Prof. Sivan Toledo
Contributors Idan Berkovits (205404130)
Omri Lifshitz (205490675)

The goal of our project was to develop firmware update “on the fly” feature, without intervening with the device’s current run.


Many IoT products, and in particular products that use wireless connections, are out of reach for physical connections and thus a different solution is required in order to update their firmware. The device has a ROM bootloader that enables updating the firmware over UART communication, but requires entering a special bootloader mode in order to do so, and thus is probably irrelevant for many IoT products. In our project, we developed a module for the CC2650 that enables updating the firmware of a running device without intervening with its current run.

Logical Modules

In this section will describe the different logical modules in the project and the developement stages.

Bootloader client

The first thing that was required for such a product was the ability to control and interact with the device. We decided to implement the protocol used by the device’s ROM bootloader. This is a packet based protocol between the bootloader and the client, where each packet is of the following format:

And the responding side responds by ACK/NACK and if necessary sends a packet in response, in the same format.

That way, we can create a client that interacts both with the module we were trying to create and the device’s ROM bootloader. The interaction with the ROM bootloader was very helpful in the first stages of the project in order to check that our client works and we are able to fully communicate with the device.

We implemented the client using .NET serial communication libraries. We created a command line interface that wraps the serial communication with the following commands:

Programming the flash using our client

The output of TI’s CCS6.2 build process is an ELF format file which needs to be loaded to the device.

The ELF file is comprised of sections of different types and permissions. During the loading process, we ignore all sections without read, write or execute permissions and thus we do not need to handle irrelevant sections such as the symbol table.

The ‘.data’ and ‘.bss’ sections are intended to be loaded to the RAM and therefore are empty and we do not need to regard them. Their data is set by the reset vector which is an executable section at address 0x0.

We therefore iterate over all sections to be written to the flash and program them using our client. The remaining sections we have to handle are:

Firmware upgrade

One of the main issues when upgrading software is avoiding overwriting the currently running code while downloading the new software.

This led us to the following design:

The first sector of the flash is reserved for a bootloader that jumps to the software we wish to run. This bootloader is never overwritten by the newly downloaded software. This sits at the first sector of the flash because the device always begins its run at address 0x0.

The CCFG is stored at the specified address as before.

The rest of the flash is separated into two slacks, each one intended for a different software; one is the currently running software and the other is designated for downloading the new software.

In order to implement this firmware upgrade mechanism, we needed to add two new functionalities to the bootloader protocol:

Wireless upgrade

To complete the goal described in the introduction, we need to implement the abovementioned bootloader protocol over wireless communication, such Bluetooth, so we will be able to update the device’s firmware without having physical connection to the device. We decided that part was out of scope for our project and focused on the parts described above.

Technical notes

Bootloader server module

We implemented software update using the device’s UART interface. We ran an independent task whose sole responsibility was to handle the UART communication. This task continuously waited in blocking mode for a byte to be received stating the size of the packet, and then waited for the full packet to be received after knowing its size. This task ran at low priority in order to not starve the rest of the system functionalities while waiting for data to be received.

We decided to implement this using the UART in blocking mode at a low priority task rather than interrupt for simplicity sake as our main goal was to show the firmware update logic and not the use of the UART interface. The use of a low priority task makes sure that we do not intervene with the device’s functionality as other events running at higher priorities and interrupt context will still run. This may be an issue if we take into consideration power consumption and so on, but again this was only done to show the firmware upgrade functionality.

ELF Loader

To parse the elf file, we used the ‘pyelftools’ python library. It gives a convenient API to iterate over the sections, including relocations.

Challenges and Unresolved Issues

The main challenge in our project was to create a relocatable executable file.

We needed to use the compiler and linker used by TI’s CCS environment as we needed to link TI’s RTOS as well. This meant that we needed to compile and link the program to a different address using the CCS environment.

In order to overcome this problem, we tried some solutions:

  1. Take the Project_Zero_Stack as an example for a project linked to a different address. We were not able to do that, we suspect because this project is used as an external library and not as a standalone.
  2. Link the program as PIC (position independent code), but after lots of research in the interface, we had to assume CCS does not support this functionality.
  3. We did manage to link the ELF as a relocatable executable using the CCS environment. This meant, that during the loading of the ELF to the flash, all of the relocations needed to be resolved. In order to do that we used many TI and ARM technical reference explaining how to resolve each relocation. Unfortunately, as we went along with developing the relocation resolver, we found that it is more difficult than we expected: the different reference manuals we used were contradicting each other, and other tools like IDA (Interactive Disassembler) did not help. In addition, it would have been very difficult to debug relocation problems (which would have been fine if we found a decent manual). Therefore, we decided settle for the parts we implemented so far, specified above.