TI 15.4-Stack OAD

This section describes the TI 15.4-Stack specific implementation of OAD. First the OAD protocol is explained, then the various software modules (OAD client, OAD storage, etc).

OAD Protocol

The OAD protocol is based on the principle that the OAD server initiates the firmware download process, to which the OAD client then requests OAD FW image blocks. The OAD client is responsible for requesting image blocks, handling error conditions, and re-requesting image block when required. Once the OAD has completed the OAD Client will reset and start running the new image.

Warning

If the device is connected to an external debugger when performing an OAD, the device could potentially not reset properly after a successful OAD. This is a known issue, however, simply unplugging/replugging or hard resetting the device will make the device boot properly. Read more about this in detail in Section Halt In Boot (HIB).

OAD Messages

The OAD Protocol uses requests and responses between the OAD server (Distributer) and OAD client (Target). The supported messages are described in Table 21..

Table 21. OAD Protocol Message Types
Message Client Server Description
FwVersionReq RX TX Request Firmware version of an OAD client node.
FwVersionRsp TX RX Response sent from an OAD client node contatining the firmware version currently running. The OAD server uses this information to determine if an update is necessary.
ImgIdentifyReq RX TX Request containing the OAD header of the new FW image sent to a specific OAD client node.
ImgIdentifyRsp TX RX Response from the OAD client node. If the OAD image header is accepted by the OAD client node then the response will contain a success status, if not it will contain a fail status. The reasons for an OAD client node to send a fail status could include: Invalid image type, Invalid version (client is running a newer version), or Invalid binary size.
SendOadImgBlockReq TX RX If the OAD client node accepts the image header in the ImgIdentifyReq then it will request OAD blocks using the SendOadImgBlockReq. The OAD client will implement a timeout for the SendOadImgBlockRsp and will re-request the OAD block n times if a timeout occurs.
SendOadImgBlockRsp RX TX The requested OAD block will be sent by the OAD server in a SendOadImgBlockRsp. The SendOadImgBlockReq’s and SendOadImgBlockRsp’s will continue until all blocks have been received by the OAD client. Default OAD_BLOCK_SIZE = 128.

OAD Message Flow

Figure 102. shows the flow of OAD messages throughout the entire OAD process.

../_images/fig-oad-protocol-flow.png

Figure 102. OAD Protocol Flow Diagram

The following sequence diagrams illustrate the message flow for the various stages in the OAD. The message flow assumes that the server is a Fully Functional Device (FFD) and the client is a Reduced Functional Device (RFD).

FW Version

The FW version is requested by the application on the server and responded to by the application on the client. The client will only receive the request when it sends a Data Request to the server.

../_images/fig-oad-fw-version.png

Figure 103. FwVersionReq and FwVersionRsp

Initiating OAD

An OAD is initiated by the OAD server and is accepted by the OAD client. The client will only receive the request when it sends a Data Request to the server. The OAD Initiate request will only be accepted by the client if the image header in the request is valid.

../_images/fig-oad-init.png

Figure 104. OAD process being initiated.

OAD Block Transfers

An OAD client sends a Image Block Request to to OAD server. There are three possible outcomes of this request:

  • A single block is transfered successfully
  • A single block is transfered, but requires multiple poll requests
  • A request time-out occurs and the request is retried

Figure 105. illustrates a message flow for a single block transfer, and assumes no timeouts occur. Block n represent block 0 to the 2nd to last image block.

../_images/fig-oad-block-transfer.png

Figure 105. Single Block Transfer.

Figure 106. illustrates a message flow for a single block transfer, where the OAD server does not retrieve the image block before the clients Data Request. Block n represent a random block in the image where the image block is not ready in time for the Data Request.

../_images/fig-oad-block-poll.png

Figure 106. Single Block Transer with Multiple Poll Requests.

Figure 107. illustrates a message flow for a single block transfer, where the OAD clients block request is lost, possibly due to a queue overflow. Block n represent a random block in the image where the image block request is lost. The below assumes a maximum of 3 Data Request time-outs.

../_images/fig-oad-block-transfer-retry.png

Figure 107. Single Block Timeout and is Retried.

