Skip to content

rp2/modmachine: Fix lightsleep returning early. #16412

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

Closed
wants to merge 2 commits into from
Closed

rp2/modmachine: Fix lightsleep returning early. #16412

wants to merge 2 commits into from

Conversation

cpottle9
Copy link
Contributor

@cpottle9 cpottle9 commented Dec 14, 2024

fixes #16181

Commit 74fb42a added code using hardware timer 2.
Prior to that only lightsleep used hardware timers specifically timer 3.
It turned off almost all hardware blocks except hardware timers and waits for an interrupt.
It did not check if the interrupt is from hardware timers or if it is from timer 3.

With this change, lightsleep now loops calling __wfi() until timer 3 has fired. Without the loop lightsleep would return very early.

Ideally this pull request should be reviewed by user @projectgus who made the changes in commit 74fb42a and has also made changes to modmachine.c lightsleep.

Testing

Tested on PICO W using thonny with code from the issue. Works fine.

fixes #16181

Commit 74fb42a added code using hardware timer 2.
Prior to that only lightsleep used hardware timers
specifically timer 3.

lightsleep now loops calling __wfi() until timer 3 has fired.
Without the loop lightsleep would return very early.

Tested on PICO W with code from the issue.
Works fine.

Signed-off-by: Carl Pottle <cpottle9@outlook.com>
Copy link

github-actions bot commented Dec 14, 2024

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt:    +0 +0.000% TEENSY40
        rp2:   +64 +0.007% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@cpottle9 cpottle9 marked this pull request as ready for review December 14, 2024 06:16
// A timer other than #3 could fire.
// Repeatedly go into low-power mode until timer 3 is no longer armed.
while (timer_hw->armed & (1u << alarm_num)) {
__wfi();
Copy link
Member

Choose a reason for hiding this comment

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

I'm pretty sure there's a race condition in here: between the check of the timer and the wfi the interrupt may go off, in which case the wfi will sit there waiting "forever" because the interrupt already fired.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed.
Needs to be fixed, but it won't wait forever. Timer 2 is going off about every 100 ms which will cause the __wfi() to return.

@dpgeorge, it is not obvious to me how to resolve the race condition.
I still a relative neophyte on ARM and micropython internals.

Copy link
Member

Choose a reason for hiding this comment

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

@peterharperuk tried to fix this before but there's not really any way to do it.

A proper fix needs the pico-sdk update (which fixes issues with the alarm pool and wfe), but the latest version of that has a new alarm pool bug...

Copy link
Contributor

@peterharperuk peterharperuk Dec 16, 2024

Choose a reason for hiding this comment

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

We've fixed the latest issue and have a tentative plan to do a small sdk update in the new year.
Also, this change would prevent wakeup for USB interrupts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@peterharperuk, it does not prevent wakeup for USB interrupts it just delays it. Potentially a lot.

Without my change the single __wfi() wakes up every 60 to 100 milliseconds due to the change in 74fb42a which added code using hardware timer 2.
USB interrupts will be masked for that time.

With the while loop I added USB interrupts will be masked for the entire delay time.
But they will get serviced eventually.

I did notice while testing that if I did a lightsleep(10000) and hit the thonny stop button the prompt would not come back until the sleep expired.
Longer delays like lightsleep(30000) and thonny would timeout if I hit the stop button.
But once the lightsleep completed (after 30 seconds) hitting the stop button again would bring it back.

I suggest to you that someone calling lightsleep() for a long time (EG: several minutes) needs to be aware thonny will timeout.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@projectgus has a pull request open for the same issue.

Likely I will close this pull request without a merge and his fix will go in.

It is end of day were I live. I will get back to this tomorrow.

fixes #16181

@dpgeorge noted a race condition in my initial commit for
this pull request.

This commit masks the race condition by changing the condition
in the while() loop.
Now the while loop continues as long as timer 3 is armed and there
are at least 50 microseconds left to run.

50 microseconds is a magic number I chose.
It could be smaller.
It is more than long enough to ensure timer 3 interrupt won't
occur before __wfi() is called.
And it is small enough someone calling lightsleep() won't
notice it returning early.

I tested using a modified version of test/ports/rp2/rp2_lightsleep.py.
In my modified version of the test lightsleep was called
for 1, 2, 4, 8, 16, ..., 4096, 8192 milliseconds.
Using time.ticks_us() I measured the time to execute lightsleep().
Pretty consistently it was 800-900 microseconds longer than requested.
I suspect this is one or more interrupts being serviced when lightsleep
re-enable interrupts before returning.

I did my testing on a PICO W connected to a PI 4 running thonny.

Signed-off-by: Carl Pottle <cpottle9@outlook.com>
@cpottle9
Copy link
Contributor Author

I tested @projectgus changes in #16431.
It fixes the issue and is cleaner than my fix.

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.

lightsleep on Pico W regression for 1.24.0
3 participants