Author Topic: Crashing through __WFI  (Read 1833 times)

0 Members and 1 Guest are viewing this topic.

Offline fearlessTopic starter

  • Contributor
  • Posts: 20
  • Country: us
Crashing through __WFI
« on: September 01, 2023, 04:05:16 pm »
Context: I'm working with a SAME54, my intent is to put the processor into BACKUP mode until an RTC match wakes it up.  Once in a blue moon, I observe execution "crashing through" the __WFI instruction that is supposed to put it into the sleep mode.  At the time this function is called, there should not be any pending interrupts.

Code:
Code: [Select]
static void enter_backup_mode(void) {
    /* Configure Backup Sleep, wait for registers to report correct values */
    PM_REGS->PM_SLEEPCFG = PM_SLEEPCFG_SLEEPMODE_BACKUP_Val;
    while ((PM_REGS->PM_INTFLAG & PM_INTFLAG_SLEEPRDY_Msk) == 0U) {
    }
    while (PM_REGS->PM_SLEEPCFG != PM_SLEEPCFG_SLEEPMODE_BACKUP_Val) {
    }
    __WFI();
    // Normally should not arrive here, but it's possible that stray interrupt
    // is causing the WFI to behave as a NOP. 
    NVIC_SystemReset();
}

Hypothesis: If I understand correctly, if there's an interrupt pending at the time the __WFI is called, then the __WFI behaves as a NOP.  So that might be what's happening.

Question: is there a straightforward way to find out if there are any pending interrupts (and possibly clear them) before executing the __WFI?
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 3727
  • Country: us
Re: Crashing through __WFI
« Reply #1 on: September 01, 2023, 05:16:22 pm »
As far as I know __WFI is not guaranteed to be free from spurious wakeup and should always be in a loop.
 

Offline fearlessTopic starter

  • Contributor
  • Posts: 20
  • Country: us
Re: Crashing through __WFI
« Reply #2 on: September 01, 2023, 05:35:31 pm »
Quote
As far as I know __WFI is not guaranteed to be free from spurious wakeup and should always be in a loop.
Can you expand on that?  On the SAME54, waking from BACKUP mode is (always) a reset, so it's not clear how you'd use a loop.

I suppose you could check to see the wakeup cause upon starting the code and just __WFI again if it's not an RTC match interrupt.  But since the bootloader is the first bit of code to be run, and since there are other circumstances that you DO want to continue (e.g. just loaded new code, got a directive from the debugger, etc.), that logic could get highly convoluted.

I'll post an alternate suggestion for critique...
 

Offline fearlessTopic starter

  • Contributor
  • Posts: 20
  • Country: us
Re: Crashing through __WFI
« Reply #3 on: September 01, 2023, 05:38:14 pm »
Here's my current thought.  Just before calling __WFI, execute this bit of code:
Code: [Select]
void prep_interrupts(void) {
    for (IRQn_Type line = 0; line <= PERIPH_MAX_IRQn; line++) {
        if (line != RTC_IRQn) {
            if (NVIC_GetEnableIRQ(line)) {
                NVIC_DisableIRQ(line);        // can put debugger bpt here to see which interrupt is enabled
            }
        }
    }
}
The intent is to make sure that all interrupts (except for RTC) are disabled prior to calling __WFI.  Any known gotcha's with that?
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Crashing through __WFI
« Reply #4 on: September 01, 2023, 06:08:58 pm »
Question: is there a straightforward way to find out if there are any pending interrupts (and possibly clear them) before executing the __WFI?
The nvic pending register should tell you. However... that should always read 0 in normal context.

Interrupts don't have to be enabled in the nvic to cause a wake-up. So you have to turn them off at the source.
Your debugger can also make wfi skip.
 

Offline fearlessTopic starter

  • Contributor
  • Posts: 20
  • Country: us
Re: Crashing through __WFI
« Reply #5 on: September 02, 2023, 04:31:07 am »
The nvic pending register should tell you. However... that should always read 0 in normal context.

Interrupts don't have to be enabled in the nvic to cause a wake-up. So you have to turn them off at the source.
Your debugger can also make wfi skip.
Just to make sure I understand what you're saying: If I execute
Code: [Select]
<configure for BACKUP mode...>;
NVIC_DisableIRQ(SERCOM0_0_IRQn);
__WFI();
asm("nop");
... and SERCOM0 subsequently generates an interrupt, it will execute the NOP following the __WFI?!?  That doesn't make sense to me -- all of the documentation (and all of my experience) says that the SAME54 will wake from BACKUP mode with a reset; not by continuing from the __WFI.

