Skip to content

Commit

Permalink
rp2: Stop machine.idle() blocking indefinitely.
Browse files Browse the repository at this point in the history
Updates rp2 port to always resume from idle within 1ms max.

When rp2 port went tickless the behaviour of machine.idle() changed as
there is no longer a tick interrupt to wake it up every millisecond. On a
quiet system it would now block indefinitely. No other port does this.

See parent commit for justification of why this change is useful.

Also adds a test case that fails without this change.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
  • Loading branch information
projectgus committed Jul 23, 2024
1 parent 81daba3 commit ba98533
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 1 deletion.
2 changes: 1 addition & 1 deletion ports/rp2/modmachine.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
}

static void mp_machine_idle(void) {
__wfe();
MICROPY_INTERNAL_WFE(1);
}

static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
Expand Down
36 changes: 36 additions & 0 deletions tests/ports/rp2/rp2_machine_idle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import machine
import time

# Verify that machine.idle() resumes execution within 0.1 and 1.1ms (should be
# 1ms max but allowing for some overhead).
#
# (A minimum sleep time for machine.idle() isn't specified but in a system like
# this with no active interrupts then we should expect some idle time before
# resuming. If it's consistently resuming immediately then that indicates a bug
# is preventing proper idle.)
#
# This test doesn't contain any rp2-specific code, but rp2 is currently the only
# tickless port - which is what led to the bug this is a regression test for.
# Some other ports (unix, esp32) have idle behaviour that resumes immediately on
# a quiet system, so this test is also less useful for those.
#
# Verification uses the average idle time, as individual iterations will always
# have outliers due to interrupts, scheduler, etc.

ITERATIONS = 500
total = 0

for _ in range(ITERATIONS):
before = time.ticks_us()
machine.idle()
total += time.ticks_diff(time.ticks_us(), before)

total /= 1000 # us to ms
average = total / ITERATIONS

# print(f"Total {total}ms average {average}ms") # uncomment for debug

if 0.1 < average < 1.1:
print("PASS")
else:
print(f"Total {total}ms average {average}ms, out of spec")
1 change: 1 addition & 0 deletions tests/ports/rp2/rp2_machine_idle.py.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PASS

0 comments on commit ba98533

Please sign in to comment.