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

Entering DFU via GPREGET, secure bootloader serial example not feeding WDT in SDKv15

Hello!

I have a custom nRF52832 board with an FTDI usb/serial part running my app successfully.

I generally program the board with a JLINK and SWD, but I am working to add DFU over usb/serial using the secure bootloader serial example.

I can set GPREGET in my app and soft reset to enter DFU in the bootloader.

I am successfully able to use nrfutil to perform a DFU of my application over serial. Also, if I set GPREGET in my app and soft reset to enter the DFU, but do not initiate the DFU update using nrfutil, eventually, the bootloader times out for inactivity after 10 seconds or so and resets back into my application.

So far so good!

However, when I enable a WDT in my app, the trouble starts.

1. If I make my WDT very long (say 20 seconds), when soft resetting into the bootloader, the ~10s inactivity timer no longer works. If I simply disable the WDT, the ~10s inactivity timer works again. If I wait out the 20 second WDT timer when in the bootloader, the WDT does its thing and resets back to my app.

2. If I make my WDT short (say 4 seconds) while leaving the inactivity timer at ~10s, the WDT is clearly not being fed by the bootloader because a soft reset back to my app happens after 4 seconds.

3. If I leave my WDT short (say 4 seconds), and I add an nrf_delay_ms of 2 seconds at the start of the bootloader, followed by manually feeding the WDT with a nrf_wdt_reload_request_set(NRF_WDT_RR0), I can see that the feeding works, as the bootloader soft resets back to my app after 6 seconds. If I leave the nrf_delay_ms of 2 seconds without the manual feeding, the bootloader soft resets back to the app after 4 seconds.

So my questions are:

1. Why does the Nordic code in the SDKv15 secure bootloader not feed my WDT? Inspecting the code, it seems like it should be feeding the WDT in both the event scheduling loop and possibly by an app timer. I can't seem to get the bootloader code to feed my WDT either "out of the box" or by hacking in an application timer.

2. Why when I enable the WDT (for example 20 seconds), does my 10s inactivity timer stop working? The inactivity timer appears to just be a simple one shot app timer performing a system reset. I don't understand how the presence of the WDT would interfere with the 10s inactivity timer.

Thanks!

