Skip to content

Commit ba98533

Browse files
committed
rp2: Stop machine.idle() blocking indefinitely.
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 <[email protected]>
1 parent 81daba3 commit ba98533

File tree

3 files changed

+38
-1
lines changed

3 files changed

+38
-1
lines changed

ports/rp2/modmachine.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
103103
}
104104

105105
static void mp_machine_idle(void) {
106-
__wfe();
106+
MICROPY_INTERNAL_WFE(1);
107107
}
108108

109109
static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {

tests/ports/rp2/rp2_machine_idle.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import machine
2+
import time
3+
4+
# Verify that machine.idle() resumes execution within 0.1 and 1.1ms (should be
5+
# 1ms max but allowing for some overhead).
6+
#
7+
# (A minimum sleep time for machine.idle() isn't specified but in a system like
8+
# this with no active interrupts then we should expect some idle time before
9+
# resuming. If it's consistently resuming immediately then that indicates a bug
10+
# is preventing proper idle.)
11+
#
12+
# This test doesn't contain any rp2-specific code, but rp2 is currently the only
13+
# tickless port - which is what led to the bug this is a regression test for.
14+
# Some other ports (unix, esp32) have idle behaviour that resumes immediately on
15+
# a quiet system, so this test is also less useful for those.
16+
#
17+
# Verification uses the average idle time, as individual iterations will always
18+
# have outliers due to interrupts, scheduler, etc.
19+
20+
ITERATIONS = 500
21+
total = 0
22+
23+
for _ in range(ITERATIONS):
24+
before = time.ticks_us()
25+
machine.idle()
26+
total += time.ticks_diff(time.ticks_us(), before)
27+
28+
total /= 1000 # us to ms
29+
average = total / ITERATIONS
30+
31+
# print(f"Total {total}ms average {average}ms") # uncomment for debug
32+
33+
if 0.1 < average < 1.1:
34+
print("PASS")
35+
else:
36+
print(f"Total {total}ms average {average}ms, out of spec")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PASS

0 commit comments

Comments
 (0)