The downfall here is that the OAD client will continue sending poll requests for a block response that will not come. This should be considered when choosing the OAD_BLOCK_REQ_POLL_DELAY. The trade off is that for longer OAD_BLOCK_REQ_POLL_DELAYs the OAD server will need to queue the block request in the MAC data queue of the embedded collector for longer. Queuing OAD blocks in the MAC data buffer for large amounts of time could lead to a queue overflow and further block requests not being serviced.

OAD Configurable Parameters

The following parameters can be configured at compile-time:

  • OAD_BLOCK_REQ_POLL_DELAY: This define is measured in ms and controls the timing on the OAD client from the sending the Block Request to sending the poll request for the OAD Block Response. A smaller OAD_BLOCK_REQ_POLL_DELAY decreases the amount of time the OAD Block is queued in the collector and hence the chance that the collector will suffer a buffer overflow. However the collector will take a finite amount of time to retrieve the OAD block, and queue the OAD Block Response, so care must be taken to make the OAD_BLOCK_REQ_POLL_DELAY long enough to allow the collector to retrieve the OAD block after receiving the OAD Block request. It is advisable to make this as small as possible in large networks where the collector needs to queue messages for many RFD’s.
  • OAD_BLOCK_REQ_RATE: This define is measured in ms. It defines the amount of time on the OAD client between 1 block request and the next block request. Reducing this will reduce the time taken to send an OAD. How quickly the OAD takes to complete is application dependent, severaly power constrained devices may need large periods between block req to allow the power source to recover. Other applications may need the OAD to complete quickly, such as in systems where a service engineer is required to perform the OAD.
  • OAD_BLOCK_SIZE: This define also affects the time taken to complete the OAD. The OAD_BLOCK_SIZE is the number of bytes sent in an OAD Block and can be from 16 to 496B. Care must be taking when setting this to a high value in environments where noise is a concern, as the chances of a block being corrupted due to an interferer increases with the size of the block.

Warning

Increasing OAD_BLOCK_SIZE past 128 on a linux VM may cause instability.

OAD Architecture

Figure 108. shows the SW Architecture for the OAD feature.

../_images/fig-oad-block-diagram.png

Figure 108. TI 15.4-Stack OAD Block Diagram

Modules

TI 15.4-Stack OAD comprises of the following software modules:

  • OAD Protocol Module
  • OAD Storage Module
  • OAD Client Module

The OAD server module is simpler than the OAD client, and does not need to handle events, error conditions, or retries. The OAD server functionality is embedded into the existing Linux collector module.

OAD Protocol Module

The OAD protocol interface provides device independent APIs, data types, and macros. The Over the Air Download (OAD) protocol module provides protocol functionality for transferring an OAD image. The APIs in this module serve as an interface to a TI-RTOS application and offers functionality for an OAD messaging protocol between OAD sever and the OAD client. The module handles the formatting and parsing of messages.

Initialization

In order to use the OAD protocol APIs, the application is required to provide application specific configuration and callbacks. For the application to process OAD Messages the following callback table must be provided:

/** @brief OADProtocol callback table
 *
 */
typedef struct
{
    fwVersionReqCb_t      pfnFwVersionReqCb; ///< Incoming FW Req
    fwVersionRspCb_t      pfnFwVersionRspCb; ///< Incoming FW Version Rsp
    oadImgIdentifyReqCb_t pfnOadImgIdentifyReqCb; ///< Incoming Image Identify Req
    oadImgIdentifyRspCb_t pfnOadImgIdentifyRspCb; ///< Incoming Image Identify Rsp
    oadBlockReqCb_t       pfnOadBlockReqCb; ///< Incoming OAD Block Req
    oadBlockRspCb_t       pfnOadBlockRspCb; ///< Incoming OAD Block Rsp
} OADProtocol_MsgCBs_t;

For the OAD module to allocate buffers and send messages the following platform specific callback must be provided:

/** @brief OADProtocol radio access functions
 *
 */
typedef struct
{
    radioAccessAllocMsg_t        pfnRadioAccessAllocMsg;   ///< Function for allocating a message buffer
    radioAccessPacketSend_t      pfnRadioAccessPacketSend; ///< Function for sending message over the radio
} OADProtocol_RadioAccessFxns_t;

OADProtocol_init() must be called before any other OADProtocol APIs. The following parametric configuration is passed:

/** @brief RF parameter struct
 *  RF parameters are used with the OADProtocol_open() and OADProtocol_Params_init() call.
 */
typedef struct {
    OADProtocol_RadioAccessFxns_t  *pRadioAccessFxns;    ///< Radio access function table
    OADProtocol_MsgCBs_t           *pProtocolMsgCallbacks;          ///< Application Callbacks for pressing packets
} OADProtocol_Params_t;

Application Interface

The OAD protocol module formats and parses the OAD messages. The following API are exposed:

/** @brief  Function to initialize the OADProtocol_Params struct to its defaults
 *
 *  @param  params      An pointer to RF_Params structure for
 *                      initialization
 *
 *  Defaults values are:
 *     pRadioAccessFxns     = {0}
 *      pCallbacks          = {0}
 */
extern void OADProtocol_Params_init(OADProtocol_Params_t *params);

/** @brief  Function that initializes the Wsn Protocol Task and creates all TI-RTOS objects
 *
 */
extern void OADProtocol_init(void);

/** @brief  Function to open the OADProtocol module
 *
 *  @param  params      An pointer to RF_Params structure for initialization
 */
extern void OADProtocol_open(OADProtocol_Params_t *params);

/** @brief  Function to parse OADProtocol packets
 *
 *  @param  srcAddr             address of the device that sent the message
 *  @param  incomingPacket      pointer to packet to be parsed
 *  @param  packetLen           length of the message
 */
extern OADProtocol_Status_t OADProtocol_ParseIncoming(void* pSrcAddr, uint8_t* incomingPacket);


/** @brief  Function to send a FW version request packet
 *
 *  @param  dstAddress          Address to send the request to
 *
 *  @return                     Status
 */
extern OADProtocol_Status_t OADProtocol_sendFwVersionReq(void* pDstAddress);

/** @brief  Function to send a FW version response packet
 *
 *  @param  dstAddress          Address to send the response to
 *  @param  fwVersion           Firmware version string to send
 *
 *  @return                     Status
 */
extern OADProtocol_Status_t OADProtocol_sendFwVersionRsp(void* pDstAddress, char *fwVersion);

/** @brief  Function to send an OAD image identify request packet
 *
 *  @param  dstAddress          Address to send the request to
 *  @param  imgId               image ID used for requesting image blocks
 *  @param  pImgInfoData        Image header
 *
 *  @return                     Status
 */
extern OADProtocol_Status_t OADProtocol_sendImgIdentifyReq(void* pDstAddress, uint8_t imgId, uint8_t *pImgInfoData);

/** @brief  Function to send an OAD image identify request packet
 *
 *  @param  dstAddress          Address to send the response to
 *  @param  status              status to send
 *
 *  @return                     Status
 */
extern OADProtocol_Status_t OADProtocol_sendOadIdentifyImgRsp(void* pDstAddress, uint8_t status);

/** @brief  Function to send an OAD block request packet
 *
 *  @param  dstAddress          Address to send the request to
 *  @param  imgId               image ID of image blocks
 *  @param  blockNum            block Number to request
 *  @param  multiBlockSize      Numer of blocks in the multi Block transfer (0 or 1 for none-multiblock)
 *
 *  @return                     Status
 *
 */
extern OADProtocol_Status_t OADProtocol_sendOadImgBlockReq(void* pDstAddress, uint8_t imgId, uint16_t blockNum, uint16_t multiBlockSize);

/** @brief  Function to send an OAD block response packet
 *
 *  @param  dstAddress          Address to send the response to
 *  @param  imgId               image ID of image blocks
 *  @param  blockNum            Block number
 *  @param  block               pointer to image block
 *
 *  @return                     Status
 *
 */
extern OADProtocol_Status_t OADProtocol_sendOadImgBlockRsp(void* pDstAddress, uint8_t imgId, uint16_t blockNum, uint8_t *block);

Usage

To use the OAD protocol module to format and parse OAD messages, the application calls the following APIs:

  • OADProtocol_init(): Initialize the OADProtocol module/task
  • OADProtocol_Params_init(): Initialize a OADProtocol_Params structure with default values. Then change the parameters from non-default values as needed.
  • OADProtocol_open(): Open an instance of the OADProtocol module, passing the initialized parameters.
  • OADProtocol_sendFwRequest(): This is an example of an OAD message that is formatted and sent.

The following code example opens OADProtocol, sends a FW version request and processes the response.:

OADProtocol_packetCBs_t OADProtocolCbs = {
  NULL, //Incoming FW Req
  fwVersionReqCb, //Incoming FW Version Rsp
  NULL, //Incoming Image Identify Req
  NULL, //Incoming Image Identify Rsp
  NULL, //Incoming OAD Block Req
  NULL, //Incoming OAD Block Rsp
};

static void fwVersionRspCb(uint16_t srcAddr, char *fwVersionStr)
{
  //Do something with srcAddr and fwVersionStr
}

void someTaskInit(void)
{
  OADProtocol_init();
}

void someTaskFxn(void)
{
  // Set Default parameters structure
  static OADProtocol_Params_t OADProtocol_params;

  // Initialize and open the Wsn Protocol Task
  OADProtocol_Params_init(&OADProtocol_params);
  OADProtocol_params.pCallbacks = &OADProtocolCbs;
  OADProtocol_open(&OADProtocol_params);

  OADProtocol_sendFwVersionReq(nodeAddress);

}

OAD Storage Module

The OAD storage interface provides device independent APIs, data types, and macros for storing the OAD image. The OAD storage module calls into oad_target, which provide the device dependent functionality to write the OAD image into flash. oad_target contains the support for internal (on-chip) and external (off-chip) OAD image storage.

OAD Storage Configuration

The oad_target module can be configured at build time to use on-chip or off-chip storage by linking or copying in the correct target c file:

  • oad_target_external_flash.c
  • oad_target_internal_flash.c

Note

For on-chip OAD, the OAD storage module must be built with FEATURE_OAD_ONCHIP defined.

Initilization

OADStorage_init() must be called before any other OADTarget APIs.

Application Interface

The OAD storage module exposes the following API for staring the OAD image:

/*********************************************************************
 * @fn      OADStorage_init
 *
 * @brief   Initialise the OAD Target Profile.
 *
 * @param   None.
 *
 * @return  None.
 */
extern void OADStorage_init(void);

/*********************************************************************
 * @fn      OADStorage_imgIdentifyRead
 *
* @brief   Read Image header and return number of blocks.
 *
 * @param   imageType   - image type indicating which image to read
 * @param   pImgHdr     - pointer to image header data
 *
 * @return  Total Blocks if image accepted, 0 if Image invalid
 */
uint16_t OADStorage_imgIdentifyRead(uint8_t imageType, OADStorage_imgIdentifyPld_t* pImgId);

/*********************************************************************
 * @fn      OADStorage_imgIdentifyWrite
 *
 * @brief   Process the Image Identify Write.  Determine from the received OAD
 *          Image Header if the Downloaded Image should be acquired.
 *
 * @param   pValue     - pointer to data to be written
 *
  * @return  Total Blocks if image accepted, 0 if Image rejected
 */
extern uint16_t OADStorage_imgIdentifyWrite(uint8_t *pValue);

/*********************************************************************
 * @fn      OADStorage_imgBlockRead
 *
 * @brief   Read Image Block.
 *
 * @param   blockNum   - block number to be written
 * @param   pBlockData - pointer for data to be read
 *
 * @return  none
 */
extern OADStorage_Status_t OADStorage_imgBlockRead(uint16_t blockNum, uint8_t *pBlockData);

/*********************************************************************
 * @fn      OADStorage_imgInfoRead
 *
 * @brief   Read an Image info.
 *
 * @param   pimgInfo - pointer for data to be read
 *
 * @return  none
 */
extern void OADStorage_imgInfoRead(uint8_t *pimgInfo);

/*********************************************************************
 * @fn      OADStorage_imgBlockWrite
 *
 * @brief   Write Image Block.
 *
 * @param   blockNum   - block number to be written
 * @param   pBlockData - pointer to data to be written
 * @param   len        - length fo block
 *
 * @return  status
 */
extern OADStorage_Status_t OADStorage_imgBlockWrite(uint32_t blockNum, uint8_t *pBlockData, uint8_t len);

