/****************************************************************************** * Filename: sbl_device_cc2650.cpp * Revised: $Date: 2013-08-21 14:33:34 +0200 (on, 21 aug 2013) $ * Revision: $Revision: 27319 $ * * Description: Serial Bootloader device file for CC2650 * * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ * * * 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. * ******************************************************************************/ #include #include "sbl_device.h" #include "sbl_device_cc2650.h" #include "serialib.h" #include #if 1 static uint32_t min(uint32_t a, uint32_t b) { if(a > b) { return b; } return a; } #endif /// Struct used when splitting long transfers typedef struct { uint32_t startAddr; uint32_t byteCount; uint32_t startOffset; bool bExpectAck; } tTransfer; static uint32_t getDeviceRev(uint32_t deviceId) { uint32_t tmp = deviceId >> 28; switch(tmp) { // Early samples (Rev 1) case 0: case 1: return 1; default: return 2; } } //----------------------------------------------------------------------------- /** \brief Constructor */ //----------------------------------------------------------------------------- SblDeviceCC2650::SblDeviceCC2650() { m_deviceRev = 0; m_pageEraseSize = SBL_CC2650_PAGE_ERASE_SIZE; if(!m_pCom) { m_pCom = new serialib(); } } //----------------------------------------------------------------------------- /** \brief Destructor */ //----------------------------------------------------------------------------- SblDeviceCC2650::~SblDeviceCC2650() { } //----------------------------------------------------------------------------- /** \brief This function sends ping command to device. * * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::ping() { DEBUG_PRINT("\n"); int retCode = SBL_SUCCESS; bool bResponse = false; if(!isConnected()) { return SBL_PORT_ERROR; } // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_PING)) != SBL_SUCCESS) { return retCode; } // // Get response // if((retCode = getCmdResponse(bResponse)) != SBL_SUCCESS) { return retCode; } return (bResponse) ? SBL_SUCCESS : SBL_ERROR; } //----------------------------------------------------------------------------- /** \brief This function gets status from device. * * \param[out] pStatus * Pointer to where status is stored. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::readStatus(uint32_t *pui32Status) { DEBUG_PRINT("\n"); uint32_t retCode = SBL_SUCCESS; bool bSuccess = false; if(!isConnected()) { return SBL_PORT_ERROR; } // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_GET_STATUS)) != SBL_SUCCESS) { return retCode; } // // Receive command response // if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { return SBL_ERROR; } // // Receive command response data // char status = 0; uint32_t ui32NumBytes = 1; if((retCode = getResponseData(&status, ui32NumBytes)) != SBL_SUCCESS) { // // Respond with NAK // sendCmdResponse(false); return retCode; } // // Respond with ACK // sendCmdResponse(true); m_lastDeviceStatus = status; *pui32Status = status; return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief This function reads device ID. * * \param[out] pui32DeviceId * Pointer to where device ID is stored. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::readDeviceId(uint32_t *pui32DeviceId) { DEBUG_PRINT("\n"); int retCode = SBL_SUCCESS; bool bSuccess = false; if(!isConnected()) { return SBL_PORT_ERROR; } // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_GET_CHIP_ID)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { return SBL_ERROR; } // // Receive response data // char pId[4]; memset(pId, 0, 4); uint32_t numBytes = 4; if((retCode = getResponseData(pId, numBytes)) != SBL_SUCCESS) { // // Respond with NAK // sendCmdResponse(false); return retCode; } if(numBytes != 4) { // // Respond with NAK // sendCmdResponse(false); setState(SBL_ERROR, "Didn't receive 4 B.\n"); return SBL_ERROR; } // // Respond with ACK // sendCmdResponse(true); // // Store retrieved ID and report success // *pui32DeviceId = charArrayToUL(pId); m_deviceId = *pui32DeviceId; // // Store device revision (used internally, see sbl_device_cc2650.h) // m_deviceRev = getDeviceRev(m_deviceId); return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief This function reads device FLASH size in bytes. * * \param[out] pui32FlashSize * Pointer to where FLASH size is stored. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::readFlashSize(uint32_t *pui32FlashSize) { DEBUG_PRINT("\n"); uint32_t retCode = SBL_SUCCESS; // // Read CC2650 DIECFG0 (contains FLASH size information) // uint32_t addr = SBL_CC2650_FLASH_SIZE_CFG; uint32_t value; if((retCode = readMemory32(addr, 1, &value)) != SBL_SUCCESS) { setState((tSblStatus)retCode, "Failed to read device FLASH size: %s", getLastError().c_str()); return retCode; } // // Calculate flash size (The number of flash sectors are at bits [7:0]) // value &= 0xFF; *pui32FlashSize = value * getPageEraseSize(); m_flashSize = *pui32FlashSize; return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief This function reads device RAM size in bytes. * * \param[out] pui32RamSize * Pointer to where RAM size is stored. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::readRamSize(uint32_t *pui32RamSize) { DEBUG_PRINT("\n"); int retCode = SBL_SUCCESS; // // Read CC2650 DIECFG0 (contains RAM size information // uint32_t addr = SBL_CC2650_RAM_SIZE_CFG; uint32_t value; if((retCode = readMemory32(addr, 1, &value)) != SBL_SUCCESS) { setState(retCode, "Failed to read device RAM size: %s", getLastError().c_str()); return retCode; } // // Calculate RAM size in bytes (Ram size bits are at bits [1:0]) // value &= 0x03; if(m_deviceRev == 1) { // Early samples has less RAM switch(value) { case 3: *pui32RamSize = 0x4000; break; // 16 KB case 2: *pui32RamSize = 0x2000; break; // 8 KB case 1: *pui32RamSize = 0x1000; break; // 4 KB case 0: // 2 KB default:*pui32RamSize = 0x0800; break; // All invalid values are interpreted as 2 KB } } else { switch(value) { case 3: *pui32RamSize = 0x5000; break; // 20 KB case 2: *pui32RamSize = 0x4000; break; // 16 KB case 1: *pui32RamSize = 0x2800; break; // 10 KB case 0: // 4 KB default:*pui32RamSize = 0x1000; break; // All invalid values are interpreted as 4 KB } } // CC1310F64 and CC1310F32 officially has 16KB SRAM, but 20KB enabled. Return official value. if((sm_chipType == 0x1310 && m_deviceRev > 1) && (m_flashSize == (64*1024) || (m_flashSize == (32*1024)))) { *pui32RamSize = 0x4000; } // // Save RAM size internally // m_ramSize = *pui32RamSize; return retCode; } //----------------------------------------------------------------------------- /** \brief This function reset the device. Communication to the device must be * reinitialized after calling this function. * * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::reset() { DEBUG_PRINT("\n"); int retCode = SBL_SUCCESS; bool bSuccess = false; if(!isConnected()) { return SBL_PORT_ERROR; } // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_RESET)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { setState(SBL_ERROR, "Reset command NAKed by device.\n"); return SBL_ERROR; } m_bCommInitialized = false; return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief This function erases device flash pages. Starting page is the page * that includes the address in \e startAddress. Ending page is the page * that includes the address . CC13/CC26xx erase * size is 4KB. * * \param[in] ui32StartAddress * The start address in flash. * \param[in] ui32ByteCount * The number of bytes to erase. * * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::eraseFlashRange(uint32_t ui32StartAddress, uint32_t ui32ByteCount) { DEBUG_PRINT("\n"); uint32_t retCode = SBL_SUCCESS; bool bSuccess = false; char pcPayload[4]; uint32_t devStatus; // // Initial check // if(!isConnected()) { return SBL_PORT_ERROR; } // // Calculate retry count // uint32_t ui32PageCount = ui32ByteCount / getPageEraseSize(); if( ui32ByteCount % getPageEraseSize()) ui32PageCount ++; setProgress( 0 ); for(uint32_t i = 0; i < ui32PageCount; i++) { // // Build payload // - 4B address (MSB first) // ulToCharArray(ui32StartAddress + i * getPageEraseSize(), &pcPayload[0]); // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_SECTOR_ERASE, pcPayload, 4)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { return SBL_ERROR; } // // Check device status (Flash failed if page(s) locked) // readStatus(&devStatus); if(devStatus != SblDeviceCC2650::CMD_RET_SUCCESS) { setState(SBL_ERROR, "Flash erase failed. (Status 0x%02X = '%s'). Flash pages may be locked.\n", devStatus, getCmdStatusString(devStatus).c_str()); return SBL_ERROR; } setProgress( 100*(i+1)/ui32PageCount ); } return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief This function reads \e ui32UnitCount (32 bit) words of data from * device. Destination array is 32 bit wide. The start address must be 4 * byte aligned. * * \param[in] ui32StartAddress * Start address in device (must be 4 byte aligned). * \param[in] ui32UnitCount * Number of data words to read. * \param[out] pcData * Pointer to where read data is stored. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::readMemory32(uint32_t ui32StartAddress, uint32_t ui32UnitCount, uint32_t *pui32Data) { DEBUG_PRINT("\n"); int retCode = SBL_SUCCESS; bool bSuccess = false; // // Check input arguments // if((ui32StartAddress & 0x03)) { setState(SBL_ARGUMENT_ERROR, "readMemory32(): Start address (0x%08X) must be a multiple of 4.\n", ui32StartAddress); return SBL_ARGUMENT_ERROR; } // // Set progress // setProgress(0); if(!isConnected()) { return SBL_PORT_ERROR; } unsigned char pcPayload[6]; uint32_t responseData[SBL_CC2650_MAX_MEMREAD_WORDS]; uint32_t chunkCount = ui32UnitCount / SBL_CC2650_MAX_MEMREAD_WORDS; if(ui32UnitCount % SBL_CC2650_MAX_MEMREAD_WORDS) chunkCount++; uint32_t remainingCount = ui32UnitCount; for(uint32_t i = 0; i < chunkCount; i++) { uint32_t dataOffset = (i * SBL_CC2650_MAX_MEMREAD_WORDS); uint32_t chunkStart = ui32StartAddress + dataOffset; #if 1 uint32_t chunkSize = min(remainingCount, SBL_CC2650_MAX_MEMREAD_WORDS); #else uint32_t chunkSize; if(remainingCount > SBL_CC2650_MAX_MEMREAD_WORDS) { chunkSize = SBL_CC2650_MAX_MEMREAD_WORDS; } else { chunkSize = remainingCount; } #endif remainingCount -= chunkSize; // // Build payload // - 4B address (MSB first) // - 1B access width // - 1B Number of accesses (in words) // ulToCharArray(chunkStart, (char *)&pcPayload[0]); pcPayload[4] = SBL_CC2650_ACCESS_WIDTH_32B; pcPayload[5] = chunkSize; // // Set progress // setProgress(((i * 100) / chunkCount)); // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_MEMORY_READ, (char *)pcPayload, 6)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { return SBL_ERROR; } // // Receive 4B response // uint32_t expectedBytes = chunkSize * 4; uint32_t recvBytes = expectedBytes; if((retCode = getResponseData((char*)responseData, recvBytes)) != SBL_SUCCESS) { // // Respond with NAK // sendCmdResponse(false); return retCode; } if(recvBytes != expectedBytes) { // // Respond with NAK // sendCmdResponse(false); setState(SBL_ERROR, "Didn't receive 4 B.\n"); return SBL_ERROR; } memcpy(&pui32Data[dataOffset], responseData, expectedBytes); //delete [] responseData; // // Respond with ACK // sendCmdResponse(true); } // // Set progress // setProgress(100); return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief This function reads \e unitCount bytes of data from device. * Destination array is 8 bit wide. * * \param[in] ui32StartAddress * Start address in device. * \param[in] ui32UnitCount * Number of bytes to read. * \param[out] pcData * Pointer to where read data is stored. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::readMemory8(uint32_t ui32StartAddress, uint32_t ui32UnitCount, char *pcData) { DEBUG_PRINT("\n"); int retCode = SBL_SUCCESS; bool bSuccess = false; // // Check input arguments // if(ui32UnitCount == 0) { setState(SBL_ARGUMENT_ERROR, "readMemory8(): Read count is zero. Must be at least 1.\n"); return SBL_ARGUMENT_ERROR; } if(!isConnected()) { return SBL_PORT_ERROR; } unsigned char pcPayload[6]; uint32_t chunkCount = ui32UnitCount / SBL_CC2650_MAX_MEMREAD_BYTES; if(ui32UnitCount % SBL_CC2650_MAX_MEMREAD_BYTES) chunkCount++; uint32_t remainingCount = ui32UnitCount; for(uint32_t i = 0; i < chunkCount; i++) { uint32_t dataOffset = (i * SBL_CC2650_MAX_MEMREAD_BYTES); uint32_t chunkStart = ui32StartAddress + dataOffset; uint32_t chunkSize = min(remainingCount, SBL_CC2650_MAX_MEMREAD_BYTES); remainingCount -= chunkSize; // // Build payload // - 4B address (MSB first) // - 1B access width // - 1B number of accesses (in bytes) // ulToCharArray(chunkStart, (char *)&pcPayload[0]); pcPayload[4] = SBL_CC2650_ACCESS_WIDTH_8B; pcPayload[5] = chunkSize; // // Set progress // setProgress( ((i*100) / chunkCount)); // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_MEMORY_READ, (char *)pcPayload, 6)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { return SBL_ERROR; } // // Receive response // uint32_t expectedBytes = chunkSize; if((retCode = getResponseData(&pcData[dataOffset], chunkSize)) != SBL_SUCCESS) { // // Respond with NAK // sendCmdResponse(false); return retCode; } if(chunkSize != expectedBytes) { // // Respond with NAK // sendCmdResponse(false); setState(SBL_ERROR, "readMemory8(): Received %d bytes (%d B expected) in iteration %d.\n", chunkSize, expectedBytes, i); return SBL_ERROR; } // // Respond with ACK // sendCmdResponse(true); } // // Set progress // setProgress(100); return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief This function writes \e unitCount words of data to device SRAM. * Max 61 32-bit words supported. Source array is 32 bit wide. * \e ui32StartAddress must be 4 byte aligned. * * \param[in] ui32StartAddress * Start address in device. * \param[in] ui32UnitCount * Number of data words (32bit) to write. * \param[in] pui32Data * Pointer to source data. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::writeMemory32(uint32_t ui32StartAddress, uint32_t ui32UnitCount, const uint32_t *pui32Data) { DEBUG_PRINT("\n"); uint32_t retCode = SBL_SUCCESS; bool bSuccess = false; // // Check input arguments // if((ui32StartAddress & 0x03)) { setState(SBL_ARGUMENT_ERROR, "writeMemory32(): Start address (0x%08X) must 4 byte aligned.\n", ui32StartAddress); return SBL_ARGUMENT_ERROR; } if(addressInBLWorkMemory(ui32StartAddress, ui32UnitCount * 4)) { // Issue warning setState(SBL_ARGUMENT_ERROR, "writeMemory32(): Writing to bootloader work memory/stack:\n(0x%08X-0x%08X, 0x%08X-0x%08X)\n", SBL_CC2650_BL_WORK_MEMORY_START,SBL_CC2650_BL_WORK_MEMORY_END, SBL_CC2650_BL_STACK_MEMORY_START,SBL_CC2650_BL_STACK_MEMORY_END); return SBL_ARGUMENT_ERROR; } if(!isConnected()) { return SBL_PORT_ERROR; } uint32_t chunkCount = (ui32UnitCount / SBL_CC2650_MAX_MEMWRITE_WORDS); if(ui32UnitCount % SBL_CC2650_MAX_MEMWRITE_WORDS) chunkCount++; uint32_t remainingCount = ui32UnitCount; char* pcPayload = new char[5 + (SBL_CC2650_MAX_MEMWRITE_WORDS*4)]; for(uint32_t i = 0; i < chunkCount; i++) { uint32_t chunkOffset = i * SBL_CC2650_MAX_MEMWRITE_WORDS; uint32_t chunkStart = ui32StartAddress + (chunkOffset * 4); uint32_t chunkSize = min(remainingCount, SBL_CC2650_MAX_MEMWRITE_WORDS); remainingCount -= chunkSize; // // Build payload // - 4B address (MSB first) // - 1B access width // - 1-SBL_CC2650_MAX_MEMWRITE_WORDS data (MSB first) // ulToCharArray(chunkStart, &pcPayload[0]); pcPayload[4] = SBL_CC2650_ACCESS_WIDTH_32B; for(uint32_t j = 0; j < chunkSize; j++) { ulToCharArray(pui32Data[j + chunkOffset], &pcPayload[5 + j*4]); } // // Set progress // setProgress( ((i * 100) / chunkCount) ); // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_MEMORY_WRITE, pcPayload, 5 + chunkSize*4)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess, 5)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { setState(SBL_ERROR, "writeMemory32(): Device NAKed command for address 0x%08X.\n", chunkStart); return SBL_ERROR; } } // // Set progress // setProgress(100); // // Cleanup // delete [] pcPayload; return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief Write \e unitCount words of data to device SRAM. Source array is * 8 bit wide. Max 244 bytes of data. Source array is 32 bit wide. * Parameters \e startAddress and \e unitCount must be a a multiple of 4. * * \param[in] ui32StartAddress * Start address in device. * \param[in] ui32UnitCount * Number of bytes to write. * \param[in] pcData * Pointer to source data. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::writeMemory8(uint32_t ui32StartAddress, uint32_t ui32UnitCount, const char *pcData) { DEBUG_PRINT("\n"); uint32_t retCode = SBL_SUCCESS; bool bSuccess = false; // // Check input arguments // if(addressInBLWorkMemory(ui32StartAddress, ui32UnitCount)) { // Issue warning setState(SBL_ARGUMENT_ERROR, "writeMemory8(): Writing to bootloader work memory/stack:\n(0x%08X-0x%08X, 0x%08X-0x%08X)\n", SBL_CC2650_BL_WORK_MEMORY_START,SBL_CC2650_BL_WORK_MEMORY_END, SBL_CC2650_BL_STACK_MEMORY_START,SBL_CC2650_BL_STACK_MEMORY_END); return SBL_ARGUMENT_ERROR; } if(!isConnected()) { return SBL_PORT_ERROR; } uint32_t chunkCount = (ui32UnitCount / SBL_CC2650_MAX_MEMWRITE_BYTES); if(ui32UnitCount % SBL_CC2650_MAX_MEMWRITE_BYTES) chunkCount++; uint32_t remainingCount = ui32UnitCount; char* pcPayload = new char[5 + SBL_CC2650_MAX_MEMWRITE_BYTES]; for(uint32_t i = 0; i < chunkCount; i++) { uint32_t chunkOffset = i * SBL_CC2650_MAX_MEMWRITE_BYTES; uint32_t chunkStart = ui32StartAddress + chunkOffset; uint32_t chunkSize = min(remainingCount, SBL_CC2650_MAX_MEMWRITE_BYTES); remainingCount -= chunkSize; // // Build payload // - 4B address (MSB first) // - 1B access width // - 1-SBL_CC2650_MAX_MEMWRITE_BYTES bytes data // ulToCharArray(chunkStart, &pcPayload[0]); pcPayload[4] = SBL_CC2650_ACCESS_WIDTH_8B; memcpy(&pcPayload[5], &pcData[chunkOffset], chunkSize); // // Set progress // setProgress( ((i * 100) / chunkCount) ); // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_MEMORY_WRITE, pcPayload, 5 + chunkSize)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess, 5)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { setState(SBL_ERROR, "writeMemory8(): Device NAKed command for address 0x%08X.\n", chunkStart); return SBL_ERROR; } } // // Set progress // setProgress(100); // // Cleanup // delete [] pcPayload; return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief Calculate CRC over \e byteCount bytes, starting at address * \e startAddress. * * \param[in] ui32StartAddress * Start address in device. * \param[in] ui32ByteCount * Number of bytes to calculate CRC32 over. * \param[out] pui32Crc * Pointer to where checksum from device is stored. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::calculateCrc32(uint32_t ui32StartAddress, uint32_t ui32ByteCount, uint32_t *pui32Crc) { DEBUG_PRINT("\n"); uint32_t retCode = SBL_SUCCESS; bool bSuccess = false; char pcPayload[12]; uint32_t ui32RecvCount = 0; // // Check input arguments // if(!addressInFlash(ui32StartAddress, ui32ByteCount) && !addressInRam(ui32StartAddress, ui32ByteCount)) { setState(SBL_ARGUMENT_ERROR, "Specified address range (0x%08X + %d bytes) is not in device FLASH nor RAM.\n", ui32StartAddress, ui32ByteCount); return SBL_ARGUMENT_ERROR; } if(!isConnected()) { return SBL_PORT_ERROR; } // // Set progress // setProgress(0); // // Build payload // - 4B address (MSB first) // - 4B byte count(MSB first) // ulToCharArray(ui32StartAddress, &pcPayload[0]); ulToCharArray(ui32ByteCount, &pcPayload[4]); pcPayload[8] = 0x00; pcPayload[9] = 0x00; pcPayload[10] = 0x00; pcPayload[11] = 0x00; // // Send command // #if 0 { int i; printf("@@send CMD_CRC32:"); for(i=0;i<12;i++) printf(" %02x",(unsigned char)pcPayload[i]); printf("\n"); } #endif if((retCode = sendCmd(SblDeviceCC2650::CMD_CRC32, pcPayload, 12)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess, 5)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { setState(SBL_ERROR, "Device NAKed CRC32 command.\n"); return SBL_ERROR; } // // Get data response // ui32RecvCount = 4; if((retCode = getResponseData(pcPayload, ui32RecvCount)) != SBL_SUCCESS) { sendCmdResponse(false); return retCode; } #if 0 { int i; printf("@@recive CRC:"); for(i=0;i<4;i++) printf(" %02x",(unsigned char)pcPayload[i]); printf("\n"); } #endif *pui32Crc = charArrayToUL(pcPayload); // // Send ACK/NAK to command // bool bAck = (ui32RecvCount == 4) ? true : false; sendCmdResponse(bAck); // // Set progress // setProgress(100); return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief Write \e unitCount words of data to device FLASH. Source array is * 8 bit wide. Parameters \e startAddress and \e unitCount must be a * a multiple of 4. This function does not erase the flash before writing * data, this must be done using e.g. eraseFlashRange(). * * \param[in] ui32StartAddress * Start address in device. Must be a multiple of 4. * \param[in] ui32ByteCount * Number of bytes to program. Must be a multiple of 4. * \param[in] pcData * Pointer to source data. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::writeFlashRange(uint32_t ui32StartAddress, uint32_t ui32ByteCount, const char *pcData) { DEBUG_PRINT("\n"); uint32_t devStatus = SblDeviceCC2650::CMD_RET_UNKNOWN_CMD; uint32_t retCode = SBL_SUCCESS; uint32_t bytesLeft, dataIdx, bytesInTransfer; uint32_t transferNumber = 1; bool bIsRetry = false; bool bBlToBeDisabled = false; std::vector pvTransfer; uint32_t ui32TotChunks = (ui32ByteCount / SBL_CC2650_MAX_BYTES_PER_TRANSFER); if(ui32ByteCount % SBL_CC2650_MAX_BYTES_PER_TRANSFER) ui32TotChunks++; uint32_t ui32CurrChunk = 0; // // Calculate BL configuration address (depends on flash size) // uint32_t ui32BlCfgAddr = getBootloaderEnableAddress(); // // Calculate BL configuration buffer index // uint32_t ui32BlCfgDataIdx = ui32BlCfgAddr - ui32StartAddress; // // Is BL configuration part of buffer? // if(ui32BlCfgDataIdx <= ui32ByteCount) { if(((pcData[ui32BlCfgDataIdx]) & 0xFF) != SBL_CC2650_BL_CONFIG_ENABLED_BM) { bBlToBeDisabled = true; setState(SBL_SUCCESS, "Warning: Bootloader will be disabled.\n"); } } if(bBlToBeDisabled) { // // Split into two transfers // pvTransfer.resize(2); // // Main transfer (before lock bit) // pvTransfer[0].bExpectAck = true; pvTransfer[0].startAddr = ui32StartAddress; pvTransfer[0].byteCount = (ui32BlCfgAddr - ui32StartAddress) & (~0x03); pvTransfer[0].startOffset = 0; // // The transfer locking the backdoor // pvTransfer[1].bExpectAck = false; pvTransfer[1].startAddr = ui32BlCfgAddr - (ui32BlCfgAddr % 4); pvTransfer[1].byteCount = ui32ByteCount - pvTransfer[0].byteCount; pvTransfer[1].startOffset = ui32BlCfgDataIdx - (ui32BlCfgDataIdx % 4); } else { pvTransfer.resize(1); pvTransfer[0].bExpectAck = true; pvTransfer[0].byteCount = ui32ByteCount; pvTransfer[0].startAddr = ui32StartAddress; pvTransfer[0].startOffset = 0; } // // For each transfer // for(uint32_t i = 0; i < pvTransfer.size(); i++) { // // Sanity check // if(pvTransfer[i].byteCount == 0) { continue; } // // Send download command // if((retCode = cmdDownload(pvTransfer[i].startAddr, pvTransfer[i].byteCount)) != SBL_SUCCESS) { return retCode; } // // Check status after download command // retCode = readStatus(&devStatus); if(retCode != SBL_SUCCESS) { setState(retCode, "Error during download initialization. Failed to read device status after sending download command.\n"); return retCode; } if(devStatus != SblDeviceCC2650::CMD_RET_SUCCESS) { setState(SBL_ERROR, "Error during download initialization. Device returned status %d (%s).\n", devStatus, getCmdStatusString(devStatus).c_str()); return SBL_ERROR; } // // Send data in chunks // bytesLeft = pvTransfer[i].byteCount; dataIdx = pvTransfer[i].startOffset; while(bytesLeft) { // // Set progress // //setProgress(addressToPage(ui32StartAddress + dataIdx)); setProgress( ((100*(++ui32CurrChunk))/ui32TotChunks) ); // // Limit transfer count // #if 1 bytesInTransfer = min(SBL_CC2650_MAX_BYTES_PER_TRANSFER, bytesLeft); #else if(bytesLeft > SBL_CC2650_MAX_BYTES_PER_TRANSFER) { bytesInTransfer = SBL_CC2650_MAX_BYTES_PER_TRANSFER; } else { bytesInTransfer = bytesLeft; } #endif // // Send Data command // if(retCode = cmdSendData(&pcData[dataIdx], bytesInTransfer) != SBL_SUCCESS) { setState(retCode, "Error during flash download. \n- Start address 0x%08X (page %d). \n- Tried to transfer %d bytes. \n- This was transfer %d.\n", (ui32StartAddress+dataIdx), addressToPage(ui32StartAddress+dataIdx), bytesInTransfer, (transferNumber)); return retCode; } if(pvTransfer[i].bExpectAck) { // // Check status after send data command // devStatus = 0; retCode = readStatus(&devStatus); if(retCode != SBL_SUCCESS) { setState(retCode, "Error during flash download. Failed to read device status.\n- Start address 0x%08X (page %d). \n- Tried to transfer %d bytes. \n- This was transfer %d in chunk %d.\n", (ui32StartAddress+dataIdx), addressToPage(ui32StartAddress + dataIdx), (bytesInTransfer), (transferNumber), (i)); return retCode; } if(devStatus != SblDeviceCC2650::CMD_RET_SUCCESS) { setState(SBL_SUCCESS, "Device returned status %s\n", getCmdStatusString(devStatus).c_str()); if(bIsRetry) { // // We have failed a second time. Aborting. setState(SBL_ERROR, "Error retrying flash download.\n- Start address 0x%08X (page %d). \n- Tried to transfer %d bytes. \n- This was transfer %d in chunk %d.\n", (ui32StartAddress+dataIdx), addressToPage(ui32StartAddress + dataIdx), (bytesInTransfer), (transferNumber), (i)); return SBL_ERROR; } // // Retry to send data one more time. // bIsRetry = true; continue; } } else { // // We're locking device and will lose access // m_bCommInitialized = false; } // // Update index and bytesLeft // bytesLeft -= bytesInTransfer; dataIdx += bytesInTransfer; transferNumber++; bIsRetry = false; } } return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief Erases all customer accessible flash sectors not protected by FCFG1 * * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::eraseFlashBank() { DEBUG_PRINT("\n"); int retCode = SBL_SUCCESS; bool bResponse = false; if(!isConnected()) { return SBL_PORT_ERROR; } // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_BANK_ERASE)) != SBL_SUCCESS) { return retCode; } // // Get response // if((retCode = getCmdResponse(bResponse)) != SBL_SUCCESS) { return retCode; } return (bResponse) ? SBL_SUCCESS : SBL_ERROR; } //----------------------------------------------------------------------------- /** \brief Writes the CC26xx defined CCFG fields to the flash CCFG area with * the values received in the data bytes of this command. * * \param[in] ui32Field * CCFG Field ID which identifies the CCFG parameter to be written. * \param[in] ui32FieldValue * Field value to be programmed. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::setCCFG(uint32_t ui32Field, uint32_t ui32FieldValue){ DEBUG_PRINT("\n"); int retCode = SBL_SUCCESS; bool bSuccess = false; if(!isConnected()) { return SBL_PORT_ERROR; } // // Generate payload // - 4B Field ID // - 4B Field value // char pcPayload[8]; ulToCharArray(ui32Field, &pcPayload[0]); ulToCharArray(ui32FieldValue, &pcPayload[4]); // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_SET_CCFG, pcPayload, 8)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { setState(SBL_ERROR, "Set CCFG command NAKed by device.\n"); return SBL_ERROR; } return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief Send command. * * \param[in] ui32Cmd * The command to send. * \param[in] pcSendData * Pointer to the data to send with the command. * \param[in] ui32SendLen * The number of bytes to send from \e pcSendData. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::sendCmd(uint32_t ui32Cmd, const char *pcSendData/* = NULL*/, uint32_t ui32SendLen/* = 0*/) { DEBUG_PRINT("\n"); // // Handle command ID for early versions // ui32Cmd = convertCmdForEarlySamples(ui32Cmd); unsigned char pktLen = ui32SendLen + 3; // +3 => <1b Length>, <1B checksum>, <1B cmd> std::vector pvPkt((pktLen)); unsigned char pktSum = generateCheckSum(ui32Cmd, pcSendData, ui32SendLen); // // Build packet // pvPkt.at(0) = pktLen; pvPkt.at(1) = pktSum; pvPkt.at(2) = (unsigned char)ui32Cmd; if(ui32SendLen) { memcpy(&pvPkt[3], pcSendData, ui32SendLen); } // // Send packet // if(m_pCom->writeBytes(&pvPkt[0], pvPkt.size()) < 1) { setState(SBL_PORT_ERROR, "\nWriting to device failed (Command 0x%04x:'%s').\n", ui32Cmd,getCmdString(ui32Cmd).c_str()); return SBL_PORT_ERROR; } // // Empty and deallocate vector // pvPkt.clear(); std::vector().swap(pvPkt); return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief Initialize connection to the CC2650 device. * * \param[in] bSetXosc * If true, try to enable device XOSC. * \return * Returns SBL_SUCCESS, ... */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::initCommunication(bool bSetXosc) { DEBUG_PRINT("\n"); bool bSuccess, bBaudSetOk; int retCode = SBL_ERROR; // // Send dummy command to see if device is already initialized at // this baud rate. // if(sendCmd(0) != SBL_SUCCESS) { return SBL_ERROR; } // // Do we get a response (ACK/NAK)? // bSuccess = false; if(getCmdResponse(bSuccess, SBL_DEFAULT_RETRY_COUNT, true) != SBL_SUCCESS) { // // No response received. Try auto baud // if(retCode = sendAutoBaud(bBaudSetOk) != SBL_SUCCESS) { return retCode; } } m_bCommInitialized = true; return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief This function returns a string with the device command name of * \e ui32Cmd. * * \param[out] ui32Cmd * The serial bootloader command. * \return * Returns std::string with name of device command. */ //----------------------------------------------------------------------------- std::string SblDeviceCC2650::getCmdString(uint32_t ui32Cmd) { DEBUG_PRINT("\n"); switch(ui32Cmd) { case SblDeviceCC2650::CMD_PING: return "CMD_PING"; break; case SblDeviceCC2650::CMD_CRC32: return "CMD_CRC32"; break; case SblDeviceCC2650::CMD_DOWNLOAD: return "CMD_DOWNLOAD"; break; case SblDeviceCC2650::CMD_GET_CHIP_ID: return "CMD_GET_CHIP_ID"; break; case SblDeviceCC2650::CMD_GET_STATUS: return "CMD_GET_STATUS"; break; case SblDeviceCC2650::CMD_MEMORY_READ: return "CMD_MEMORY_READ"; break; case SblDeviceCC2650::CMD_MEMORY_WRITE: return "CMD_MEMORY_WRITE"; break; case SblDeviceCC2650::CMD_RESET: return "CMD_RESET"; break; default: return "Unknown command"; break; } } //----------------------------------------------------------------------------- /** \brief This function returns a string with the device status name of * \e ui32Status serial bootloader status value. * * \param[out] ui32Status * The serial bootloader status value. * \return * Returns std::string with name of device status. */ //----------------------------------------------------------------------------- std::string SblDeviceCC2650::getCmdStatusString(uint32_t ui32Status) { DEBUG_PRINT("\n"); switch(ui32Status) { case SblDeviceCC2650::CMD_RET_FLASH_FAIL: return "FLASH_FAIL"; break; case SblDeviceCC2650::CMD_RET_INVALID_ADR: return "INVALID_ADR"; break; case SblDeviceCC2650::CMD_RET_INVALID_CMD: return "INVALID_CMD"; break; case SblDeviceCC2650::CMD_RET_SUCCESS: return "SUCCESS"; break; case SblDeviceCC2650::CMD_RET_UNKNOWN_CMD: return "UNKNOWN_CMD"; break; default: return "Unknown status"; break; } } //----------------------------------------------------------------------------- /** \brief This function sends the CC2650 download command and handles the * device response. * * \param[in] ui32Address * The start address in CC2650 flash. * \param[in] ui32Size * The total number of bytes to program on the device. * * \return * Returns SBL_SUCCESS if command and response was successful. */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::cmdDownload(uint32_t ui32Address, uint32_t ui32Size) { DEBUG_PRINT("\n"); int retCode = SBL_SUCCESS; bool bSuccess = false; // // Check input arguments // if(!addressInFlash(ui32Address, ui32Size)) { setState(SBL_ARGUMENT_ERROR, "Flash download: Address range (0x%08X + %d bytes) is not in device FLASH nor RAM.\n", ui32Address, ui32Size); return SBL_ARGUMENT_ERROR; } if(ui32Size & 0x03) { setState(SBL_ARGUMENT_ERROR, "Flash download: Byte count must be a multiple of 4\n"); return SBL_ARGUMENT_ERROR; } // // Generate payload // - 4B Program address // - 4B Program size // char pcPayload[8]; ulToCharArray(ui32Address, &pcPayload[0]); ulToCharArray(ui32Size, &pcPayload[4]); // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_DOWNLOAD, pcPayload, 8)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess)) != SBL_SUCCESS) { return retCode; } // // Return command response // return (bSuccess) ? SBL_SUCCESS : SBL_ERROR; } //----------------------------------------------------------------------------- /** \brief This function sends the CC2650 SendData command and handles the * device response. * * \param[in] pcData * Pointer to the data to send. * \param[in] ui32ByteCount * The number of bytes to send. * * \return * Returns SBL_SUCCESS if command and response was successful. */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::cmdSendData(const char *pcData, uint32_t ui32ByteCount) { DEBUG_PRINT("\n"); uint32_t retCode = SBL_SUCCESS; bool bSuccess = false; // // Check input arguments // if(ui32ByteCount > SBL_CC2650_MAX_BYTES_PER_TRANSFER) { setState(SBL_ERROR, "Error: Byte count (%d) exceeds maximum transfer size %d.\n", ui32ByteCount, SBL_CC2650_MAX_BYTES_PER_TRANSFER); return SBL_ERROR; } // // Send command // if((retCode = sendCmd(SblDeviceCC2650::CMD_SEND_DATA, pcData, ui32ByteCount)) != SBL_SUCCESS) { return retCode; } // // Receive command response (ACK/NAK) // if((retCode = getCmdResponse(bSuccess, 3)) != SBL_SUCCESS) { return retCode; } if(!bSuccess) { return SBL_ERROR; } return SBL_SUCCESS; } //----------------------------------------------------------------------------- /** \brief This function returns the page within which address \e ui32Address * is located. * * \param[in] ui32Address * The address. * * \return * Returns the flash page within which an address is located. */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::addressToPage(uint32_t ui32Address) { DEBUG_PRINT("\n"); return ((ui32Address - SBL_CC2650_FLASH_START_ADDRESS) / getPageEraseSize() ); } //----------------------------------------------------------------------------- /** \brief This function checks if the specified \e ui32StartAddress (and range) * is located within the device RAM area. * * \param[in] ui32StartAddress * The start address of the range * \param[in] pui32Bytecount * (Optional) The number of bytes in the range. * \return * Returns true if the address/range is within the device RAM. */ //----------------------------------------------------------------------------- bool SblDeviceCC2650::addressInRam(uint32_t ui32StartAddress, uint32_t ui32ByteCount/* = 1*/) { DEBUG_PRINT("\n"); uint32_t ui32EndAddr = ui32StartAddress + ui32ByteCount; if(ui32StartAddress < SBL_CC2650_RAM_START_ADDRESS) { return false; } if(ui32EndAddr > (SBL_CC2650_RAM_START_ADDRESS + getRamSize())) { return false; } return true; } //----------------------------------------------------------------------------- /** \brief This function checks if the specified \e ui32StartAddress (and range) * is located within the device FLASH area. * * \param[in] ui32StartAddress * The start address of the range * \param[in] pui32Bytecount * (Optional) The number of bytes in the range. * * \return * Returns true if the address/range is within the device RAM. */ //----------------------------------------------------------------------------- bool SblDeviceCC2650::addressInFlash(uint32_t ui32StartAddress, uint32_t ui32ByteCount/* = 1*/) { DEBUG_PRINT("\n"); uint32_t ui32EndAddr = ui32StartAddress + ui32ByteCount; if(ui32EndAddr > (SBL_CC2650_FLASH_START_ADDRESS + getFlashSize())) { return false; } return true; } //----------------------------------------------------------------------------- /** \brief This function returns the FLASH address of the bootloader enable * configuration. * * \return * Returns true if the address/range is within the device RAM. */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::getBootloaderEnableAddress() { DEBUG_PRINT("\n"); return SBL_CC2650_FLASH_START_ADDRESS + getFlashSize() - getPageEraseSize() + SBL_CC2650_BL_CONFIG_PAGE_OFFSET; } //----------------------------------------------------------------------------- /** \brief This function checks if the specified \e ui32StartAddress (and range) * overlaps the bootloader's working memory or stack area. * * The bootloader does not protect against writing to these ranges, but * doing so is almost guaranteed to crash the bootloader and requires a * reboot. SRAM ranges used by the bootloader: * \li Work memory @ 0x20000000-0x2000016F * \li Stack area @ 0x20000FC0-0x20000FFF * * \param[in] ui32StartAddress * The start address of the range * \param[in] pui32Bytecount * (Optional) The number of bytes in the range. * * \return * Returns true if the address/range is within the device RAM. */ //----------------------------------------------------------------------------- bool SblDeviceCC2650::addressInBLWorkMemory(uint32_t ui32StartAddress, uint32_t ui32ByteCount/* = 1*/) { DEBUG_PRINT("\n"); uint32_t ui32EndAddr = ui32StartAddress + ui32ByteCount; if(ui32StartAddress <= SBL_CC2650_BL_WORK_MEMORY_END) { return true; } if((ui32StartAddress >= SBL_CC2650_BL_STACK_MEMORY_START) && (ui32StartAddress <= SBL_CC2650_BL_STACK_MEMORY_END)) { return true; } if((ui32EndAddr >= SBL_CC2650_BL_STACK_MEMORY_START) && (ui32EndAddr <= SBL_CC2650_BL_STACK_MEMORY_END)) { return true; } return false; } //----------------------------------------------------------------------------- /** \brief This function converts the command ID if the connected device * is an early version. Affected commands are * \li CMD_MEMORY_READ * \li CMD_MEMORY_WRITE * \li CMD_SET_CCFG * \li CMD_BANK_ERASE * * \param[in] ui32Cmd * The command that must be converted. * * \return * Returns the correct command ID for the connected device. */ //----------------------------------------------------------------------------- uint32_t SblDeviceCC2650::convertCmdForEarlySamples(uint32_t ui32Cmd) { DEBUG_PRINT("\n"); if(m_deviceRev != 1) { // No conversion needed, return early. return ui32Cmd; } switch(ui32Cmd) { case SblDeviceCC2650::CMD_MEMORY_READ: return SblDeviceCC2650::REV1_CMD_MEMORY_READ; case SblDeviceCC2650::CMD_MEMORY_WRITE: return SblDeviceCC2650::REV1_CMD_MEMORY_WRITE; case SblDeviceCC2650::CMD_SET_CCFG: return SblDeviceCC2650::REV1_CMD_SET_CCFG; case SblDeviceCC2650::CMD_BANK_ERASE: return SblDeviceCC2650::REV1_CMD_BANK_ERASE; default: // No conversion needed, return original command. return ui32Cmd; } }