Skip to content

functioning samd deep sleep ('alarm.time' & 'alarm.pin') #5425

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
Oct 25, 2021

Conversation

maholli
Copy link

@maholli maholli commented Oct 4, 2021

This builds upon #5048, to achieve a preliminary-but-functional alarm module for the SAMD port.1

This partially addresses #4837 via the following features:

alarm.time

  • true deep sleep. ✅ Functional BACKUP sleepmode (lowest power consumption).
    • Feather M4 Express consumes ~0.6mA during deep sleep while powered via BAT=4V
  • fake deep sleep. 🚧 Functional but with bugs.
    • Achieved with RTC->COMP[1] and a callback in port.c's RTC_Handler
    • The Feather M4 express can loop through main.py -> fake deep sleep -> soft reset continuously without issue. HOWEVER, the same build & code on a PyCubed board always crashes into HardFault_Handler on the 4th iteration. Always.
      • I've be en unsuccessful at determining why, even with GDB.
      • The crash into HardFault_Handler doesn't occur when built with DEBUG=1
      • @hierophect took a quick look through the code and nothing jumped out at him. @tannewt do you have any insight?
        • 👉 could use help here

alarm.pin

  • true deep sleep. ✅ Functional using the RTC's TAMPER function to wake us from BACKUP sleepmode.
    • Same power consumption on Feather M4 Express as TimeAlarm.
    • Downside is only a handful of pins can be used for TAMPER.2
      • I only have PA02 configured atm.
        • 👉 I could use help with how to map the desired pin->number to the correct tamper input. (i.e. user requests PB00 so that correlates to TAMPCTRL.bit.IN2ACT)
  • fake deep sleep. 🚧 Functional but with bugs.
    • Achieved with the same TAMPER interrupt as true deep sleep, but with a callback in port.c's RTC_Handler to catch fake sleep
    • Board will POR after about 4 wake/fake-sleep cycles of main.py. Similar behavior to TimeAlarm, but doesn't crash into HardFault_Handler.
  • light sleep. ✅ Functional using EIC to wake from sleepmode STANDBY
    • I got this working but without any dynamic clock stuff, so it's only achieving the same power consumption as time.sleep(). So we may want to disable it for the time being to not confuse folks.

alarm.wake_alarm

  • ✅ Functional
    • Can discern between pin and time alarms when waking from deep sleep. Achieved by caching TAMPID registers during rtc_init before RTC reset.

alarm.exit_and_deep_sleep_until_alarms

  • ✅ Functional
    • Both time and pin alarms can be set when deep sleeping. There is no added power consumption when setting both these wake up options.


original post

alarm.time

  • true deep sleep. ✅ Functional BACKUP sleepmode (lowest power consumption).
    • Feather M4 Express consumes ~0.6mA during deep sleep while powered via BAT=4V
  • fake deep sleep. 🚧 Functional but not complete.
    • Achieved via a port_interrupt_after_ticks call during TimeAlarm setup and checks to port_idle_until_interrupt during "pretending to deep sleep".
      • Not sure if this creates opportunity for false wake-up since it relies on COMP0?
      • It's done this way because (as far as I can tell) CMP1 will not fire after initializing the RTC and using CMP0. Thus, I stuck with CMP0 for fake sleep. It's not an issue for deep sleep since I can SWRST the RTC for that.
    • The Feather M4 express can loop through main.py -> fake deep sleep -> soft reset continuously without issue. HOWEVER, the same build & code on a PyCubed board always crashes into HardFault_Handler on the 4th iteration. Always.
      • I've be en unsuccessful at determining why, even with GDB. I am able to get this backtrace:
        #0  0x00044d06 in reset_into_safe_mode ()
        #1  0x000053c4 in HardFault_Handler ()
        #2  <signal handler called>
        #3  0x2001d720 in ?? ()
        #4  0x00022830 in gc_collect_end ()
        #5  0x200015e0 in _mscd_itf ()
        Backtrace stopped: previous frame identical to this frame (corrupt stack?)
      • The crash into HardFault_Handler doesn't occur when built with DEBUG=1
      • @hierophect took a quick look through the code and nothing jumped out at him. @tannewt do you have any insight?
        • 👉 could use help here