/*********************************************************************
 * @fn      OADStorage_eraseImgPage
 *
 * @brief   Erases an Image page. Note this is only needed if an image
 *          page has been corrupted typically OADStorage_imgBlockWrite
 *          pre-erase all pages
 *
 * @param  none
 *
 * @return  OADStorage_Status_t
 */
extern OADStorage_Status_t OADStorage_eraseImgPage(uint32_t page);

/*********************************************************************
 * @fn      OADStorage_imgFinalise
 *
 * @brief   Process the Image Block Write.
 *
 * @param  none
 *
 * @return  status
 */
extern OADStorage_Status_t OADStorage_imgFinalise(void);

/*********************************************************************
 * @fn      OADStorage_createFactoryImageBackup
 *
 * @brief   This function creates factory image backup of current running image
 *
 * @param   None
 *
 * @return  status  OADStorage_Status_Success/OADStorage_FlashError
 *
 */
extern uint8_t OADStorage_createFactoryImageBackup(void);
/*********************************************************************
 * @fn      OADStorage_checkFactoryImage
 *
 * @brief   This function check if the valid factory image exists on external
 *          flash
 *
 * @param   None
 *
 * @return  TRUE If factory image exists on external flash, else FALSE
 *
 */
extern bool OADStorage_checkFactoryImage(void);

/*********************************************************************
 * @fn      OADStorage_close
 *
 * @brief   Releases the resource required for OAD stoarage.
 *
 * @param  none
 *
 * @return none
 */
extern void OADStorage_close(void);

OAD Client Module

The OAD client module has been designed to provide a simple and customizable implementation for the customer. In its most rudimentary form, this module is responsible for accepting/rejecting an OAD interaction based on image header criteria, storing the image in its appropriate location, and causing a device reset if the download is successful so that the downloaded application image is run by the BIM.

The OAD client module provides a service to process and generate application events for OAD client functionality. It is specific to a TI 15.4-Stack application and houses the code required for the application to act as an OAD target, reducing the code needing to be added in the application files. The OAD client uses the services provided by the OAD protocol and OAD storage modules.

Initialization

In order for the OAD client module to process and generate application events it needs to be initialized with an event handle used to set event masks as they occur and the applications semaphore that can be used to wake up the application after an event is set.

OADClient_init() must be called before the module can process OAD events. The following parametric configuration is passed:

/** @brief RF parameter struct
 *  RF parameters are used with the SOADProtocol_open() and SOADProtocol_Params_init() call.
 */
typedef struct {
    uint16_t *pEvent;             ///< Event handle to post to
#ifdef OSAL_PORT2TIRTOS
    Semaphore_Handle eventSem;    ///< Semaphore to post event
#else
    ICall_Semaphore eventSem;    ///< Semaphore to post event
#endif /*OSAL_PORT2TIRTOS */
} OADClient_Params_t;

Application Interface

The OAD Client module formats and parses the OAD messages. The following API is exposed:

 /** @brief  Function to open the SOADProtocol module
 *
 *  @param  params      An pointer to OADClient_Params_t structure for initialization
 */
extern void OADClient_open(OADClient_Params_t *params);

 /** @brief  Function to process OAD events
 *
 *  @param  pEvent      Event to process
 */
extern void OADClient_processEvent(uint16_t *pEvent);

/** @brief  Function abort OAD
*
*  @param  resume      set to true if a auto resume is required
*/
void OADClient_abort(bool resume);

/** @brief  Function abort OAD
*
*  @param  delay      time in ms to start resume
*/
void OADClient_resume(uint32_t delay);

Example Function Trace

Figure 109. shows the typical function flow for an OAD client to send a message from the application through the OAD client and OAD protocol modules to the MAC API.

../_images/fig-oad-req-message-flow.png

Figure 109. Block Request message flow.

Figure 110. shows the typical function flow for an OAD client receiving a messge from the OAD server through the OAD protocol module to the MAC API.

../_images/fig-oad-rsp-message-flow.png

Figure 110. Block Response processing.

OAD Server

There is no separate tool for the OAD server, OAD server functionality is provided by the Linux collector application as part of the TI 15.4-Stack Linux SDK. When the collector application is built without the IS_HEADLESS flag an interactive command-line interface to the collector application exposes the following functionality:

  • Devices can be selected
  • A firmware file can be selected for transfer
  • LED toggle requests can be sent to the selected device
  • Version requests can be sent to the selected device
  • Firmware update requests can be sent to the selected device

