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.

Parents
  • 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.

Reply
  • 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.

Children
No Data
Related