Resuming USB Communications after exiting VLPS with PLL

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Resuming USB Communications after exiting VLPS with PLL

Jump to solution
2,892 Views
chuckh
Contributor III

Hello,

I'm using a PLL with an external oscillator to run the USB module on a KL25.  When the USB bus is suspended, I bring the device to VLPS.  When I resume (either from a TSI ISR or the Asyncronous USB wakeup) my understanding is that I have to re-enable the PLL.  I'm doing this and it seems to be working, but every so often I find that I lose communications with the device and have to power cycle.

Does anyone know the exact steps required to reinitialize the PLL, AND, how to handle that in code that has multiple ISR wakeup sources that may preempt eachother?

Thanks

Labels (1)
1 Solution
2,247 Views
mjbcswitzerland
Specialist V

Chuck

I haven't used USB together with recovery from VLPS so don't have a proven solution to offer but I use the following code when waking from a mode that has disabled the PLL (when the PLL is needed).

...

    __sleep_mode();                                                      // enter low power mode using wait for interrupt processor state

    if ((SYSTEM_CONTROL_REGISTER & SLEEPDEEP) && (SMC_PMPROT & SMC_PMPROT_AVLP)) // VLPS sleep mode exit requires the PLL to be reconfigured

        MCG_C5 = ((CLOCK_DIV - 1) | MCG_C5_PLLSTEN0);                    // move from state FEE to state PBE (or FBE)

        MCG_C6 = ((CLOCK_MUL - MCG_C6_VDIV0_LOWEST) | MCG_C6_PLLS);      // configure the origional PLL settings again

        while ((MCG_S & MCG_S_PLLST) == 0) {                             // loop until the PLLS clock source becomes valid

        }

        while ((MCG_S & MCG_S_LOCK) == 0) {                              // loop until PLL locks

        }

        MCG_C1 = (MCG_C1_CLKS_PLL_FLL | MCG_C1_FRDIV_1024);              // finally move from PBE to PEE mode - switch to PLL clock

        while ((MCG_S & MCG_S_CLKST_MASK) != MCG_S_CLKST_PLL) {          // loop until the PLL clock is selected

        }

    }

    uEnable_Interrupt();                                                 // enable interrupts so that the masked interrupt that became pending can be taken

....

This tends to take about 200us or so to complete.

Interrupts are only enabled once the PLL has locked (remembering that it is not a pending interrupt that wakes the processor but any enabled interrupt source).

Once interrupts are enabled there is no difference to any other time when interrupts can pre-empt each other so this shouldn't play any role in the wake up part.

Regards

Mark

View solution in original post

0 Kudos
14 Replies
2,247 Views
chuckh
Contributor III

Update:

I've implemented the changes regarding disabling interrupts around the VLPS.  My problem is not fixed, though.

I've done some investigating and I've learned the following:

1) When the problem occurs, the PC cannot send data to the device using the BULK_OUT endpoint.  It times out.  There is no recovery for the pipe that I have found.

2) When the problem occurs, the PC is still able to send data/communicate using the control endpoint.  It seems to be unaffected.

3) When the problem occurs, the PC is still able to receive data on the BULK_IN endpoint

4) Until the problem occurs, the PC is able to use the Control, BULK_IN, and BULK_OUT without issue.

I have tried to re-arm the BULK_OUT endpoint (when I get a special message on the control pipe), and the BULK_OUT transfer status changes from USB_STATUS_IDLE to USB_STATUS_TRANSFER_IN_PROGRESS, which I what I hoped for, but then when I send data on the BULK_OUT pipe from the PC, it still times out and the transfer status becomes IDLE again.

I have also tried to de-init all the non-control endpoints and re-init them (again, with a special message on the control pipe).  This results in the same behavior: transfer status changes to USB_STATUS_TRANSFER_IN_PROGRESS, and then back to IDLE after an attempt to send a message to the device on the BULK_OUT pipe (which times out).

It's obviously not a PLL or clock problem since the control pipe and the BULK_IN pipe work fine still.

What am I missing that could cause this behavior with the BULK_OUT pipe?

