As part of fault detection / debugging, it's useful to have a panic() function that halts the processor.
It is easy enough to disable interrupts and put the processor in an infinite busy loop (while (1)). However, that burns power, and I am looking for an alternative that keeps the processor in a low power state (until NMI or hard reset). Is there a way to implement such a halting fuction?
On an emulated M4 (Qemu), I tried disabling interrupts and WFI:
panic() { CPSID I; while(1) { WFI } }
*EDIT: my original post had a typo: CPSIE instead of CPSID
But this won't work of course, because WFI has to ignore the interrupt priority to be at all useful, so once an interrupt of any priority happens to occur, the WFI returns. The ISR is not invoked of course, so the interrupt remains pending. WFI is re-entered immediately but also returns immediately (since that interrupt is still pending). So, the panic again becomes a busy loop that goes in and out of WFI on every cycle, so it burns as much power as a while(1) busy loop.
I don't think even clearing all pending interrupts in the NVIC will help, because the signal from the device that outputs the interrupt will remain asserted, so a level-triggered interrupt will just immediately re-pend itself again. Correct? And, clearing the interrupt in the device is not an option, because the whole point is to not run any code after panic(), so certainly do not want to execute the ISR.
I haven't tried on real hardware yet, but I expect the real hardware has the same behavior as this emulator (because this is the expected behavior by spec, right?).
Why not disable all interrupts in the NVIC if you do not want to service these anyway?
Hi there,
CPSIE does not disable interrupts. Do you mean CPSID?
You can try setting FAULTMASK - if using C programming with CMSIS-CORE, you can find APIs like
__disable_fault_irq()
__disable_irq()
(see https://www.keil.com/pack/doc/CMSIS/Core/html/group__Core__Register__gr.html )
This worked: in addition to CPSID, disabled all external interrupts by writing 0xffffffffff to NVIC_ICERx *and* disabled all internal interrupts separately (e.g. SysTick interrupt by disabling SysTick timer, etc.). Then, core stayed in WFI.
Thank you.
Yes, I meant CPSID, sorry for the typo. Thanks!
Just for sake of record, I just found out that besides external interrupts I also had the internal SysTick interrupt, and I also just checked that SysTick interrupt causes return from WFI even with the F flag too.
BTW, this behavior of WFI can be exploited to change CPU speed. Disable all interrupts but one for wake up. CPSID, Reduce clock, WFI, wakeup, restore clock, enable interrupts, CPSIE, handle wakeup interrupt (e.g. update time).