cancel
Showing results for 
Search instead for 
Did you mean: 

WFI hardfault on wakeup

cmurp77
Associate III
Posted on September 29, 2014 at 02:28

Hello everyone:

Here is my problem.

I am using the STM32L152. When I execute the WFI instruction, the processor goes to sleep just fine. However, when it wakes up from an external interrupt line, the processor immediately hardfaults. I have breakpoints set just before and after the WFI instruction. When the processor wakes, I should hit the second breakpoint, but instead it just hard faults. 

Here is some more information. All the registers (to my knowledge) are set correctly. I have looked at all the information I can find on the forum and have tried pretty much every suggestion. The flash is enabled (FLITF), the LPSDSR bit is cleared in the power register, etc.. All clocks are in their correct state. This is a textbook case of sleep mode: no disabling of flash, no low power sleep, just plain sleep mode. The manual says all that is needed is a WFI instruction. 

Now.. if I set the DEEPSLEEP bit in the cortex m3 SCR, the processor does 'not' hardfault and instead resumes execution where it is supposed to. All other settings being the same. 

Lastly, I have looked at the hard fault stacking of registers. It seems to be stacking things alright with an odd exception of the xPSR register. When I enter the hard fault, the stacked value for the xPSR register has the Thumb bit cleared. Inspecting the stack and state of the CPU (xPSR, NVIC, registers) at the instruction just before the WFI instruction reveals a proper CPU state as well as the Thumb bit set. I am also running an RTOS. I have tried to run the WFI code inside a task as well as outside a task. The stack being used (be it the process stack or the main stack) does not matter. Either stack hardfaults.

Any help is greatly appreciated. I am hoping I am doing something stupid with just a bit being misplaced somewhere.
18 REPLIES 18
frankmeyer9
Associate II
Posted on September 30, 2014 at 16:18

Have you tried to examine the CFSR contents after a hardfault ?

You can try Josephs Yiu's famous hardfault handler code to dissect your issue.

cmurp77
Associate III
Posted on September 30, 2014 at 16:29

As stated in a previous reply, the CFSR register indicates IACCVIOL and tells me to look at the stacked PC and registers. Nothing out of the ordinary except the Thumb bit in the xPSR being cleared. 

The one thing I seem to have not made clear is that there is a 99% chance this has nothing to do with code executing and an instruction / access causing a hard fault from the code. If this were the case, I would be able to trace it in a heartbeat. How do I know this? The only difference between me hard faulting and not is the DEEPSLEEP SCR bit. I have breakpoints set in every place that code 'can' possibly resume after sleeping. None of those breakpoints are hit. The core just jumps to the hard fault handler immediately.

cmurp77
Associate III
Posted on September 30, 2014 at 17:11

Based on the previous response, I have saved the context of the hard fault based on Joseph's handler. 

Here is the information:

r0: 0

r1: 0x40010404

r2: 0xE000E280

r3: 0x00800000

r12: 0

lr: 0xFFFFFFF9

pc: 0xF86AF000

xPSR : 0x60000027

Looks like r1 is accessing the EXTI module as that was my last function call.xPSR is messed up (I think), because the thumb bit (bit 24) is cleared. It is my understanding, that bit has to always be set otherwise it hard faults. The PC is pointing to garbage as I have no clue what that value means. The LR seems to be indicating that it entered an interrupt handler before the hardfault (assuming the 0xFF...9) means entering an interrupt. However the ICSR tells me nothing was entered except for the hard fault handler. CFSR indicates IACVIOL

On another execution of the handler, everything is the same except the PC is 0xB580BD00 and the xPSR is 0x61000027. The thumb bit is set here. All I did is reset the chip and pressed the button again.

cmurp77
Associate III
Posted on September 30, 2014 at 17:30

Here is something interesting that I found. I examined the stack pointer that is given as the parameter into the hard fault. I can clearly see all the registers that I previously posted in the stack. HOWEVER, what I can also see is another stack frame. That stack frame has the registers that I posted EXCEPT they are valid! The xPSR register has the thumb bit set, and and the lr and pc registers point exactly where they should - right after the WFI instruction. 

Why would the chip stack for an interrupt twice like this? The only interrupt I have enabled is the EXTI and I have already confirmed, that interrupt entry is never touched. I could understand if possibly the chip restacked if an interrupt was interrupted; however, the ICSR tell me no interrupt has been interrupted AND my EXTI handler was never entered. It's like the chip tried to start stacking for the EXTI interrupt and then after stacking it decided something was wrong and hard faulted.

stm322399
Senior
Posted on September 30, 2014 at 19:56

I tried to reproduce on STL32L DISCOVERY.

It did work with and without DEEPSLEEP.

Anyway, I have some difference with your test case:

* i am using HSI instead of HSE (because I havo no XTAL on this board)

* I only configure PA0 is rising edge interrupt source through EXTI

* I am using UART1 as debug stuff (putc here, putc there ...), one can also use GPIO and watch on scope.

I suspect the debugger to be too intrusive for WFI stuffs, I recommend to avoid it. I am afraid of what may happen when a breakpoint is hit and debug subsystem not completely wake up or whatever else. That's why I used another mean to test my runtime flow. Uart has the advantage to double check that clocks are running at the good speed when character are successfully output.

cmurp77
Associate III
Posted on September 30, 2014 at 20:28

SO I think we have a resolution. 

Just for the heck of it, I decided to change to the MSI clock right before I executed the WFI for sleep. To my amazement, no more hard faults. Then when I woke up, I switched back to the HSE. I looked at the datasheet and saw no mention of a requirement to slow the clock down OR to switch clocks before the WFI instruction. 

Here is where it gets interesting. Since the datasheet and errata tells me that I don't have to switch the clock before and after waking, I switched the code back to the HSE with no clock switch before the sleep. I then combined a few things. I tried disabling interrupts right before the WFI and enabling them after waking (after the WFI). Still hard faulted. BUT THEN! I inserted a NOP right between the WFI and the enabling of interrupts. Mind = blown. No more hard faults. 

Assuming this fixes the problem, my initial testing says it does, what does this tell me? ST's errata on the L152 about the WFE instruction requiring the NOP after it is not complete. It should include the WFI instruction as well IF you are running at 32MHz before and after sleep I guess. My hunch is that you cannot have interrupts enabled at 32MHz, go to sleep then wake up via an interrupt (EXTI in this case) and immediately enter the interrupt handler. It looks like the core requires a few clock cycles (NOP) between waking up and re enabling of interrupts to work correctly.

markoknez
Associate
Posted on September 30, 2014 at 23:15

I just had similar problem so here's my report. Everything worked ok if DBGMCU_Config was not called (to setup WFI for debugging). If this is called I would get HardFaults (well if not then I could not connect debugger but by blinking leds I would think that everything is ok). My configuration was simple delay function on STM32L100RC.

function delay(ms){

configure_timer(ms);

start_timer

WFI

stop_timer

}

HardFault would happen just after the WFI instruction. I tried to add NOP commands and wrap WFI into function call (by errata for WFE instruction) but without any success, but when I slowed down the clock from 32MHz to 16MHz it did the trick. I guess this should be some kind of chip problem, but I could not investigate further in depth since I'm newcomer to ARMs. Thank you for your post, would be stuck here probably forever without it.

cmurp77
Associate III
Posted on October 01, 2014 at 03:48

No problem. I am running at 32MHz, so I can't slow the clock down. I think it has something to do with the clock speed, WFI instruction and interrupt behavior right when the chip wakes up. With the MSI, everything works fine, but running at 32MHz, it blows up. It seems you cannot enable interrupts until you let a few clocks go by with the addition of the NOP instruction after the WFI. This is why I disable interrupts right before the WFI, do the NOP, then re enable after the WFI. I was afraid that having interrupts disabled while sleeping would mean that the core would not wake up when I got an interrupt. I found a post on a FreeRTOS forum that said all you need is the interrupt pending, it does not matter if it is actually enabled in the NVIC (wont be executed, just pended). Furthermore, since the cpsid/e instructions enable or disable an AND gate somewhere in the NVIC, you don't need the AND gate enabled and can disable interrupts before WFI; the core will wake up if an interrupt is pended in a peripheral.

nijzendoorn
Associate
Posted on January 13, 2016 at 09:46

I had a similar problem, which was caused by the clock to the PWR domain not being enabled. When I set RCC->APB1ENR |= RCC_APB1ENR_PWR; the standby and resume work like expected.