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

Ending up in WDT_IRQHandler() without knowingly using the watchdog timer

Hi.

I have a custom board based on the nRF51822. Application uses s130, SDK 11.0.0. It's a peripheral. Using gcc.

I'm finding that when field testing my device, it will often stop running. If I then bring it back to the desk and attach gdb without resetting, I can see I'm in the watchdog timer IRQ handler:

$ make gdb-client-no-reset
...
0x0001edce in WDT_IRQHandler ()
...
(gdb) bt full
#0  0x0001ed84 in Reset_Handler ()
No symbol table info available.
(gdb) info symbol 0x0001edce
WDT_IRQHandler in section .text

Why on earth would that be? I'm not setting up the watchdog timer in my application at all. In nrf_drv_config.h:

#define WDT_ENABLED 0

However, a bunch of the IRQ handlers are ending up at that same address, if I check my .Map file:

 .text          0x000000000001ed84       0x4c _build/gcc_startup_nrf51.os
                0x000000000001ed84                Reset_Handler
                0x000000000001edc4                NMI_Handler
                0x000000000001edc8                SVC_Handler
                0x000000000001edca                PendSV_Handler
                0x000000000001edcc                SysTick_Handler
                0x000000000001edce                SWI4_IRQHandler
                0x000000000001edce                TEMP_IRQHandler
                0x000000000001edce                QDEC_IRQHandler
                0x000000000001edce                SWI5_IRQHandler
                0x000000000001edce                TIMER0_IRQHandler
                0x000000000001edce                TIMER1_IRQHandler
                0x000000000001edce                ECB_IRQHandler
                0x000000000001edce                Default_Handler
                0x000000000001edce                SWI3_IRQHandler
                0x000000000001edce                CCM_AAR_IRQHandler
                0x000000000001edce                WDT_IRQHandler
                0x000000000001edce                RNG_IRQHandler
                0x000000000001edce                TIMER2_IRQHandler
                0x000000000001edce                SWI1_IRQHandler
                0x000000000001edce                RTC0_IRQHandler
                0x000000000001edce                POWER_CLOCK_IRQHandler
                0x000000000001edce                RADIO_IRQHandler
                0x000000000001edce                LPCOMP_IRQHandler

(Edit) The IPSR is 0x18 at the time:

(gdb) mon regs
R0 = 00000060, R1 = 0001EDCF, R2 = 680ED406, R3 = 00008F7B
R4 = 20007F87, R5 = 3E894F53, R6 = FFFFFFFF, R7 = 00000000
R8 = FFFFFFFF, R9 = FFFFFFFF, R10= 1FFF8000, R11= 00000000
R12= FFFF0001, R13= 20007F38, MSP= 20007F38, PSP= FFFFFFFC
R14(LR) = FFFFFFF9, R15(PC) = 0001ED84
XPSR 91000018, APSR 90000000, EPSR 01000000, IPSR 00000018
CFBP 00000000, CONTROL 00, FAULTMASK 00, BASEPRI 00, PRIMASK 00

(Edit 2) Which is the Timer 0 IRQ, from nrf51.h:

/* ----------------------  nrf51 Specific Interrupt Numbers  ---------------------- */
  POWER_CLOCK_IRQn              =   0,              /*!<   0  POWER_CLOCK                                                      */
  RADIO_IRQn                    =   1,              /*!<   1  RADIO                                                            */
  UART0_IRQn                    =   2,              /*!<   2  UART0                                                            */
  SPI0_TWI0_IRQn                =   3,              /*!<   3  SPI0_TWI0                                                        */
  SPI1_TWI1_IRQn                =   4,              /*!<   4  SPI1_TWI1                                                        */
  GPIOTE_IRQn                   =   6,              /*!<   6  GPIOTE                                                           */
  ADC_IRQn                      =   7,              /*!<   7  ADC                                                              */
  TIMER0_IRQn                   =   8,              /*!<   8  TIMER0                                                           */
  TIMER1_IRQn                   =   9,              /*!<   9  TIMER1                                                           */
  TIMER2_IRQn                   =  10,              /*!<  10  TIMER2                                                           */
  RTC0_IRQn                     =  11,              /*!<  11  RTC0                                                             */
  TEMP_IRQn                     =  12,              /*!<  12  TEMP                                                             */
  RNG_IRQn                      =  13,              /*!<  13  RNG                                                              */
  ECB_IRQn                      =  14,              /*!<  14  ECB                                                              */
  CCM_AAR_IRQn                  =  15,              /*!<  15  CCM_AAR                                                          */
  WDT_IRQn                      =  16,              /*!<  16  WDT                                                              */
  RTC1_IRQn                     =  17,              /*!<  17  RTC1                                                             */
  QDEC_IRQn                     =  18,              /*!<  18  QDEC                                                             */
  LPCOMP_IRQn                   =  19,              /*!<  19  LPCOMP                                                           */
  SWI0_IRQn                     =  20,              /*!<  20  SWI0                                                             */
  SWI1_IRQn                     =  21,              /*!<  21  SWI1                                                             */
  SWI2_IRQn                     =  22,              /*!<  22  SWI2                                                             */
  SWI3_IRQn                     =  23,              /*!<  23  SWI3                                                             */
  SWI4_IRQn                     =  24,              /*!<  24  SWI4                                                             */
  SWI5_IRQn                     =  25               /*!<  25  SWI5                                                             */

