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
cmurp77
Associate III
Posted on September 29, 2014 at 21:26

Nobody has had this issue?

Posted on September 29, 2014 at 21:43

Nobody has had this issue?

Evidently not... You understand how forums work right?

What does it do if you don't have it on the debugger? Try instrumenting it rather than using breakpoints.

If it's hardware related I'd suspect the flash or prefetch mechanism, but no errata seemed to immediately suggest that was a known issue.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
cmurp77
Associate III
Posted on September 29, 2014 at 21:59

I do understand how forums work. It's odd that I get a response a mere few minutes after I push the thread to the top after not getting a peep for a day isn't it? :)

I am not the type of person to post on forums lightly, and as such, I have already done as much research as is available about this particular problem online via google and other sources. I am not turning to a forum, because somebody here might have the answer. 

It has already been instrumented outside the debugger and the save behavior ensues. The chip still hard faults. I essentially have a few LEDs turn on and off in various places in the execution, and I can tell that I get a reset when I come out of sleep, indicating the hard fault. 

Two other pieces of information I forgot to mention in the original post:

1. I have the DSB and ISB instructions before the WFI and also the NOP after the WFI in case; however, the code never executes the NOP as it hardfaults immediately after coming back from sleep. 

2. One of the hard fault status registers indicates an IACCVIOL (invalid instruction access). To that end, I have inspected the stack to reveal nothing out of the ordinary except that I see the xPSR register value that is stacked having the Thumb bit 'cleared'. This of course, would cause an invalid execution. 

Posted on September 29, 2014 at 22:16

It's odd that I get a response a mere few minutes after I push the thread to the top after not getting a peep for a day isn't it? :)

 

Not really, I read it when it first posted, I don't have any specific experience of the L152 behaving like this, so didn't respond then, and probably not any more helpful now.

PRFTEN, FLASH_ACR
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
cmurp77
Associate III
Posted on September 30, 2014 at 00:50

Are you asking about the configuration of the prefetch? I have the ACR register set to enable prefetch and enable 64 bit access. I run at 32MHz and so have 1 wait state. I remember awhile back when I did not configure the mechanism correctly and it would hardfault due to the fact of an incorrect configuration. 

The processor runs fine and I have had no trouble with it up until I try going to sleep. The odd thing is that deep sleep seems to work perfectly. I have tried to do some other things like disabling interrupts before the WFI instruction and then re enabling them when I wake up. This allows the processor to run and execute a few interrupts but then after those times waking back up, same problem - hard fault. 

Additionally, if instead of using an interrupt to wake back up I press the stop button on the debugger, the processor wakes up and begins execution in the correct spot without hard faulting. During debug, I have the DBG_STOP / SLEEP / STANDBY bits set in the DBGMCU module. 

One other thing. I normally only keep clocks to modules on when I am modifying that module's registers. Therefore, I would turn on the clock to the PWR module, modify the registers and then turn the clock off again. I have done this with pretty much all my modules without issues (GPIO / SYSCFG, etc.) When I go into sleep mode, ideally there is no clock to the PWR module; however, I have set the proper configuration bits. I thought that maybe you need to keep some clocks running to various modules when going to sleep, so I have also tried keep clocks on to those modules while going to sleep (mainly PWR). It does not make a difference. 

My understanding is that you only need a clock to the module if it is running and or you need to modify the registers to the module. Otherwise, you can turn the clock off as the register data is still available to the internal circuits. You just can't modify the registers without the clock (obviously - D flip flops).

stm322399
Senior
Posted on September 30, 2014 at 08:38

Could it be that you have a undefined interrupt handler that generates this hard fault ?

By the way, I will strongly advise you to trim down your case until to get the smallest code that reproduce this problem and post on the forum.

stm322399
Senior
Posted on September 30, 2014 at 08:47

Additionally, it could be useful to post here the state of NVIC at the moment you take the hard fault, just to check any pending interrupt/event.

Regarding the DEEPSLEEP configuration, the RM of L152 says that several conditions (on pending NIVC, RTC etc ....) must be met, otherwise the CPU won't enter STOP mode and continues to execute. It is possible that this explains why you are not hardfaulting when DEEPSLEEP is set.

cmurp77
Associate III
Posted on September 30, 2014 at 13:53

The chip does in fact go to sleep when I execute a WFI with or without the deepsleep bit set in the SCR. This is because the debugger shows no more code executing. When I exit from deep sleep the next line of code after the WFI instruction is executed. Like I said, I have no problem going to sleep, it is when I wake up from sleep (not deep sleep) that I get the hardfault. 

