Flash Memory Structure
Each application consists of two images (one per core), the internal flash is divided into two applications with an additional two applications residing on the external flash.
Internal Flash
App0
Bootloader
App1
Main application
Flash Storage
Available for app non-volatile data, currently unused
Metadata
App data used by the bootloader
External Flash
Configuration Data
Storage location for all Gumband non-volatile user settings and configuration
App2
Storage location for any OTA updates.
App3
Golden image used in the event that the device needs to be reverted to a known working state (implementation is TBD).
Bootloader
Our bootloader is app0, it is always the first application to run and handles:
Booting of the default app
This is the normal behaviour when the device is power cycled.
Pending updates from external flash
When a new image has been written to App2, either by the OTA update process or by the bootloader itself, a flag in the configuration data region is set. The bootloader detects this flag and attempts to copy App2 into the App1 location then boot it.
USB bootloading
The bootloader allows for the uploading of either App2 or App1 over the USB interface.
Updating Firmware
When an application is compiled and built 2 elf files are created, one containing the M0+ application and one containing the M4 application. A postprocessor (cymcuelftool) signs and combines these into a single elf file and then generates a single cyacd2 file. This cyacd2 file contains the flash memory to be programmed into the device (stored in ASCII format) and is similar to a hex file. By default this image contains the entire application flash for both cores of the device but to support partial updates we split it into two separate files, one per core (using the gumband_parser application) for additional information on this see the partial updates section. Further information for the cyacd2 file format can be found in Appendix C of Cypress document AN213924.
OTA Updates
Over the air updates go through the main Gumband application running on the M0+, which means this application must be running and uncorrupted, a broken application could render future OTA updates impossible without physical intervention with the device.
The main application accepts the image over the master GBTT socket (see OTA Firmware Update Protocol) and writes that image to external flash (App2 location). The bootloader handles transferring it to internal flash.
If the image is corrupted during transfer or writing, the main application should be unaffected as there are verification steps in the Gumband application after download and in the bootloader before copying the image to internal flash.
USB Updates
Normal operation of the USB bootloader loads the application directly to internal flash (overwriting App1 immediately). The drawback to this approach is that if an unbootable corrupt or broken image is loaded it will have erased the previous main application, in this scenario the hardware will simply stay in the bootloader. The bootloader can also load the image into external flash (App2 location) just like OTA however this is a slower method of updating. This happens automatically if the flash image is specifically labelled as app2 in the cyacd2 header.
Arduino WorkFlow
Building firmware under Arduino actually only compiles the M4 elf image and combines it with a precompiled M0+ image stored in the Arduino Gumband install (subject to change 10/20...). The generated cyacd2 file is sent directly to a gumband_loader executable which performs the firmware update via the COM port, the default behaviour is to overwrite the existing internal flash on the hardware during this process (this file is not written to external flash). Based on the current board selection within the Arduino IDE a partial update (M4 app only) or full update (including the M0+ app) is performed.
Partial Firmware Updates
Overview
A normal firmware update would pushes the entire cyacd2 file to the device, however in many cases much of this file is blank flash (0x00), additionally the M0+ image (holding the Gumband Core) shouldn't change as frequently as user code so should be identical to the existing image on the hardware. As such it is possible to only push the new M4 code which can reduce the size of the cyacd2 file and thus the amount of flash that needs to be written (which is ultimately the slowest aspect of the update process) thereby reducing the programming time significantly. This also allows us to push Gumband core updates to the M0 image without changing the existing user code on the device.
File Parser
To achieve this data reduction the gumband_parser post processing executable is utilised. This application performs the following tasks:
Opens a complete cyacd2 file and determines which sections that don't need to be uploaded.
Updates the header info appId to reflect the correct image location in external or internal flash1 and the new image size.
Recalculates the final CRC for the new application size and appends it at the end of the new image.
Notes:
Memory location must always be external for OTA updates and internal for USB updates - if not a flash row error will be seen and the update will fail.
Current Issues
This only works if the M4 code is larger than the previous M4 code
This parser only works if the Linker script matches exactly what's defined at compile time in the gumband_parser executable
OTA Firmware Update Protocol
The master will issue an MQTT 'info' command to the hardware containing a json packet with the 'firmware' command (see Gumband INFO Events)
The hardware will proceed to enter RAW TCP mode on the existing GBTT socket.
The hardware will wait for the firmware exchange to happen via the firmware file exchange protocol as outlined below.
The new firmware image will be saved to external flash memory on the Gumband hardware.
After receiving and verifying the image the MCU will reboot into the bootloader which will recognise that a new image has been received and copy the external image to the internal flash of the MCU.
Once the new image is written to internal flash the bootloader will restart into this main application.
In the event that the new image is corrupt or has an error that prevents OTA updates the bootloader will support reading a 'golden image' from flash to unbrick the device and provide basic functionality. This process cannot be executed remotely and will require a low level of physical intervention (a factory reset button push sequence, TBD)
Firmware File Exchange Protocol
Once the direct TCP socket is opened the Gumband firmware exchange protocol follows the Cypress protocol almost exactly.
For an overview of the Cypress Protocol see Appendix C of document AN86526.
For an overview of the cyacd2 file format see Appendix C of document AN213924.
The following description outlines the sequence of events happening in the firmware exchange and calls out the specific C functions executing them in the cybootloaderutils source code from Cypress (used in the gumband_loader application for updates over USB).
CyBtldr_StartBootloadOperation_v1()
Steps 1 and 2 steps of the above flowchart.
Send the command to start the bootloader (0x38) along with the Product ID (from line 1 of the firmware file).
If Hardware accepts the Product ID as a match then it replies with it's Silicon ID, Silicon Rev and Bootloader Ver.
The uploader then verifies the Silicon information against the device that the new firmware was compiled for (on line 1 of the firmware file.
CyBtldr_ParseAppStartAndSize_v1()
Read the application start address and length (from line 2 of the firmware file)
CyBtldr_SetApplicationMetaData()
The PSoC4 flowchart is slightly different as it does not contain the exact same metadata exchange as the PSoC6 but this is essentially the steps 3 and 4 from the above flowchart
Send the command to set the metadata (0x4C) along with the application start address and length
Check for a valid response
ProcessMetaRow_v1()
Not sure what's going on here I don't think we're using the encryption stuff and I think the info was handled in the previous step...
ProcessDataRow_v1()
Send the command to program a new row (0x39) along with the row address (the first 4 bytes of the line in the firmware file) and the entire data row (the remaining bytes of the line from the firmware file - I believe each row is 512bytes)
Check for a valid response
Note I'm pretty sure we're not using the ERASE and VERIFY commands on the loader side as that's handled automatically in the HW, TBD.
CyBtldr_VerifyApplication_v1()
This happens after the entire file has been loaded to the hardware
Send the command to verify the application (0x4A)
Check for a valid response
CyBtldr_EndBootloadOperation()
Send command to end bootloader (0x3B)
If successful: Returns success and the Gumband hardware restarts into the bootloader which attempts to copy the new image into the App1 location and execute it.
If unsuccessful: Returns an error and the Gumband hardware returns to normal execution