A typical view of this interface

TI Collector
Nwk: Started
Sensor 0x0001: Temp 25, RSSI -18
Sensor 0x0002: OAD Block 211 of 960


Info: Sending 0x0002 FW update Req
cmd: u

The available commands are:

  • sxx: Select a device. Example ‘s1’| ‘s0x1234’
  • o: Toggle the permit join
  • l: List devices
  • t: Send an LED toggle request to selected device
  • v: Send a version request to selected device
  • u: Send FW update request to selected device
  • d: Send disassociation request to selected device
  • fxx: Set FW file from configured OAD FW dir. Example ‘f sensor_mac_oad_cc13x2lp_app.bin’

OAD Pause and Resume

The TI 15.4-Stack OAD feature supports the following robustness features:

  1. Timeouts: This is when a the data request for a block response is not answered. The data request delay from an OAD block request being sent is set by OAD_BLOCK_REQ_POLL_DELAY ms, it is advised that this be set as short as possible to avoid unnecessary queueing of data in the co-processor. A timeout is typically caused by the Linux collector taking too long to read the OAD Block from the FW Image file (due to CPU load). The number of timeouts before a retry is set by OAD_MAX_TIMEOUTS.
  2. Retries: A retry is when the maximum number timeouts has expired before the OAD Block Response has been received. In his case the OAD Block Request is resent. The number of retires before an OAD abort is set by OAD_MAX_RETRIES.
  3. Aborts: The OAD is aborted after there are OAD_MAX_RETRIES block requests with no response. After an OAD abort the OAD is attempted to be resumed after OAD_BLOCK_AUTO_RESUME_DELAY ms, if the OAD abort again on the same block number the OAD is terminated.

The OAD is aborted if the device orphans, but when the device rejoins an OAD resume is attempted. If the device is reset / powered off during an OAD the device will attempt to resume when it rejoins. The block it resumes from is set to the first block of the page it was aborted from in case the flash page was corrupted by the power cycle.

Support for multiple OAD files

The OAD protocol supports multiple OAD images by using an Image ID that is sent when the collector initiates the OAD and then in each OAD block request / response. This ensures that the device always receives a block from the correct FW image, especially in the case where a device loses power or orphans and it is not known when it will come back online. When an OAD image file is selected on the collector it is assigned a new image ID and added to a table, when a block request is received the image ID in the block request is used to find the correct FW image file. This guarantees that a device will always get a block from the correct image, no matter how long it is offline.

OAD Default Settings

Most OAD settings are defined in oad_client.c and oad_config.h, and already discussed in OAD Pause and Resume. In addition to this you can override the OAD_BLOCK_SIZE from the default of 128 by defining it in the project options. Setting this higher than 128 is not advised as this is the setting used during system testing.

Beacon Mode, Non Beacon Mode and Frequency Hopping network modes are supported. In Non Beacon Mode and Frequency Hopping the default OAD parameters are:

#define OAD_BLOCK_REQ_RATE            200
#define OAD_BLOCK_REQ_POLL_DELAY      50
#define OAD_MAX_TIMEOUTS              3
#define OAD_MAX_RETRIES               3
#define OAD_BLOCK_AUTO_RESUME_DELAY   5000

Under normal conditions the sensor sends a block request every 200ms, with a typical file of 920 blocks this takes ~3 minutes. In Beacon Mode only one data request can be sent during 1 beacon interval. The default OAD settings are:

#define BEACON_INTERVAL             ((((0x01) << (CONFIG_MAC_BEACON_ORDER)) * \
                                      (SYMBOL_DURATION) * (BASE_SUPER_FRAME_DURATION)) / (1000)) // ms
#define OAD_BLOCK_REQ_RATE            ((BEACON_INTERVAL) - 100)
#define OAD_BLOCK_REQ_POLL_DELAY      ((BEACON_INTERVAL) - 400)
#define OAD_MAX_TIMEOUTS              3
#define OAD_MAX_RETRIES               3
#define OAD_BLOCK_AUTO_RESUME_DELAY   (CONFIG_MAC_SUPERFRAME_ORDER * 5)

