This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Cortex-M4: guaranteed wakeup from WFI?

Note: This was originally posted on 8th August 2013 at http://forums.arm.com

Hi all!

I'm wondering how to ensure that the Cortex-M4 is always woken up on an interrupt from the WFI instruction when executing code like this:

__disable_irq();
// do something ...
__enable_irq();
__WFI();

If an interrupt is triggered between __enable_irq() and __WFI(), the interrupt handler is processed [font="Arial"]immediately and completed before [/font][font="Courier New"]__WFI() is executed, i.e., [/font][font="Arial"][font="Courier New"]__WFI()[/font] will wait for the next interrupt such that the first one is effectively lost. The ATmega* processors have a very simple solution to this problem: enabling interrupts is always delayed by one clock cycle such that the processor can safely enter sleep mode before interrupts can occur and wake up the processor. Thereby it is guaranteed that the processor is always woken up on an interrupt. How is this done on the Cortex-M4? As far as I know, there is no such enable interrupt delay, or is there a totally different mechanism to ensure wakeup on interrupts?

Thanks & kind regards,
Markus


P.S.: A lengthy discussion on a similar topic is here, though in German language.
[/font]
  • Note: This was originally posted on 9th August 2013 at http://forums.arm.com


    Swapping the order of the __WFI() and the __enable_irq() should work.

    Interesting. Is it guaranteed that pending interrupts are immediately executed as soon as interrupts are enabled, i.e., that after the code sequence

    __WFI();
    __enable_irq();
    __disable_irq();

    at least one interrupt handler was completed?

    Thanks & kind regards,
    Markus
  • Note: This was originally posted on 9th August 2013 at http://forums.arm.com


    __WFI();
    __enable_irq();
    __ISB();
    __disable_irq();

    I have just been pointed to the following page:

    http://infocenter.ar...a/BIHBFEIB.html

    The Cortex-M doesn't require the __ISB(), though ARM recommends to use it to ensure compatibility with other implementations of the ARMv7-M architecture.So these four lines of code seem to be the answer to my initial question!

    Thanks & kind regards,
    Markus
  • Note: This was originally posted on 9th August 2013 at http://forums.arm.com


    Could you use WFE instead of WFI?  WFE (Wake for Event) will cause the processor to wake on arrival of an interrupt (if they aren't masked) or an Event.  Where an Event can be generated by executing SEV.

    If you put a SEV instruction into your interrupt handler you'd know that the event register was set, meaning the next WFE instruction would "fail".  As long as you have the WFE in a loop (re-checking the sleep condition on waking) this shouldn't be a problem.


    Good point! The manual says about WFE that if the event register is set:

    "The processor clears the register to 0 and continues executing instructions without entering sleep mode."

    Since WFE is a single instruction, I assume that testing the condition and clearing the flag is an atomic operation, i.e., no code (including interrupt handlers) can be executed after testing the condition and before clearing the flag, right? If this is true, I guess the problem is solved.

    This should probably be used together with SEVONPEND=1 since otherwise a forgotten SEV might lead to a race condition as noted above.

    Thanks & kind regards,
    Markus
  • Note: This was originally posted on 9th August 2013 at http://forums.arm.com


    __WFI();
    __enable_irq();
    __ISB();
    __disable_irq();

    Should be sufficient.

    Looks reasonable. The manual says "Exception entry occurs when there is a pending exception with sufficient priority and either the processor is in Thread mode...", so I guess there is no risk of interrupt "starvation" when enabling interrupts only for a very short period of time (like in the example above).

    Kind regards,
    Markus
  • Note: This was originally posted on 9th August 2013 at http://forums.arm.com


    __disable_irq();
    // do something ...
    __WFI();
    __enable_irq();

    Sounds impossible, but it is the way to do, all other version causes race conditions and are not reliable.

    Ok, got it. I was just a bit unsure whether this is the correct approach since the manual doesn't even mention the underlying problem (the ATmega* manual at least has a code example with a single-line comment referring to reliable sleep and wakeup).

    Does anybody know of a good introductory text on synchronization on microprocessor/-controller level?

    Thanks & kind regards,
    Markus
  • Note: This was originally posted on 9th August 2013 at http://forums.arm.com

    Hi,

    you should do it that way:

    __disable_irq();
    // do something ...
    __WFI();
    __enable_irq();

    Sounds impossible, but it is the way to do, all other version causes race conditions and are not reliable.
    Have a look at:
    http://comments.gman...rm.kernel/25008

    regards

    spachner
  • Note: This was originally posted on 9th August 2013 at http://forums.arm.com


    Interesting. Is it guaranteed that pending interrupts are immediately executed as soon as interrupts are enabled, i.e., that after the code sequence

    __WFI();
    __enable_irq();
    __disable_irq();

    at least one interrupt handler was completed?

    Thanks & kind regards,
    Markus


    __WFI();
    __enable_irq();
    __ISB();
    __disable_irq();

    Should be sufficient.
    s.
  • Note: This was originally posted on 8th August 2013 at http://forums.arm.com

    Swapping the order of the __WFI() and the __enable_irq() should work.

    hth
    s.
  • Note: This was originally posted on 9th August 2013 at http://forums.arm.com

    I don't tend to use the M class, but...

    Could you use WFE instead of WFI?  WFE (Wake for Event) will cause the processor to wake on arrival of an interrupt (if they aren't masked) or an Event.  Where an Event can be generated by executing SEV.

    If you put a SEV instruction into your interrupt handler you'd know that the event register was set, meaning the next WFE instruction would "fail".  As long as you have the WFE in a loop (re-checking the sleep condition on waking) this shouldn't be a problem.