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

Generating Production Image without DFU OTA

Due to time pressure, I'm starting this thread based on an existing ticket: https://devzone.nordicsemi.com/f/nordic-q-a/51423/firmware-on-custom-board-application-bootloader, so that I can get as many eyes on this as possible.

My setup:

  • Board: Custom PCB based on NRF52840
  • SDK: 15.2.0
  • Development Environment: Segger Embedded Studio

I am trying to assemble an image that can be flashed on the custom board at the factory. I have followed the Nordic tutorials, and I've gotten to the point where I can assemble an image (Application, Bootloader (+bootloader settings) and Softdevice). The problem is the device does not boot up.

Here's the situation:

  1. The custom board does not yet have a BLE antenna, so we cant do BLE DFU, yet
  2. All UARTS have been used up, so we can't do DFU via UART either
  3. The only viable path for now is to program the image using nrfjprog.

Questions:

  1. Any insights on why the device does not boot up?
  2. Regarding the private/public key aspects of the process, I understand the public key is linked with the bootloader project. How does one link the private key with the bootloader settings, if possible?
  3. Is there a way to bypass the key validation, because, again, right now we can't do DFU.
  4. There is a chance the issue might be the same as this: https://devzone.nordicsemi.com/f/nordic-q-a/19068/uploading-application-and-bootloader-application-not-starting.How do I do this in SDK 15.2.0?

Any and all insights are appreciated!

Thanks a mil,

Tim

