This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Very simple SPI bootloader

Hi,

I'm implementing a simple bootloader which will not use BLE.

  • Application will transfer the data from mobile devices using BLE and this data will be stored to external SPI flash memory. I've already managed to make writing/reading data over SPI.
  • Custom bootloader should only read and verify data from external SPI flash and write that data to nRF flash. Bootloader should be able to update any address on the chip except itself, meaning that we want this bootloader to be able to update SoftDevice also. After it flashes what it needs to flash, it should go into newly flashed application.

I have trouble understanding vector mapping and how to do it right. Can someone please give an example with a bit more explanation?

Thnx!

Parents
  • Maybe the example mentioned in this thread here can give you some pointers on how to handle interrupt forwarding without the softdevice. However, an easier approach may just be to use the SD to forward interrupts to your bootloader/application (sd_softdevice_vector_table_base_set()). Then use the MBR to enable update of the SD itself.

    EDIT 17.11

    Think I have a better idea of what you want to do now after reading your question again, but please correct me if I'm wrong:

    First you want to receive the image over BLE and store it to external flash over SPI. Then once finished, use the bootloader to load the received FW image into application or SD space on the nRF51.

    So if my understanding is correct I think I would have used the SDK bootloader as a basis, and modify it so it runs independent of the Softdevice once a FW image is successfully loaded to the SPI flash. That is, don't call ble_stack_init() or use any of the sd_ flash APIs (pstorage). When the bootloader is running independent of the softdevice you can access the the NVMC module directly (example). Thus, delete and update the softdevice from the bootloader without using the MBR (MBR can't access "SPI" memory).

    Update sequence:

    • FW image is uploaded over BLE, only difference is that it's loaded to SPI flash (softdevice enabled at this point).
    • Bootloader verifies integrity of received image. CRC and image size received from init packet and start packet respectively.
    • Set a flag in bootloader settings to indicate new FW image is ready to be swapped. Similiar to what we already do when updating the softdevice/bootloader.
    • Bootloader does a system reset (soft reset - NVIC_SystemReset)
    • MBR forwards execution directly to the bootloader without invoking the softdevice
    • Bootloader checks for update in progress flag on startup, similiar to if (bootloader_dfu_sd_in_progress()) in existing example.
    • Prepare flash region (delete) and start loading the image from "SPI" flash to Softdevice/App region.
    • Update flags in bootloader settings when complete and reset.
    • Go into DFU mode or start application if it's present and valid.

    Also, before you enter the application the following functions must have been called:

    sd_mbr_command_t com = {SD_MBR_COMMAND_INIT_SD, }; // MBR starts forwarding interrupts to SD
    
    uint32_t err_code = sd_mbr_command(&com);
    APP_ERROR_CHECK(err_code);
    
    interrupts_disable();
    
    err_code = sd_softdevice_vector_table_base_set(CODE_REGION_1_START); // Set interrupt forwarding address (application vector table)
    APP_ERROR_CHECK(err_code);
    
    bootloader_util_app_start(CODE_REGION_1_START); // ASM routine to branch to app reset handler.
    

    Note that I have not had a chance to test the approach I described above, so it is possible that I've missed something.

Reply
  • Maybe the example mentioned in this thread here can give you some pointers on how to handle interrupt forwarding without the softdevice. However, an easier approach may just be to use the SD to forward interrupts to your bootloader/application (sd_softdevice_vector_table_base_set()). Then use the MBR to enable update of the SD itself.

    EDIT 17.11

    Think I have a better idea of what you want to do now after reading your question again, but please correct me if I'm wrong:

    First you want to receive the image over BLE and store it to external flash over SPI. Then once finished, use the bootloader to load the received FW image into application or SD space on the nRF51.

    So if my understanding is correct I think I would have used the SDK bootloader as a basis, and modify it so it runs independent of the Softdevice once a FW image is successfully loaded to the SPI flash. That is, don't call ble_stack_init() or use any of the sd_ flash APIs (pstorage). When the bootloader is running independent of the softdevice you can access the the NVMC module directly (example). Thus, delete and update the softdevice from the bootloader without using the MBR (MBR can't access "SPI" memory).

    Update sequence:

    • FW image is uploaded over BLE, only difference is that it's loaded to SPI flash (softdevice enabled at this point).
    • Bootloader verifies integrity of received image. CRC and image size received from init packet and start packet respectively.
    • Set a flag in bootloader settings to indicate new FW image is ready to be swapped. Similiar to what we already do when updating the softdevice/bootloader.
    • Bootloader does a system reset (soft reset - NVIC_SystemReset)
    • MBR forwards execution directly to the bootloader without invoking the softdevice
    • Bootloader checks for update in progress flag on startup, similiar to if (bootloader_dfu_sd_in_progress()) in existing example.
    • Prepare flash region (delete) and start loading the image from "SPI" flash to Softdevice/App region.
    • Update flags in bootloader settings when complete and reset.
    • Go into DFU mode or start application if it's present and valid.

    Also, before you enter the application the following functions must have been called:

    sd_mbr_command_t com = {SD_MBR_COMMAND_INIT_SD, }; // MBR starts forwarding interrupts to SD
    
    uint32_t err_code = sd_mbr_command(&com);
    APP_ERROR_CHECK(err_code);
    
    interrupts_disable();
    
    err_code = sd_softdevice_vector_table_base_set(CODE_REGION_1_START); // Set interrupt forwarding address (application vector table)
    APP_ERROR_CHECK(err_code);
    
    bootloader_util_app_start(CODE_REGION_1_START); // ASM routine to branch to app reset handler.
    

    Note that I have not had a chance to test the approach I described above, so it is possible that I've missed something.

Children
  • Correct the MBR is loaded at address 0 and is included as a part of the SD binary. On startup the MBR will forward execution to the address stored in UICR->BOOTLOADERADDR if set. In our examples we place the bootloader above the application, see memory layout.

    Replacing the SD itself will be handled in the MBR without involvement from the bootloader, and will not return execution to the bootloader before its finished (will also handle unexpected resets).

  • Ok, that helped a bit with some facts.

    So basically I can modify "serial single bank bootloader" template and make it read stuff from external flash over SPI. But, how to make MBR read data directly from external flash? I guess there is no way? I would have to copy data for SD to chip flash and then tell MBR to overwrite it?

    The problem is I will have my own file from which SD will be updated. Where can I read some info about how SD firmware file must look like in temporary bank(bank 1) so MBR will be able to update it on it's own from bank 1 to bank 0?

    Is there a source code for "sd_softdevice_vector_table_base_set()" function? Why not use that function each time we go from bootloader to application and vice versa so we don't need to use a solution as it was mentioned on the link you provided above?

    Thank you!

  • Yes, that's it!

    • application transfers data to SPI external flash, raises flags, restarts
    • bootloader reads flags and flashes the chip (new SD or new app), clears flags and enters new app

    This means I don't have to use vector mapping, since if I use modified SDK bootloader, MBR will always call bootloader 1st. And after bootloading is done, when I call the stuff you wrote, I will have SD flashed so it will work. And since this is very simple bootloader(KISS principle), it will never need updating of it's own.

    I think I got it now, I'll wait until you confirm this last few sentences. Just one simple question. If size of SD changes it will change CODE_REGION_1_START address, correct? So I guess I should have this address as a variable which I can write/read and not have it hard-coded in the bootloader?

    Thank you very much! This helped me to grasp everything into solution.

  • One more question. When updating SD directly as mentioned above, I should keep MBR intact, correct? Meaning I should skip first 4096 bytes(address 0x0 - 0x1000) when deleting and writing new SD.

    Thank you!

Related