0 Kudos
2,247 Views
chuckh
Contributor III

What about OSCINIT0?  SHould I be checking that at all?

0 Kudos
2,247 Views
chuckh
Contributor III

Also, what about an aborted stop (STOPA)?  Do I need to handle that differently?

0 Kudos
2,248 Views
mjbcswitzerland
Specialist V

Chuck

I haven't used USB together with recovery from VLPS so don't have a proven solution to offer but I use the following code when waking from a mode that has disabled the PLL (when the PLL is needed).

...

    __sleep_mode();                                                      // enter low power mode using wait for interrupt processor state

    if ((SYSTEM_CONTROL_REGISTER & SLEEPDEEP) && (SMC_PMPROT & SMC_PMPROT_AVLP)) // VLPS sleep mode exit requires the PLL to be reconfigured

        MCG_C5 = ((CLOCK_DIV - 1) | MCG_C5_PLLSTEN0);                    // move from state FEE to state PBE (or FBE)

        MCG_C6 = ((CLOCK_MUL - MCG_C6_VDIV0_LOWEST) | MCG_C6_PLLS);      // configure the origional PLL settings again

        while ((MCG_S & MCG_S_PLLST) == 0) {                             // loop until the PLLS clock source becomes valid

        }

        while ((MCG_S & MCG_S_LOCK) == 0) {                              // loop until PLL locks

        }

        MCG_C1 = (MCG_C1_CLKS_PLL_FLL | MCG_C1_FRDIV_1024);              // finally move from PBE to PEE mode - switch to PLL clock

        while ((MCG_S & MCG_S_CLKST_MASK) != MCG_S_CLKST_PLL) {          // loop until the PLL clock is selected

        }

    }

    uEnable_Interrupt();                                                 // enable interrupts so that the masked interrupt that became pending can be taken

....

This tends to take about 200us or so to complete.

Interrupts are only enabled once the PLL has locked (remembering that it is not a pending interrupt that wakes the processor but any enabled interrupt source).

Once interrupts are enabled there is no difference to any other time when interrupts can pre-empt each other so this shouldn't play any role in the wake up part.

Regards

Mark

0 Kudos
2,247 Views
chuckh
Contributor III

I think this is the piece I have been missing, that I need to disable interrupts before entering VLPS and then reenable them after PLL locked.

Thanks.

0 Kudos
2,247 Views
mjbcswitzerland
Specialist V

Chuck

Apart from the fact that interrupts remain disabled while clean up work is done after low power mode exit, 99.99% of real-world solutions would want to disasble interrupts before "deciding" to actually enter the low power mode.

In real systems there will usually be many interrupts that could be occurring at any time and most will be triggreing system events that need to be handled and completed before low power mode can be finally entered. If the decision that it is time to enter low power mode is made without disabling interrupts one of these interrupts could occur as the sleep (WFI) instruction is scheduled for execution, pre-empt it, handle the interrupt that signals that the sysstem needs to process something and then the sleep mode will be entered with a system event queued and something potentially seriously fail because the processor is in a sleep mode but should actually be doing something important instead. This fact will only be seen when another event causes the processor to wake, which may be some time later, and the catastriophic failuire has already resulted....

Therefore it is typical to disable interrupts, check that there is no reason not to enter the sleep mode and, since nothing is seen, sleep mode entered. If there happens to be an interrupt occuring between disabling interrupts and executing the sleep instruction it will simply cause immediate exit and the interrupt will be handled and things continue correctly . The sleep mode can then be entered again later.

The Cortex Sleep modes are designed as they are (allowing wakeup due to a masked interrupt rather than a pending interrupt) to allow this (otherwise it would be virtually useless).

You can compare with the Coldfire's low power mode instruction (stop command) which has a parameter allowing the interrupt mask to be set to a certain priority level by the instruction so that it can be executed with disabled interrupts and will "open up" a certain interrupt priority level as the instruction is executed in order tro allow certain events to wake the processor (in this case it allows only pending - and not masked - interrupts to wake but due to the capabilities of the instruction it works very well).

