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.

LAUNCHXL-CC1312R1: EventPend is handle by a task during execution of watcgDog timer interrupt callback.

Part Number: LAUNCHXL-CC1312R1
Other Parts Discussed in Thread: CC1310

In my code we are using the Watchdog timer callback to Postevent to each task in order to request aliveSignal. I was expecting that only after the watchDogTimer callBack function is finished the TIRTOS will wakeup the task in order to handle the event it pends. but looks like this is not the way it works, I found that immediately after posting the event the TI-RTOS activate the task that pend for this event and execute it, and than return back to the watchdog timer callback to continue it's activity.

I found this behaver by toggling 2 DIOs : one is set/clr when Enter/exit watchdog timer callback, and the second DIO activated the the task eventPend is handled.

Here is a logic capture that demonstrate it:

 

Preconditions:

WatchDog interrupt used is WATCHDOG_INT_TYPE_INT(instead of WATCHDOG_INT_TYPE_NMI).

IntPrioritySet(INT_WDT_IRQ, 0);

Could you please clarify this behavior. Am I wrong with my expectation?

  • Hi Yigal,

    It makes sense that the watchdog runs in the lowest priority context here. But as far as I know we don't have any documentation for this, so let me reach out to the developer team.

  • Here is a copy from screenshot SY/BIOS TI-RTOS Users Guide: 

    "If a Swi is posted, it runs only after all pending Hwis have run. A Swi function in progress can be
    preempted at any time by a Hwi; the Hwi completes before the Swi function resumes. On the other hand,
    Swi functions always preempt tasks. All pending Swis run before even the highest priority task is allowed
    to run. In effect, a Swi is like a task with a priority higher than all ordinary tasks."

    From above it can be understood that my result are not the expected behavior. Could you clarify what I'm doing wrong here.

  • I would like to add additional important information that I just discoverקג.

    above unexpected behavior happened only when I initialize my system using command: 

    Power_setConstraint( PowerCC26XX_SB_DISALLOW );

  • Hi Yigal,

    The watchdog uses a nonmaskable interrupt, whish bypasses the normal interrupt handling. For this reason we don't support BIOS API calls fro the watchdog callback context. From the header of Watchdog API documentation:


    The Watchdog interrupt is configured as a non-maskable interrupt (NMI) and the user-defined callback function is called in the context of NMI. Because the Watchdog interrupt is non-maskable, it is not safe to call any BIOS APIs from the Watchdog callback function.

    If you absolutely need the WDT to be in a sensible context (rather than being absolutely guaranteed to trigger regardless of state), we can walk you through the changes needed to get the callback to operate normally - but obviously this comes with some risk, because a long critical section could theoretically delay the WDT interrupt long enough for the timer to kill the system. 

  • Hi,

    In our system the WDT is configured to use the INT_WDT_IRQ instead of INT_NMI_FAULT interrupt. therefore I'm not expecting this to happen.

    Even when using NMI, I'm not expecting this kind of behavior because HWI have the highest priority over OS.

    Following are the changes we implemented in order to use INT_WDT_IRQ :

    file : WatchdogCC26XX.c, 

    change line 168 with :  HwiP_plug(INT_WDT_IRQ, (void *)WatchdogCC26XX_callbackFxn);

    change line 260 with: WatchdogIntTypeSet(WATCHDOG_INT_TYPE_INT); 

    I'm attaching an example code that demonstrate the problem, In this example you should replace the TI-RTOS cfg file with the zipped file Modem_CC1312R1.cfg.

    Please help investigating this issue.

    3124.TI_setPort_Example.zip

  • Hi Yigal,

    In addition to the code changes you posted, can you test swapping out HwiP_plug for a new HwiP object? (HwiP_plug is deliberately bypassing the BIOS event dispatcher so there is probably still some odd behavior caused by this.)

    You could create it in this sort of manner. The important pieces are calling out INT_WDT_IRQ and setting a priority.

    HwiP_Handle hwiHandle;
    HwiP_Params hwiParams;
    
    HwiP_Params_init(&hwiParams);
    hwiParams.arg = thisCanProbablyBeIgnored;
    hwiParams.priority = intPriority;
    
    hwiHandle = HwiP_create(INT_WDT_IRQ, myInterruptFxn, &hwiParams);

  • Issue is not resolved:
    you can see in below logic capture that D1 (DIO24) is toggle within the context of the watchDog timer callback (D0. DIO22)

    I tried to set the priority to 0,1,7,8 but all behave the same as shown in above logic capture.

    Here is the modification I implemented:

    #if 1        
            HwiP_Handle hwiHandle;
            HwiP_Params hwiParams;
            HwiP_Params_init(&hwiParams);
            hwiParams.arg = 0;
            hwiParams.priority = 1;
            hwiHandle = HwiP_create(INT_WDT_IRQ, (HwiP_Fxn)WatchdogCC26XX_callbackFxn, &hwiParams);        
    #else        
            
            //HwiP_plug(INT_NMI_FAULT, (void *)WatchdogCC26XX_callbackFxn);
            HwiP_plug(INT_WDT_IRQ, (void *)WatchdogCC26XX_callbackFxn); //yigal
    #endif

  • Just to take one step back to understand the high level: 

    - What do you want to use the DIOs for in the watchdog callback? Would you be able to move the DIO access to the task that is pending on the event that the callback is setting?

    - Not sure if I understand the overall watchdog usage. In the example you send me it looked like you cleared the watchdog timer in the watchdog. If that is the case, who do you detect if all the tasks are actually running since the callback would always be run. 

  • The example attached is just s simple code that shows how the system is working and to illustrate the problem I see, by toggling DIOs.

    the watchdog callback is tripped every 1 sec, on enter/exit this function I toggle DIO22 and also postEvent.

    the mainThread task is pend for this event.

    I expect that only after watchdog callback is is finished the mainTread will handle the event and therefore will toggle DIO24.

    In our original code, the watchdog callback pendevent to each task for requesting aliveSignal, this aliveSignal is tested in the watchDog callback, and in case it is missing , on the next second a reset will be initiated.

    During testing our system I found that sometimes there is a watchdog reset, therefore I added DIOs in order to debug it, and during this stage I found this strange behavior, that sometimes the  eventPend is handled during the context of the watchDog callback.

    From this point I created a simple example to illustrate the problem.

    I would like to add additional important information that I mention before. The unexpected behavior happened only when I initialize my system using command: Power_setConstraint( PowerCC26XX_SB_DISALLOW );

  • - What is the background for using Power_setConstraint( PowerCC26XX_SB_DISALLOW );? Is it just for the debug version? 

    - If I understand you correctly, you have done the changes described in this post to be able to use the DIOs for debugging? 

    - If that is the case, is it possible to debug the system differently? One suggestion is to set the DIO after the event pend in the different tasks. Dependent on how many DIOs you have available you can potentially use a different DIO for each task to see which one them that is not triggered. I assume you will always enter the watchdog callback and that it's the alive signaling that fails and you have to find out which task that has failed to respond. 

  • 1. Our project is to migrate our system from CC1310, TIRTOS 2.20.00.06 to CC1312 SDK4.40.04.04.

    In this system there are 2 functions power_setPowerConstrain(), power_clrPowerConstrain() each functions have a set of command as below, and we use it to enable/disable CPU sleep mode:

      

    void power_setPowerConstrain( void )
    
    {
    
    Power_setConstraint( PowerCC26XX_SB_VIMS_CACHE_RETAIN );
    
    Power_setConstraint( PowerCC26XX_SD_DISALLOW );
    
    Power_setConstraint( PowerCC26XX_SB_DISALLOW );
    
    Power_setConstraint( PowerCC26XX_IDLE_PD_DISALLOW );
    
    Power_setConstraint( PowerCC26XX_NEED_FLASH_IN_IDLE );
    
    }
    
    
    void power_clrPowerConstrain( void )
    
    {
    
    Power_releaseConstraint( PowerCC26XX_SB_VIMS_CACHE_RETAIN );
    
    Power_releaseConstraint( PowerCC26XX_SD_DISALLOW );
    
    Power_releaseConstraint( PowerCC26XX_SB_DISALLOW );
    
    Power_releaseConstraint( PowerCC26XX_IDLE_PD_DISALLOW );
    
    Power_releaseConstraint( PowerCC26XX_NEED_FLASH_IN_IDLE );
    
    }

    For debugging this issue I started to comment  part of the code and I found that when I comment the use of above two function it works as expected, No watchdog Reset are performed. So I started to narrow it more and I found that when I comment the use of power_clrPowerConstrain(), and comment Power_setConstraint( PowerCC26XX_SB_DISALLOW ) it start to work as expected.

      

    2. Correct. I wrote a simple example (based on TI watchdog example) that illustrate our system behavior just for debugging this issue.

    3. This is exactly what I did, and this is also the reason I open this issue because of the strange behavior I found.

    As I wrote I also wrote a simple example that illustrate the problem when using only one Task, this example was attached before for your use.

    Thanks for helping 

  • If you comment out  Power_setConstraint( PowerCC26XX_SB_DISALLOW ) the chip should go down in standby meaning that the watchdog is not clocked. The watchdog will not time out and you will not get a reset while in standby. 

    Meaning that the task where you use power_setPowerConstrain() for some reason is not sending "I'm alive"? Is it this you are trying to confirm? 

  • Please see below code.

    If I comment out  Power_setConstraint( PowerCC26XX_SB_DISALLOW ), there is a watchDog interrupt every 1 sec,  and the code work as expected, meaning the event_pent is handled only after returning from watchdogTimerCallback().

    If Power_setConstraint( PowerCC26XX_SB_DISALLOW ) is not commented out,

    After Event_post() (line 10) is executed, the CPU switch to handle Event_pend() (Line 38), instead of continue to finish the watchdogTimerCallback() function. This is the problem I'm trying to resolve.

    /*
     *  ======== watchdogTimerCallback ========
     */
    void watchdogTimerCallback(void)
    {
        // Set port 22
        board_setPin(GPIOCC26XX_DIO_22);
        
        //post Event to mainTread
        Event_post( event.handle, 0x01 );
    
        Watchdog_clear( g_watchdogHandler );
    
        // Set port 22
        board_clrPin(GPIOCC26XX_DIO_22);
    
    }
    
    /*
     *  ======== mainThread ========
     */
    void mainThread(void *arg0)
    {
        Watchdog_init();
    
        board_openPin(GPIOCC26XX_DIO_22, PIN_GPIO_OUTPUT_EN  | PIN_PUSHPULL | PIN_GPIO_LOW);
        board_openPin(GPIOCC26XX_DIO_24, PIN_GPIO_OUTPUT_EN  | PIN_PUSHPULL | PIN_GPIO_LOW);
        
        /* Open a Watchdog driver instance */
        watchdogDriver_initModule(WATCHDOG_DISABLE_RESET, watchdogTimerCallback);
        
        // This will disable cpu from getting into sleep. Am I correct?
        // If below line is commented All work as expected.
      	Power_setConstraint( PowerCC26XX_SB_DISALLOW ); //
        
        while (1) 
        {
            uint32_t events = Event_pend(event.handle, Event_Id_NONE,  0xFFFFFFFF, BIOS_WAIT_FOREVER);
    
            board_setPin(GPIOCC26XX_DIO_24);
            board_clrPin(GPIOCC26XX_DIO_24);
        }
    }
    

  • Not sure if I see the same as you. I'm using the example you posted and have changed the callback to:

    /*
     *  ======== watchdogTimerCallback ========
     */
    void watchdogTimerCallback(void)
    {
        // Set port 22
    //    board_setPin(GPIOCC26XX_DIO_24);
    //    GPIO_toggle(CONFIG_GPIO_21);
        //post Event to mainTread
        Event_post( event.handle, 0x01 );
    
        Watchdog_clear( g_watchdogHandler );
        GPIO_toggle(CONFIG_GPIO_21);
        // Set port 22
    //    board_clrPin(GPIOCC26XX_DIO_24);
    
    }

    I have also added Power_setConstraint( PowerCC26XX_SB_DISALLOW );  before the while(1).

    Then the state of the DIO is changed every second. Since the GPIO toggle is the last line in the callback it doesn't look like something is skipped. 

  • Hi,

    With the changes you implemented you can't see the problem I'm facing because the GPIO_toggle() on end of callback will not show the actions that performed during the callback.

    Therefore it is needed to setPin on entering to callback and clrPin upon exiting from callback.

    as I wrote, when doing above experiment I found that immediately after the Event_post() within callback context, the CPU goes to mainThread handle the Event_pend, and then return back to callback to clear watchdog and clrPin.

    This kind of behavior is not expected in our system (not the example that just illustrate the issue I'm trying to investigate) .

  • Not fully following you here. As I understood it, when you are running your code  it looks like nothing after Event_post( event.handle, 0x01 ); is executed in the callback. The GPIO_toggle() shows that the code reach the end of the callback. 

  • This is just an example code therefore nothing is there, I removed big part of the code in order to simplify the example.

    In our original code after the Event_post() we are clearing some variables, and also testing if there is a need to perform reset, therefore it is important that this code will be excepted before the Event_pend() handle.

    I hope now it's clear Slight smile

  • I'm still playing with the example you posted in https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/994812/launchxl-cc1312r1-not-able-to-set-clear-gpio-inside-watchdog-timer-callback-function

    For some reason I didn't get DIO22 and DIO24 to work so I switched to DIO11 and DIO12.

    The callback function I use looks like:

    /*
     *  ======== watchdogTimerCallback ========
     */
    void watchdogTimerCallback(void)
    {
        // Set port 12
        board_setPin(GPIOCC26XX_DIO_12);
        //post Event to mainTread
        Event_post( event.handle, 0x01 );
    
        Watchdog_clear( g_watchdogHandler );
        // Set port 12
        board_clrPin(GPIOCC26XX_DIO_12);
    
    }

    To avoid that the DIO domain is not powered when waking up I have prevented the device to go down in standby using  Power_setConstraint(PowerCC26XX_SB_DISALLOW); // as shown below:

    /*
     *  ======== mainThread ========
     */
    void *mainThread(void *arg0)
    {
        GPIO_init();
        Watchdog_init();
        board_openPin(GPIOCC26XX_DIO_11, PIN_GPIO_OUTPUT_EN  | PIN_PUSHPULL | PIN_GPIO_LOW);
        board_openPin(GPIOCC26XX_DIO_12, PIN_GPIO_OUTPUT_EN  | PIN_PUSHPULL | PIN_GPIO_LOW);
    
        /* Open a Watchdog driver instance */
        watchdogDriver_initModule(WATCHDOG_DISABLE_RESET, watchdogTimerCallback);
    
        Power_setConstraint(PowerCC26XX_SB_DISALLOW); //
    
        while (1)
        {
            uint32_t events = Event_pend(event.handle, Event_Id_NONE,  0xFFFFFFFF, BIOS_WAIT_FOREVER);
            Task_sleep(100);
            board_setPin(GPIOCC26XX_DIO_11);
            board_clrPin(GPIOCC26XX_DIO_11);
        }
    }
    

    Then I get the following from the logic analyzer:

    where channel0 is set in the main thread and channel1 in the callback. 

    Meaning that I see the expected behavior meaning that the code in the callback is run to the end before the content of the main thread is run. 

  • This is strange that for you it behave as expected.

    I believe that our projects are not identical, therefore we have a different behavior. maybe this will lead us to the solution.

    I'm attaching again my entire project for you to test.

    Please share your project with me, I would like also to test it.

    7230.TI_setPort_Example.zip

  • workspace_10v2_watchdog_gpio.7z

    I will look into your project a bit later. I have attached the project I played with yesterday. Disregard the "gpiointerrupt" example, I imported this at one point for reference and forgot to remove it again. 

  • Are you using CCS?

  • Yes, I'm using CCS. I had issues using IAR (never use it) 

  • This is a problem. in order to debug this issue we must have the same environment. we are using IAR 8.50.1

  • I agree but both Marie and me have issues running this project in IAR. 

    What I did when I tested your project was to import the watchdog example in CCS and copy/ paste the files from your project and into the CCS project. What should be possible is to take the example in IAR and copy in the files from the project I posted. 

  • Hi TER,

    After commenting out line "Task_sleep(100);" in the mainThread, the issue can be reproduce.

    Please try it again with your system. 

  • I see the same as you.

    I added this code:

    /*
     *  ======== watchdogTimerCallback ========
     */
    void watchdogTimerCallback(void)
    {
    //      CPUdelay(1000);
    //    // Set port 12
        board_setPin(GPIOCC26XX_DIO_12);
        board_clrPin(GPIOCC26XX_DIO_12);
        //post Event to mainTread
        Event_post( event.handle, 0x01 );
    
        board_setPin(GPIOCC26XX_DIO_12);
        board_clrPin(GPIOCC26XX_DIO_12);
    
        Watchdog_clear( g_watchdogHandler );
    //    // Set port 12
        board_setPin(GPIOCC26XX_DIO_12);
        board_clrPin(GPIOCC26XX_DIO_12);
    
    }

    while (1)
        {
            uint32_t events = Event_pend(event.handle, Event_Id_NONE,  0xFFFFFFFF, BIOS_WAIT_FOREVER);
    
            //Task_sleep(100);
            board_setPin(GPIOCC26XX_DIO_11);
            board_clrPin(GPIOCC26XX_DIO_11);
            CPUdelay(1000);
            board_setPin(GPIOCC26XX_DIO_11);
            board_clrPin(GPIOCC26XX_DIO_11);
        }

    And get 

    As a test, I commented out the watchdog code and replaced the callback with a timer callback with the same code and that works as expected.

    This indicate that 

    should be followed.

    I know I have been looking at a very simplified example but I would recommend restructuring the code some. The watchdog callback should normally only be reached if the watchdog times out, basically an error event. Clearing of the watchdog should therefore be done elsewhere in the code, the same with checking alive signals from all tasks. 

  • Hi

    In the example I send earlier (and also with our system) the TI file WatchdogCC26XX.c is modified for setting the watchDog callback to work with INT_WDT_IRQ instead of NMI.

    Please add this file to the example project ad test.

    WatchdogCC26XX.c
    /*
     * Copyright (c) 2015-2020, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include <stdint.h>
    #include <stdlib.h>
    
    #include <ti/drivers/dpl/DebugP.h>
    #include <ti/drivers/dpl/HwiP.h>
    
    #include <ti/drivers/Power.h>
    #include <ti/drivers/power/PowerCC26XX.h>
    
    #include <ti/drivers/watchdog/WatchdogCC26XX.h>
    
    #include <ti/devices/DeviceFamily.h>
    #include DeviceFamily_constructPath(driverlib/watchdog.h)
    
    /* Function prototypes */
    void WatchdogCC26XX_clear(Watchdog_Handle handle);
    void WatchdogCC26XX_close(Watchdog_Handle handle);
    int_fast16_t  WatchdogCC26XX_control(Watchdog_Handle handle, uint_fast16_t cmd,
            void *arg);
    void WatchdogCC26XX_init(Watchdog_Handle handle);
    Watchdog_Handle WatchdogCC26XX_open(Watchdog_Handle handle, Watchdog_Params *params);
    int_fast16_t WatchdogCC26XX_setReload(Watchdog_Handle handle,
            uint32_t ticks);
    uint32_t WatchdogCC26XX_convertMsToTicks(Watchdog_Handle handle,
        uint32_t milliseconds);
    
    /* WatchdogCC26XX internal functions */
    static void WatchdogCC26XX_initHw(Watchdog_Handle handle);
    
    /* WatchdogCC26XX internal callback function */
    static void WatchdogCC26XX_callbackFxn(void);
    
    /* WatchdogCC26XX global variables */
    static Watchdog_Handle    watchdogHandle;
    static Watchdog_Callback  watchdogUserCallback;
    
    /* Watchdog function table for CC26XX implementation */
    const Watchdog_FxnTable WatchdogCC26XX_fxnTable = {
        WatchdogCC26XX_clear,
        WatchdogCC26XX_close,
        WatchdogCC26XX_control,
        WatchdogCC26XX_init,
        WatchdogCC26XX_open,
        WatchdogCC26XX_setReload,
        WatchdogCC26XX_convertMsToTicks
    };
    
    /* Maximum allowable setReload value */
    #define MAX_RELOAD_VALUE        0xFFFFFFFF
    #define WATCHDOG_DIV_RATIO      32            /* Watchdog division ratio */
    #define MS_RATIO                1000          /* millisecond to second ratio */
    
    /*
     *  ======== WatchdogCC26XX_callbackFxn ========
     */
    static void WatchdogCC26XX_callbackFxn(void)
    {
        /* Call user callback function */
        (watchdogUserCallback)((uintptr_t)watchdogHandle);
    }
    
    /*
     *  ======== WatchdogCC26XX_clear ========
     */
    void WatchdogCC26XX_clear(Watchdog_Handle handle)
    {
        WatchdogIntClear();
    }
    
    /*
     *  ======== WatchdogCC26XX_close ========
     */
    void WatchdogCC26XX_close(Watchdog_Handle handle)
    {
        /*
         *  Not supported for CC26XX - Once the INTEN bit of the WDTCTL
         *  register has been set, it can only be cleared by a hardware
         *  reset.
         */
        DebugP_assert(false);
    }
    
    /*
     *  ======== WatchdogCC26XX_control ========
     *  @pre    Function assumes that the handle is not NULL
     */
    int_fast16_t WatchdogCC26XX_control(Watchdog_Handle handle, uint_fast16_t cmd,
            void *arg)
    {
        /* No implementation yet */
        return (Watchdog_STATUS_UNDEFINEDCMD);
    }
    
    /*
     *  ======== Watchdog_init ========
     */
    void WatchdogCC26XX_init(Watchdog_Handle handle)
    {
        WatchdogCC26XX_Object *object = handle->object;
    
        object->isOpen = false;
    }
    
    /*
     *  ======== WatchdogCC26XX_open ========
     */
    Watchdog_Handle WatchdogCC26XX_open(Watchdog_Handle handle, Watchdog_Params *params)
    {
        unsigned int                   key;
        WatchdogCC26XX_Object         *object;
    
        /* get the pointer to the object and hwAttrs */
        object = handle->object;
    
        /* disable preemption while checking if the WatchDog is open. */
        key = HwiP_disable();
    
        /* Check if the Watchdog is open already with the HWAttrs */
        if (object->isOpen == true) {
            HwiP_restore(key);
            DebugP_log1("Watchdog: Handle %x already in use.", (uintptr_t)handle);
            return (NULL);
        }
    
        object->isOpen = true;
        HwiP_restore(key);
    
        /* initialize the Watchdog object */
        object->debugStallMode = params->debugStallMode;
        object->resetMode      = params->resetMode;
    
        /* setup callback function if defined */
        if (params->callbackFxn != NULL) {
            watchdogHandle = handle;
            watchdogUserCallback = params->callbackFxn;
            
            //HwiP_plug(INT_NMI_FAULT, (void *)WatchdogCC26XX_callbackFxn);
            HwiP_plug(INT_WDT_IRQ, (void *)WatchdogCC26XX_callbackFxn); //yigal
        }
    
        /* initialize the watchdog hardware */
        WatchdogCC26XX_initHw(handle);
    
        DebugP_log1("Watchdog: handle %x opened" ,(uintptr_t)handle);
    
        /* return handle of the Watchdog object */
        return (handle);
    }
    
    /*
     *  ======== WatchdogCC26XX_setReload ========
     */
    int_fast16_t WatchdogCC26XX_setReload(Watchdog_Handle handle, uint32_t ticks)
    {
        unsigned int                   key;
    
        /* disable preemption while unlocking WatchDog registers */
        key = HwiP_disable();
    
        /* unlock the Watchdog configuration registers */
        WatchdogUnlock();
    
        /* make sure the Watchdog is unlocked before continuing */
        while(WatchdogLockState() == WATCHDOG_LOCK_LOCKED)
        { }
    
        /* update the reload value */
        WatchdogReloadSet(ticks);
    
        /* lock register access */
        WatchdogLock();
    
        HwiP_restore(key);
    
        DebugP_log2("Watchdog: WDT with handle 0x%x has been set to "
            "reload to 0x%x", (uintptr_t)handle, ticks);
    
        return (Watchdog_STATUS_SUCCESS);
    }
    
    /*
     *  ======== WatchdogCC26XX_hwInit ========
     *  This function initializes the Watchdog hardware module.
     *
     *  @pre    Function assumes that the Watchdog handle is pointing to a hardware
     *          module which has already been opened.
     */
    static void WatchdogCC26XX_initHw(Watchdog_Handle handle) {
        unsigned int                   key;
        uint32_t                       tickValue;
        WatchdogCC26XX_Object          *object;
        WatchdogCC26XX_HWAttrs const   *hwAttrs;
    
        /* get the pointer to the object and hwAttrs */
        object = handle->object;
        hwAttrs = handle->hwAttrs;
    
        /* convert milliseconds to watchdog timer ticks */
        tickValue = WatchdogCC26XX_convertMsToTicks(handle, hwAttrs->reloadValue);
    
        /* disable preemption while unlocking WatchDog registers */
        key = HwiP_disable();
    
        /* unlock the Watchdog configuration registers */
        WatchdogUnlock();
    
        /* make sure the Watchdog is unlocked before continuing */
        while(WatchdogLockState() == WATCHDOG_LOCK_LOCKED)
        { }
    
        WatchdogReloadSet(tickValue);
    
        /* set reset mode */
        if (object->resetMode == Watchdog_RESET_ON) {
            WatchdogResetEnable();
        }
        else {
            WatchdogResetDisable();
        }
    
        /* set debug stall mode */
        if (object->debugStallMode == Watchdog_DEBUG_STALL_ON) {
            WatchdogStallEnable();
        }
        else {
            WatchdogStallDisable();
        }
    
        /* enable the Watchdog interrupt as a non-maskable interrupt */
        //WatchdogIntTypeSet(WATCHDOG_INT_TYPE_NMI);
        WatchdogIntTypeSet(WATCHDOG_INT_TYPE_INT); //yigal
    
      
        /* enable the Watchdog */
        WatchdogEnable();
        
        IntEnable(INT_WDT_IRQ);
    
        /* lock the Watchdog configuration registers */
        WatchdogLock();
    
        HwiP_restore(key);
    }
    
    /*
     *  ======== WatchdogCC26XX_convertMsToTicks ========
     *  This function converts the input value from milliseconds to
     *  Watchdog clock ticks.
     */
    uint32_t WatchdogCC26XX_convertMsToTicks(Watchdog_Handle handle,
        uint32_t milliseconds)
    {
        uint32_t                       tickValue;
        uint32_t                       convertRatio;
        uint32_t                       maxConvertMs;
        ClockP_FreqHz                   freq;
    
        /* Determine milliseconds to clock ticks conversion ratio */
        /* Watchdog clock ticks/sec = CPU clock / WATCHDOG_DIV_RATIO */
        /* Watchdog clock ticks/ms = CPU clock / WATCHDOG_DIV_RATIO / 1000 */
        ClockP_getCpuFreq(&freq);
        convertRatio = freq.lo / WATCHDOG_DIV_RATIO / MS_RATIO;
        maxConvertMs = MAX_RELOAD_VALUE / convertRatio;
    
        /* convert milliseconds to watchdog timer ticks */
        /* check if value exceeds maximum */
        if (milliseconds > maxConvertMs) {
            tickValue = 0;  /* return zero to indicate overflow */
        }
        else {
            tickValue = (uint32_t)(milliseconds * convertRatio);
        }
    
        return(tickValue);
    }
    

  • Hi,

    Is my last reply clear? Are you testing it?

  • As I understand it the change from NMI to INT_WDT_IRQ were due the watchdog callback called when the DIO power domain was not on. 

    But: From a system level perspective, is it safe to use INT_WDT_IRQ? Meaning that if the watchdog is needed, this indicate that one or more tasks in the system have stopped responding and the system needs a reset. Hence a NMI interrupt is default to ensure priority. 

    Since I observed the same as you using NMI, do you expect a different result using INT_WDT_IRQ? 

  • In our system based on the CC1310, the watchDog interrupt used is the INT_WDT_IRQ. This design is working properly for years therefore I would not like to change the design due to upgrading our platform to CC1312+sdk.

    In TI manual the note for not using bios function within the context of watchdog interrupt function is only when the interrupt is NMI, Therefore I'm expecting my example to behave differently, by only after watchdog interrupt is finished, the event_pend will be handled.

    BTW - The behavior I'm expecting are the same as it is working/behave  today when using our old project based on CC1310.

  • Thank you for the clarification. I got the impression from https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/994812/launchxl-cc1312r1-not-able-to-set-clear-gpio-inside-watchdog-timer-callback-function that using INT_WDT_IRQ was something new. I will change to INT_WDT_IRQ on my end and verify the functionality. If it gives the same results and with the NMI I have to consult with SW R&D. 

  • Question: Have you tried to run the same simplified example on CC1310? The watchdog and the hwi handling should be equal on CC1310 and CC1312 and the software should therefore behave in the same way. How did you modify the driver for CC1310?  

    I got this feedback from SW R&D:

    1. WatchdogCC26XX.c replaced INT_NMI_FAULT with INT_WDT_IRQ in the HwiP_plug() call.
    2. HwiP_plug() directly accesses the vector table and writes your fxn pointer into the entry selected. This circumvents the Hwi dispatcher.
    3. When you call Event_post(), the Task scheduler is run at the end of the call. Usually when this happens, the Hwi dispatcher has disabled Task scheduling while we are in ISR context. However, you just circumvented the dispatcher. Which means that the scheduler actually runs and will see that your event Task is ready to run and will then start executing

    Remediation options:

    • Stick to the old NMI interrupt
    • Use HwiP_construct() to create the watchdog interrupt.
  • I didn't tried the simplified example on CC1310. The reason I started this investigation was because I saw sometimes watchdog reset when using new platform (cc1312), During the debugging of this issue I found that the event_post is handled during the watchdog context, Than I tested it with CC1310 and I saw it work as expected (all was done with our official firmware). And only then I open this thread and created the simple example so it will be easier to illustrate.

    Are you able to see the problem as I see?

  • Yes. The reason is covered in the previous post. 

  • Could you please clarify how I can fix it?

  • As I commented in a previous post: "I know I have been looking at a very simplified example but I would recommend restructuring the code some. The watchdog callback should normally only be reached if the watchdog times out, basically an error event. Clearing of the watchdog should therefore be done elsewhere in the code, the same with checking alive signals from all tasks. "

    Based on the information in this thread it seems to me that you are not using the watchdog in the intended way. For some reason it seems to work on CC1310 but that doesn't mean it has been a recommended or ideal implementation.  

    I would recommend to rewrite your code some to use the watchdog slightly different instead of trying to get it to work in a not recommended way.