(Edit 3) On another occurrence of the same behaviour in the field, I reattach gdb at the desk and find myself in the reset handler with the ISPR set to zero:

$ make gdb-client-no-reset

Reading symbols from _build/biketracker_app_s130.elf...done.
0x0002b236 in nrf_delay_us (number_of_us=999) at /Users/Eliot/dev/nRF5_SDK_11.0.0_89a8197/components/drivers_nrf/delay/nrf_delay.h:166
166	__ASM volatile (
...
(gdb) mon regs
R0 = 000000CC, R1 = 00000003, R2 = 00000754, R3 = 000003E7
R4 = 00000000, R5 = 00000000, R6 = FFFFFFFF, R7 = 00000000
R8 = FFFFFFFF, R9 = FFFFFFFF, R10= 1FFF8000, R11= 00000000
R12= FFFFFFFF, R13= 20007F88, MSP= 20007F88, PSP= FFFFFFFC
R14(LR) = 0002067B, R15(PC) = 0001ED84
XPSR 21000000, APSR 20000000, EPSR 01000000, IPSR 00000000
CFBP 00000000, CONTROL 00, FAULTMASK 00, BASEPRI 00, PRIMASK 00
(gdb) bt full
#0  0x0001ed84 in Reset_Handler ()
No symbol table info available.

(Edit 4)

My gdb-client-no-reset make target does this

gdb-client-no-reset:
	printf "target remote localhost:2331\nload\neval \"monitor exec SetRTTAddr %%p\", &_SEGGER_RTT\n" > $(OUTPUT_PATH).gdbinit-noreset
	$(GDB) -x $(OUTPUT_PATH).gdbinit-noreset $(OUTPUT_PATH)*.elf

(Edit 5)

Attaching my error handling functions. error.c

(Edit 6)

Since this was working for me in SDK 10 and is not in SDK 11, here's the diff between the TWI library across SDKs:

diff -r nRF51_SDK_10.0.0_dc26b5e/components/libraries/twi/app_twi.c nRF5_SDK_11.0.0_89a8197/components/libraries/twi/app_twi.c
13d12
< #include <stdbool.h>
16a16
> #include "sdk_common.h"
74c74
< static ret_code_t start_transfer(app_twi_t const * p_app_twi)
---
> static ret_code_t start_transfer(app_twi_t * p_app_twi)
85,89c85,112
<     if (APP_TWI_IS_READ_OP(p_transfer->operation))
<     {
<         return nrf_drv_twi_rx(&p_app_twi->twi, address,
<             p_transfer->p_data, p_transfer->length,
<             (p_transfer->flags & APP_TWI_NO_STOP));
---
>     nrf_drv_twi_xfer_desc_t xfer_desc;
>     uint32_t                flags;
> 
>     xfer_desc.address       = address;
>     xfer_desc.p_primary_buf = p_transfer->p_data;
>     xfer_desc.primary_length = p_transfer->length;
> 
>     /* If it is possible try to bind two transfers together. They can be combined if:
>      * - there is no stop condition after current transfer.
>      * - current transfer is TX.
>      * - there is at least one more transfer in the transaction.
>      * - address of next trnasfer is the same as current transfer.
>      */
>     if ((p_transfer->flags & APP_TWI_NO_STOP) &&
>         !APP_TWI_IS_READ_OP(p_transfer->operation) &&
>         ((current_transfer_idx+1) < p_app_twi->p_current_transaction->number_of_transfers) &&
>         APP_TWI_OP_ADDRESS(p_transfer->operation) ==
>         APP_TWI_OP_ADDRESS(p_app_twi->p_current_transaction->p_transfers[current_transfer_idx+1].operation)
>     )
>     {
>         app_twi_transfer_t const * p_second_transfer =
>             &p_app_twi->p_current_transaction->p_transfers[current_transfer_idx+1];
>         xfer_desc.p_secondary_buf = p_second_transfer->p_data;
>         xfer_desc.secondary_length = p_second_transfer->length;
>         xfer_desc.type = APP_TWI_IS_READ_OP(p_second_transfer->operation) ? NRF_DRV_TWI_XFER_TXRX :
>                                                                             NRF_DRV_TWI_XFER_TXTX;
>         flags = (p_second_transfer->flags & APP_TWI_NO_STOP) ? NRF_DRV_TWI_FLAG_TX_NO_STOP : 0;
>         p_app_twi->current_transfer_idx++;
93,95c116,120
<         return nrf_drv_twi_tx(&p_app_twi->twi, address,
<             p_transfer->p_data, p_transfer->length,
<             (p_transfer->flags & APP_TWI_NO_STOP));
---
>         xfer_desc.type = APP_TWI_IS_READ_OP(p_transfer->operation) ? NRF_DRV_TWI_XFER_RX :
>                 NRF_DRV_TWI_XFER_TX;
>         xfer_desc.p_secondary_buf = NULL;
>         xfer_desc.secondary_length = 0;
>         flags = (p_transfer->flags & APP_TWI_NO_STOP) ? NRF_DRV_TWI_FLAG_TX_NO_STOP : 0;
96a122,123
> 
>     return nrf_drv_twi_xfer(&p_app_twi->twi, &xfer_desc, flags);
180c207
<     if (p_event->type != NRF_DRV_TWI_ERROR)
---
>     if (p_event->type == NRF_DRV_TWI_EVT_DONE)
238,241c265,266
<     if (err_code != NRF_SUCCESS)
<     {
<         return err_code;
<     }
---
>     VERIFY_SUCCESS(err_code);
> 
339,342c364
<         if (result != NRF_SUCCESS)
<         {
<             return result;
<         }
---
>         VERIFY_SUCCESS(result);

This is only at the app_twi_() level of course. At the nrf_drv_() level, there are 2K lines of code difference, too much to post here.

  • Those regs dumps just don't make sense. Looking at the second one, the first thing gdb printed was 0x0002b236 and you were in nrf_delay. Then the regs dump next shows you at the reset handler but with the rest of the registers set to something which make no sense if you really were in the reset handler. The SP shows there is a big old stack stacked, in the other dump you can't be in the reset handler and in interrupt context, well you could but only if you literally jumped to the reset handler and obviously that's not it.

    There are some clues. The LR in the second example, 0x0002067B, that looks real, what's there, my guess would be just after a call to nrf_delay from your code.

    I know nothing about gdbserver, so I can't say whether that command line is right. I would however use JLink directly to connect, halt and read the regs because you know what that does.

  • possibilities that gdb connects when you're in a reset loop, shows where you are, the chip resets and halts in the reset handler and so where you were when you attached isn't where you are when the debug starts. Or gdbserver does some strange thing setting the PC to the reset handler (in much the same way Crossworks or SES do when you use STARTUP_FROM_RESET so you can hold a board in reset whilst you attach to it and then continue from there).

  • I'm still confused as to the reset behaviour, but I now know this is happening when a 2 second repeated mode timer I've set times out. In the handler for that timer, I clear an interrupt on my accelerometer, which involves doing a TWI transfer using app_twi_perform(), with no handler function passed in. Obviously this is all happening in the context of a RTC1_IRQHandler() call. Is that OK? Would I be better to do as little work as possible in the RTC1_IRQHandler() context, then use the scheduler or the main loop to do the subsequent app_twi_perform() call? This was working just fine on SDK 10, I swear.

  • This question has become a bit of a mess and the answer was similarly messy, with many issues to be aware of.

    First, in order to prevent the reset on reattaching the Segger, use the file command in your .gdbinit in place of the load command. I pass the binary file name to the file command in .gdbinit, rather than putting in on the command line to gdb. You may also want to look at the -noir arg to the GDB server, although I haven't needed that so far.

    Second, I was doing too much work in my interrupt handler when the timer timed out. Why this was never an issue with SDK 10 and is an issue with SDK 11, I have no idea, but it was worth fixing anyway. I've ended up using the scheduler a lot more. I just schedule a function in the interrupt handler and then execute it later in the main loop.

    As for why I appeared to be in WDT_IRQHandler() but was not using the watchdog at all, there are similar posts here and I think really gdb was just confused because a bunch of the unimplemented interrupt handlers are all at the same address (0x000000000001edce), but I never really got to the bottom of that entirely.

Related