I have also checked the state of the NVIC on hardfault to see what the deal was. When the chip hardfaults, the NVIC ICSR indicates exception 3 active (hard fault). I then wanted to see if there was an interrupt being interrupted via the ISR pending / ISR interrupted bits. There is no other interrupt pending and or being interrupted. 

I am waking the chip up via EXTI interrupt. All exception handlers are properly installed in the vector table. I have absolutely no issue with the EXTI interrupt outside of sleep mode and the handler gets executed as expected. Since the EXTI interrupt is waking the device, I have a breakpoint set on the EXTI handler. When I come out of deep sleep the handler gets executed as expected; when I come out of normal sleep -> hardfault. 

I would be happy to attempt to write the smallest possible code to show on the forum; however, I do not think that would be beneficial: 1. The code exists in a full blown RTOS code base with many layers of abstraction and 2. There is no special code that I am performing to make this problem happen. It literally IS as simple as executing a WFI instruction. The only thing that I believe would be beneficial is sharing the state of the processor registers right before the WFI is executed and when the chip hard faults. 

I am going to write the simplest piece of code outside of my giant code base in a simple main function which configures the clock and goes right to sleep. I will post back when I have this code that reproduces the issue. 

cmurp77
Associate III
Posted on September 30, 2014 at 14:25

void main(void)
{
// Initialize clock
HAL_Clock_Init();
HAL_Clock_DeviceWake();
// Initialize device
HAL_Device_Init();
HAL_Device_SetVTOR(0x08000000);
// Initialize HAL layers
HAL_EXTI_Init();
// Configure EXTI interrupt
HAL_GPIO_Config(BUTTON1_GPIOOPTIONS);
HAL_EXTI_Config(BUTTON1_EXTIOPTIONS, BUTTON1_GPIOOPTIONS, Pin_Trigger);
HAL_EXTI_Enable(BUTTON1_PIN);
while(1)
{
HAL_Clock_DeviceSleep();
}
}
static void Pin_Trigger(uint8_t pin, uint8_t pinLevel)
{
__no_operation();
}
void HAL_Clock_Init(void)
{
// Switch to multispeed oscillator
RCC->CR |= RCC_CR_MSION_BIT;
while(~RCC->CR & RCC_CR_MSIRDY_BIT);
RCC->CFGR &= ~(RCC_CFGR_SW_BITS);
while((RCC->CFGR & RCC_CFGR_SWS_BITS) != RCC_CFGR_SW_MSI);
// Enable flash prefetch
FLASH->ACR |= (FLASH_ACR_ACC64_BIT);
while(~FLASH->ACR & FLASH_ACR_ACC64_BIT);
FLASH->ACR |= (FLASH_ACR_PRFTEN_BIT);
// Enable wait state since we are above 16MHz
FLASH->ACR |= (FLASH_ACR_LATENCY_BIT);
// Disable and configure the PLL
RCC->CR &= ~RCC_CR_PLLON_BIT;
while(RCC->CR & RCC_CR_PLLRDY_BIT);
RCC->CFGR = (RCC_CFGR_PLLSRC_BIT | (PLL_MUL << 
RCC_CFGR_PLLMUL_BIT_SHIFT
) | (PLL_DIV << RCC_CFGR_PLLDIV_BIT_SHIFT));
// Switch to PLL
SwitchToPLL();
// Clear peripheral clock count
periphClockCount
= 
0
;
}
static void SwitchToPLL(void)
{
// Enable high speed external oscillator
RCC->CR |= RCC_CR_HSEON_BIT;
while(~RCC->CR & RCC_CR_HSEON_BIT);
// Enable and switch to PLL with high speed external oscillator input
RCC->CR |= RCC_CR_PLLON_BIT;
while(~RCC->CR & RCC_CR_PLLRDY_BIT);
RCC->CFGR |= (RCC_CFGR_SW_PLL);
while((RCC->CFGR & RCC_CFGR_SWS_BITS) != (RCC_CFGR_SWS_PLL << 
RCC_CFGR_SWS_BIT_SHIFT
));
// Turn off multispeed oscillator
RCC->CR &= ~RCC_CR_MSION_BIT;
while(RCC->CR & RCC_CR_MSIRDY_BIT);
}
void HAL_Clock_DeviceWake(void)
{
// Turn on GPIO clocks
HAL_GPIO_Enable();
}
void HAL_Clock_DeviceSleep(void)
{
// Disable GPIO clocks
HAL_GPIO_Disable();
// Determine what kind of sleep mode we can enter
if (periphClockCount == 0)
{
// Enter deep sleep
DeepSleep();
}
else
{
// Enter sleep
Sleep();
}
}
static void DeepSleep(void)
{
// Set deep sleep bit
//SCB->SCR |= SCB_SCR_SLEEPDEEP_BIT;
// Go to deep sleep
__DSB();
__ISB();
__WFI();
// Switch back to HSE / PLL
//SwitchToPLL();
// Clear deep sleep flag
//SCB->SCR &= ~SCB_SCR_SLEEPDEEP_BIT;
}

__weak void HardFaultHandler(void)
{
// Reset device
HAL_Device_Reset();
}
#pragma section = ''CSTACK''
#pragma location = ''.IntVector''
__root void* const intVector[] =
{
__section_end(''CSTACK''),
(void*)ResetHandler,
(void*)NULL, // (void*)NMIHandler,
(void*)HardFaultHandler,
(void*)NULL, // (void*)MemManageHandler,
(void*)NULL, // (void*)BusFaultHandler,
(void*)NULL, // (void*)UsageFaultHandler,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)SVCHandler,
(void*)NULL, // (void*)DebugMonHandler,
(void*)NULL, // (void*)0,
(void*)PendSVHandler,
(void*)SysTickHandler,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)RTCWakeupHandler,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)EXTI0Handler,
(void*)EXTI1Handler,
(void*)EXTI2Handler,
(void*)EXTI3Handler,
(void*)EXTI4Handler,
(void*)DMA1Channel1Handler,
(void*)DMA1Channel2Handler,
(void*)DMA1Channel3Handler,
(void*)DMA1Channel4Handler,
(void*)DMA1Channel5Handler,
(void*)DMA1Channel6Handler,
(void*)DMA1Channel7Handler,
(void*)NULL, // (void*)0,
(void*)USBHPHandler,
(void*)USBLPHandler,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)EXTI9_5Handler,
(void*)NULL, // (void*)0,
(void*)Timer9Handler, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)Timer2Handler, // (void*)Timer2Handler,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)EXTI15_10Handler,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)Timer6Handler,
(void*)NULL,
(void*)NULL, // (void*)0,
(void*)NULL,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0,
(void*)DMA2Channel1Handler,
(void*)DMA2Channel2Handler,
(void*)DMA2Channel3Handler,
(void*)DMA2Channel4Handler,
(void*)DMA2Channel5Handler,
(void*)NULL, // (void*)0,
(void*)NULL, // (void*)0
};

Here is the simplest piece of code with the most important sections in it. I just pasted everything as one line stream of code. Keep in mind many of those blocks are in different files and or headers. I first initialize the device by setting up the clock and calling the wake function. GPIO_Enable / disable functions simply turn on or off all GPIO clocks in the RCC clock enable registers. I wont go over the GPIO / EXTi configuration in detail (however I will if someone deems it absolute necessary); however the code essentially configures one of my GPIO pins as input with pull up and as an EXTI interrupt. If you comment out the code in the while loop, you can sit there for an hour, press the button, and the Pin_Trigger function will get called via EXTI interrupt. There is an EXTI layer that is not shown that essentially is the interrupt handler for all the EXTI interrupts. It essentially processes the EXTI interrupt line, clear the pending bits in the EXTI PR and then calls the callback (Pin_Trigger) with some parameters if a callback was defined for that line. The EXTI interrupt works fine. The piece of code that is important is the sleep function in the while loop. When this code executes, it puts the chip to sleep via the DeepSleep function. Due to some code that I don't want to rewrite for this demonstration, all the DeepSleep function does is execute a WFI after DSB / ISB. The device goes to sleep as I can put a breakpoint in the while loop and see that the while loop is not executing the deep sleep function again. If you press ''stop'' on the debugger, the chip starts executing code right after the WFI in the deep sleep function. If you press the button configured as an EXTI, the device hard faults. If you uncomment the lines in the DeepSleep function where I set / clear the DEEPSLEEP bit, the device executes code properly and my EXTI handler is executed, then the device goes back to sleep. The hardfault handler is shown above. All it does is reset the device when a hard fault happens. I put a breakpoint on the reset function to determine if the hard fault handler gets executed.