Compare also the ST Micro's STR9 stop mode which doesn't support entering the stop mode with interrupts disabled (it would never exit) - it is unuseable - as they will admit themselves - and must have been designed by someone unfamiliar with real-world SW/system needs.

Possibly your own design is not so critical but systems are often moving between low power modes (not necessary extremently low power modes with special restriction and clocking requirements) and run modes hundreds or even thousands of times a second to save power whenever possible during 'normal' operation.

Regards

Mark

2,247 Views
chuckh
Contributor III

Okay.  I had added the disabling of interrupts and re-enable as you mentioned in your original post.  This seemed to solve my issue as I was able to go in and out of VLPS for 90+ hours with 4 different devices and did not hang up.  However, one of the devices did stop communicating over USB after that.

I have a few follow up questions:

1) When you say "check that there is no reason not to enter sleep mode", what do you mean?

2) For the Disable Interrupts, you mean asm("CPSID i"), right?

3) The conditional part of your code ( if (SYSTEM_CONTROL_REGISTER & SLEEPDEEP....) is that required?  Is there really a case where I can wake from VLPS and I won't have SLEEPDEEP set?

4) Is it necessary to disable the clock monitor?  I'm not set to handle any clock monitor interrupts, so I didn't bother to shut it off.

5) Do compiler optimizations matter here?

5) Anything else that might come to mind? 

I've pasted my code below:

inline void EnterVeryLowPowerStopMode(void)

{

  //Prepare for entry into Very Low Power Stop (VLPS) when Sleep-Now is entered with SLEEPDEEP=1

  SMC_PMCTRL =SMC_PMCTRL_STOPM(0b010);

  //SLEEPDEEP=1

  SCB_SCR = SCB_SCR_SLEEPDEEP_MASK;

  DisableInterrupts;

  //WFI

  asm("wfi");

  //if ((SYSTEM_CONTROL_REGISTER & SLEEPDEEP) && (SMC_PMPROT & SMC_PMPROT_AVLP)) // VLPS sleep mode exit requires the PLL to be reconfigured

  {

  #ifdef HW_4 /*24 MHZ*/

  /* MCG_C5: ??=0,PLLCLKEN=0,PLLSTEN=1,PRDIV=0x0B (24MHz / 12)*/

  MCG_C5 = (uint8_t)0x2BU;

  #else /*8MHZ*/

  /* MCG_C5: ??=0,PLLCLKEN=0,PLLSTEN=1,PRDIV=0x03 (8MHZ / by 4) */

  MCG_C5 = (uint8_t)0x23U;

  #endif

  /* MCG_C6: LOLIE=0,PLLS=1,CME=0,VDIV=0x18 */

  MCG_C6 = (uint8_t)0x58U;

  while ((MCG_S & MCG_S_PLLST_MASK) == 0) {                             // loop until the PLLS clock source becomes valid

  }

  while ((MCG_S & MCG_S_LOCK0_MASK) == 0) {                              // loop until PLL locks

  }

  /* MCG_C1: CLKS=0,FRDIV=3,IREFS=0,IRCLKEN=0,IREFSTEN=0 */

  MCG_C1 = (uint8_t)0x18U;

  while((MCG_S & 0x0CU) != 0x0CU) {    /* Wait until output of the PLL is selected */

  }

  }

  EnableInterrupts;

}

0 Kudos
2,247 Views
mjbcswitzerland
Specialist V

Chuck

1) When you say "check that there is no reason not to enter sleep mode", what do you mean?

This depends on your system design. In the one that I use I have a task that controls sleep mode whenever it can. It has to check that no other tasks are "pending" otherwise it could leave an event queued until the system wakes up again.

2) For the Disable Interrupts, you mean asm("CPSID i"), right?

Yes - or __disable_irq() if you use Keil or __disable_interrupt() if you use IAR (which are intrinsics to do the same).