Parents
  • Hello Tim,

     

    Any insights on why the device does not boot up?

     Probably something wrong with the bootloader settings (not present, or not using the correct key).

     

    Regarding the private/public key aspects of the process, I understand the public key is linked with the bootloader project. How does one link the private key with the bootloader settings, if possible?

     Yes. You must sign the bootloader settings with the private key. Please see the example nrfutil command here. The only thing that is missing is the private.key. It should look something like this:

    nrfutil settings generate --family NRF52840 --application app.hex --application-version 3 --bootloader-version 2 --bl-settings-version 1 --key-file private.key settings.hex

    (assuming that the private key is located in the same folder as you run cmd from.

     

    Is there a way to bypass the key validation, because, again, right now we can't do DFU.

     Yes, but to be fair, I don't see why you want the bootloader if you can't use it at this point in time. But yes, there is a way to bypass the key check, by using the pca10056_ble_debug bootloader instead of the pca10056_ble bootloader. Please note that the debug bootloader uses more flash. You can also just sign the bootloader settings with the key. If you don't have the possibility to do DFU, this is just as good as using the debug bootloader, and in addition, it doesn't take up as much flash as the debug bootloader. 

     

    There is a chance the issue might be the same as this: https://devzone.nordicsemi.com/f/nordic-q-a/19068/uploading-application-and-bootloader-application-not-starting.How do I do this in SDK 15.2.0?

     I don't think it is related. Do you use the NFC pins for something else?

    When you generate the bootloader settings, and flash it with the application, the bootloader and the softdevice, then the application should start. If not, please give me a detailed description on how you performed it. Maybe it reveals some information on where the issue is. 

    But I suspect the only thing you are missing is the "--private-key private.key" in the command you use to generate the bootloader settings.

  • Well, to start with, the entire process is scripted, as typing in all the commands manually can be quite tedious.

    Step 1: I start by generating the public and private keys:

    private_key_path="../path_to_private_key/private.key"
    public_key_file_path="../path_to_public_key/public_key.c"
    
    #Delete any existing keys before generating new ones
    echo "Deleting any existing keys.."
    rm -f $private_key_path
    rm -f $public_key_file_path
    echo "OK!"
    
    # Generate private key
    echo "Generating private key.."
    nrfutil keys generate $private_key_path
    echo "OK!"
    
    # Generate public key based on private key
    echo "Generating public key.."
    nrfutil keys display --key pk --format code $private_key_path --out_file $public_key_file_path
    echo "OK!"

    Both keys are generated in the same folder. I have linked that directory inside segger embedded studio so that everytime i generate a new pair of keys, I don't need to manually copy them all the time.

    Step2: Build the pca10056_ble bootloader project.

    Step 3: I use a script to assemble all the components in a single folder (MBR, BL, SD and App). The script merely copies them from their default destinations:

    #---------- Settings ----------------#
    # Application and Bootloader images (need to be built)
    application_path="../path_to_compiled_app/Exe/nordsense_wms_application_v2.hex"
    bootloader_path="../path_to_compiled_bootloader/secure_ble_s140_bootloader.hex"
    
    # Softdevice and MBR paths (exist as precompiled images in the Nordic SDK)
    softdevice_path="../sdk_15_2_0/components/softdevice/s140/hex/s140_nrf52_6.1.0_softdevice.hex"
    master_boot_record_path="../sdk_15_2_0/components/softdevice/mbr/nrf52840/hex/mbr_nrf52_2.3.0_mbr.hex"
    
    # Destination to copy the images to
    components_destination="../production/images"
    #-----------------------------------#
    
    # Copy master boot record
    echo "Fetching Master Boot Record (MBR) Image..."
    cp -p $master_boot_record_path $components_destination
    echo "OK!"
    
    # Copy Softdevice
    echo "Fetching Softdevice (SD) Image..."
    cp -p $softdevice_path $components_destination
    echo "OK!"
    
    # Copy Bootloader 
    echo "Fetching Bootloader Image..."
    cp -p $bootloader_path $components_destination
    echo "OK!"
    
    # Copy Application 
    echo "Fetching Application Image..."
    cp -p $application_path $components_destination
    echo "OK!"

    Step 4: Generate the bootloader settings:

    #---------- Settings ----------------#
    device_family="NRF52840"
    app_image_path="../path_to_compiled_app/application.hex"
    app_version="0"
    bl_version="0"
    bl_settings_version="1"
    key_file="../path_to_private_key/private.key"
    output_file="../production/images/ns_wms_bootloader_settings.hex"
    softdevice_file="../production/images/s140_nrf52_6.1.0_softdevice.hex"
    #-----------------------------------#
    
    #Delete any existing bootloader settings image
    rm -f $output_file
    nrfutil settings generate --family $device_family --application $app_image_path --application-version $app_version --bootloader-version $bl_version --bl-settings-version $bl_settings_version --softdevice $softdevice_file --key-file $key_file $output_file
    nrfutil settings display $output_file

    And finally step 5: assemble the images (now all images are in one folder):

    #---------- Settings ----------------#
    production_images_folder="../production/images"
    
    bl_image_path=$production_images_folder/secure_ble_s140_bootloader.hex
    bl_settings_path=$production_images_folder/bootloader_settings.hex
    app_path=$production_images_folder/nordsense_wms_application_v2.hex
    sd_path=$production_images_folder/s140_nrf52_6.1.0_softdevice.hex
    mbr_path=$production_images_folder/mbr_nrf52_2.3.0_mbr.hex
    
    merged_bl_path=$production_images_folder/m_wms_bootloader.hex
    merged_app_path=$production_images_folder/m_wms_application.hex
    production_image_path=$production_images_folder/m_wms_app_production_1_0_0.hex
    #-----------------------------------#
    
    echo "---------------------------------------------------------------"
    
    # Delete any existing images
    echo "Deleting existing images.."
    rm -f $production_images_folder/*.hex
    echo "OK!"
    
    # Fetch the production image components (Master Boot Record, Softdevice, Bootloader and Application image)
    echo "Fetching Production Image Components.."
    ./fetch_production_image_components.sh
    
    # Generate bootloader settings image
    echo "Generating Bootloader Setting image.."
    ./build_bootloader_settings.sh
    echo "OK!"
    
    # Merge bootloader and bootloader settings
    echo "Merging Bootloader and Bootloader Settings Images.."
    mergehex -m $bl_image_path $bl_settings_path -o $merged_bl_path
    echo "OK!"
    
    # Merge bootloader (+settings) and Application
    echo "Merging Updated Bootloader and Application Images.."
    mergehex -m $merged_bl_path $app_path -o $merged_app_path
    echo "OK!"

    I run these scripts from my "scripts" folder.

    I open the compiled hex using NRF connect programmer, and it looks good. All the components start at the right addresses (could you please verify that?):

    app_production_1_0_0.hex

    I feel like I've done everything right up till now (thanks for your help in the previous post). I still can't get the application to boot.

    A LED normally lights up when I'm debugging the code. I also expect the LED to light up when I flash the assembled images, but nothing happens.

    I also saw something a little weird (maybe it's nothing), but when I generated the bootloader settings and ran the command "nrfutil settings display" right after, the outputs are a little different, plus there's a warning: Bad access at 0x7F000: not enough data to read 4 contiguous bytes:

    Generated Bootloader DFU settings .hex file and stored it in: ../production/images/ns_wms_bootloader_settings.hex
    
    Bootloader DFU Settings:
    * File:                     ../production/images/ns_wms_bootloader_settings.hex
    * Family:                   NRF52840
    * Start Address:            0x000FF000
    * CRC:                      0x390CDFC7
    * Settings Version:         0x00000001 (1)
    * App Version:              0x00000000 (0)
    * Bootloader Version:       0x00000000 (0)
    * Bank Layout:              0x00000000
    * Current Bank:             0x00000000
    * Application Size:         0x0002AD34 (175412 bytes)
    * Application CRC:          0x2D2EC708
    * Bank0 Bank Code:          0x00000001
    * Softdevice Size:          0x00024E7C (151164 bytes)
    * Boot Validation CRC:      0x00000000
    * SD Boot Validation Type:  0x00000000 (0)
    * App Boot Validation Type: 0x00000000 (0)
    
    Bad access at 0x7F000: not enough data to read 4 contiguous bytes
    
    Bootloader DFU Settings:
    * File:                     ../production/images/ns_wms_bootloader_settings.hex
    * Family:                   NRF52840
    * Start Address:            0x000FE000
    * CRC:                      0x390CDFC7
    * Settings Version:         0x00000001 (1)
    * App Version:              0x00000000 (0)
    * Bootloader Version:       0x00000000 (0)
    * Bank Layout:              0x00000000
    * Current Bank:             0x00000000
    * Application Size:         0x0002AD34 (175412 bytes)
    * Application CRC:          0x2D2EC708
    * Bank0 Bank Code:          0x00000001
    * Softdevice Size:          0x00000000 (0 bytes)
    * Boot Validation CRC:      0x00000000
    * SD Boot Validation Type:  0x00000000 (0)
    * App Boot Validation Type: 0x00000000 (0)

    Any insights you have will be very much appreciated.

    Until then I'll try running the debug version of the bootloader.

    Thanks Edvin,

    Tim

  • Thank you, Tim.

    I see the same output as you when I try to use "nrfutil settings display" but it still works. Please note that this message does not appear when I generate the settings. 

    So, if you don't see the application starting, can you describe how you determine that it doesn't start? It is the lack of the LED, right?

    Does the LED show up if you only program the SD + application (but not the bootloader or BL settings)?

    You still use Segger Embedded Studio (SES), right?

    Can you try to recompile your application without optimization (please let me know if you are not sure how to do this), and then try to repeat the procedure?

    Then try to open your application project in SES, and then press "Tools" -> "Attach Debugger". Set a breakpoint in the top of your main() function, and then reset the device. Does that breakpoint hit?

    Can you also try to enable logging in your application project, and see if anything pops up there?

    NB: When I program all your 4 .hex files, LED1 on my DK lights up. Does it do that on your side? What HW are you running on? Is it the nRF52840 DK? How do you program your files? nRF Connect programmer? I tried both this and nrfjprog, and when I program all 4 hex files, it turns on LED1. Do you program the hex files at the same time, or one at the time? (I did all 4 at once)

  • Thanks for your elaborate troubleshooting! Very much appreciated!

    Well, I have been using a custom board based on the NRF52840.

    I decided yesterday to take a step back and try a much simpler application (the blinky freertos example), and the NRF52840 DK. I repeated the exact steps I've been doing so far (I expected all 4 LEDS to be blinking in a certain pattern as soon as the programming was finished). Instead LED1 and LED2 just lights up.

    Funny enough I then tried using my own application image, but this time on the DK. It was the same result - LED1 and LED2 just lights up. This is definitely some kind of error, as my custom board LED is mapped to P0.20, and the LEDs on the DK are mapped to P0.13 - P0.16.

    Can you try building an image using the blinky freertos example? If you can get that to work, then perhaps you could share exactly how you did it. That could hopefully be a good start to figuring this out.

    To answer your questions:

    1. Yes, I use SES,
    2. Regarding optimization, I open the project options, choose the Configuration (Debug/Release), then go to Code Generation. Finally I set the optimization level to None. Can you confirm this is the proper way to do it?

    In the meantime, I'll try using the debug version of the bootloader.

  • Update: I tried looking into why exactly LEDS 1 and 2 light up when the flashing is completed. Turns out that, in main.c (secure_bootloader_pca10056_ble), a function gets called (dfu_observer), which notifies of DFU events.

    By default, it's setup like this:

    static void dfu_observer(nrf_dfu_evt_type_t evt_type)
    {
        switch (evt_type)
        {
            case NRF_DFU_EVT_DFU_FAILED:
            case NRF_DFU_EVT_DFU_ABORTED:
            case NRF_DFU_EVT_DFU_INITIALIZED:
                bsp_board_init(BSP_INIT_LEDS);
                bsp_board_led_on(BSP_BOARD_LED_0);
                bsp_board_led_on(BSP_BOARD_LED_1);
                bsp_board_led_off(BSP_BOARD_LED_2);
                break;
            case NRF_DFU_EVT_TRANSPORT_ACTIVATED:
                bsp_board_led_off(BSP_BOARD_LED_1);
                bsp_board_led_on(BSP_BOARD_LED_2);
                break;
            case NRF_DFU_EVT_DFU_STARTED:
                break;
            default:
                break;
        }
    }

    I moved bsp_board_init(BSP_INIT_LEDS); to main, and changed the rest of the function to:

    static void dfu_observer(nrf_dfu_evt_type_t evt_type)
    {
        bsp_board_leds_off();
        
        switch (evt_type)
        {
            case NRF_DFU_EVT_DFU_FAILED:
                bsp_board_led_on(BSP_BOARD_LED_0); // LED1
            break;
            case NRF_DFU_EVT_DFU_ABORTED:
                bsp_board_led_on(BSP_BOARD_LED_1); // LED2
            break;
            case NRF_DFU_EVT_DFU_INITIALIZED:
                bsp_board_led_on(BSP_BOARD_LED_2); // LED3
                break;
            case NRF_DFU_EVT_TRANSPORT_ACTIVATED:
                bsp_board_led_on(BSP_BOARD_LED_3); // LED4
                break;
            case NRF_DFU_EVT_DFU_STARTED:
                bsp_board_led_on(BSP_BOARD_LED_0); // LED1
                bsp_board_led_on(BSP_BOARD_LED_1); // LED2
            default:
                break;
        }
    }

    On flashing the assembled image, LED3 turns on - which leads me to think it is stuck in DFU mode. This log confirms it:

    <info> app: Inside main
    <debug> app: In nrf_bootloader_init
    <debug> nrf_dfu_settings: Calling nrf_dfu_settings_init()...
    <debug> nrf_dfu_flash: Initializing nrf_fstorage_nvmc backend.
    <debug> nrf_dfu_settings: Settings OK
    <debug> app: Enter nrf_bootloader_fw_activate
    <info> app: No firmware to activate.
    <debug> app: Enter nrf_dfu_app_is_valid
    <debug> app: No valid app to boot.
    <debug> app: DFU mode because app is not valid.
    <info> nrf_bootloader_wdt: WDT is not enabled
    <debug> app: in weak nrf_dfu_init_user
    <debug> app: timer_stop (0x200057D4)
    <debug> app: timer_activate (0x200057D4)
    <info> app: Entering DFU mode.
    <debug> app: Initializing transports (found: 1)
    <debug> nrf_dfu_ble: Initializing BLE DFU transport
    <debug> nrf_dfu_ble: Setting up vector table: 0x000F1000
    <debug> nrf_dfu_ble: Enabling SoftDevice.
    <debug> nrf_dfu_ble: Configuring BLE stack.
    <debug> nrf_dfu_ble: Enabling the BLE stack.
    <debug> nrf_dfu_ble: No advertising name found
    <debug> nrf_dfu_ble: Using default advertising name
    <debug> nrf_dfu_ble: Advertising...
    <debug> nrf_dfu_ble: BLE DFU transport initialized.
    <debug> nrf_dfu_flash: Initializing nrf_fstorage_sd backend.
    <debug> app: Enter main loop

    As for using the debug version of the bootloader - how exactly do you use it? Specifically how do you load both the application and bootloader and debug?

  • Hello Tim,

     

    Tim Adu said:
    I decided yesterday to take a step back and try a much simpler application (the blinky freertos example), and the NRF52840 DK. I repeated the exact steps I've been doing so far (I expected all 4 LEDS to be blinking in a certain pattern as soon as the programming was finished). Instead LED1 and LED2 just lights up.

     This means that the bootloader has rejected the application, and the device is in DFU mode. You should be able to see the device advertising as "DfuTarg". 

    So if you programmed all 4 objects, SD, BL, BL_settings, and APP, this means that there is something wrong with your BL settings (signed with the wrong keys), or you have not programmed one of the files properly. 

    Can you please try to unzip a new unmodified SDK, so that you are sure you don't have anything changed in your SDK, and try the ble_app_buttonless application together with the bootloader. I have explained everything I know about bootloaders in the post that you link to in the top of this post, and I don't want to write it all again. 

    In your unmodified SDK, please create your own keys, public and private. compile the bootloader and the ble_app_buttonless_dfu example, and the bootloader, and copy their hex files to a folder, together with a copy of your keys and the softdevice. Then run the following .bat script. I saw your previous scripts, but I don't know where you link to, and whether the pats are correct. Let's keep everything very simple, until we get it working:

    nrfjprog -e
    nrfjprog --program softdevice.hex
    nrfjprog --program bootloader.hex
    nrfjprog --program ble_app_buttonless_dfu.hex
    nrfutil settings generate --family NRF52840 --application application.hex --application-version 1 --bootloader-version 1 --bl-settings-version 1 --key-file private.key bl_settings.hex
    nrfjprog --program bl_settings.hex
    nrfjprog --reset

    Do this before you look into the _debug version of the bootloader. 

Reply
  • Hello Tim,

     

    Tim Adu said:
    I decided yesterday to take a step back and try a much simpler application (the blinky freertos example), and the NRF52840 DK. I repeated the exact steps I've been doing so far (I expected all 4 LEDS to be blinking in a certain pattern as soon as the programming was finished). Instead LED1 and LED2 just lights up.

     This means that the bootloader has rejected the application, and the device is in DFU mode. You should be able to see the device advertising as "DfuTarg". 

    So if you programmed all 4 objects, SD, BL, BL_settings, and APP, this means that there is something wrong with your BL settings (signed with the wrong keys), or you have not programmed one of the files properly. 

    Can you please try to unzip a new unmodified SDK, so that you are sure you don't have anything changed in your SDK, and try the ble_app_buttonless application together with the bootloader. I have explained everything I know about bootloaders in the post that you link to in the top of this post, and I don't want to write it all again. 

    In your unmodified SDK, please create your own keys, public and private. compile the bootloader and the ble_app_buttonless_dfu example, and the bootloader, and copy their hex files to a folder, together with a copy of your keys and the softdevice. Then run the following .bat script. I saw your previous scripts, but I don't know where you link to, and whether the pats are correct. Let's keep everything very simple, until we get it working:

    nrfjprog -e
    nrfjprog --program softdevice.hex
    nrfjprog --program bootloader.hex
    nrfjprog --program ble_app_buttonless_dfu.hex
    nrfutil settings generate --family NRF52840 --application application.hex --application-version 1 --bootloader-version 1 --bl-settings-version 1 --key-file private.key bl_settings.hex
    nrfjprog --program bl_settings.hex
    nrfjprog --reset

    Do this before you look into the _debug version of the bootloader. 

Children
  • Thanks Edvin,

    I followed your reccommendations and was able to generate a production image that worked. Then I tried with an even simpler application (ble_app_blinky) and was able to get it to work as well.

    Then I tried using the debug bootloader with my application. It came in very handy as I could see that it actually accepted the app and the app did load, but something else was blocking from running further. It was my logger Cold sweat

    In my application I am using RTT as the logger backend since I have exhausted all my UARTS. The problem was the macro

    SEGGER_RTT_CONFIG_DEFAULT_MODE

    was by default set to 2 (blocking mode). I set it to 0 and it works perfectly!

    I'm not sure how much of a difference it makes, but I also used the --no-backup option when generating the bootloader settings.

    Thanks for all your help!

    Tim

Related