Under normal conditions the OAD will take CONFIG_SUPERFRAME_ORDER * 920 seconds.

The sensor_oad project has 2 defines related to the OAD feature, defined by default in the Application/Defines/sensor_oad .opts file:

  • FEATURE_BLE_OAD This includes the OAD linker command file and OAD TI-RTOS config file that supports updates through BLE.
  • FEATURE_NATIVE_OAD This includes the TI 15.4-Stack OAD client. This results in an application that supports the OAD messages needed to receive an OAD update over the TI 15.4-Stack network.

Removing the FEATURE_NATIVE_OAD symbol will result in an application that is capable of being updated through a BLE link only. For a TI 15.4-Stack OAD both FEATURE_BLE_OAD and FEATURE_NATIVE_OAD should be defined.

Turbo OAD Protocol

The Turbo OAD Protocol was designed to speed up the traditional OAD protocol, specifically in terms of power consumption and over the air time.

Turbo OAD enables the support of using delta software updates in the OAD protocol. Delta updates are software updates that contain only the changed or updated content in the new software image.

This section describes the Turbo OAD process and how it is used in conjunction with the traditional OAD protocol described above. For information on how to enable Turbo OAD, please refer to the TI 15.4 stack OAD examples’ readme files.

../_images/fig-toad-high-level.png

Figure 111. High Level Overview of Turbo OAD integration

As shown in Figure 111., the core logic of Turbo OAD to create delta images is done with the Turbo OAD Image Tool, while reconstructing the delta image is done on the OAD Target device.

Turbo OAD Image Tool

The Turbo OAD Image tool is invoked as a post-build step by the OAD project. The purpose of the tool is to perform the delta and run-length encoding processes. The Turbo OAD Image tool takes in two binary images and outputs one delta image, which is then sent to the target OAD device through the OAD protocol described above.

The tool will also determine if a Turbo OAD is needed based on the delta image. If there is no power / image size savings by using Turbo OAD, then regular OAD will be used. The tool will output an error message indicating that the delta image was not created.

Turbo OAD Block Transfer Overview

The OAD protocol for transferring the image mainly remains the same. However, instead of directly writing received OAD blocks to external/internal flash, OAD blocks are cached in RAM and are decoded (during the OAD image transfer) before writing to flash. This has the following implications:

  • No additional flash space is required to store the delta image.
  • RAM is required to store received OAD blocks and a write buffer to store decoded image data before flashing. In general, the more RAM used, the faster the OAD and decoding process will be.
  • Since the RAM used is dynamically allocated, the OAD update could fail on initialization if there are insufficient resources to decode the delta image.
  • OAD block requests may need to be temporarily ‘paused’ if the block buffer cache is full and the device is still decoding data. OAD block requests will resume once there is space in the storage buffer to continue the transfer.

Turbo OAD Image Reconstruction Process Overview

The 15.4 OAD protocol initiates the OAD transfer by having the collector select the delta image to transfer and sending the ImgIdentifyReq to the sensor. The Turbo OAD segment header fields have been added to the ImgIdentifyReq to facilitate the transfer of delta images. For more information regarding the Turbo OAD header segment, see the Turbo OAD Delta Image Header section.

../_images/fig-toad-image-validation.png

Figure 112. Turbo OAD Image Validation and Reconstruction Overview

Warning

Performing a Turbo OAD to a target device that has been flashed with traditional OAD firmware is not supported.

The image reconstruction process is done during the OAD transfer. The decoding process consists of a temporary block cache (ring buffer) that stores received blocks which have not been decoded. Decoded blocks are written to an image buffer that is then flashed to the device when filled.

The process begins with receiving an OAD block from the collector. After a control block instruction has been received, the decoding process will begin. When the data from the OAD block has been decoded, the block will be marked to indicate that the space can be used to store a subsequent OAD block.

Note that each OAD block is a pre-determined size as specified by the OAD_BLOCK_SIZE define. A Delta Image block can be any arbitrary size, and typically will be larger than OAD_BLOCK_SIZE. The implication of this is that multiple OAD blocks must be received by the sensor until a decoding instruction can fully be processed. However, the complete instruction does not need to be received in order to begin the decoding process.