If on the other hand you're saying that a SERCOM0 interrupt will wake the SAME54 from BACKUP mode and arrive at the NOP instruction, well, I'm not sure that's right either.  Only a limited number of sources can wake the processor from BACKUP mode, including RTC, WDT, BOD33, System Reset and a couple others.

On the third hand  ;) it's entirely possible I missed your point.
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 825
  • Country: es
Re: Crashing through __WFI
« Reply #6 on: September 02, 2023, 06:04:26 am »
I’m not familiar with SAMs, but perhaps there is some latency between hitting the WFI instruction and actually entering the BACKUP mode, during which is possible to “exit” the WFI? (the WFI instruction itself is not a direct “hey, core, enter low power mode now!” thing, it’s main purpose is to wait for interrupt and continue, the low power entry is done by some other hw block triggered by the core upon hitting a WFI). Check the RM for BACKUP entry timing.
Or, if you are saying that true exit from BACKUP is system reset (on a hw level), it should be ok to just do while(1) { __WFI(); } - a spurious “WFI exit” would just retry the WFI, but a real BACKUP exit would reset and never get looped here.
 
The following users thanked this post: fearless

Offline bugnate

  • Regular Contributor
  • *
  • Posts: 58
  • Country: us
Re: Crashing through __WFI
« Reply #7 on: September 02, 2023, 06:28:31 am »
ejeffrey told you the the only thing you really need to know here:

As far as I know __WFI is not guaranteed to be free from spurious wakeup and should always be in a loop.

The "in a loop" bit just means that you have to be able to detect that a spurious wakeup happened (eg RTC time was less than expected) and re-try the sleep. There are lots of reasons why spurious wakeups could happen, some you can mitigate (eg disable peripherals) and some you cannot. But you must code assuming they can happen.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8179
  • Country: fi
Re: Crashing through __WFI
« Reply #8 on: September 02, 2023, 06:56:58 am »
Pseudocode:
WFI
check what to do, + do it
WFI again

Expressed as loop:

while(1)
{
   WFI
   check what to do, + do it
}

Expressed differently

AGAIN:
   WFI
   check what to do, + do it
   goto AGAIN;


Not difficult.
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26907
  • Country: nl
    • NCT Developments
Re: Crashing through __WFI
« Reply #9 on: September 02, 2023, 08:11:17 am »
IIRC it is possible not all interrupts are disabled immediately but need a few clock cycles to propagate through the peripherals. Looking at my own code I wrote a long time ago (for NXP LPC1700 series), I see I have 3 __NOP operations before calling __WFI to make sure all interrupts are actually disabled & handled before going to sleep.

Adding some more debugging code to check the wake-up source (as others suggested) is a good plan to see if there aren't any interrupt enabled that shouldn't be. I'm also wondering why the MCU is reset after a wake-up. Why not re-initialise the parts that are disabled and continue?
« Last Edit: September 02, 2023, 08:45:00 am by nctnico »
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline fearlessTopic starter

  • Contributor
  • Posts: 20
  • Country: us
Re: Crashing through __WFI
« Reply #10 on: September 02, 2023, 01:23:38 pm »
I believe I finally understand.  It's important to understand that the SAME54 -- when conditioned to enter BACKUP mode -- will enter BACKUP mode upon executing __WFI provided there's no pending interrupt.  And the ONLY return from BACKUP mode is a system reset.  This is why I thought a loop wouldn't make sense.

But as I understand it, if there is a pending interrupt, the __WFI acts like a NOP and will "crash through" as I initially described it.  So the following is a candidate:
Code: [Select]
<prep SAME54 for backup mode>
while(1) {
    __WFI();     // normally this will enter BACKUP mode and never return (since waking from BACKUP is a system reset)
    asm("nop");  // execution will arrive here ONLY IF the SAME54 did not enter BACKUP mode.
}

I'll give that a shot.  If anyone has specific experience with the SAME54 and its backup mode, feel free to chime in!


 

Offline fearlessTopic starter

  • Contributor
  • Posts: 20
  • Country: us