Jeremy

  • Hi Jeremy, 

     

    Could you clarify the inactivity timeout is 10 seconds ? By default our inactivity timeout is 120 seconds: NRF_BL_DFU_INACTIVITY_TIMEOUT_MS = 120000

    We do have a 10 second timeout but it's the timeout after you update the softdevice and waiting for the application to be updated for example. (NRF_BL_DFU_CONTINUATION_TIMEOUT_MS)

    Could you try to test by simply starting the WDT in the bootloader and see if there is any difference ? 

  • Yes, I can confirm that I mean that I changed NRF_BL_INACTIVITY_TIMEOUT_MS to be 10s (ie 10000).  When I do enable the WDT in my app, I am able to see that inactivity timeout taking effect (and changes to it taking effect).

    Enabling the WDT in my app, I can feed it in my app, but I can’t get the boot loader to feed it, and it seems to cause the inactivity timer to no longer work. 

    I will try starting the WDT in the bootloader as a test as you suggest.

  • Ok, I added the following lines to the top of the secure bootloader main.c:

    void wdt_event_handler(void) {
        NVIC_SystemReset();    
    }
    
    int main(void) {
        uint32_t err_code;
        nrf_drv_wdt_config_t config = {                                           \
            .behaviour          = (nrf_wdt_behaviour_t)1, \
            .reload_value       = 10000,                   \
            .interrupt_priority = 7,                   \
        };
        err_code = nrf_drv_wdt_init(&config, wdt_event_handler);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_wdt_channel_alloc(&m_channel_id);
        APP_ERROR_CHECK(err_code);
        nrf_drv_wdt_enable(); 
    
        // Protect MBR and bootloader code from being overwritten.
        uint32_t ret_val = nrf_bootloader_flash_protect(0, MBR_SIZE, false);
        APP_ERROR_CHECK(ret_val);
        ret_val = nrf_bootloader_flash_protect(BOOTLOADER_START_ADDR, BOOTLOADER_SIZE, false);
        APP_ERROR_CHECK(ret_val);
        ...

    I also enable the WDT in the config, added nrfx_wdt.c to the Makefile and had to comment out WDT_IRQHandler from nrf_bootloader_wdt.c which conflicts with the one in nrfx_wdt.c.

    For this test, I removed all WDT code from my application.

    When I run enter the bootloader, the WDT will fire after 10s and bring me back from the bootloader to my application (via a soft reset). In other words, the WDT is not being "fed" or reset. No matter what I set the NRF_BL_INACTIVITY_TIMEOUT_MS to, it does not fire (i.e. 5s, 2s, etc) when the WDT is enabled.

    If I remove this WDT code from the top of the secure boot loader example's main.c, then the NRF_BL_INACTIVITY_TIMEOUT_MS works. I can set it to 5s, 2s (i.e. 5000ms or 2000ms) and it correctly brings me back from the bootloader to my application after the specified idle period.

    To be clear, I would expect with this added WDT code, and the NRF_BL_INACTIVITY_TIMEOUT_MS set to 5s, that after 5s of idle, it would reset back to my application. I would further expect if I set the WDT to 2s (i.e. 2000ms) with the NRF_BL_INACTIVITY_TIMEOUT_MS set to 10s, that nothing would happen at 2s (because the WDT should be being fed by the code in nrf_bootloader_wdt.c).

    Thoughts?

  • A little bit more information: I built the debug version of the bootloader, and configured it to log to SEGGER_RTT over SWD/JLINK.

    Here we can see that the nrf_bootloader_wdt detected that the WDT was enabled, and notice it says that it is starting a timer to feed the WDT and it sits idle at the log entry "<debug> app: Enter main loop".

    However at 10s after entering the bootloader, it is rebooted, and we can see the log entry "<info> app: Inside main". So the WDT is actually NOT being fed.

    I suspect that what is happening is that the event loop / app_scheduler is not working for some reason when the WDT is enabled, and neither the app timer for feeding the WDT nor the app timer for NRF_BL_INACTIVITY_TIMEOUT_MS are working. The puzzling thing is this is only when the WDT is enabled. Otherwise, things work.

    <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> app: Enter nrf_bootloader_fw_activate
    <info> app: No firmware to activate.
    <debug> app: Enter nrf_dfu_app_is_valid
    <debug> app: Return true. App was valid
    <debug> app: DFU mode requested via GPREGRET.
    <info> nrf_bootloader_wdt: WDT enabled CRV:10240 ms
    <info> nrf_bootloader_wdt: Starting a timer (7680 ms) for feeding watchdog.
    <info> app_timer: RTC: initialized.
    <debug> app: in weak nrf_dfu_init_user
    <info> app: Entering DFU mode.
    <debug> app: Initializing transports (found: 1)
    <debug> nrf_dfu_serial_uart: serial_dfu_transport_init()
    <debug> nrf_dfu_serial_uart: serial_dfu_transport_init() completed
    <debug> nrf_dfu_flash: Initializing nrf_fstorage_nvmc backend.
    <debug> app: Enter main loop
    <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> app: Enter nrf_bootloader_fw_activate
    <info> app: No firmware to activate.
    <debug> app: Enter nrf_dfu_app_is_valid
    <debug> app: Return true. App was valid
    <warning> nrf_dfu_settings: No additional data erased
    <debug> app: Running nrf_bootloader_app_start with address: 0x00001000
    <debug> app: Disabling interrupts. NVIC->ICER[0]: 0x0
    <debug> app: running irq table set
    <debug> app: After running irq table set
    

    Here is what the log looks like if I disable the WDT, and the NRF_BL_INACTIVITY_TIMEOUT_MS works:

    <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> app: Enter nrf_bootloader_fw_activate
    <info> app: No firmware to activate.
    <debug> app: Enter nrf_dfu_app_is_valid
    <debug> app: Return true. App was valid
    <debug> app: DFU mode requested via GPREGRET.
    <info> nrf_bootloader_wdt: WDT is not enabled
    <debug> app: scheduler init
    <debug> app: in weak nrf_dfu_init_user
    <info> app_timer: RTC: initialized.
    <info> app: Entering DFU mode.
    <debug> app: Initializing transports (found: 1)
    <debug> nrf_dfu_serial_uart: serial_dfu_transport_init()
    <debug> nrf_dfu_serial_uart: serial_dfu_transport_init() completed
    <debug> nrf_dfu_flash: Initializing nrf_fstorage_nvmc backend.
    <debug> app: Enter main loop
    <debug> app: wait_for_event
    <debug> app: wait_for_event
    <info> app: Inactivity timeout.
    <debug> app: Resetting bootloader.
    <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> app: Enter nrf_bootloader_fw_activate
    <info> app: No firmware to activate.
    <debug> app: Enter nrf_dfu_app_is_valid
    <debug> app: Return true. App was valid
    <warning> nrf_dfu_settings: No additional data erased
    <debug> app: Running nrf_bootloader_app_start with address: 0x00001000
    <debug> app: Disabling interrupts. NVIC->ICER[0]: 0x0
    <debug> app: running irq table set
    <debug> app: After running irq table set
    

  • So I have things working, but basically, as above, if I have WDT enabled in my app, the app timers in the bootloader library component do not work at all for me (neither the timer to feed the WDT nor the inactivity timeout).

    So instead, I am using the nrfx_timer library myself and handling the feeding of the WDT and resetting for inactivity in the main.c of the secure bootloader.

    So in my modified main.c of the secure bootloader example, I have code like the following:

    ...
    #include "nrf_drv_timer.h"
    #include "nrf_wdt.h"
    
    const nrf_drv_timer_t ACTIVITY_TIMER = NRF_DRV_TIMER_INSTANCE(0);
    
    #define COUNTDOWN_INIT 30
    
    static int countdown = COUNTDOWN_INIT;
    
    void feed_wdt() 
    {
        if (nrf_wdt_started())
        {
            for (nrf_wdt_rr_register_t i = NRF_WDT_RR0; i < NRF_WDT_RR7; i++)
            {
                if (nrf_wdt_reload_request_is_enabled(i))
                {
                    nrf_wdt_reload_request_set(i);
                }
            }
        }        
    }
    
    static void dfu_observer(nrf_dfu_evt_type_t evt_type)
    {
        switch (evt_type)
        {
            case NRF_DFU_EVT_DFU_COMPLETED:
            case NRF_DFU_EVT_DFU_ABORTED:
                nrf_drv_timer_disable(&ACTIVITY_TIMER);
                break;
            case NRF_DFU_EVT_DFU_STARTED:
            case NRF_DFU_EVT_OBJECT_RECEIVED:
                countdown = COUNTDOWN_INIT;
                feed_wdt();
                break;
            default:
                break;
        }
    }
    
    void activity_timer_event_handler(nrf_timer_event_t event_type, void* p_context)
    {
        switch (event_type)
        {
            case NRF_TIMER_EVENT_COMPARE0:
                countdown--;
                if( countdown < 0) {
                    nrf_drv_timer_disable(&ACTIVITY_TIMER);
                    NVIC_SystemReset();    
                } else {
                    feed_wdt();
                }
                break;
    
            default:
                //Do nothing.
                break;
        }
    }
    
    
    int main(void) {
        nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
        uint32_t err_code = nrf_drv_timer_init(&ACTIVITY_TIMER, &timer_cfg, activity_timer_event_handler);
        APP_ERROR_CHECK(err_code);
        nrf_drv_timer_extended_compare(
             &ACTIVITY_TIMER, NRF_TIMER_CC_CHANNEL0, nrf_drv_timer_ms_to_ticks(&ACTIVITY_TIMER, 500), NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
        nrf_drv_timer_enable(&ACTIVITY_TIMER);
    
        // Protect MBR and bootloader code from being overwritten.
        uint32_t ret_val = nrf_bootloader_flash_protect(0, MBR_SIZE, false);
        APP_ERROR_CHECK(ret_val);
        ...
    }

Related