alarm.pin

  • true deep sleep. ✅ Functional using the RTC's TAMPER function to wake us from BACKUP sleepmode.
    • Same power consumption on Feather M4 Express as TimeAlarm.
    • Downside is only a handful of pins can be used for TAMPER.2
      • I only have PA02 configured atm.
        • I could use help with how to map the desired pin->number to the correct tamper input. (i.e. user requests PB00 so that correlates to TAMPCTRL.bit.IN2ACT)
  • fake deep sleep. ❌ not functional
    • Couldn't decide on the best way to inform and configure the tamper pins for fake deep sleep.
    • 👉 could use help here
  • light sleep. ✅ Functional using EIC to wake from sleepmode STANDBY
    • I got this working but without any dynamic clock stuff, so it's only achieving the same power consumption as time.sleep(). So we may want to disable it for the time being to not confuse folks.

alarm.wake_alarm

  • half working. It can tell when waking from BACKUP but I'm unable to discern between a TimeAlarm and PinAlarm cause.
    • This is because the RTC->MODE0.INTENSET register is getting cleared during reset, so I'm unable to rule out if TAMP interrupt woke us.
    • 👉 could use help here


Footnotes

  1. @hierophect I ended up just cherry-picking https://github.com/hierophect/circuitpython/commit/3f92ca82f09901d0122141be3dc250f3c8087659 in order to incorporate those commits. Let me know if this is sufficient.

  2. Only the following pins can be configured for tamper input IN0:PB00; IN1:PB02; IN2:PA02; IN3:PC00; IN4:PC01 There's also OUT:PB01 but haven't checked if that can generate an interrupt to wake. 2

@maholli maholli marked this pull request as ready for review October 4, 2021 22:44
@tannewt tannewt self-requested a review October 5, 2021 21:31
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

I think you'll want to dig into the hard fault status register (HFSR) which could give you a better clue to the fault. This is a good reference for further hard fault debugging: https://interrupt.memfault.com/blog/cortex-m-fault-debug

Don't delete: ports/espressif/certificates/nina-fw

@maholli
Copy link
Author

maholli commented Oct 5, 2021

@tannewt thank you for that link! Very helpful. I'll dive into this next.

Don't delete: ports/espressif/certificates/nina-fw

This occurred when I cherry-picked Lucian's commit. How do I restore it (submodule directory)?

@tannewt
Copy link
Member

tannewt commented Oct 7, 2021

Don't delete: ports/espressif/certificates/nina-fw

This occurred when I cherry-picked Lucian's commit. How do I restore it (submodule directory)?

I think you'll want to rebase and fix the commit that originally removes it.

1 similar comment
@tannewt
Copy link
Member

tannewt commented Oct 7, 2021

Don't delete: ports/espressif/certificates/nina-fw

This occurred when I cherry-picked Lucian's commit. How do I restore it (submodule directory)?

I think you'll want to rebase and fix the commit that originally removes it.

@maholli
Copy link
Author

maholli commented Oct 9, 2021

Update

  1. submodule ports/espressif/certificates/nina-fw restored to the correct commit
  2. Fake sleep overhauled. Now handles TimeAlarm and PinAlarm via port.c's RTC_Handler

Overall it still has some quirks but it's certainly functioning.

@maholli
Copy link
Author

maholli commented Oct 10, 2021

More updates

  1. alarm.wake_alarm can now tell between waking up from pin or time
  2. alarm.exit_and_deep_sleep_until_alarms can now wake from concurrent pin and/or time alarms 🎉

Question:

I make individual time/pin callbacks from port.c as shown here. This requires a couple extra imports in port.c:

#include "shared-bindings/alarm/time/TimeAlarm.h"
#include "shared-bindings/alarm/pin/PinAlarm.h"

@tannewt Would it be more memory efficient to call a new function from /alarm/.__init__.c that checks and calls the appropriate callback? Not sure the impact given any compiler magic.

@maholli maholli requested a review from tannewt October 10, 2021 19:33
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think calling the callbacks directly is fine because port.c is still relatively private piece of code. I don't think there is much difference from the compiler's perspective if you added a wrapper function.

Please get the CI passing before I review again.

@maholli maholli requested a review from tannewt October 12, 2021 00:51
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more question and then it should be good. Thanks for your persistence.

@maholli maholli requested a review from tannewt October 25, 2021 20:43
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Let's get this in.

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

Successfully merging this pull request may close these issues.

3 participants