3) The conditional part of your code ( if (SYSTEM_CONTROL_REGISTER & SLEEPDEEP....) is that required?  Is there really a case where I can wake from VLPS and I won't have SLEEPDEEP set?

I need this in my code because the low power task also checks whether peripherals are still "doing something". For example if there is a UART transferring a block of data via DMA it can't put the system to VLPS mode since this would freeze the UART and DMA in mid-flight. The task then choses to go to the WAIT mode (saves less power but still lower power than RUN) until the peripheral has completed. Then it goes to VLPS - this means that after the wakup the SLEEPDEEP bit could be set (needs clock recovery if VLPS mode) or it could be cleared (the clock recovery can be skipped since we are only waking from WAIT this time).

4) Is it necessary to disable the clock monitor?  I'm not set to handle any clock monitor interrupts, so I didn't bother to shut it off.

I don't disable the clock monitor because I don't use it, but if you do I suppose you would have to otherwise it would immediately give a failure in VLPS mode.

5) Do compiler optimizations matter here?

Shouldn't have any effect - I always use "-os"

6) Anything else that might come to mind? 

It depends on the overall system. When I use USB I use only WAIT mode to save power (this reduces current consumption by about 50% on average compared to staying in RUN mode) but has no impact on performance.When the USB is not in use VLPS mode shouldn't cause problems as long as this mode is no longer used once it is "known" that USB has been connected.

Regards

Mark

0 Kudos
2,247 Views
chuckh
Contributor III

When our USB gets a suspend signal and the USB ISR hits the sleep code, I set a flag.  I also clear this flag at the beginning of the USB ISR with the assumption that if I get another USB interrupt, we no longer need to be suspended.  Then in my main loop when I'm not doing anything, I check for this flag and if it is set I enter VLPS.  My understanding is that the USB has an asynchronous resume interrupt which can wake out of VLPS.  This seems to work 99% of the time.  Our device is bus powered and we are always connected.

Is it possible that the MCU gets stuck in one of the loops waiting for a clock to lock?  That could explain the unresponsiveness.

0 Kudos
2,247 Views
mjbcswitzerland
Specialist V

Chuck

>>Is it possible that the MCU gets stuck in one of the loops waiting for a clock to lock?

One would hope not since this would make the processor unuseable (if it can happen under valid operating conditions).

Toggle some ports or LEDs at certain location and then you will be able to confirm or dismiss such things (eg. toggle a port while waiting for a clock to lock)

Regards

Mark

0 Kudos
2,247 Views
chuckh
Contributor III

Okay.  I'm going to test with the LEDs tonight.  In the meantime, do you know much about the USB Stack?  The stack I have looks at the SLEEP bit in USBxISTAT and based on that enables the asyncronous interrupt.  I set a flag at this point to indicate we can go to VLPS.  I am currently clearing this flag after any USB interrupt.  Is that the proper way to handle the suspend?

0 Kudos
2,247 Views
mjbcswitzerland
Specialist V

Chuck

The USB device will be handling reset, sleep and resume interrupts and I wouls say that the sleep event is what would cause a low power mode to be entered. When the USB cable is pulled you may get a reset and/or suspend event since the bus state is not well defined (as the cable is removed).

Depending on how you are generating the USB clock (some times it is derived from the main PLL but it can have its own clock course or ist own PLL - depending on HW and the processor used) it may be an idea to not allow USB enumeration to start until you are sure that this clock is also Ok and stabilised. This may mean resetting the USB module or removing th D+ pull-up / pull-down until you are sure things are all ready.

Regards

Mark

0 Kudos
2,247 Views
chuckh
Contributor III

To Clarify:

Our MCU is USB bus powered.  When the cable is pulled, we loose power, so that's not what I'm talking about.  The resume interrupt (asyncronous) wakes out of VLPS. 

0 Kudos
2,247 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi, Chuck,

KL25 example code provides [low_power_mode] mode about low power mode transfer and wake up , there also include VLPS mode.

I abstract VLPS mode related code for your reference:

VLPS.png

When wake up from VLPS mode, it need to check core works in VLPR mode or RUN mode and MCG module status.

More detailed info, please download and check from below link:

http://cache.freescale.com/files/32bit/software/KL25_SC.exe


Wish it helps.
best regards
Ma Hui

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos