1.8 PRIME Service Bootloader
The PRIME Service Bootloader application implements a bootloader designed to manage firmware images. These images are received through the PRIME upgrade protocol defined in the PRIME specification, but the Bootloader application is not aware of that.
Name | Path | Boards |
---|---|---|
Service Bootloader | \smartenergy_prime_apps\apps\prime_apps\prime_service_bootloader\bootloader_pic32cxmtg.X | PIC32CXMTG-EK |
Service Bootloader | \smartenergy_prime_apps\apps\prime_apps\prime_service_bootloader\bootloader_pic32cxmtsh.X | PIC32CXMTSH-DB |
Application Example
The PRIME Service Bootloader application implements a bootloader designed to manage firmware images. These images are received through the PRIME upgrade protocol defined in the PRIME specification, but the Bootloader application is not aware of that.
- Non-volatile data storage.
The PRIME Service Bootloader needs it to manage the bootloading process and
the boot configuration.
- For PIC32CXMT it is implemented using the User
Signature.
static void lAPP_BOOTLOADER_UpdateUserSignature(uint8_t pagesCnt, BOOT_STATE state) { /* Update values */ bootConfig[16] = pagesCnt; bootConfig[17] = (uint8_t) state; /* Erase the user signature */ SEFC0_UserSignatureErase(BOOT_USER_SIGNATURE_BLOCK); /* Update the user signature */ (void) SEFC0_UserSignatureWrite((void*) userSignBuf, (uint32_t) BOOT_USER_SIGNATURE_SIZE_64, (SEFC_USERSIGNATURE_BLOCK) BOOT_USER_SIGNATURE_BLOCK, (SEFC_USERSIGNATURE_PAGE) BOOT_USER_SIGNATURE_PAGE); }
- For PIC32CXMT it is implemented using the User
Signature.
- Memory handling. The PRIME Service Bootloader needs to delete, copy and
verify pages.
- For PIC32CXMTG it is implemented using the SEFC0
peripheral.
static uint8_t lAPP_BOOTLOADER_DeletePage(uint32_t addr) { SEFC0_RegionUnlock(addr); while (SEFC0_IsBusy()) { ; } SEFC0_PageErase(addr); // Function erases 16 pages while (SEFC0_IsBusy()) { ; } return 1; } static uint8_t lAPP_BOOTLOADER_CopyPage(uint32_t srcAddr, uint32_t dstAddr) { uint8_t *page; uint8_t i; (void) memcpy(pageBlock, (uint8_t *) (srcAddr), BOOT_FLASH_16PAGE_SIZE); page = &pageBlock[0]; for (i = 0; i < BOOT_FLASH_PAGES_NUMBER; i++) { if (SEFC0_PageWrite((uint32_t *) page, dstAddr) == false) { return 0; } while (SEFC0_IsBusy()) { ; } page += BOOT_FLASH_PAGE_SIZE; dstAddr += BOOT_FLASH_PAGE_SIZE; } return 1; } static uint8_t lAPP_BOOTLOADER_VerifyPage(uint8_t *ramPage, uint8_t *flashPage, uint16_t pageSize) { uint16_t i = 0; while (i < pageSize) { if (ramPage[i] != flashPage[i]) { return 0; } i++; } return 1; }
- For PIC32CXMTG it is implemented using the SEFC0
peripheral.
Application Functionality
The PRIME Service Bootloader application functionality is very simple as it just reads and writes memory pages according to the information that has been stored previously in the User Signature.
case APP_BOOTLOADER_STATE_INIT:
{
bool appInitialized = true;
if (appInitialized) {
/* Ensure all priority bits are assigned as preemption */
/* priority bits. */
NVIC_SetPriorityGrouping(__NVIC_PRIO_BITS);
__set_BASEPRI(0);
/* Disable User Signature write protection */
SEFC0_WriteProtectionSet(0);
/* Enable write and read User Signature rights */
/* (block 0 / area 1) */
SEFC0_UserSignatureRightsSet(
SEFC_EEFC_USR_RDENUSB1_Msk | SEFC_EEFC_USR_WRENUSB1_Msk);
app_bootloaderData.state = APP_BOOTLOADER_STATE_SERVICE_TASKS;
}
break;
}
- Extract the boot configuration from the User
Signature:
/* Read the boot configuration from user signature */ (void) SEFC0_UserSignatureRead((void*) userSignBuf, (uint32_t) BOOT_USER_SIGNATURE_SIZE_64, (SEFC_USERSIGNATURE_BLOCK) BOOT_USER_SIGNATURE_BLOCK, (SEFC_USERSIGNATURE_PAGE) BOOT_USER_SIGNATURE_PAGE); /* Get boot configuration */ bootConfig = userSignBuf + BOOT_CONFIG_OFFSET_USER_SIGN; cfgKey = ((uint32_t) bootConfig[3]) << 24; cfgKey += ((uint32_t) bootConfig[2]) << 16; cfgKey += ((uint32_t) bootConfig[1]) << 8; cfgKey += (uint32_t) bootConfig[0];
- Extract the boot information, if the boot configuration is
valid:
/* Get boot information */ imageSize = (uint32_t) (bootConfig[7]) << 24; imageSize += (uint32_t) (bootConfig[6]) << 16; imageSize += (uint32_t) (bootConfig[5]) << 8; imageSize += (uint32_t) (bootConfig[4]); origAddr = (uint32_t) (bootConfig[11]) << 24; origAddr += (uint32_t) (bootConfig[10]) << 16; origAddr += (uint32_t) (bootConfig[9]) << 8; origAddr += (uint32_t) (bootConfig[8]); destAddr = (uint32_t) (bootConfig[15]) << 24; destAddr += (uint32_t) (bootConfig[14]) << 16; destAddr += (uint32_t) (bootConfig[13]) << 8; destAddr += (uint32_t) (bootConfig[12]); pagesCounter = bootConfig[16]; bootState = bootConfig[17];
- Trigger the firmware swap, if
needed:
/* Check if swap fw is needed. If not, load defaults. */ if (lAPP_BOOTLOADER_IsSwapCmd(imageSize, origAddr, destAddr) == true) { /* Swap fw */ (void) lAPP_BOOTLOADER_SwapFwVersion(imageSize, origAddr, destAddr); /* Clear boot configuration (leave configuration key) */ (void) memset(&bootConfig[4], 0, 16); /* Update user signature */ lAPP_BOOTLOADER_UpdateUserSignature(0, BOOT_IDLE); }
- Start the user
application:
/* Set the stack pointer to the start of the firmware application */ __set_MSP(*(int *) (BOOT_FLASH_APP_FIRMWARE_START_ADDRESS)); /* Offset the start of the vector table (first 6 bits must be * zero) */ /* The register containing the offset, from 0x00000000, is at * 0xE000ED08 */ SCB->VTOR = ((uint32_t) BOOT_FLASH_APP_FIRMWARE_START_ADDRESS & SCB_VTOR_TBLOFF_Msk); __DSB(); __ISB(); __enable_irq(); /* * (int *) 0xE000ED08 = FIRMWARE_START_ADDRESS; */ /* Jump to the start of the firmware, casting the address as * function pointer to the start of the firmware. We want to jump * to the address of the reset. */ /* Handler function, that is the value that is being pointed at * position FIRMWARE_RESET_ADDRESS */ void (*runFirmware)(void) = NULL; runFirmware = (void (*)(void))(*(uint32_t *) BOOT_FLASH_APP_FIRMWARE_RESET_ADDRESS); runFirmware(); while (1) { ; }