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.

CC3235MODSF: LPDS wake up & gpio wakeup

Part Number: CC3235MODSF
Other Parts Discussed in Thread: SYSCONFIG

I'm developing an I2C slave driver and now require to cooperate with LPDS mode.

As the master does not know when the CC3235 is awake i would always signal a wakeup using a falling edge on GPIO2 then send read/write a message after certain delay.

The LPDS so far seems to work the device enters and leaves periodically the sleep mode that I verified by

 - setting a GPIO(9) in LOW parked state and HIGH active state -> monitorable when LPDS is entered/exited
 - debugger disconnects when I enable the policy

I have registered this function to unsing the Power_registerNotify with the PowerCC32XX_AWAKE_LPDS flag

int I2CCC32XX_postNotify_test(unsigned int eventType, uintptr_t eventArg, uintptr_t clientArg)
{
    GPIO_setConfig(MP913, pinconfig_save);
    GPIO_write(MP913, 1);

    // Reconfigure the hardware when returning from LPDS
    I2CCC32XX_initHw_test((I2C_Handle) 0);

    dma_hndl = UDMACC32XX_open();
    dma_rx_setup();

    //i2c_timer_start(50000);

    GPIO_write(MP913, 0);

    return (Power_NOTIFYDONE);
}

Now there are 2 problems I encountered.

First this function does not seem to be called whatsoever. There is a testpoint on our PCB (MP913 = "i2c_hw_ini" in this picture) and I use this to indicate for entering/leaving this notify function.
You can see that MP914_sleep is going up and down as we periodically enter/exit sleep -> the GPIO is parked/activated upon return.
Yet the "i2c_hw_init" (MP913) that is set high in the notify callback does not move at all.

Secondly if I activate that uncommented line "i2c_timer_start" which opens and starts a oneshot timer I end up in a blocked AWAKE state.
The function again seems not to be called as the test point GPIO (MP913) is not going high at all but the MP914_sleep shows the ECU is AWAKE.

Between these two recordings only this single uncommented line is the difference.

Also even if we assume the GPIO set is not happening in the callback properly (MP913 does not cause action) but every other function is called and executed the I2C master still can not read/write the CC3235 because I get bus errors or NACK -> which points me to the same issue I2C is not inited (init function was not called).

Question #1: despite clearly entering the sleep/awake I do not see the postNotify getting called

Question #2: how is this possible that a seemingly not called function change causes the CPU to stay awake

  • Hi,

    I do not recommend editing the power driver directly. There are internal dependencies that are not meant to be changed by the application. It is likely the way you have implemented this is by initializing the I2C driver before the parked pins are fully restored, causing some fault in the system.

    The easiest way to re-initialize the I2C after LPDS is to do this in the wakeupGPIOFxnLPDS function, defined in the power struct in SysConfig.

    Best regards,

    Sarah

  • "I do not recommend editing the power driver directly."

    I never wrote that I edited the power driver at any point.
    The I2CCC32XX_postNotify_test is a callback function in my I2C slave driver that I register using Power_registerNotify.

    "It is likely the way you have implemented this is by initializing the I2C driver before the parked pins are fully restored"

    I did not park the pin (MP913) that I would use to indicate that my callback got called. I simply initialized it as output and toggle it in the callback.
    I initialize the GPIOs in the main (before OS scheduler init). The I2C is initialized in a task. I would assume the Power_registerNotify called in the GPIO_Init (and also in the I2C_Init) that requires this Power Object is forming a linked list thus when the GPIO_Init is called prior to I2C_Init's registerNotify the custom driver entry of mine is added to the end of the linked list (or at least behind the GPIO) therefore the GPIOs are restored prior to the call of my custom callback(?).

    "The easiest way to re-initialize the I2C after LPDS is to do this in the wakeupGPIOFxnLPDS function"
    If I implement only the suggested GPIO wakeup function: what happens if the CPU is already awake the time the falling edge happens and the LPDS GPIO (in my case GPIO2) is pulled low? Will that wakeupGPIO function be triggered? According to my testing it does not happen.

    Also at the beginning of this function I restore manually the GPIO config of the controlled GPIO should that not make sure it is configured as output?

    If I use the callback "resumeLPDSHookFxn" I do not see the MP913 GPIO toggling  -> is not fired when the GPIO wakes up the CPU although clearly it leaves the LPDS state.

    If I use the callback "wakeupGPIOFxnLPDS" I see the GPIO toggling but only when the ECU was sleeping.

    The driver defined callback "I2CCC32XX_postNotify_test" that is registered via Power_registerNotify is never called.

    How are these callbacks supposed to work? I would expect that whenever the ECU wakes up/leaves LPDS for any reason

     - the notify callbacks registered via registerNotify are called unconditionally
     - the resumeLPDSHook is called unconditionally

    The wakeupGPIO is called when the wakes up due to the configured GPIO event.

    My major problem (origin of the above post) is: no matter how i try to reconfigure the I2C peripheral in these callback the BUS is stuck and/or NACK is signaled when I try to read/write from the CC3235 after it enabled the sleep policy. NACK and bus error despite I defined the callbacks as per sprui18j and also compared my implementation to the I2C/SPI master implementation.

  • Further analysis:

    I can confirm that the registerNotify seems to work, the intended driver function is called the issue I had is probably caused by the late GPIO configuration restore.

    I believe my issue was caused by not properly handling the constraint and therefore it caused sporadic sleeps while my transmissions were ongoing.
    So I added this code.

    When awaken by GPIO -> disallow LPDS (i2c receive will release the constraint)
    When a GPIO falling edge is detected -> disallow LPDS (i2c receive will release the constraint)

    GPIO2 is set to be the LPDS wake-up source and also the configuration is set to trigger the "gpio2_irq" on a falling edge.

    void wakeupGPIOFxnLPDS_cb(uint_least8_t index)
    {
        Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);
    }
    
    void gpio2_irq(uint_least8_t index)
    {
        uint8_t i;
    
        if( index == CONFIG_GPIO_2 )
        {
            Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);
    
            for( i = 0; i < 4; i++ )
            {
                GPIO_write(MP913, 1);
                GPIO_write(MP913, 0);
            }
        }
    }

    BUT it doesnt work always.

    Here is the recording of the I2C bus and the GPIOs:

    red: LPDS - GPIO2 when pulled LOW the CC3235 should wake up.
    green: dummy IO set to parked LOW state, set to HIGH when awake
    yellow: I2C message received from master

    As you can see periodic messages are sent and most of them are received. There are 2 gaps.

    zoomed GAP1

    Here we see that the ECU is awake (green line is high while red goes LOW) but nothing on MP913 and the I2C address is also not ACKnowledged.
    Despite the GPIO2 is set to cause a falling edge interrupt (see routine above) the yellow line is also not toggling -> missed LPDS constraint set.
    Question #1 how is this possible that the IRQ is missed? LPDS pins can not be used as IRQ?

    zoomed GAP2

    Here it seems that the ECU was sleeping while received the falling edge on the LPDS line, woke up, marked the sleep constraint SET (see wakeupGPIOFxnLPDS_cb) then immmediately went back to sleep.

    Question #2: how is this possible? Power_setConstraint can not be used from IRQ context? Than why is it working in most of the cases?

    EDIT: 

    further checking GAP2 problem it seems that a periodic event in the app wakes up the CPU actually.
    Here is a zoomed frame of the wakeup happening BUT the ecu goes immediately back to sleep.

    Same but zoomed out a bit, with both markers are visible. It seems that the wakeup actually happened due to a 1s interval in the app that I have and the CPU did not react on the wakeup signal at all. 

    Normally when the LPDS goes LOW the green line (signaling wakeup) goes high 8.85ms later.
    But in these cases (when I dont get a call to wakeupGPIOFxnLPDS_cb) green line goes high 5ms later, seems that the CPU reacts sooner to an app/sw interrupt and misses to report the wakeup by GPIO?

    edit2:

    A wakeup signal completely missed between 2 (LPDS red goes low -> no yellow spikes).

    zoomed on this shows the CPU goes to sleep 150us later the falling edge happens and ignored?

    edit3:

    after a while the CPU just doesnt wake up:

  • Hi,

    I will have to take another day to review your entire post, but I can give some explanation that may be helpful.

    • resumeLPDSHookFxn is called before parked pins are restored. This means this function is only really useful with semaphores or message queues to trigger a thread that will run once the scheduler is re-enabled.
    • wakeupGPIOFxnLPDS is called after pins are restored. This is only called if the device is in LPDS when the GPIO triggers.
    • If you want to check your wake condition, you can use PRCMLPDSWakeupCauseGet(). The wake options are HOST_IRQ (NWP activity), GPIO, or timer (which is set by the RTOS when you use the power policy).

    I don't recommend changing power constraints in the power policy handlers. Power constraints and dependencies are already built into the drivers based on hardware peripheral behavior. You should not need to handle this in your application.

    If you want to initialize the I2C whenever you wake from LPDS, I recommend implementing a semaphore or message queue with a thread that can open and execute the I2C code. You can post from resumeLPDSHookFxn, wakeupGPIOFxnLPDS, or a regular GPIO interrupt, depending on your desired use case and wake status.

    Best regards,

    Sarah

  • Thanks for the clarification of function calls.

    "I don't recommend changing power constraints in the power policy handlers."

    Why? Checking implementation of Power_setConstraint seems to do an atomic access to constraint variables and triggers no activity in the OS.
    Checking the SPICC32XXDMA.c the Power_releaseConstraint is called even from an interrupt context. Worst case the resumeLPDS and wakeupGPIOFxnLPDS are called in IRQ (out of OS) context?

    "Power constraints and dependencies are already built into the drivers based on hardware peripheral behavior. You should not need to handle this in your application."

    Unfortunately the I2C SLAVE driver implementation is not part of the SDK so I think I will need to do it my own.

    Due to sleep mode preventing the I2C operation our  idea is that via the GPIO2 (that is also a wakeup source) we would indicate if there is an incoming message soon from the master:
     - If the CC3235 is awake a falling edge should trigger a constraint set -> preventing the CPU from sleep till a message is received.
     - If the CC3235 is sleeping -> falling edge should wake up the CPU -> powerNotify initializes I2C peripheral -> constraint should prevent the CPU from sleep till a message is received

  • I believe I tackled most of the above issues except one (at least I'm not able to reproduce the no-gpio-toggling problem and the going-back-to-sleep-despite-constraint-set).

    The only one left is now that the CPU is just going to sleep and doesn't wake up anymore.

    red: LPDS pulled low -> wakeup
    yellow: processing if i2c msg
    green: GPIO parked low in sleep, active HIGH when awake.

    The master wishes to send messages and after a while the CC3235 just enters sleep and never wakes up again.
    Since debug is not an option, GPIOs are not available (the CPU is sleeping) I am stuck at this point (removed almost all app related code - could not narrow it down what is causing it).

    This "stress test" with the written driver works perfectly fine tested it for 15-20 minutes if I disable the policy thus the issue is definetly related to the sleep mode. When policy is enabled it happens 20-60seconds after the test is executed.

  • Update:

    The issue is caused by a GPIO read in the resumLPDS function.

    A simple example is enough (setup an empty function to resumeLPDS, enable the policy) to reproduce the issue.

    void resumeLPDS(void)
    {
        volatile uint8_t i= GPIO_read(CONFIG_GPIO_22);
    }

    If the GPIO_read is in the resumeLPDS the wakeup happens a few dozens of times the the ECU is blocked (in sleep?).

    Here I have an endless loop setting/releasing the constraint (yellow spike) the green is the LPDS indicator (GPIO is set high at wakeup, and parked LOW)

    If I remove the above GPIO_read from the resumeLPDS function everything works perfectly fine.

    Could you please confirm?

  • using

    if( GPIOPinRead((GPIOCC32XX_GPIO_02 >> 8) & 0xFF, GPIOCC32XX_GPIO_02 & 0xFF) == 0 )

    instead of 

    if( GPIO_read(CONFIG_GPIO_2) == 0 )

    doest not ressult in "stuck in sleep".

    edit: I realized GPIOPinRead was called wrongly in the above example. When correcting it to 

    if( GPIOPinRead(gpioBaseAddresses[(GPIOCC32XX_GPIO_02 >> 8) & 0xFF], GPIOCC32XX_GPIO_02 & 0xFF) == 0 )

    I am able to reproduce the issue. So it seems that the access to the GPIO port base memory area is causing the issue.

    edit2:
    Placing this GPIO_read to the driver registered notify callback causes no issue either. It seems to me there is some race condition between the memory mapped GPIO register access and wakeup proceedure/initialization? Or some sync/delay mechanism is missing between restoring the GPIO register bank and safe read?

  • Hi,

    Yes, that makes sense since resumeLPDSHookFxn is called before the power driver has fully exited sleep and the peripherals are restored. The GPIO driver is unable to detect when the peripheral module is restored, so there is some race condition when used incorrectly. That's why this hook function is limited as a callback; it should not call any driver functions.

    Best regards,

    Sarah

  • Thanks.

    Unfortunately I can't debug the issue further but it seems weird to me that a_GPIO_register_access (so it isnt the driver that fails it is the register access) might or might not work when calling it from the resume function.

    My guess is that the GPIO peripheral is clocked but not enough time given to settle the peripheral clock.

    What I find not ok is:

     - the "might work". It should either not work or it works.
     - the user has no way to check the correct wakeup source from resume function IF the CPU wake up with the GPIO already low OR the wake event happens in parallel to the GPIO falling/rising edge there would be no way to check the wakeup cause(s). (this was the reason i added GPIO read and i'm not using the wakeupGPIOFxnLPDS).

    For my case the reading was moved to the notify callback and I can now read the GPIO correctly (at least long term tests did  reveal the issue) and prevent returning to sleep.