Skip to content

Ports/RP2: Allow CLK_SYS and dependants to be left running during LightSleep() #16183

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

mungewell
Copy link
Contributor

Summary

This is a re-hash of my previous bug/pull request to the Pico SDK:
raspberrypi/pico-sdk#1668

Now that MicroPython has pulled the clock initiation into our own code, we can implement directly.

When calling 'lightsleep()' pass the bit-fields for which clocks you want to remain on. If these are dependants of CLK_SYS then that is also kept on. The bit-fields are also use when re-initing the clocks, which are left on are then left as they are.

Testing

I validate this on a RP Pico (RP2040). It is specific to the Clocks on the RP2040 and R2350.

Attached code will demonstrate using the FIFO and PIO to continuously flash the LED, whilst the CPU(s) are placed into 'lightsleep()'. This has the advantage of being MUCH lower power draw; normally 5V @ 19mA, then after ~10s it starts using 'lighsleep()' and the power draw drops to 5V @ 7mA - all whilst still flashing the LED.

The demo code is an extract of my larger project.

blink_fifo_thread.zip

This is a re-hash of my previous bug/pull request to the Pico SDK:
raspberrypi/pico-sdk#1668

Now that MicroPython has pulled the clock initiation into our own
code, we can implement directly.

When calling 'lightsleep()' pass the bit-fields for which clocks
you want to remain on. If these are dependants of CLK_SYS then that
is also kept on.

The bit-fields are also use when re-initing the clocks, the ones
which are left on are left as they are.
Copy link

codecov bot commented Nov 8, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.57%. Comparing base (5dfbd43) to head (c4d7237).
Report is 111 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #16183   +/-   ##
=======================================
  Coverage   98.57%   98.57%           
=======================================
  Files         164      164           
  Lines       21345    21345           
=======================================
  Hits        21041    21041           
  Misses        304      304           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

github-actions bot commented Nov 8, 2024

Code size report:


@mungewell
Copy link
Contributor Author

The first commit is not needed, I guess I screwed up the update to current HEAD...
commit a28b961

@mungewell mungewell changed the title Allow CLK_SYS and dependants to be left running during LightSleep() Ports/RP2: Allow CLK_SYS and dependants to be left running during LightSleep() Nov 8, 2024
mungewell added a commit to mungewell/pico-timecode that referenced this pull request Nov 11, 2024
This demos the ability to 'lightsleep()' whilst keeping the PIO clocks
running and __still__ producing LTC from the data stored in the FIFO.

When enabled the power draw (on a bare Pico) drops from 3.8V @ 30mA
to 3.8V @ 11mA.

See:
micropython/micropython#16183
@projectgus projectgus self-requested a review November 12, 2024 22:41
Copy link
Contributor

@projectgus projectgus left a comment

Choose a reason for hiding this comment

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

(Gah, I wrote a long comment here and github seems to have eaten it. I'll try and write it out again...)

My mistake, I messed up strategically after merging the fix in #15301 that solved our USB glitching problem. I should have gone to pico-sdk maintainers right then and asked them to support these use cases, rather than committing MicroPython to a long term fork of this part of the Pico SDK. We're already getting reports of problems with sleep in RP2350, and the new RP chips and SDK code changes will only keep coming.

EDIT: I did actually do this and the request raspberrypi/pico-sdk#1746 is still open, I just have a bad memory! 😆

Bottom line is that it's not viable for us to try and support this kind of low-level platform support code ourselves in the long term, and I should have thought strategically about it back in July.

I'll make a comment on your upstream issue report now and see if the pico-sdk developers are interested to re-engage on this. In the meantime, we can probably keep merging MicroPython-specific changes to clocks_extra.c for now - but I'd sleep better if we had a path to eventually upstreaming them.

@@ -246,7 +260,9 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
#endif

// Go into low-power mode.
__wfi();
if (timer_hw->armed & (1 << 3)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This has the same race described here #16412 (comment)

(but alternative fix for this problem is hopefully incoming)

@@ -141,6 +143,12 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
}
}

if (n_args >= 2)
req_sleep_en0 |= mp_obj_get_int(args[1]);
Copy link
Contributor

Choose a reason for hiding this comment

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

It's very simple to implement, but unfortunately raw register bits isn't really something we want to expose in the machine API.

machine.lightsleep() is regretfully not well defined across ports now, but the guideline is to have some amount of abstraction that works similarly across ports. Even if we made this as a rp2-port-only addition, the inevitable introduction of new RP series chips means that there could be new or different registers into the future, which might need more special-case API choices.

I note you and @cpottle9 have both come to similar conclusions although you're trying to solve slightly different problems (more context here).

I am wondering if there's a way forward where we can support keeping some peripherals active during lightsleep, without exposing register bits in the Python API.

For PIO, it could mean adding a light_sleep_enable flag for PIO init (similar to proposed for esp32 in #16102). Then I suspect the peripheral driver could manipulate necessary sleep_en bits directly, and keep them set until PIO is deinit-ed again.

The same thing could be added for other peripherals as well. I think reference-counting the initialised peripherals is probably the most complex part. So this shouldn't take particularly much code size to implement, I (naively) think.

The logic for whether to stop CLK_SYS will probably need to look much like it does in this PR already, although I'm hopeful we can eventually upstream some of that into pico-sdk.

How does that sound?

Copy link
Contributor

Choose a reason for hiding this comment

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

@mungewell, take a look at my github repo RP2-PowerControl.

It implements code to disable RP2040 and RP2350 hardware blocks using the SLEEP_EN0/1 and WAKE_EN0/1 registers.
This allows you to turn off hardware blocks you don't need ever or turn off hardware blocks when sleeping (EG: time.sleep_ms).

The difference between machine.lightsleep() and time.sleep_ms() is lightsleep turns off one or both PLL blocks depending on whether USB is needed during lightsleep().
The PLL blocks are fairly power hungry.

Using the code in RP2-PowerControl you can turn off all the hardware blocks that lightsleep() turns off except for the PLLs. Or you can choose to keep hardware blocks you need turned on.

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

Successfully merging this pull request may close these issues.

4 participants