OAD blocks are still requested at the rate defined by OAD_BLOCK_REQ_RATE. However, if the OAD block cache is full, the OAD transfer will be paused until there is room to store the next block.

../_images/fig-toad-state-machine.png

Figure 113. Turbo OAD Decoding State Machine

Table 22. Turbo OAD Decoding State Machine Descriptions
State Description
Uninitialized Starting state before the Turbo OAD module has been initialized
Idle The module is waiting for new data to be received in the block cache
Decoding The module is currently decoding a delta block
Waiting The module is either currently decoding a delta block or is ready to begin decoding the next block. However, not enough data is available in the block cache to continue decoding
Done Completion of decoding the delta image

Since the length of the uncompressed delta block is variable, the decoding process will yield to the application task if the image buffer has been flushed, or a full instruction has been decoded. If an instruction has not been fully decoded, the decoding process will resume after any pending application code has been executed.

Turbo OAD Delta Image Header

Upon completion of the OAD transfer and delta image decoding, the Turbo OAD process is complete, and the normal process of verifying the image CRC and rebooting remains the same.

The modified OAD Image Header can be seen in oad_image_header_app.c as shown here:

{
    .imgID = OAD_IMG_ID_VAL,
    .crc32 = DEFAULT_CRC,
    .bimVer = BIM_VER,
    .metaVer = META_VER,                   //!< Metadata version */

    .techType      = OAD_WIRELESS_TECH_TIMAC,    //!< Wireless protocol type BLE/TI-MAC/ZIGBEE etc. */

    .imgCpStat = DEFAULT_STATE,            //!< Image copy status bytes */
    .crcStat = DEFAULT_STATE,              //!< CRC status */
    .imgNo = 0x1,                          //!< Image number of 'image type' */
    .imgVld = 0xFFFFFFFF,                  //!< In indicates if the current image in valid 0xff - valid, 0x00 invalid image */
    .len = INVALID_LEN,                     //!< Image length in bytes. */
    .softVer = SOFTWARE_VER,               //!< Software version of the image */
    .hdrLen = offsetof(imgHdr_t, fixedHdr.rfu) + sizeof(((imgHdr_t){0}).fixedHdr.rfu),
                                            //!< Total length of the image header */
    .rfu = 0xFFFF,                         //!< reserved bytes */
#ifdef __TI_COMPILER_VERSION__
    .prgEntry = (uint32_t)&prgEntryAddr,
    .imgEndAddr = (uint32_t)&flashEndAddr,
#elif  defined(__IAR_SYSTEMS_ICC__)
    .prgEntry   = (uint32_t)(__section_begin(".intvec")), //!< Program entry address */
    .imgEndAddr = (uint32_t)(__section_end("ROSTK")),
#endif
  #if defined HAL_IMAGE_A
    .imgType =  OAD_IMG_TYPE_PERSISTENT_APP,
  #else
    .imgType = OAD_IMG_TYPE_APPSTACKLIB,
  #endif
  }
Table 23. Turbo OAD Image Segment Values
Field Value
Segment Type 0x05 (Delta Image)
Wireless Technology 0xFFFD (Sub-1 Ghz) or 0xFFFB (2.4 Ghz)
Reserved 0xFF
Payload Length 0x14
Table 24. Turbo OAD Segment Header
Field Size (in bytes) Description
isDeltaImg 1 Indicates if the image is a delta image
memoryCfg 1 OAD flash configuration (off-chip or on-chip)
newImgCrc 4 Crc value of the new/updated application image to be flashed on the device
oldImgCrc 4 Crc value of the old/current application image running on the device
toadMetaVer 1 Version of the delta image header segment
toadVer 1 Version of the Turbo OAD decoding algorithm
Table 25. Modified OAD Header data
Field Value Notes
Image Length Delta Image Length Uncompressed image’s length is stored in the Turbo OAD segment header after image is reconstructed

The Turbo OAD Version and Original Image CRC field is used during the ImgIdentifyReq to ensure that the delta image can properly be reconstructed. The length of the new image is stored in the Turbo OAD header because the OAD header’s length field is the size of the delta image and not the reconstructed image size. When the OAD header is received from delta image, the length field will be replaced with the value stored in the ‘Image Length’ field.

Warning

Turbo OAD is currently not supported for on-chip OAD.