Re: Crashing through __WFI
« Reply #11 on: September 02, 2023, 01:25:19 pm »
I'm also wondering why the MCU is reset after a wake-up. Why not re-initialise the parts that are disabled and continue?

...because that's how the SAME54 is designed to work in BACKUP mode.  It provides other low-power modes (e.g. STANDBY) that do what you describe.
 

Offline fearlessTopic starter

  • Contributor
  • Posts: 20
  • Country: us
Re: Crashing through __WFI
« Reply #12 on: September 02, 2023, 01:29:56 pm »
it should be ok to just do while(1) { __WFI(); } - a spurious “WFI exit” would just retry the WFI, but a real BACKUP exit would reset and never get looped here.

Yes - I believe you correctly described the solution as well as why it works.  Thanks.
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 3727
  • Country: us
Re: Crashing through __WFI
« Reply #13 on: September 02, 2023, 02:57:45 pm »

But as I understand it, if there is a pending interrupt, the __WFI acts like a NOP and will "crash through" as I initially described it.  So the following is a candidate:
Code: [Select]
<prep SAME54 for backup mode>
while(1) {
    __WFI();     // normally this will enter BACKUP mode and never return (since waking from BACKUP is a system reset)
    asm("nop");  // execution will arrive here ONLY IF the SAME54 did not enter BACKUP mode.
}

Yes that all sounds right with a couple of refinements.  1) there is likely a window between WFI executing and backup mode activating, it's likely that interrupts in this time window can also cause it to wake up.  This doesn't really matter as there is no way to tell whether it happened "right before" or "right after" WFI but it might increase the window of vulnerability more than you expect 2) other CPU conditions (including non use visible state) can also potentially cause WFI to wake / not sleep   debug probe interactions are the most common and maybe the only example on  a processor like this but the point is to not try to prevent every possible interrupt to make WFI "reliable" but to use a loop to reenter it.
« Last Edit: September 02, 2023, 02:59:22 pm by ejeffrey »
 

Offline bson

  • Supporter
  • ****
  • Posts: 2270
  • Country: us
Re: Crashing through __WFI
« Reply #14 on: September 02, 2023, 08:00:51 pm »
If you have interrupts disabled via PRIMASK but there's a pending interrupt, then WFI won't block.  So the most likely cause here is a bug in your program that doesn't reenable interrupts prior to WFI.
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Crashing through __WFI
« Reply #15 on: September 02, 2023, 09:23:50 pm »
You did read this?
Quote
Note: A small latency happens between the store instruction and actual writing of the SLEEPCFG
register due to bridges. Software has to make sure the SLEEPCFG register reads the wanted value
before executing WFI instruction.

What you can try is turning off all peripherals first.

Two things you must consider.
1. The NVIC receives signals from the chip. The NVIC is a core component, not modified by the vendor.
2. The entry to sleep is hooked into the _WFI instruction. So you must first ensure the core design requirements are fulfilled for _WFI to work, before even the backup state is initiated.

Quote
WFI is a hint instruction that suspends execution until one of the following events occurs:
• a non-masked interrupt occurs and is taken
• an interrupt masked by PRIMASK becomes pending
• a Debug Entry request.
« Last Edit: September 02, 2023, 09:38:26 pm by Jeroen3 »
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14490
  • Country: fr
Re: Crashing through __WFI
« Reply #16 on: September 02, 2023, 09:27:56 pm »
Pseudocode:
WFI
check what to do, + do it
WFI again

Expressed as loop:

while(1)
{
   WFI
   check what to do, + do it
}

Expressed differently

AGAIN:
   WFI
   check what to do, + do it
   goto AGAIN;


Not difficult.

Yes that's how it should usually be done.
Then it doesn't matter whether WFI returns immediately or not.
Using it *merely* to wait on some interrupt while being guaranteed to do nothing until said interrupt happens is IMO a misuse of WFI.
The whole benefit is that it usually puts the CPU in a low-power mode. If you don't need low-power, a simple busy loop would do the trick just fine.

Calling WFI in a "busy loop", as shown above, is (again, usually) the right compromise.

Of course, if you have one interrupt source that potentially wakes up the CPU too often (which could counterbalance the benefit of the low power achieved with WFI), and this interrupt doesn't interest you as a wake-up source, you may want to disable it before. It all depends on the use case really, but it's often not a good idea and the simple wait loop is much simpler and much safer.

