Skip to content

stm32: fix low-power regressions after executor/low_power split#5713

Open
fe1es wants to merge 1 commit intoembassy-rs:mainfrom
fe1es:stm32-low-power
Open

stm32: fix low-power regressions after executor/low_power split#5713
fe1es wants to merge 1 commit intoembassy-rs:mainfrom
fe1es:stm32-low-power

Conversation

@fe1es
Copy link
Copy Markdown
Contributor

@fe1es fe1es commented Mar 24, 2026

This fixes STM32 low-power regressions introduced by #5671.

Before this patch, low-power sleep/wake could HardFault or fail to awake from deep sleep.

This restores the previous STM32 low-power behavior by:

  • removing the outer executor critical_section from the STM32 sleep path, which fixes the HardFault around low-power sleep/wake
  • restoring RTC interrupt-side on_wakeup_irq_or_event(), which fixes failure to awake from deep sleep
  • returning early when pause_time() or platform::enter_stop() fails, which avoids continuing into an invalid low-power path after setup failure

Tested on:

  • stm32u073cc
  • time-driver-any (TIM15)
  • executor-thread

@xoviat xoviat enabled auto-merge March 24, 2026 14:30
@xoviat xoviat disabled auto-merge March 24, 2026 14:30
@Dirbaio
Copy link
Copy Markdown
Member

Dirbaio commented Mar 24, 2026

The critical section is needed to prevent interrupt handlers from running with the time driver stopped:

  • User registers a custom irq handler
  • Main thread sleeps
  • User irq fires
  • User irq handler runs.
  • User irq handler uses Instant::now(), which breaks.

With the critical section:

  • User registers a custom irq handler
  • Main thread enters critical section
  • Main thread sleeps
  • User irq fires which makes the core exit WFI. Handler doesn't run yet because of the CS
  • Main thread calls on_wakeup_irq_or_event
  • Main thread exits critical section
  • User irq handler runs.
  • User irq handler uses Instant::now(), which now works because the time driver is properly restarted.

Running on_wakeup_irq_or_event only on some irqs like the RTC IRQ doesn't help, because it's not guaranteed wakeup is due to the RTC IRQ, and even if it is other irqs with the same/higher priority can run before it.

We should try to figure out why the hardfaults are happening and fix those. I don't think "use a CS to sleep" is inherently wrong, the cause must be something else.

@fe1es
Copy link
Copy Markdown
Contributor Author

fe1es commented Mar 25, 2026

I agree that the critical section is needed as on_wakeup_irq_or_event isn't called from each on_interrupt anymore.

I found that the HardFault seems to depend on debug-in-sleep/stop state rather than the critical section itself:

  • with config.enable_debug_during_sleep = false, it works even with the outer critical section
  • with config.enable_debug_during_sleep = true, it HardFaults in the low-power sleep/wake path
  • even a firmware that HardFaults starts working again after a physical power cycle

This looks similar to the STM32 WFI / debug-sleep issue described here: https://cliffle.com/blog/stm32-wfi-bug/. As suggested in that post, I tried inserting isb right after wfi, but that didn't fix the HardFault though.

@xoviat xoviat added the trusted label Mar 30, 2026
@xoviat
Copy link
Copy Markdown
Contributor

xoviat commented Mar 30, 2026

bender run

@xoviat xoviat removed the trusted label Mar 30, 2026
@Dirbaio
Copy link
Copy Markdown
Member

Dirbaio commented Mar 31, 2026

there's registers that can be checked that should give info on why the hardfault is happening. Maybe that gives clues https://interrupt.memfault.com/blog/cortex-m-hardfault-debug

This looks similar to the STM32 WFI / debug-sleep issue described here: https://cliffle.com/blog/stm32-wfi-bug/.

I don't think it's this, that only happens in super old stm32's

@fe1es
Copy link
Copy Markdown
Contributor Author

fe1es commented Apr 1, 2026

I followed the Memfault HardFault-debug approach by capturing the stacked fault frame and disassembling around the captured pc.

Setup:

  • stm32u073cc
  • time-driver-any (TIM15)
  • embassy rev: cf6a6cd
  • sleep entry: embassy_time::Timer::after(..)
  • enable_debug_during_sleep = true
exc_return = 0xfffffff9
pc         = 0x080004a2
lr         = 0x080004a3
xpsr       = 0x01000000
ICSR       = 0x00412003   // HardFault active, RTC_TAMP pending
SCR        = 0x00000004   // SLEEPDEEP still set
0x0800049a: bl __dsb
0x0800049e: bl __wfi
0x080004a2: bl __isb   <- faulting PC
0x080004a6: bl __dsb

In a follow-up build, I inserted a nop immediately after WFI:

exc_return = 0xfffffff9
pc         = 0x080004a2
lr         = 0x080004a3
xpsr       = 0x01000000
ICSR       = 0x00412003   // HardFault active, RTC_TAMP pending
SCR        = 0x00000004   // SLEEPDEEP still set
0x0800049a: bl __dsb
0x0800049e: bl __wfi
0x080004a2: bl __nop   <- faulting PC
0x080004a6: bl __isb
0x080004aa: bl __dsb

The fault does not seem to be specific to the original post-WFI instruction. Replacing it with nop moved the fault to that nop, which suggests the problem is tied to the immediate post-WFI boundary.

I also tried an EXTI wake test, and that path worked without HardFault.

I found a similar STM32U5 errata ES0595 Rev 2, section 2.2.6 (“HardFault on wakeup from Stop mode may occur in debug mode”), but I’m not sure it's the same root cause on U0.

Related links:

It looks like the issues other than the HardFault were fixed by #5753. Since I don’t have a safe fix to propose for the HardFault itself, I’d like to close this PR for now.

@xoviat
Copy link
Copy Markdown
Contributor

xoviat commented Apr 1, 2026

It looks like the issues other than the HardFault were fixed by #5753. Since I don’t have a safe fix to propose for the HardFault itself, I’d like to close this PR for now.

Is this your analysis or are you simply repeating what I said? The stm32wb behavior is still different to what it was before, but I haven't had time to fully examine this.

@xoviat
Copy link
Copy Markdown
Contributor

xoviat commented Apr 1, 2026

The other thing you could try is to recover from the hardfault and continue program execution at 0x080004a6. If this approach works, we could document this solution as a workaround to this issue.

@fe1es
Copy link
Copy Markdown
Contributor Author

fe1es commented Apr 2, 2026

Is this your analysis or are you simply repeating what I said? The stm32wb behavior is still different to what it was before, but I haven't had time to fully examine this.

I was referring to the other two issues I tried to fix with this PR. I tested with commit cf6a6cd on stm32u073cc and confirmed that it worked as expected. I’m not sure whether the behavior now exactly matches the previous one, but so far I haven’t encountered any issues other than the HardFault.

The other thing you could try is to recover from the hardfault and continue program execution at 0x080004a6. If this approach works, we could document this solution as a workaround to this issue.

I could try it as an experiment, but I don’t think “recover from HardFault and continue” sounds like a general workaround. Instead, I’d suggest using something like defmt_serial to debug STOP mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants