This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC1310: Turbo OAD CRC Failed

Part Number: CC1310
Other Parts Discussed in Thread: UNIFLASH

Hi guys:

Example of using two CC1310 MCUs to test OAD
SDK version: Simplelink_cc13x0usdk_4_20-02_07, ti154stack_Lux_x64_8_30_00_05

I tried to send an OAD request on Linux, But the sensor will display CRC Failed

The steps for me to create an incremental package are as follows:

  1.  python hexmerge.py -o devices_1310.hex "--overlap=error" ../Release/devices_1310_v0.9.0.hex bim_1310.hex
    Download devices_1310.hex to MCU via UniFlash
  2. python oad_image_tool.py -v 0x0090 -t offchip -i app ../Release/devices_1310_v0.9.0.hex -ob a.bin -m 0x14F0
  3. python oad_image_tool.py -v 0x0091 -t offchip -i app ../Release/devices_1310_v0.9.1.hex -ob b.bin -m 0x14F0

  4. python toad_image_tool.py -oimg a.bin -nimg b.bin -o ab.dim 
    Transfer ab.dim to Linux and issue it to the sensor using the 'u' commandimage.png
    The above figure shows the output after the last instruction of sensor load

    I have also tried removing the offchip in steps 2-3 above, but the result was CRC Failed
     Can you help me? Thank you 

 

  • I have defined FEATURE_TOAD
  • Hi faker,

    Does a regular OAD work? Is it just failing for Turbo OAD? Is there a difference between image a.bin and b.bin, or just the version number?

    I suggest you use a version of the TI 15.4 Linux SDK the matches the embedded device SDK (https://www.ti.com/tool/download/TI-15-4-STACK-GATEWAY-LINUX-SDK/4.40.00.03), there might have been changes in the way the CRC is calculated.

    Best regards,

    Daniel

  • Hi Daniel:


    1. Ordinary OAD can be used
    The version numbers were modified in the a.bin and b.bin codes, and also when generating the bin from hex (as can be seen from the above command)
    3. I will try the version SDK you mentioned

    Best regards,

    faker

  • Hi Daniel:

    I have tried the ti154stack_ linux_ x64_ 4_ 40_00 SDK
    The result is the same, Turbo OAD will still fail, incremental OAD still results in CRC failure

    Best regards,

    faker

  • Hi faker,

    Can you share the version of the modules you are using? Try to use the ones showned in the Readme

    • Python 2.7
    • Python intelhex-2.1
    • Python crcmod-1.7

    python -V
    
    pip show crcmod
    
    pip show intelhex

    Best regards,

    Daniel

  • Hi Daniel:


    python -V
    Python 2.7.18
    Name: crcmod
    Version: 1.7
    Name: intelhex
    Version: 2.2.1

    Best regards,

    faker

  • Hi Daniel:

    Has it been tested

    Best regards,

    faker

  • Hi faker,

    I'm sorry, I had issues with my Linux setup. I will start testing it now, I will give you and updated tomorrow.

    Best regards,

    Daniel

  • Hi Daniel:

    okay, thank you. I will wait for your response

    Best regards,

    faker

     

  • Hi faker,

    I don't have an answer yet, but I have my environment working, I believe you have already done these steps, but for those reading:

    • TIRTOS_IN_ROM is defined by default in the CC13x0 projects but not for the TI 15.4 Linux SDK, so you need to define 
      CFLAGS += -DTIRTOS_IN_ROM
       in ti15stack_linux_/example/collector/Makefile and rebuild the host_collector by running build_all.sh
    • if build_all.sh returns with errors, check  CC1312R: ti154stack_linux_x64_4_40_00_03 examples don't build out the box 

    Now, back to the TOAD, I can see in the collector_log.txt that the delta image "ab.dim" is being detected as a regular image and not as a TOAD image, I believe this might be the reason for the CRC error. I'll get back to you later.

    Best regards,

    Daniel

  • Hi faker,

    I haven't found the issue yet, but I can definitely see the same. Not sure whether it is on the generated delta image or the embedded side when reading the headers.

    Best regards,

    Daniel

  • Hi Daniel:

    Thank you for continuing to investigate this issue, and I am still trying to resolve it

    Is the OAD of CC1312 and CC1310 incompatible? I tried to use the load command for CC1312 in the Linux I ported, but the first frame command reported an error, which means the packet length cannot be read

    Best regards,

    faker

  • Hi Daniel:

    May I ask if there are any results yet

    Best regards,

    faker

  • Hi faker,

    I have looked into it but with no success yet, unfortunately I haven't had much time either.

    Best regards,

    Daniel

  • Hi Daniel:

    I checked the code on the Linux side, and it checks whether the 16th byte is' TURBOAD 'to determine if it is TOAD,
    I have checked my 'ab. dim' file and found that 'TURBOAD' is located at 0x500
    So it basically indicates that there was a problem compiling the incremental package with 'toad_image_tool. py' using this file. I hope you can continue to study it

    Best regards,

    faker

  • Hi faker, 

    Yes, you are right, however I haven't been able to get it work without a CRC error yet. I'm sorry for the delay.

    Best regards,

    Daniel

  • Hi Daniel:

    We hope to resolve this issue soon, as this feature appears to be very important

    Best regards,

    Daniel

  • Hi Daniel:

    Excuse me, is there any progress

    Best regards,

    Daniel

  • Hi faker,

    I apologize for the delay, it took quite some work to find the issue, I appreciate your patience.

    Please replace the following 3 files:

    1. On the Linux side, replace example/collector/collector.c

    There were two issues here: first, there was a wrong offset in fseek(oadFile, IMG_HDR_ADDR, SEEK_SET); it was hardcoded to 0x00 which didn't work for TIRTOS_IN_ROM. Second, sizeof("TURBOOAD") returns 9 due to the null terminator. This caused the image to be recognized as a regular OAD and not a Turbo OAD.

    After replacing the file, rebuild your Linux host:

    ./build_all.sh clean
    ./build_all.sh

    4265.collector.c

    2. After fixing the first part, the image was still rejected because a fail in the boundary check, so I replaced a "less than" with a "less than or equal".

    After this change, rebuild your project. Since the CCS project is copying (instead of linking) from the source code, you have to replace it locally inside your project and in the SDK for future use.

    simplelink_cc13x0_sdk_4_20_02_07/source/ti/ti154stack/apps/sensor_oad_offchip/CC13X0_LAUNCHXL/oad_client.c

    CCS project/Application/oad_client.c

    Now rebuild your project and generate a new .hex

    /******************************************************************************
    
     @file oad_client.c
    
     @brief OAD Client
    
     Group: WCS LPC
     Target Device: cc13x0
    
     ******************************************************************************
     
     Copyright (c) 2016-2022, Texas Instruments Incorporated
     All rights reserved.
    
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions
     are met:
    
     *  Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
    
     *  Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
    
     *  Neither the name of Texas Instruments Incorporated nor the names of
        its contributors may be used to endorse or promote products derived
        from this software without specific prior written permission.
    
     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    
     ******************************************************************************
     
     
     *****************************************************************************/
    #ifndef OAD_CLIENT_H
    #define OAD_CLIENT_H
    
    #ifdef FEATURE_NATIVE_OAD
    /******************************************************************************
     Includes
     *****************************************************************************/
    #include <string.h>
    #include <stdint.h>
    
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Semaphore.h>
    
    #include "mac_util.h"
    #include "jdllc.h"
    #include "ssf.h"
    #include "sensor.h"
    #include "board_lcd.h"
    #include "util_timer.h"
    #include "mac_timer.h"
    #include "mac_spec.h"
    #include "oad_client.h"
    
    #include "native_oad/oad_protocol.h"
    #include "native_oad/oad_storage.h"
    
    #if defined(FEATURE_TOAD)
    #include "turbo_oad/turbo_oad.h"
    #endif
    
    #include "hal_board.h"
    #include <ti/devices/DeviceFamily.h>
    #include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
    #include DeviceFamily_constructPath(driverlib/flash.h)
    
    /******************************************************************************
     Constants and definitions
     *****************************************************************************/
    // Software version
    #define FW_VERSION          "v2.0"
    
    #ifdef FEATURE_TOAD
    // Version of Turbo OAD header used for creating the delta image
    #define TOAD_META_VERSION   0x01
    
    // Version of Turbo OAD used for creating the delta image
    #define TOAD_VERSION        0x01
    #endif
    
    /* symbol durations for sub-1g only */
    #if (CONFIG_PHY_ID == APIMAC_STD_US_915_PHY_1)
        #define SYMBOL_DURATION         (SYMBOL_DURATION_50_kbps)  //us
    #elif (CONFIG_PHY_ID == APIMAC_GENERIC_US_915_PHY_132)
        #define SYMBOL_DURATION         (SYMBOL_DURATION_200_kbps) //us
    #elif (CONFIG_PHY_ID == APIMAC_GENERIC_US_LRM_915_PHY_129)
        #define SYMBOL_DURATION         (SYMBOL_DURATION_LRM)      //us
    #else
        #define SYMBOL_DURATION         (SYMBOL_DURATION_50_kbps)  //us
    #endif
    
    #define BEACON_INTERVAL             ((((0x01) << (CONFIG_MAC_BEACON_ORDER)) * \
                                          (SYMBOL_DURATION) * (BASE_SUPER_FRAME_DURATION)) / (1000)) // ms
    
    #if (CONFIG_MAC_SUPERFRAME_ORDER == 15)
    #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
    #else
    #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 ((BEACON_INTERVAL) * 5)
    #endif
    
    /*!
     OAD block variables.
     */
    static uint16_t oadBNumBlocks = 0;
    static uint16_t oadBlock = 0;
    static ApiMac_sAddr_t oadServerAddr = {0};
    static bool oadInProgress = false;
    static uint8_t oadImgId = 0;
    static uint8_t oadRetries = 0;
    static uint8_t oadTimeouts = 0;
    
    #if defined(FEATURE_TOAD)
    uint8_t oadImgIdPld[OAD_IMG_FULL_HDR_SIZE];
    #else
    uint8_t oadImgIdPld[OADProtocol_IMAGE_ID_LEN];
    #endif
    
    OADClient_Params_t oadClientParams;
    
    static Clock_Struct oadClkStruct;
    static Clock_Handle oadClkHandle;
    
    static uint32_t defaultPollInterval;
    
    /* Turbo OAD control variables */
    bool isToadImage = false;
    static bool isOADPaused = false;
    
    /******************************************************************************
     Local function prototypes
     *****************************************************************************/
    
    static void oadClockInitialize(void);
    static void oadClockCallback(UArg a0);
    static void oadClockSet(uint32_t oadTimeout);
    static void displayOadBlockUpdate(uint16_t oadBlock, uint16_t oadBNumBlocks, uint8_t retries);
    static void displayOadStatusUpdate(OADStorage_Status_t status);
    static void oadFwVersionReqCb(void* pSrcAddr);
    static void oadImgIdentifyReqCb(void* pSrcAddr, uint8_t imgId, uint8_t *imgMetaData);
    static void oadBlockRspCb(void* pSrcAddr, uint8_t imgId, uint16_t blockNum, uint8_t *blkData);
    void* oadRadioAccessAllocMsg(uint32_t msgLen);
    static OADProtocol_Status_t oadRadioAccessPacketSend(void* pDstAddr, uint8_t *pMsg, uint32_t msgLen);
    
    #if defined(FEATURE_TOAD)
    static void toadWriteDeltaBlock(uint8_t* pData, uint32_t len);
    #endif
    
    /******************************************************************************
     Callback tables
     *****************************************************************************/
    
    static OADProtocol_RadioAccessFxns_t  oadRadioAccessFxns =
        {
          oadRadioAccessAllocMsg,
          oadRadioAccessPacketSend
        };
    
    static OADProtocol_MsgCBs_t oadMsgCallbacks =
        {
          /*! Incoming FW Req */
          oadFwVersionReqCb,
          /*! Incoming FW Version Rsp */
          NULL,
          /*! Incoming Image Identify Req */
          oadImgIdentifyReqCb,
          /*! Incoming Image Identify Rsp */
          NULL,
          /*! Incoming OAD Block Req */
          NULL,
          /*! Incoming OAD Block Rsp */
          oadBlockRspCb
        };
    
    /******************************************************************************
     Public Functions
     *****************************************************************************/
    
    /*!
     Initialize this application.
    
     Public function defined in sensor.h
     */
    void OADClient_open(OADClient_Params_t *params)
    {
        OADProtocol_Params_t OADProtocol_params;
    
        memcpy(&oadClientParams, params, sizeof(OADClient_Params_t));
    
        OADProtocol_Params_init(&OADProtocol_params);
        OADProtocol_params.pRadioAccessFxns = &oadRadioAccessFxns;
        OADProtocol_params.pProtocolMsgCallbacks = &oadMsgCallbacks;
    
        OADProtocol_open(&OADProtocol_params);
    
        oadClockInitialize();
    }
    
    /*!
     Application task processing.
    
     Public function defined in sensor.h
     */
    void OADClient_processEvent(uint16_t *pEvent)
    {
        /* Is it time to send the next sensor data message? */
        if(*pEvent & SENSOR_OAD_TIMEOUT_EVT)
        {
            static int32_t lastBlock = -1;
            static bool oadComplete = false;
    
            if(oadComplete)
            {
                /* reset to BIM */
                SysCtrlSystemReset();
    
                /* Clear complete flag in case reset did not work */
                oadComplete = false;
    
            }
    
            if(oadInProgress)
            {
                if(oadBlock == lastBlock && (!isToadImage || (isToadImage && !isOADPaused)))
                {
                    if(oadTimeouts++ > OAD_MAX_TIMEOUTS)
                    {
                        /*
                         * allow 3 retries before aborting
                         */
                        if(oadRetries++ > OAD_MAX_RETRIES)
                        {
                            /*abort OAD with auto resume */
                            OADClient_abort(true);
                        }
                        else
                        {
                            /* reduce the lastBlock by 1 to force a re-request */
                            lastBlock -= 1;
                            oadTimeouts = 0;
                        }
                    }
                    else
                    {
                        /*
                         * req timed out, set timer again
                         * */
                        oadClockSet(OAD_BLOCK_REQ_RATE);
                        Ssf_setPollClock(OAD_BLOCK_REQ_POLL_DELAY);
                    }
                }
    
                if( (oadBlock < oadBNumBlocks) && (oadBlock != lastBlock) )
                {
                    if(oadBlock == 0)
                    {
                        /* Only store image header and image ID on first block */
                        Ssf_oadInfoUpdate((uint16_t*) &lastBlock, oadImgIdPld, &oadImgId, &oadServerAddr);
                    }
                    else
                    {
                        Ssf_oadInfoUpdate((uint16_t*) &lastBlock, 0, 0, 0);
                    }
                    lastBlock = oadBlock;
    
                    /* Send Block request specifying number of blocks per multi
                     * blocks. The multi block hard coded to 1 here, but it is
                     * intended that an advanced user would use information from
                     * oadRetries and Sensor_msgStats to implement a sliding window
                     * algorithm
                     */
                    OADProtocol_sendOadImgBlockReq(&oadServerAddr, oadImgId, oadBlock, 1);
    
                    /* update display */
                    displayOadBlockUpdate(oadBlock, oadBNumBlocks, oadRetries);
    
                    /*
                     * Send poll request to get rsp in OAD_BLOCK_REQ_POLL_DELAY ms
                     * to flush the block response from OAD servers MAC Queue
                     */
                    Ssf_setPollClock(OAD_BLOCK_REQ_POLL_DELAY);
    
                    /*
                     * set timer to time out in OAD_BLOCK_REQ_RATE ms to request
                     * or re-request block
                     * */
                    oadClockSet(OAD_BLOCK_REQ_RATE);
                }
    #if defined(FEATURE_TOAD)
                else if(oadBlock >= oadBNumBlocks && (!isToadImage ||
                        (isToadImage && TOAD_getState() == TOAD_State_decodingDone)))
                {
                    if (isToadImage)
                    {
                        TOAD_close();
                        isToadImage = false;
                        isOADPaused = false;
    
                        Util_clearEvent(pEvent, SENSOR_TOAD_DECODE_EVT);
                    }
    #else
                else if(oadBlock >= oadBNumBlocks)
                {
    #endif
    
                    OADStorage_Status_t status;
    
                    /*
                     * Check that CRC is correct and mark the image as new
                     * image to be booted in to by BIM on next reset
                     */
                    status = OADStorage_imgFinalise();
    
                    /* Display result */
                    displayOadStatusUpdate(status);
    
                    /* Stop any further OAD message processing */
                    oadInProgress = false;
                    lastBlock = -1;
                    oadBlock = 0;
    
                    /* set block count to 0 in NV */
                    Ssf_oadInfoUpdate(&oadBlock, 0, 0, 0);
    
                    /* Set poll rate back to default */
                    Jdllc_setPollRate(defaultPollInterval);
    
                    /* If OAD was successful reset the device */
                    if(status == OADStorage_Status_Success)
                    {
                        oadComplete = true;
    
                        /*
                         * Set timer 1 more time to reset the device
                         * after the display has been written to
                         */
                        oadClockSet(OAD_BLOCK_REQ_RATE);
                    }
                    else
                    {
                        //something went wrong abort
                        oadClockSet(0);
                    }
                }
            }
    
            /* Clear the event  */
            Util_clearEvent(pEvent, SENSOR_OAD_TIMEOUT_EVT);
        }
    
    #if defined(FEATURE_TOAD)
        if(*pEvent & SENSOR_TOAD_DECODE_EVT)
        {
            // Start or continue decoding the next stored delta block
            if (TOAD_decode() == TOAD_Status_decodingError)
            {
                Util_clearEvent(pEvent, SENSOR_TOAD_DECODE_EVT);
                OADClient_abort(false);
            }
    
            // Resume OAD if there is available room in the block cache
            if (isOADPaused && !TOAD_isBlockCacheFull())
            {
                Ssf_setPollClock(OAD_BLOCK_REQ_POLL_DELAY);
                oadClockSet(OAD_BLOCK_REQ_RATE);
                isOADPaused = false;
            }
    
            /* If TOAD is blocked on waiting for data in the block cache,
             * stop attempting to continue decoding until more data arrives
             */
            if (TOAD_getState() == TOAD_State_decodingWaiting ||
                (TOAD_getState() == TOAD_State_idle && TOAD_isBlockCacheEmpty()))
            {
                Util_clearEvent(pEvent, SENSOR_TOAD_DECODE_EVT);
            }
    
            if (TOAD_getState() == TOAD_State_decodingDone)
            {
                // Set the timer to finalize the image
                oadClockSet(OAD_BLOCK_REQ_RATE);
                Util_clearEvent(pEvent, SENSOR_TOAD_DECODE_EVT);
            }
        }
    #endif
    }
    
    /*!
     Abort the OAD.
    
     Public function defined in sensor.h
     */
    void OADClient_abort(bool resume)
    {
        if(oadInProgress)
        {
            static uint16_t prevResumeBlock = 0;
    
            /* Stop OAD timer */
            oadClockSet(0);
    
            /* Set poll rate back to default */
            Jdllc_setPollRate(defaultPollInterval);
    
            displayOadStatusUpdate(OADStorage_Aborted);
    
            /* start timer to auto resume */
            if( resume &&
                (OAD_BLOCK_AUTO_RESUME_DELAY > 0) &&
                (oadBlock != prevResumeBlock) )
            {
                oadClockSet(OAD_BLOCK_AUTO_RESUME_DELAY);
                prevResumeBlock = oadBlock;
                oadRetries = 0;
                oadTimeouts = 0;
            }
            else
            {
                /*abort OAD */
                oadInProgress = false;
                OADStorage_close();
    
    #if defined(FEATURE_TOAD)
                if (isToadImage)
                {
                    TOAD_close();
                    isToadImage = false;
                    isOADPaused = false;
                }
    #endif
            }
        }
    }
    
    /*!
     Resume the OAD.
    
     Public function defined in sensor.h
     */
    void OADClient_resume(uint32_t delay)
    {
    
        Ssf_getOadInfo(&oadBlock, oadImgIdPld, &oadImgId, &oadServerAddr);
    
        if(oadBlock != 0)
        {
            uint32_t page = (oadBlock * (OAD_BLOCK_SIZE)) / EFL_PAGE_SIZE;
            /* Round block number to nearest page boundary in case flash page is
             * corrupted
             */
            oadBlock = (page * EFL_PAGE_SIZE) / (OAD_BLOCK_SIZE);
    
            // Flash pages need to be erased for delta images
            oadBNumBlocks = OADStorage_imgIdentifyWrite(oadImgIdPld, true);
            if(oadBNumBlocks > 0)
            {
                oadInProgress = true;
    
    #if defined(FEATURE_TOAD)
                // Restart from the beginning for delta images
                if(memcmp(((OADProtocol_imgIdentifyPld_t*)oadImgIdPld)->deltaImgID, TOAD_IMG_ID_VAL, TOAD_IMG_ID_LEN) == 0)
                {
                    oadRetries = 0;
                    oadTimeouts = 0;
                    oadBlock = 0;
                    isToadImage = true;
    
                    TOAD_params_t toadParams;
                    TOAD_paramsInit(&toadParams);
    
                    toadParams.pAppImg = (uint8_t*)APP_START_ADDR;
                    toadParams.oadBlockSize = OAD_BLOCK_SIZE;
                    toadParams.pfnWriteDataCb = toadWriteDeltaBlock;
                    toadParams.newImgLen = ((OADProtocol_imgIdentifyPld_t*)oadImgIdPld)->newImgLen * HAL_FLASH_WORD_SIZE;
    
                    TOAD_status_t toadStatus = TOAD_Status_initializationError;
                    toadStatus = TOAD_init(&toadParams);
    
                    if(toadStatus != TOAD_Status_success)
                    {
                        OADClient_abort(false);
                        return;
                    }
                }
                else
                {
    #endif
                    //erase the page in case it was corrupted during a reset / power cycle
                    OADStorage_eraseImgPage(page);
    #if defined(FEATURE_TOAD)
                }
    #endif
                oadClockSet(delay);
            }
        }
    }
    
    /******************************************************************************
     Local Functions
     *****************************************************************************/
    /*!
     * @brief   OAD timeout handler function.
     *
     * @param   a0 - ignored
     */
    static void oadClockCallback(UArg a0)
    {
        (void)a0; /* Parameter is not used */
    
        Util_setEvent(oadClientParams.pEvent, SENSOR_OAD_TIMEOUT_EVT);
    
        /* Wake up the application thread when it waits for clock event */
        Semaphore_post(oadClientParams.eventSem);
    }
    
    static void oadClockInitialize(void)
    {
        /* Initialize the timers needed for this application */
        oadClkHandle = Timer_construct(&oadClkStruct,
                                            oadClockCallback,
                                            OAD_BLOCK_REQ_RATE,
                                            0,
                                            false,
                                            0);
    }
    
    /*!
     Set the oad clock.
    
     Public function defined in ssf.h
     */
    static void oadClockSet(uint32_t oadTimeout)
    {
        /* Stop the Reading timer */
        if(Timer_isActive(&oadClkStruct) == true)
        {
            Timer_stop(&oadClkStruct);
        }
    
        /* Setup timer */
        if ( oadTimeout )
        {
            Timer_setTimeout(oadClkHandle, oadTimeout);
            Timer_start(&oadClkStruct);
        }
    }
    
    /*!
     The application calls this function to indicate oad block.
    
     Public function defined in ssf.h
     */
    static void displayOadBlockUpdate(uint16_t oadBlock, uint16_t oadBNumBlocks, uint8_t retries)
    {
        static uint8_t lastRetries;
    
        LCD_WRITE_STRING_VALUE("OAD Block: ", oadBlock + 1, 10, 3);
        LCD_WRITE_STRING_VALUE("of ", oadBNumBlocks, 10, 3);
    
        if(lastRetries != retries)
        {
            LCD_WRITE_STRING_VALUE("OAD Retries: ", retries, 10, 3);
        }
    }
    
    /*!
     The application calls this function to indicate oad block.
    
     Public function defined in ssf.h
     */
    static void displayOadStatusUpdate(OADStorage_Status_t status)
    {
        switch(status)
        {
        case OADStorage_Status_Success:
            LCD_WRITE_STRING("OAD completed successfully", 3);
            break;
        case OADStorage_CrcError:
            LCD_WRITE_STRING("OAD CRC failed", 3);
            break;
        case OADStorage_Aborted:
            LCD_WRITE_STRING("OAD aborted", 3);
            break;
        default:
            LCD_WRITE_STRING("OAD failed", 3);
            break;
        }
    }
    
    /*!
     * @brief      FW Version request callback
     */
    static void oadFwVersionReqCb(void* pSrcAddr)
    {
        char fwVersionStr[OADProtocol_FW_VERSION_STR_LEN] = FW_VERSION;
    
        //Send response back
        OADProtocol_sendFwVersionRsp(pSrcAddr, fwVersionStr);
    }
    
    #if defined(FEATURE_TOAD)
    /******************************************************************************
     * @fn     toadWriteDeltaBlock
     *
     * @brief  Writes a decoded delta block to flash
     *
     * @param  pData - address of the block data to write
     * @param  len   - amount of data to write to flash
     */
    static void toadWriteDeltaBlock(uint8_t* pData, uint32_t len)
    {
        static uint32_t writeOffset = 0;
        OADStorage_Status_t status = OADStorage_Status_Success;
    
        status = OADStorage_imgBlockWrite(writeOffset, pData, len);
    
        if(status != OADStorage_Status_Success)
        {
            displayOadStatusUpdate(status);
    
            /* OAD abort with no auto resume */
            OADClient_abort(false);
            return;
        }
    
        writeOffset += len;
    }
    #endif
    
    /*!
     * @brief      OAD image identify request callback
     */
    static void oadImgIdentifyReqCb(void* pSrcAddr, uint8_t imgId, uint8_t *imgMetaData)
    {
        if (oadInProgress)
        {
            //Send fail response back, but continue existing OAD thats in progress
            OADProtocol_sendOadIdentifyImgRsp(pSrcAddr, 0);
            return;
        }
    
        /* Store the img header in case of resume */
    #if defined(FEATURE_TOAD)
        memcpy(oadImgIdPld, imgMetaData, OAD_IMG_FULL_HDR_SIZE);
    #else
        memcpy(oadImgIdPld, imgMetaData, OADProtocol_IMAGE_ID_LEN);
    #endif
    
        /*store the image ID to get blocks for */
        oadImgId = imgId;
    
        OADStorage_Status_t status = OADStorage_Failed;
        oadBNumBlocks = OADStorage_imgIdentifyWrite(oadImgIdPld, true);
    
        oadServerAddr.addrMode = ((ApiMac_sAddr_t*)pSrcAddr)->addrMode;
        if(oadServerAddr.addrMode == ApiMac_addrType_short)
        {
            oadServerAddr.addr.shortAddr = ((ApiMac_sAddr_t*)pSrcAddr)->addr.shortAddr;
        }
        else
        {
            memcpy(oadServerAddr.addr.extAddr, ((ApiMac_sAddr_t*)pSrcAddr)->addr.extAddr,
                   (APIMAC_SADDR_EXT_LEN));
        }
    
        if(oadBNumBlocks)
        {
            OADProtocol_imgIdentifyPld_t* imgIdentifyPld = (OADProtocol_imgIdentifyPld_t*)&oadImgIdPld;
            if(memcmp(&imgIdentifyPld->deltaImgID, (char *)TOAD_IMG_ID_VAL, TOAD_IMG_ID_LEN) == 0)
            {
    #if defined(FEATURE_TOAD)
                // Also ensure delta image is compatible by comparing CRC from internal flash
                uint32_t oldImgLen = imgIdentifyPld->oldImgLen * HAL_FLASH_WORD_SIZE;
                uint16_t imgDataCrc = 0xFFFF;
    
                /* Validate header len is within the bounds of internal flash before
                 * calculating CRC. Use <= so an image that fills exactly up to the
                 * page-31 boundary (startAddr*4 + oldImgLen == PAGE_SIZE*31) is
                 * accepted; last valid byte is at (PAGE_SIZE*31 - 1).
                 */
                if ((imgIdentifyPld->startAddr * HAL_FLASH_WORD_SIZE) + oldImgLen <= (HAL_FLASH_PAGE_SIZE * 31))
                {
                    // Start CRC calculation after OAD header bytes
                    imgDataCrc = OADStorage_crcCalc(OAD_IMG_HDR_ADDR / HAL_FLASH_PAGE_SIZE,
                                                   (OAD_IMG_HDR_ADDR % HAL_FLASH_PAGE_SIZE) + OADProtocol_IMAGE_ID_LEN,
                                                    HAL_FLASH_PAGE_SIZE, oldImgLen - (OADProtocol_IMAGE_ID_LEN + BIM_HEADER_ADDR),
                                                    false);
                }
    
                if (imgIdentifyPld->toadMetaVer == TOAD_META_VERSION &&
                        imgIdentifyPld->toadVer == TOAD_VERSION &&
                        imgIdentifyPld->oldImgCrc == imgDataCrc)
                {
                    isToadImage = true;
    
                    TOAD_params_t toadParams;
                    TOAD_paramsInit(&toadParams);
    
                    toadParams.pAppImg = (uint8_t*)APP_START_ADDR;
                    toadParams.oadBlockSize = OAD_BLOCK_SIZE;
                    toadParams.pfnWriteDataCb = toadWriteDeltaBlock;
                    toadParams.newImgLen = imgIdentifyPld->newImgLen * HAL_FLASH_WORD_SIZE;
    
                    TOAD_status_t toadStatus = TOAD_Status_initializationError;
                    toadStatus = TOAD_init(&toadParams);
    
                    // Sending delta Turbo OAD image if no initialization errors
                    status = (toadStatus == TOAD_Status_success ? OADStorage_Status_Success : OADStorage_Rejected);
                }
                else
                {
                    /* Reject delta image if version is unsupported, built with the
                     * wrong memory configuration, or created from a different old image
                     */
                    status = OADStorage_Rejected;
                }
    #else
                // Reject delta image if Turbo OAD is not enabled
                status = OADStorage_Rejected;
    #endif
            }
            else
            {
                // Sending regular OAD image
                status = OADStorage_Status_Success;
            }
        }
        else
        {
            // Error reading image since oadBNumBlocks is 0
            status = OADStorage_Rejected;
        }
    
        if (status == OADStorage_Status_Success)
        {
            oadInProgress = true;
            oadRetries = 0;
            oadTimeouts = 0;
            oadBlock = 0;
    
            //Save current Poll interval
            defaultPollInterval = Ssf_getPollClock();
    
            //Send success response back
            OADProtocol_sendOadIdentifyImgRsp(pSrcAddr, 1);
    
            //Set OAD time out to OAD_BLOCK_REQ_RATE ms
            oadClockSet(OAD_BLOCK_REQ_RATE);
        }
        else
        {
            displayOadStatusUpdate(status);
    
            //Send fail response back
            OADProtocol_sendOadIdentifyImgRsp(pSrcAddr, 0);
        }
    }
    
    /*!
     * @brief      OAD image block response callback
     */
    static void oadBlockRspCb(void* pSrcAddr, uint8_t imgId, uint16_t blockNum, uint8_t *blkData)
    {
        OADStorage_Status_t status = OADStorage_Status_Success;
    
        if( (blockNum == oadBlock) && (oadInProgress) && (oadImgId == imgId))
        {
            oadBlock++;
            oadRetries = 0;
            oadTimeouts = 0;
    
            if(isToadImage)
            {
    #if defined(FEATURE_TOAD)
                if (TOAD_addBlockToCache(blkData) == TOAD_Status_noMemory)
                {
                    // Should not happen, but if so, re-request the block when more room is available
                    oadBlock--;
                }
    
                if (TOAD_isBlockCacheFull())
                {
                    // Stop OAD timer until there is more room in the cache to continue the transfer
                    oadClockSet(0);
                    isOADPaused = true;
                }
    
                Util_setEvent(oadClientParams.pEvent, SENSOR_TOAD_DECODE_EVT);
    #endif
            }
            else
            {
                status = OADStorage_imgBlockWrite((blockNum * OAD_BLOCK_SIZE), blkData, OAD_BLOCK_SIZE);
    
                if(status != OADStorage_Status_Success)
                {
    #ifndef CUI_DISABLE
                    displayOadStatusUpdate(status);
    #endif
                    /* OAD abort with no auto resume */
                    OADClient_abort(false);
                    return;
                }
            }
    
            //set poll rate back to default
            Ssf_setPollClock(defaultPollInterval);
            Jdllc_setPollRate(defaultPollInterval);
        }
    }
    
    /*!
     * @brief      Radio access function for OAD module to send messages
     */
    void* oadRadioAccessAllocMsg(uint32_t msgLen)
    {
        uint8_t *msgBuffer;
    
        //allocate buffer for CmdId + message
        msgBuffer = Ssf_malloc(msgLen + 1);
    
        memset(msgBuffer, 0, msgLen + 1);
    
        return msgBuffer + 1;
    }
    
    /*!
     * @brief      Radio access function for OAD module to send messages
     */
    static OADProtocol_Status_t oadRadioAccessPacketSend(void* pDstAddr, uint8_t *pMsg, uint32_t msgLen)
    {
        OADProtocol_Status_t status = OADProtocol_Failed;
        uint8_t* pMsduPayload;
    
        //buffer should have been allocated with oadRadioAccessAllocMsg,
        //so 1 byte before the oad msg buffer was allocated for the Smsgs_cmdId
        pMsduPayload = pMsg - 1;
        pMsduPayload[0] = Smsgs_cmdIds_oad;
    
        if(Sensor_sendMsg(Smsgs_cmdIds_oad, (ApiMac_sAddr_t*) pDstAddr, true,
                   msgLen+1 , pMsduPayload) == true)
        {
            status = OADProtocol_Status_Success;
        }
    
        //free the memory allocated in oadRadioAccessAllocMsg
        Ssf_free(pMsduPayload);
    
        return status;
    }
    
    #endif //FEATURE_NATIVE_OAD
    
    #endif //OAD_CLIENT_H
    

    3. Finally, the image would still compute a wrong CRC because the tool was covering the NV region were the network information is stored. Update the implelink_cc13x0_sdk_4_20_02_07/tools/ti154stack/turbo_oad/toad_image_tool.py

    Generate a new a.bin, b.bin, ab.dim

    """
    /******************************************************************************
    
     @file toad_image_tool.py
    
     @brief This tool generates the Turbo OAD (delta) image files
    
     Group: LPRF SW RND
     $Target Device: DEVICES $
    
     ******************************************************************************
     $License: BSD3 2019 $
     ******************************************************************************
     $Release Name: PACKAGE NAME $
     $Release Date: PACKAGE RELEASE DATE $
     *****************************************************************************/
    """
    
    from __future__ import print_function
    import argparse
    import textwrap
    import os
    import sys
    import struct
    import ntpath
    import tempfile
    import json
    from collections import namedtuple
    
    import delta_util
    
    # -----------------------------------------------------------------------------
    #                             Conditional Module Setup
    # -----------------------------------------------------------------------------
    SDK_PRODUCT_CC13X2_CC26X2 = "cc13x2_cc26x2"
    SDK_PRODUCT_CC13X0 = "cc13x0"
    SDK_PRODUCT = SDK_PRODUCT_CC13X0
    
    if SDK_PRODUCT == SDK_PRODUCT_CC13X2_CC26X2:
        # Get SDK path to import OAD image tool modules
        SDK_DIR = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),
                                                "..", "..", ".."))
        OAD_TOOLS_DIR = os.path.join(SDK_DIR, "tools", "common", "oad")
    
        sys.path.append(OAD_TOOLS_DIR)
        import computeCRC32
        import signUtil
        import imgBinUtil as util
    elif SDK_PRODUCT == SDK_PRODUCT_CC13X0:
        import crcmod
        import product_util_13x0 as product
    
        # CRC Poly used by OAD for CC13x0
        crc16 = crcmod.mkCrcFun(0x11021, rev=False, initCrc=0x0000, xorOut=0x0000)
    
    # -----------------------------------------------------------------------------
    #                                   Constants
    # -----------------------------------------------------------------------------
    
    __version__ = "1.1"
    __prog__ = "toad_image_tool"
    
    # Python 2 only supported for 13x0 SDK
    PYTHON_VERSION = sys.version_info.major
    
    MEMORY_CFG_OFFCHIP = "offchip"
    MEMORY_CFG_ONCHIP = "onchip"
    WORD_LEN_IN_BYTES = 4
    
    # Product Specific Constants
    
    # 13x2/26x2
    SEG_TYPE_DELTA = 5
    IMG_PAYLOAD_LEN = 12
    DELTA_IMG_MEMORY_CFG_OFFCHIP = 0x01
    DELTA_IMG_MEMORY_CFG_ONCHIP = 0x02
    
    PRODUCT_CC13X2_CC26X2_DELTA_SEG_LEN = 0x14
    PRODUCT_CC13X2_CC26X2_FIXED_IMG_HEADER_FORMAT = "<8sIBBHBBBBIIIIIHH"
    PRODUCT_CC13X2_CC26X2_DELTA_SEG_FORMAT = "<BHBIBBBBII"
    PRODUCT_CC13X2_CC26X2_FIXED_IMG_HEADER_FIELDS = """imgID crc32 bimVer metaVer techType imgCpStat
                                                       crcStat imgType imgNo imgVld len prgEntry softVer
                                                       imgEndAddr hdrLen rfu2"""
    PRODUCT_CC13X2_CC26X2_DELTA_SEG_FIELDS = """segType wirelessTech rfuSeg payloadLen isDeltaImg
                                                toadMetaVer toadVer memoryCfg oldImgCrc newImgLen"""
    
    if SDK_PRODUCT == SDK_PRODUCT_CC13X2_CC26X2:
        FixedImgHdr = namedtuple("FixedImgHdr", PRODUCT_CC13X2_CC26X2_FIXED_IMG_HEADER_FIELDS)
        DeltaSegHdr = namedtuple("DeltaSegHdr", PRODUCT_CC13X2_CC26X2_DELTA_SEG_FIELDS)
    elif SDK_PRODUCT == SDK_PRODUCT_CC13X0:
        FixedImgHdr = namedtuple("FixedImgHdr", product.FIXED_IMG_HEADER_FIELDS)
        DeltaSegHdr = namedtuple("DeltaSegHdr", product.DELTA_SEG_FIELDS)
    
    
    # -----------------------------------------------------------------------------
    #                                   Functions
    # -----------------------------------------------------------------------------
    
    
    def print_console_header():
        """Prints the program entry header"""
    
        print("************************************************")
        print("TI Turbo OAD Image Tool")
        print("Version: " + __version__)
        print("************************************************")
    
    
    def print_args_info(old_img_path, new_img_path, delta_path, memory_cfg):
        """Prints out the main arguments of the program"""
    
        print("OAD Configuration: " + memory_cfg)
        print("Old Image: " + ntpath.basename(old_img_path))
        print("New Image: " + ntpath.basename(new_img_path))
        print("Output Delta Image: " + ntpath.basename(delta_path))
        print("************************************************")
        print("Runtime Output:")
    
    
    def print_comp_savings(original_len, compressed_len):
        """Prints out the size savings of the delta image"""
    
        savings = (1.0 - float(compressed_len)/original_len) * 100.0
    
        print()
        print("New Image Length:   {:>9,} {:>5}".format(original_len, "bytes"))
        print("Delta Image Length: {:>9,} {:>5}".format(compressed_len, "bytes"))
        print("                   ================")
        print("Savings:  {:.4}%".format(savings))
        print()
        print("************************************************")
    
    
    def print_delta_header(path):
        """
        This function prints out information contained in an delta image header
    
        :param path: Path to the image file
        :return:
        """
    
        try:
            with open(path, "rb") as file:
                image = bytearray(file.read())
    
        except (OSError, IOError) as e:
            # Skip verbose printing if error
            return
    
        if SDK_PRODUCT == SDK_PRODUCT_CC13X2_CC26X2:
            delta_seg_offset = util.getSegAddr(path, SEG_TYPE_DELTA)
            delta_header = DeltaSegHdr._make(struct.unpack(PRODUCT_CC13X2_CC26X2_DELTA_SEG_FORMAT,
                                                            image[delta_seg_offset:(delta_seg_offset + PRODUCT_CC13X2_CC26X2_DELTA_SEG_LEN)]))
    
            hdr = delta_header._asdict()
            # Checks the the wireless tech type
            tech_value = hdr["wirelessTech"]
            hdr["wirelessTech"] = ''
            if tech_value == 0:
                hdr["wirelessTech"] = "No Wireless Technology"
            if tech_value & 1 == 0:
                hdr["wirelessTech"] += "[BLE]"
            if tech_value & 2 == 0:
                hdr["wirelessTech"] += "[TIMAC Sub1G]"
            if tech_value & 4 == 0:
                hdr["wirelessTech"] += "[TIMAC 2.4G]"
            if tech_value & 8 == 0:
                hdr["wirelessTech"] += "[Zigbee]"
            if tech_value & 16 == 0:
                hdr["wirelessTech"] += "[RF4CE]"
            if tech_value & 32 == 0:
                hdr["wirelessTech"] += "[Thread]"
            if tech_value & 64 == 0:
                hdr["wirelessTech"] += "[EasyLink]"
    
            hdr["memoryCfg"] = "Off-Chip" if hdr["memoryCfg"] == DELTA_IMG_MEMORY_CFG_OFFCHIP else "On-Chip"
    
            print(textwrap.dedent("""
            ____________________________
                     Delta HDR
            ____________________________
            Field            |      Value
            Segment Type     |      Delta Segment
            Wireless Tech    |      {wirelessTech}
            Segment Length   |      0x{payloadLen:X}
            isDeltaImg       |      {isDeltaImg}
            toadMetaVer      |      {toadMetaVer}
            toadVer          |      {toadVer}
            memoryCfg        |      {memoryCfg}
            oldImgCrc        |      0x{oldImgCrc:X}
            newImgLen        |      0x{newImgLen:X}
                """.format(**hdr)))
    
        elif SDK_PRODUCT == SDK_PRODUCT_CC13X0:
            delta_seg_offset = product.find_header_offset(image) + product.DELTA_SEG_OFFSET
            delta_header = DeltaSegHdr._make(struct.unpack(product.DELTA_SEG_FORMAT,
                                                            image[delta_seg_offset:(delta_seg_offset + product.DELTA_SEG_LEN)]))
    
            hdr = delta_header._asdict()
            print(textwrap.dedent("""
            ____________________________
                     Delta HDR
            ____________________________
            Field            |      Value
            deltaImgID       |      {deltaImgID}
            toadMetaVer      |      {toadMetaVer}
            toadVer          |      {toadVer}
            oldImgCrc        |      0x{oldImgCrc:X}
            newImgLen        |      0x{newImgLen:X}
            oldImgLen*       |      0x{oldImgLen:X} (*Padding Removed)
                """.format(**hdr)))
    
    
    def write_delta_image(old_header, new_header, new_img, delta_img):
        """
        Creates the OAD header segments for a Turbo OAD delta image
    
        :param old_header: byte array of the old image header
        :param new_header: byte array of the new image header
        :param new_img: byte array of the new image
        :param delta_img: byte array containing the delta of the current and new application images
        :return:
        """
        # OAD binaries are word aligned
        if SDK_PRODUCT == SDK_PRODUCT_CC13X0:
            # Delta segment does not already exist like in 13x2/26x2
            new_header += bytearray(product.DELTA_SEG_LEN)
    
            # Bytes before header when TIRTOS_IN_ROM are not delta compressed if enabled
            total_len = product.find_header_offset(new_img) + len(new_header) + len(delta_img)
        else:
            total_len = len(new_header) + len(delta_img)
    
        remaining_bytes = 0 if total_len % 4 == 0 else 4 - (total_len % 4)
        pad_buffer = bytearray([0xFF for x in range(remaining_bytes)])
        total_len += len(pad_buffer)
    
        # Set product specific variables for delta image generation
        if SDK_PRODUCT == SDK_PRODUCT_CC13X2_CC26X2:
            fixed_img_hdr_format = PRODUCT_CC13X2_CC26X2_FIXED_IMG_HEADER_FORMAT
            fixed_img_hdr_len = util.FIXED_HDR_LEN
    
            delta_seg_format = PRODUCT_CC13X2_CC26X2_DELTA_SEG_FORMAT
            delta_seg_len = PRODUCT_CC13X2_CC26X2_DELTA_SEG_LEN
        elif SDK_PRODUCT == SDK_PRODUCT_CC13X0:
            total_len = int(total_len / 4)
            fixed_img_hdr_format = product.FIXED_IMG_HEADER_FORMAT
            fixed_img_hdr_len = product.FIXED_IMG_HEADER_LEN
    
            delta_seg_format = product.DELTA_SEG_FORMAT
            delta_seg_len = product.DELTA_SEG_LEN
    
        if SDK_PRODUCT == SDK_PRODUCT_CC13X2_CC26X2:
            # A temporary file needs to be created since the OAD image tool operates
            # off of file paths for adding signature and CRC
            temp_new_img = tempfile.NamedTemporaryFile(delete=False)
            temp_new_img.write(new_img)
            temp_new_img.close()
    
        with open(vargs.output, "w+b") as outFile:
            new_img_fixed_hdr = FixedImgHdr._make(struct.unpack(fixed_img_hdr_format, new_header[:fixed_img_hdr_len]))
    
            # Calculate CRC fields for oldImgCrc validation on initiating OAD transfer
            # old_img_data_crc used for validating delta image is compatible with old image (excludes OAD header)
            # new_img_data_crc used for OAD header CRC calculation done on OAD completion
            if SDK_PRODUCT == SDK_PRODUCT_CC13X2_CC26X2:
                old_img_data_crc = computeCRC32.crc32_withOffset(vargs.old_img, len(old_header))
                old_img_data_crc = int(old_img_data_crc, 16)
                new_img_data_crc = computeCRC32.crc32_withOffset(vargs.new_img, len(new_header))
                new_img_data_crc = int(new_img_data_crc, 16)
    
                # Delta information segment
                delta_offset = util.getSegAddr(temp_new_img.name, SEG_TYPE_DELTA)
                delta_seg_header = DeltaSegHdr._make(struct.unpack(delta_seg_format,
                                                                   new_header[delta_offset:(delta_offset + delta_seg_len)]))
                delta_seg_header = delta_seg_header._replace(isDeltaImg=True, oldImgCrc=new_img_data_crc,
                                                             newImgLen=new_img_fixed_hdr.len)
    
                # Update header with new signature and CRC after adding delta
                with open(temp_new_img.name, "w+b") as f:
                    updated_new_img = bytearray(new_img)
                    updated_new_img = updated_new_img.replace(updated_new_img[delta_offset:(delta_offset + delta_seg_len)],
                                                              struct.pack(delta_seg_format, *delta_seg_header), 1)
                    f.write(updated_new_img)
    
                if util.isSecure(temp_new_img.name):
                    if vargs.key_file:
                        signUtil.signImage(temp_new_img.name, vargs.key_file)
                    else:
                        raise Exception("Key file not specified for secure image!")
    
                computeCRC32.computeCRC32(temp_new_img.name)
    
                # Update header for delta image
                with open(temp_new_img.name, "rb") as f:
                    new_header = f.read(len(new_header))
    
                new_img_fixed_hdr = FixedImgHdr._make(struct.unpack(fixed_img_hdr_format, new_header[:fixed_img_hdr_len]))
    
                delta_seg_header = DeltaSegHdr._make(struct.unpack(delta_seg_format,
                                                                   new_header[delta_offset:(delta_offset + delta_seg_len)]))
                delta_seg_header = delta_seg_header._replace(oldImgCrc=old_img_data_crc)
    
            elif SDK_PRODUCT == SDK_PRODUCT_CC13X0:
                delta_offset = product.DELTA_SEG_OFFSET
                old_img_data = open(vargs.old_img, "r+b").read()
                header_offset = product.find_header_offset(old_img_data)
    
                # NV pages (flash >= FLASH_NV_BASE) must be excluded from:
                #   - oldImgCrc: runtime NV writes (after network join) would
                #     otherwise change the running image CRC vs the binary CRC
                #   - newImgLen/oldImgLen: sensor CRC check and BIM copy length
                #     must not cover NV so NV is preserved across OAD updates
                #   - new image crc field: checkDL() verifies this CRC over the
                #     same app-only range written to external flash
                nv_offset_bin = product.FLASH_NV_BASE - product.FLASH_APP_BASE
                bim_hdr_offset = header_offset  # 0x4F0 for TIRTOS_IN_ROM, 0 otherwise
    
                if PYTHON_VERSION == 2:
                    # Lengths in 4-byte words, app code only (NV excluded)
                    old_img_len = nv_offset_bin / WORD_LEN_IN_BYTES
                    new_img_len = nv_offset_bin / WORD_LEN_IN_BYTES
    
                    # CRC over old app code only (start after OAD header, end before NV)
                    old_img_data_crc = crc16(old_img_data[(header_offset + len(old_header))
                                                          :nv_offset_bin])
    
                    # CRC over new app code only, skipping 4 CRC bytes at bim_hdr_offset.
                    # The len field at (bim_hdr_offset + 6) will be overwritten to
                    # new_img_len by OADStorage_imgBlockWrite before writing to external
                    # flash, so the stored crc must be computed with that modified value.
                    img_len_off = bim_hdr_offset + 6  # IMG_LEN_OFFSET = OAD_IMG_HDR_OSET(4) + offsetof(len)(2)
                    new_img_for_crc = bytearray(new_img[:nv_offset_bin])
                    new_img_for_crc[img_len_off]     = new_img_len & 0xFF
                    new_img_for_crc[img_len_off + 1] = (new_img_len >> 8) & 0xFF
                    new_img_crc_data = (bytes(new_img_for_crc[:bim_hdr_offset]) +
                                        bytes(new_img_for_crc[bim_hdr_offset + 4:nv_offset_bin]))
                    new_img_crc = crc16(new_img_crc_data)
    
                    delta_seg_header = DeltaSegHdr(deltaImgID=product.DELTA_SEG_DELTA_IMG_ID,
                                                   toadMetaVer=product.DELTA_SEG_DELTA_META_VER,
                                                   toadVer=product.DELTA_SEG_DELTA_TOAD_VER,
                                                   oldImgCrc=old_img_data_crc,
                                                   newImgLen=new_img_len,
                                                   oldImgLen=old_img_len)
                else:
                    # Lengths in 4-byte words, app code only (NV excluded)
                    old_img_len = nv_offset_bin // WORD_LEN_IN_BYTES
                    new_img_len = nv_offset_bin // WORD_LEN_IN_BYTES
    
                    # CRC over old app code only (start after OAD header, end before NV)
                    old_img_data_crc = crc16(old_img_data[(header_offset + len(old_header))
                                                          :nv_offset_bin])
    
                    # CRC over new app code only, skipping 4 CRC bytes at bim_hdr_offset.
                    # The len field at (bim_hdr_offset + 6) will be overwritten to
                    # new_img_len by OADStorage_imgBlockWrite before writing to external
                    # flash, so the stored crc must be computed with that modified value.
                    img_len_off = bim_hdr_offset + 6  # IMG_LEN_OFFSET = OAD_IMG_HDR_OSET(4) + offsetof(len)(2)
                    new_img_for_crc = bytearray(new_img[:nv_offset_bin])
                    new_img_for_crc[img_len_off]     = new_img_len & 0xFF
                    new_img_for_crc[img_len_off + 1] = (new_img_len >> 8) & 0xFF
                    new_img_crc_data = (bytes(new_img_for_crc[:bim_hdr_offset]) +
                                        bytes(new_img_for_crc[bim_hdr_offset + 4:nv_offset_bin]))
                    new_img_crc = crc16(new_img_crc_data)
    
                    delta_seg_header = DeltaSegHdr(deltaImgID=bytearray(product.DELTA_SEG_DELTA_IMG_ID.encode()),
                                                   toadMetaVer=product.DELTA_SEG_DELTA_META_VER,
                                                   toadVer=product.DELTA_SEG_DELTA_TOAD_VER,
                                                   oldImgCrc=old_img_data_crc,
                                                   newImgLen=new_img_len,
                                                   oldImgLen=old_img_len)
    
                # Update the crc field in the new image header with the app-only CRC
                # (computed with modified len field, matching what ext flash will contain)
                new_img_fixed_hdr = new_img_fixed_hdr._replace(crc=new_img_crc)
    
            # Update delta image with length of the delta image instead of the full image
            new_img_fixed_hdr = new_img_fixed_hdr._replace(len=total_len)
    
            delta_header = bytearray(new_header)
            delta_header = delta_header.replace(new_header[:fixed_img_hdr_len],
                                                struct.pack(fixed_img_hdr_format, *new_img_fixed_hdr), 1)
            delta_header = delta_header.replace(new_header[delta_offset:(delta_offset + delta_seg_len)],
                                                struct.pack(delta_seg_format, *delta_seg_header), 1)
    
            # Bytes before header when TIRTOS_IN_ROM are not delta compressed if enabled
            if SDK_PRODUCT == SDK_PRODUCT_CC13X0:
                if product.find_header_offset(new_img) != 0x0:
                    outFile.write(new_img[:product.find_header_offset(new_img)])
    
            outFile.write(delta_header)
            outFile.write(delta_img)
            outFile.write(pad_buffer)
    
            if SDK_PRODUCT == SDK_PRODUCT_CC13X2_CC26X2:
                os.remove(temp_new_img.name)
    
    
    def open_binary(path):
        """
        Reads an OAD image from the specified path
    
        :param path: Path to the image file
        :return: A tuple consisting of the OAD image header and the OAD image data
        """
    
        try:
            with open(path, "rb") as file:
                file.seek(0x00, os.SEEK_SET)
                image = bytearray(file.read())
    
                # Read header bytearray, dependent on product SDK
                if SDK_PRODUCT == SDK_PRODUCT_CC13X2_CC26X2:
                    header_len = util.getOverheadSize(path) + IMG_PAYLOAD_LEN
                    header_offset = 0x0
                elif SDK_PRODUCT == SDK_PRODUCT_CC13X0:
                    header_len = product.FIXED_IMG_HEADER_LEN
                    header_offset = product.find_header_offset(image)
    
                file.seek(header_offset, os.SEEK_SET)
                header = bytearray(file.read(header_len))
    
        except (OSError, IOError) as e:
            print("Fatal Error: -- {:s}. Exiting.".format(e.strerror))
            sys.exit(1)
    
        return (header, image)
    
    
    def parse_json_args(args):
        """
        Parses a JSON file and adds additional arguments to the arguments dictionary
    
        :param args: Arguments dictionary
        :return:
        """
    
        try:
            with open(args.json, "rb") as file:
                if sys.version_info.minor < 6:
                    json_content = file.read()
                    json_args = json.loads(json_content.decode("utf-8"))
                else:
                    json_args = json.load(file)
                args.__dict__.update(json_args)
    
        except (OSError, IOError) as e:
            print("Fatal Error: -- {:s}. Exiting.".format(e.strerror))
            sys.exit(1)
    
    
    def parse_cmdline_args():
        """Parses command line arguments"""
    
        parser = argparse.ArgumentParser(
            prog=__prog__,
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=textwrap.dedent('''
              Generates a Turbo OAD delta image from two OAD images
            '''),
            epilog=textwrap.dedent('''
              Example:
                %(prog)s -oimg app_v1.bin -nimg app_v2.bin -o app_v2.dim -m offchip
            '''))
    
        parser.add_argument("-oimg", "--old_img",
                            help="Path to an OAD binary of the old image running on the device")
        parser.add_argument("-nimg", "--new_img", required=True,
                            help="Path to an OAD binary of the new image")
        parser.add_argument("-o", "--output", required=True,
                            help="Output path to write the delta image")
        parser.add_argument("-j", "--json", help="Path to json file containing program arguments")
        parser.add_argument("-k", "--key_file", nargs="?",
                            help="Provide the location of the file containing your private key")
        parser.add_argument("-m", "--memory_cfg", choices=[MEMORY_CFG_OFFCHIP, MEMORY_CFG_ONCHIP],
                            help="OAD memory configuration (offchip/onchip)")
        parser.add_argument("-v", "--version", action="version", version="%(prog)s " + __version__)
        parser.add_argument("-verbose", "--verbose", action="store_true")
    
        return parser.parse_args()
    
    
    if __name__ == "__main__":
        vargs = parse_cmdline_args()
    
        # 13x0 does not support on-chip OAD, input required argument for the user if not specified
        if SDK_PRODUCT == SDK_PRODUCT_CC13X0 and (vargs.memory_cfg is None or vargs.memory_cfg == ""):
            vargs.memory_cfg = "offchip"
    
        # Load additional arguments if passed a JSON file
        if not(vargs.json is None or vargs.json == ""):
            parse_json_args(vargs)
    
            # exit early if turbo oad is not enabled
            if not vargs.enabled:
                # exit silently
                sys.exit(0)
    
        # Ensure required arguments are passed in either via cmd line or json file:
        if (vargs.old_img is None or vargs.old_img == "") or (vargs.memory_cfg is None or vargs.memory_cfg == ""):
            if not(vargs.json is None or vargs.json == ""):
                print("Info: Path to old image is not specified. Delta image not created.")
                sys.exit(0)
            else:
                if SDK_PRODUCT == SDK_PRODUCT_CC13X0:
                    print("Error: --old_img must be specified")
                else:
                    print("Error: --memory_cfg and --old_img must be specified")
                sys.exit(1)
    
        # Prevent creation of on-chip images
        if vargs.memory_cfg == MEMORY_CFG_ONCHIP:
            print("Error: On-chip memory configuration is not supported")
            sys.exit(1)
    
        # first, print a neat header
        print_console_header()
    
        # Open image binaries and store data for delta encoding
        old_img_header, old_img = open_binary(vargs.old_img)
        new_img_header, new_img = open_binary(vargs.new_img)
    
        # Display input file and output file name info
        print_args_info(vargs.old_img, vargs.new_img, vargs.output, vargs.memory_cfg)
    
        # OAD headers and NV pages are excluded from the delta image.
        # NV exclusion ensures the delta decoder only touches app code, so
        # runtime NV contents (written after network join) are preserved and
        # BIM copies only the app pages (newImgLen words) leaving NV intact.
        if SDK_PRODUCT == SDK_PRODUCT_CC13X0:
            nv_offset_bin = product.FLASH_NV_BASE - product.FLASH_APP_BASE
            delta_img = delta_util.create_delta(
                old_img[product.find_header_offset(old_img) + len(old_img_header):nv_offset_bin],
                new_img[product.find_header_offset(new_img) + len(new_img_header):nv_offset_bin])
        else:
            delta_img = delta_util.create_delta(old_img[len(old_img_header):],
                                                new_img[len(new_img_header):])
    
        write_delta_image(old_img_header, new_img_header, new_img, delta_img)
    
        if vargs.verbose:
            print_delta_header(vargs.output)
    
        # Print out statistics
        delta_img_size = os.path.getsize(vargs.output)
        print_comp_savings(len(new_img), delta_img_size)
    
        # Print out warning if it is not advantages to perform a delta update
        savings = (1.0 - float(delta_img_size) / len(new_img)) * 100.0
        if savings < 25.0:
            print("Warning: Recommended to use regular OAD since size savings " +
                  "is less than 25% of the new image size")
    

    Best regards,

    Daniel

  • Hi Daniel:

    Thank you, I will verify the following as soon as possible.

    Thank you again

    Best regards,

    Daniel

  • Hi Daniel:

    I tested the method you provided
    May I ask if you forgot to upload the product_util_13x0.py file? From my observation, it seems that two constants have been added to this file:
    1.FLASH_NV_BASE
    2.FLASH_APP_BASE

    Best regards,

    Daniel

  • Hi faker,

    Yes your are right! I forgot to include those on my previous answer:

    FLASH_APP_BASE = 0x1000
    FLASH_NV_BASE = 0x1E000

    """
    /******************************************************************************
    
     @file product_util_13x0.py
    
     @brief This file contains a collection of utility functions used for
            specific SDK products
    
     Group: LPRF SW RND
     $Target Device: DEVICES $
    
     ******************************************************************************
     $License: BSD3 2019 $
     ******************************************************************************
     $Release Name: PACKAGE NAME $
     $Release Date: PACKAGE RELEASE DATE $
     *****************************************************************************/
    """
    
    # -----------------------------------------------------------------------------
    #                                   Constants
    # -----------------------------------------------------------------------------
    
    # Embedded Constants
    HEADER_ADDR_TIRTOS_ROM = 0x4F0
    
    # OAD Header Formats
    FIXED_IMG_HEADER_FORMAT = "<HHHH4sHBB"
    DELTA_SEG_FORMAT = "<8sBBHHH"
    FIXED_IMG_HEADER_FIELDS = "crc crcShadow ver len uid startAddr imgType state"
    DELTA_SEG_FIELDS = "deltaImgID toadMetaVer toadVer oldImgCrc newImgLen oldImgLen"
    
    # OAD Header Constants
    FIXED_IMG_HEADER_LEN = 16
    
    FIXED_IMG_HEADER_CRC_SHADOW_OFFSET = 2
    FIXED_IMG_HEADER_LEN_OFFSET = 6
    FIXED_IMG_HEADER_USERID_OFFSET = 8
    FIXED_IMG_HEADER_STATE_OFFSET = 15
    
    FIXED_IMG_HEADER_DEFAULT_CRC_SHADOW = bytearray([0xFF, 0xFF])
    FIXED_IMG_HEADER_DEFAULT_USERID = bytearray("EEEE", "utf-8")
    FIXED_IMG_HEADER_DEFAULT_STATE = bytearray([0xFF])
    
    # Turbo OAD Header Constants
    DELTA_SEG_LEN = 16
    DELTA_SEG_OFFSET = 16
    
    DELTA_SEG_DELTA_IMG_ID = "TURBOOAD"
    DELTA_SEG_DELTA_META_VER = 1
    DELTA_SEG_DELTA_TOAD_VER = 1
    
    # NV flash configuration for TIRTOS_IN_ROM CC13X0
    # CRC and delta encoding must exclude NV pages so runtime NV writes
    # (after network join) do not cause oldImgCrc mismatch.
    # Match the linker: ONE_PAGE_NV => FLASH_NV_BASE = 0x1E000 (page 30)
    #                  TWO_PAGE_NV => FLASH_NV_BASE = 0x1D000 (pages 29-30)
    FLASH_APP_BASE = 0x1000   # APP_START_ADDR: binary starts here in flash
    FLASH_NV_BASE  = 0x1E000  # ONE_PAGE_NV; change to 0x1D000 for TWO_PAGE_NV
    
    # -----------------------------------------------------------------------------
    #                                   Functions
    # -----------------------------------------------------------------------------
    
    
    def find_header_offset(binary):
        """
        Reads the OAD image binary and finds the location of the OAD image header
    
        :param binary: A bytearray of the OAD .bin file
        :return: The address offset of the OAD image header
        """
    
        # Detect if TIRTOS_IN_ROM is enabled for the binary
        header = bytearray(binary[:FIXED_IMG_HEADER_LEN])
    
        # Auto-detecting header based on default values of the CRC shadow, user ID, and state fields
        crc_shadow = header[FIXED_IMG_HEADER_CRC_SHADOW_OFFSET:(FIXED_IMG_HEADER_CRC_SHADOW_OFFSET + 2)]
        user_id = header[FIXED_IMG_HEADER_USERID_OFFSET:(FIXED_IMG_HEADER_USERID_OFFSET + 4)]
        state = header[FIXED_IMG_HEADER_STATE_OFFSET:(FIXED_IMG_HEADER_STATE_OFFSET + 1)]
    
        if (crc_shadow == FIXED_IMG_HEADER_DEFAULT_CRC_SHADOW and
                user_id == FIXED_IMG_HEADER_DEFAULT_USERID and
                state == FIXED_IMG_HEADER_DEFAULT_STATE):
            return 0x0
        else:
            return HEADER_ADDR_TIRTOS_ROM
    

    Best regards,

    Daniel

  • Hi Daniel:

    I downloaded the file you provided and replaced the files in the current project, but there were some issues:
    1. oad  is  successful
    2. TOAD shows "Failed" on the sensor end


    May I ask if there are any other modifications that have not been uploaded yet? Here are my commands:

     python oad_image_tool.py -v 0x0100 -t onchip -i app ../Release/devices_1310_v1.0.0.hex -ob a.bin -m 0x14F0 -r :0x1e000

     python oad_image_tool.py -v 0x0200 -t onchip -i app ../Release/devices_1310_v2.0.0.hex -ob b.bin -m 0x14F0 -r :0x1e000

    python toad_image_tool.py -oimg a.bin -nimg b.bin -o ab.dim

    Best regards,

    Daniel

  • Hi faker,

    I'm doing it similarly to how you described it in your first post. So, off-chip and without the range -r

    • python oad_image_tool.py -v 0x0090 -t offchip -i app ../Release/devices_1310_v0.9.0.hex -ob a.bin -m 0x14F0
    • python oad_image_tool.py -v 0x0091 -t offchip -i app ../Release/devices_1310_v0.9.1.hex -ob b.bin -m 0x14F0

    Can you try that again.

    Also make sure you have erased any image from the external flash using flash-rover GitHub - TexasInstruments/flash-rover: Read and write to external flash on a TI CC13xx/CC26xx device directly from host · GitHub

    And UniFlash to erase the internal memory and flash it again with BIM and the new .bin

    ./flash-rover -d cc1310 -x L200XXXX erase --mass-erase

    Best regards,

    Daniel

  • Hi Daniel,

    I have tried with and without the parameter '- r', and the result is the same

    Best regards,

    Daniel

  • Hi faker,

    What about off-chip? Since you are using the off-chip BIM make sure you mass-erase the external flash as described above, and use a new off-chip image. It is possible BIM is booting from one of the previous existing images.

  • Hi Daniel,

    I tried to erase the off chip flash and re erase the MCU on-chip flash using 'erase_extflash_cc13x0lp. hex', and downloaded the firmware again, but the result was still 'Failed'

    Best regards,

    Daniel

  • Hi faker,

    I don't see this "Failed" in the code, is it something you added? Can you tell me when is triggered?

    Best regards,

    Daniel

  • Hi Daniel,


    It is the display OadStatusUpdate () function in the oad_cient. c file

    I removed the three characters "OAD" in front of it for a unified prompt, as shown in Figure 2

    Best regards,

    Daniel

  • Hi faker,

    Can you try with a default sensor_oad with no modification? Or are you willing to share your a.bin, b.bin and ab.dim?

    On you oad_client.c can you add on line 720. Rebuild and flash it again.

    if (imgIdentifyPld->oldImgCrc != imgDataCrc)                                                                                                                                    
    {                                                                                                                                                                         
        LCD_WRITE_STRING_VALUE("TOAD:cmpCRC ", imgIdentifyPld->oldImgCrc, 16, 3);                                                                                                 
        LCD_WRITE_STRING_VALUE("TOAD:sensrCRC ", imgDataCrc, 16, 3);                                                                                                                           
    } 

    Best regards,

    Daniel

  • Hi Daniel,

    Forum replies seem unable to upload bin files directly. Could you please request through my friend so that I can send you my files directly

    Best regards,

    Daniel

  • Hi Daniel,

    I added a statement for you to print CRC, but the code cannot be executed at this point,
    Because my log shows that the verification failed after the first frame of data was sent, as shown in the figure with a value of 72

    Best regards,

    Daniel

  • Hi Daniel,

    I also found a problem,
    If only the version numbers of my a.bin and b.bin have been changed, it is possible to create a travel subcontract (around 1kb)
    But on the Linux side, there will be an error message stating that the file cannot be found

    Best regards,

    Daniel

  • Hi faker,

    Please regenerate the bin using -t offchip and -v 0x0090, -v 0x0091 as in your first post.

    Regarding the second issue, I don't know if it is a bug as there is no difference between images, anyway, that redirects the focus of the thread, let's keep it to the original issue.

    Best regards,

    Daniel

  • Hi Daniel,

    I have tested it and it has indeed been resolved. Thank you

    Best regards,

    Daniel