If the goal is to put the CPU into some sleep mode and have it woken up only from a given wake-up event, many MCU do have other means of achieving this, some waking up only from a given wake-up event, configurable and independent from interrupts, so that both can be handled separately, and you don't need to constantly mess with interrupts enabling and disabling.
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 825
  • Country: es
Re: Crashing through __WFI
« Reply #17 on: September 03, 2023, 12:03:25 pm »
Just for curiosity: try replacing WFI with WFE. If my guess is correct, WFE should also trigger the low power mode entry, but it should ignore occasional pending interrupts (since it is Wait For Event). Such replacement would be wrong for LP modes allowing wake up and continue (WFE has a different exit condition), but could be and easier to use option for "wake up via reset" modes.
« Last Edit: September 03, 2023, 12:06:28 pm by abyrvalg »
 

Offline fearlessTopic starter

  • Contributor
  • Posts: 20
  • Country: us
Re: Crashing through __WFI
« Reply #18 on: September 05, 2023, 07:20:39 pm »
2. The entry to sleep is hooked into the _WFI instruction. So you must first ensure the core design requirements are fulfilled for _WFI to work, before even the backup state is initiated.
All true.  The "prep SAME54 for backup mode" code that I didn't show does just that.  The full implementation is:
Code: [Select]
static void enter_backup_mode(void) {
    /* Configure Backup Sleep */
    PM_REGS->PM_SLEEPCFG = PM_SLEEPCFG_SLEEPMODE_BACKUP_Val;
    /* Wait till the voltage regulator low power mode is ready */
    while ((PM_REGS->PM_INTFLAG & PM_INTFLAG_SLEEPRDY_Msk) == 0U) {
    }
    /* Wait till SLEEPCFG reads the desired value */
    // NOTE: this while() loop is omitted in the library PM_BackupModeEnter()
    // implementation (v3.16.0), though the documentation requires it.
    // If a future version on PM_BackupModeEnter() corrects this, this code
    // should revert to using the library version.
    while (PM_REGS->PM_SLEEPCFG != PM_SLEEPCFG_SLEEPMODE_BACKUP_Val) {
    }
    while(true) {
        // If no interrupt is pending, this will enter BACKUP mode and will only
        // exit BACKUP mode with a reset.  If an interrupt is pending, the __WFI
        // will behave as a no-op and execution will continue...
        __WFI();
        // Arrive here if there was a pending interrupt.  Toggle TP_J103P6 to
        // show that we hit this point and loop until the interrupt is serviced.
        TP_J103P6_OutputEnable();
        TP_J103P6_Toggle();
    }
}
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14490
  • Country: fr
Re: Crashing through __WFI
« Reply #19 on: September 05, 2023, 08:39:36 pm »
Just for curiosity: try replacing WFI with WFE. If my guess is correct, WFE should also trigger the low power mode entry, but it should ignore occasional pending interrupts (since it is Wait For Event). Such replacement would be wrong for LP modes allowing wake up and continue (WFE has a different exit condition), but could be and easier to use option for "wake up via reset" modes.

Yes, WFE is the ARM Cortex way of doing what I suggested (waiting on events rather than interrupts). The wake-up source(s) have to be set up properly beforehand.
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 825
  • Country: es
Re: Crashing through __WFI
« Reply #20 on: September 05, 2023, 10:02:10 pm »
What I’m trying to say: for LP modes where the core state is not maintained (like OP’s mode resuming via reset) a WFE could be a better alternative to WFI since it does not depend on pending interrupts and should go to sleep from a first try.
And it should work with the same wakeup configuration since the wakeup is thru reset and the core would not remember was it WFI or WFE, we don’t need to satisfy these instruction’s wait conditions to wake up.
« Last Edit: September 05, 2023, 10:04:47 pm by abyrvalg »
 

Offline fearlessTopic starter

  • Contributor
  • Posts: 20
  • Country: us
Re: Crashing through __WFI
« Reply #21 on: September 06, 2023, 12:59:54 am »
Though I 've never seen Microchip suggest WFE instead of WFI for entering backup mode, I'm intrigued and will give it a try as soon as I stabilize the current set of changes.

Many thanks for the suggestions!
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Crashing through __WFI
« Reply #22 on: September 06, 2023, 06:09:54 am »
Is it possible that entering WFI or sleep causes some analog glitch in your specific situation that triggers wakeup?
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf