I was trying to track down high, approx. 6mA, processor current consumption and I have found high current consumption if the FPU executes a divide by 0. If I comment out the line the current consumption goes down to tens of micro amps. If the line is executed then the current is high since the processor is constantly coming out of sleep.
How does the the SDK deal with a divide by 0 with the FPU? I thought this was a hard fault?
By default an FPU divide by zero doesn't do anything, you can set flags in the System Control Block to make it execute a Usage fault (which escalates to a hardfault if you don't have a handler); in this way it's much like the unaligned access on the M3/M4, it only faults if you ask it to.
I see nothing in the startup file which enables those flags, so it most likely just sets the divide by zero flag in the FPU (which is sticky) and returns zero.
EDIT 2016-03-31: To be more inline with the release notes of the SDK the example code has been changed slightly. The two solutions are also presented equally, although it seems that only the interrupt solution works with FreeRTOS.
EDIT 2016-03-11: Added explanation of why sd_app_evt_wait() does not allow the chip to sleep if pending FPU interrupt is not cleared. Solution with FPU interrupt implementation added as best solution. Previous solution kept as reference.
The FPU will generate an exception when dividing by 0 due to overflow/underflow. This exception will trigger the FPU interrupt, see here. If the interrupt is not cleared/handled the CPU will not be able to go to sleep.
The CPU will not be able to go to sleep if you are using sd_app_evt_wait(). It will return if there are any pending interrupts, even if the interrupts are not enabled through NVIC_EnableIRQ(..). You could use __WFE() instead, but then the application will wake up a lot more from events that are only handled by the SoftDevice. With SoftDevice S132 v2.0.0 sd_app_evt_wait() will also turn off MWU before calling WFE(), so using __WFE() from the application instead will lead to an increase in sleep power consumption of about 800uA.
One way to fix this is to implement the FPU_IRQHandler. First make sure to have the latest Device Family Pack (8.5.0 at the time) such that the IRQ is included in the vector table. Set priority and enable IRQ in main:
Implement the FPU_IRQHandler and clear the FPSCR. The FPSCR state is pushed on the stack when we enter the interrupt and popped when we exit the the interrupt. Therefore we cannot use __set_FPSCR() since the value will be overwritten when we exit the interrupt. We have to change the stack value instead:
#define FPU_EXCEPTION_MASK 0x0000009F
uint32_t *fpscr = (uint32_t *)(FPU->FPCAR+0x40);
*fpscr = *fpscr & ~(FPU_EXCEPTION_MASK);
The other solution is to clear the interrupt every time the CPU wakes up from sleep. For example change the power_manage() function to:
#define FPU_EXCEPTION_MASK 0x0000009F
static void power_manage(void)
__set_FPSCR(__get_FPSCR() & ~(FPU_EXCEPTION_MASK));
uint32_t err_code = sd_app_evt_wait();
Hmm that's confusing. If the interrupt is disabled then it shouldn't wake the processor from WFI, nor WFE, unless SEVONPEND is also set in which case even if disabled it should trigger WFE to wake, but only once, not continuously. Don't see why it continues to wake the processor.
So is the SD not entering the sleep state when it "sees" the event condition? This seems like another posting that RK had commented on, where the source of the sleep-exit-trigger was not apparent
or I could just be wrong in my understanding of how disabled interrupts wake up the CPU. I thought they basically didn't, and if they did it was only once, only in WFE and only in certain circumstances. But I've been wrong before and usually end up learning something.