This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2538DK: CC2538 Watchdog not resetting MCU

Part Number: CC2538DK

Hi all,

I've spent a good few hours on this, and am at a loss.  We have a product that is using the OpenThread framework and I am attempting to get the watchdog timer working on it so that a device may recover itself should it fail in the field.

As it happens, that's exactly what the watchdog timer is for, and reading through the documentation it seems simple enough.  Trouble is… maybe the dog isn't barking loud enough to reset the MCU.

For reference, I have committed my changes here.  Toolchain is GNU ARM GCC (GNU Tools for ARM Embedded Processors 6-2017-q2-update) running on Linux/AMD64.

The following is the output from objdump, showing the initialisation sequence in C and assembly… to my eye, this assembly code looks correct.  The initialisation code is based on what I've seen in the foundation firmware, and also what I understand of the documentation.  This is supposed to:

1. Turn off the watchdog by clearing the EN bit
2. Read the current configuration into temporary storage
3. Set the timer interval to 32768 ticks (~1 second)
4. Write that to the configuration register
5. Re-read the configuration
6. Clear the (undocumented!) mode bit
7. Set the enable bit
8. Write the temporary value to the configuration register

    /* Initialise watchdog */
    {
        uint32_t tmp;

        HWREG(SMWDTHROSC_WDCTL) &= ~(1 << 3);     /* Turn off */
  21184a:       4b0d            ldr     r3, [pc, #52]   ; (211880 <PlatformInit+0xa8>)
  21184c:       681a            ldr     r2, [r3, #0]
  21184e:       f022 0208       bic.w   r2, r2, #8
  211852:       601a            str     r2, [r3, #0]
        tmp = HWREG(SMWDTHROSC_WDCTL);
  211854:       681a            ldr     r2, [r3, #0]
        tmp &= ~0x03;
  211856:       f022 0203       bic.w   r2, r2, #3
        tmp |= (0 << 0);
        HWREG(SMWDTHROSC_WDCTL) = tmp;
  21185a:       601a            str     r2, [r3, #0]

        tmp = HWREG(SMWDTHROSC_WDCTL);
  21185c:       681a            ldr     r2, [r3, #0]
        tmp &= ~(1 << 2);
  21185e:       f022 0204       bic.w   r2, r2, #4
        tmp |= (1 << 3);
  211862:       f042 0208       orr.w   r2, r2, #8
        HWREG(SMWDTHROSC_WDCTL) = tmp;
  211866:       601a            str     r2, [r3, #0]
  211868:       bd08            pop     {r3, pc}
        HWREG(GPIO_C_BASE | (0x0f << 2)) ^= 0xff;
  21186a:       6813            ldr     r3, [r2, #0]
  21186c:       f083 03ff       eor.w   r3, r3, #255    ; 0xff
  211870:       6013            str     r3, [r2, #0]
  211872:       e7e8            b.n     211846 <PlatformInit+0x6e>
  211874:       400d9400        .word   0x400d9400
  211878:       400db420        .word   0x400db420
  21187c:       400d40d0        .word   0x400d40d0
  211880:       400d5000        .word   0x400d5000

In the main loop, the following code is executed (once again, showing the disassembly with source from objdump):

void PlatformProcessDrivers(otInstance *aInstance)
{
  211884:       b538            push    {r3, r4, r5, lr}
    static uint16_t leds_delay = 0;
    static uint8_t leds_dir = 0;
    static uint16_t leds_speed = 10000;
    sInstance = aInstance;
    if (!leds_delay) {
  211886:       4a25            ldr     r2, [pc, #148]  ; (21191c <PlatformProcessDrivers+0x98>)
    sInstance = aInstance;
  211888:       4b25            ldr     r3, [pc, #148]  ; (211920 <PlatformProcessDrivers+0x9c>)
{
  21188a:       4604            mov     r4, r0
    sInstance = aInstance;
  21188c:       6018            str     r0, [r3, #0]
    if (!leds_delay) {
  21188e:       8813            ldrh    r3, [r2, #0]
  211890:       4615            mov     r5, r2
  211892:       b9f3            cbnz    r3, 2118d2 <PlatformProcessDrivers+0x4e>
        if (leds_dir)
  211894:       4923            ldr     r1, [pc, #140]  ; (211924 <PlatformProcessDrivers+0xa0>)
  211896:       4b24            ldr     r3, [pc, #144]  ; (211928 <PlatformProcessDrivers+0xa4>)
  211898:       780a            ldrb    r2, [r1, #0]
            leds >>= 1;
  21189a:       7818            ldrb    r0, [r3, #0]
        if (leds_dir)
  21189c:       b1aa            cbz     r2, 2118ca <PlatformProcessDrivers+0x46>
            leds >>= 1;
  21189e:       0840            lsrs    r0, r0, #1
        else
            leds <<= 1;
  2118a0:       7018            strb    r0, [r3, #0]

        if (!(leds & 0x0f)) {
  2118a2:       7818            ldrb    r0, [r3, #0]
  2118a4:       0700            lsls    r0, r0, #28
  2118a6:       d106            bne.n   2118b6 <PlatformProcessDrivers+0x32>
            if (leds_dir)
  2118a8:       b18a            cbz     r2, 2118ce <PlatformProcessDrivers+0x4a>
                leds = 2;
  2118aa:       2002            movs    r0, #2
            else
                leds = 4;
            leds_dir = !leds_dir;
  2118ac:       fab2 f282       clz     r2, r2
  2118b0:       0952            lsrs    r2, r2, #5
                leds = 4;
  2118b2:       7018            strb    r0, [r3, #0]
            leds_dir = !leds_dir;
  2118b4:       700a            strb    r2, [r1, #0]
        }
        HWREG(GPIO_C_BASE | (0x0f << 2)) = leds;
  2118b6:       781a            ldrb    r2, [r3, #0]
  2118b8:       4b1c            ldr     r3, [pc, #112]  ; (21192c <PlatformProcessDrivers+0xa8>)
  2118ba:       601a            str     r2, [r3, #0]
        leds_delay = leds_speed;
  2118bc:       4b1c            ldr     r3, [pc, #112]  ; (211930 <PlatformProcessDrivers+0xac>)
  2118be:       881b            ldrh    r3, [r3, #0]
  2118c0:       802b            strh    r3, [r5, #0]
    } else {
        leds_delay--;
    }

    /* If button is held, pause */
    if (!HWREG(GPIO_C_BASE | ((1 << 4) << 2))) {
  2118c2:       4b1c            ldr     r3, [pc, #112]  ; (211934 <PlatformProcessDrivers+0xb0>)
  2118c4:       681b            ldr     r3, [r3, #0]
  2118c6:       b93b            cbnz    r3, 2118d8 <PlatformProcessDrivers+0x54>
  2118c8:       e7fe            b.n     2118c8 <PlatformProcessDrivers+0x44>
            leds <<= 1;
  2118ca:       0040            lsls    r0, r0, #1
  2118cc:       e7e8            b.n     2118a0 <PlatformProcessDrivers+0x1c>
                leds = 4;
  2118ce:       2004            movs    r0, #4
  2118d0:       e7ec            b.n     2118ac <PlatformProcessDrivers+0x28>
        leds_delay--;
  2118d2:       3b01            subs    r3, #1
  2118d4:       8013            strh    r3, [r2, #0]
  2118d6:       e7f4            b.n     2118c2 <PlatformProcessDrivers+0x3e>
        while (1);
    }
    if (!HWREG(GPIO_C_BASE | ((1 << 6) << 2)))
  2118d8:       4b17            ldr     r3, [pc, #92]   ; (211938 <PlatformProcessDrivers+0xb4>)
  2118da:       681b            ldr     r3, [r3, #0]
  2118dc:       b91b            cbnz    r3, 2118e6 <PlatformProcessDrivers+0x62>
        leds_speed = 2500;
  2118de:       f640 12c4       movw    r2, #2500       ; 0x9c4
  2118e2:       4b13            ldr     r3, [pc, #76]   ; (211930 <PlatformProcessDrivers+0xac>)
  2118e4:       801a            strh    r2, [r3, #0]

    if (!HWREG(GPIO_C_BASE | ((1 << 7) << 2)))
  2118e6:       4b15            ldr     r3, [pc, #84]   ; (21193c <PlatformProcessDrivers+0xb8>)
  2118e8:       681b            ldr     r3, [r3, #0]
  2118ea:       b91b            cbnz    r3, 2118f4 <PlatformProcessDrivers+0x70>
        leds_speed = 40000;
  2118ec:       f649 4240       movw    r2, #40000      ; 0x9c40
  2118f0:       4b0f            ldr     r3, [pc, #60]   ; (211930 <PlatformProcessDrivers+0xac>)
  2118f2:       801a            strh    r2, [r3, #0]

    /* Feed the watchdog */
    {
        uint32_t tmp = HWREG(SMWDTHROSC_WDCTL) & 0x0f;
  2118f4:       4a12            ldr     r2, [pc, #72]   ; (211940 <PlatformProcessDrivers+0xbc>)
  2118f6:       6813            ldr     r3, [r2, #0]
  2118f8:       f003 030f       and.w   r3, r3, #15
        HWREG(SMWDTHROSC_WDCTL) = 0x50 | tmp;
  2118fc:       f043 0150       orr.w   r1, r3, #80     ; 0x50
        HWREG(SMWDTHROSC_WDCTL) = 0xa0 | tmp;
  211900:       f043 03a0       orr.w   r3, r3, #160    ; 0xa0
        HWREG(SMWDTHROSC_WDCTL) = 0x50 | tmp;
  211904:       6011            str     r1, [r2, #0]
        HWREG(SMWDTHROSC_WDCTL) = 0xa0 | tmp;
  211906:       6013            str     r3, [r2, #0]
    }

    // should sleep and wait for interrupts here
    cc2538UartProcess();
  211908:       f000 fc48       bl      21219c <cc2538UartProcess>
    cc2538RadioProcess(aInstance);
  21190c:       4620            mov     r0, r4
  21190e:       f000 f9cf       bl      211cb0 <cc2538RadioProcess>
    cc2538AlarmProcess(aInstance);
  211912:       4620            mov     r0, r4
}
  211914:       e8bd 4038       ldmia.w sp!, {r3, r4, r5, lr}
    cc2538AlarmProcess(aInstance);
  211918:       f7ff bf1e       b.w     211758 <cc2538AlarmProcess>
  21191c:       20002aba        .word   0x20002aba
  211920:       20002980        .word   0x20002980
  211924:       20002abc        .word   0x20002abc
  211928:       20002ab9        .word   0x20002ab9
  21192c:       400db03c        .word   0x400db03c
  211930:       20000008        .word   0x20000008
  211934:       400db040        .word   0x400db040
  211938:       400db100        .word   0x400db100
  21193c:       400db200        .word   0x400db200
  211940:       400d5000        .word   0x400d5000

Now there's some re-ordering of instructions going on there, but the gist of what was said is still present.  Notably, the while(1) loop when the LEFT button is pressed.  This is simulating the CPU getting stuck and needing a reset from the watchdog.  The watchdog though never triggers.  If I hit that button, then break into the code with the debugger, I see the following state:

(gdb) printf "ctl=%08x stat=%08x wdt=%08x\n", (*(volatile uint32_t*)(0x400d2000)), (*(volatile uint32_t*)(0x400d2004)), (*(volatile uint32_t)(0x400d5000))
ctl=00000000 stat=004c0000 wdt=00000008

That there, is the state of the clock control and status registers, and of the watchdog register.  As can be seen, both crystal oscillators are turned on and running, I/O and system clock is running at 32MHz, and the watchdog is turned on.  The documentation states that with those settings, it should reset after a second (32768 ticks at 32.768kHz = 1 second).

Clearly I need to do something more to enable the watch dog, but neither the user guide for the CC2538, nor the examples in the foundation firmware are shedding any light on how this is supposed to work.

  • So it turns out… that for the 32kHz oscillator to work and by extension, for any peripheral relying on it (e.g. watchdog, sleep timer), the two pins that connect to the crystal, namely PD6 and PD7, must be manually configured as analogue input pins.

        /* Force analogue enabled on PD6/PD7 */
        HWREG(IOC_PD6_OVER) = 0x01;
        HWREG(IOC_PD7_OVER) = 0x01;

    Credit to Benoît for finding that in this thread.  It is disappointing to see that after nearly 4 years, there was no follow-up from TI regarding this.