Skip to content

Commit b93a76e

Browse files
committed
patches: RP2040 Fix 64-bit uptime being wrapped as a 32-bit value
1 parent 6816d44 commit b93a76e

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
From e67b7f8d7a719db34a71cbcd793237ccaf6cf6d5 Mon Sep 17 00:00:00 2001
2+
From: Simon Arlott <sa.me.uk>
3+
Date: Mon, 19 May 2025 18:46:50 +0100
4+
Subject: [PATCH 1/2] RP2040: us_ticker: use API to force a timer event and get
5+
an interrupt
6+
7+
us_ticker_irq_handler() must only be called from interrupt context
8+
---
9+
targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c | 2 +-
10+
1 file changed, 1 insertion(+), 1 deletion(-)
11+
12+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c
13+
index b3b8497188c..3a5e1f1686f 100644
14+
--- a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c
15+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c
16+
@@ -99,7 +99,7 @@ void us_ticker_set_interrupt(timestamp_t timestamp)
17+
18+
void us_ticker_fire_interrupt(void)
19+
{
20+
- us_ticker_irq_handler();
21+
+ hardware_alarm_force_irq(alarm_num);
22+
}
23+
24+
void us_ticker_disable_interrupt(void)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
From 2ec69171e77ebd105064c8c2c9ba4df58bb1a992 Mon Sep 17 00:00:00 2001
2+
From: Simon Arlott <sa.me.uk>
3+
Date: Mon, 19 May 2025 18:59:40 +0100
4+
Subject: [PATCH 2/2] RP2040: us_ticker: don't modify the system uptime
5+
6+
There is only one user of this API, the mbed timer queue. It sets targets
7+
a maximum of (2**32)//16*7 microseconds in the future. Assuming there are
8+
no other timer events to be scheduled, after 3 instances of this at 5637s
9+
uptime the 64-bit uptime gets wrapped.
10+
11+
Never modify the system uptime because that makes it unusable when the
12+
64-bit uptime that should never wrap, unexpectedly does. With the uptime
13+
proceeding as normal into large 64-bit values the 32-bit timestamp needs
14+
special handling.
15+
16+
It's ambiguous what the 32-bit timestamp means because time advances while
17+
the ticker functions are being called, so the 64-bit time could wrap
18+
between calculating the next 32-bit timestamp and setting it as the target
19+
time.
20+
21+
The only way to avoid this is to know for certain what the caller meant by
22+
keeping track of the last 32-bit value that was read. This relies on there
23+
only being caller but other mbed timer implementations already keep track
24+
of the last call to us_ticker_read() to handle ambiguity in timestamp
25+
values.
26+
27+
Track the last read of the full 64-bit time too, so that we can always
28+
prepare the correct 64-bit time value. Avoid reading the current time
29+
repeatedly because it could change between calls.
30+
31+
If the timestamp is in the near future it's possible that it has been set
32+
too late and the time has been missed. Force a timer interrupt when this
33+
happens.
34+
---
35+
.../TARGET_RP2040/us_ticker.c | 39 ++++++++++---------
36+
1 file changed, 21 insertions(+), 18 deletions(-)
37+
38+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c
39+
index 3a5e1f1686f..2062ac36118 100644
40+
--- a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c
41+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c
42+
@@ -54,6 +54,7 @@ const ticker_info_t* us_ticker_get_info()
43+
}
44+
45+
static const uint8_t alarm_num = 0;
46+
+static uint64_t last_read_u64 = 0;
47+
48+
static void us_ticker_irq_handler_internal(uint alarm_src) {
49+
if (alarm_num == alarm_src) {
50+
@@ -69,30 +70,32 @@ void us_ticker_init(void)
51+
52+
uint32_t us_ticker_read()
53+
{
54+
- return time_us_32();
55+
+ uint64_t now_u64 = time_us_64();
56+
+
57+
+ core_util_critical_section_enter();
58+
+ last_read_u64 = now_u64;
59+
+ core_util_critical_section_exit();
60+
+
61+
+ return now_u64;
62+
}
63+
64+
-void us_ticker_set_interrupt(timestamp_t timestamp)
65+
+void us_ticker_set_interrupt(timestamp_t timestamp_u32)
66+
{
67+
core_util_critical_section_enter();
68+
69+
- uint64_t _timestamp = (uint64_t)timestamp;
70+
-
71+
- if (timestamp < time_us_32()) {
72+
- //32 bit timestamp has been wrapped
73+
- //We need to provide a 64 bit timestamp able to fire the irq for this round
74+
- _timestamp = (((time_us_64() >> 32) + 1) << 32) + timestamp;
75+
- } else {
76+
- //Then, at the next round, wrap the 64 bit timer to follow the 32 bit one
77+
- if ((time_us_64() >> 32) > 0) {
78+
- uint64_t current_time = time_us_64();
79+
- uint64_t wrapped_time = current_time - 0xFFFFFFFF;
80+
- timer_hw->timelw = (uint32_t)wrapped_time;
81+
- timer_hw->timehw = 0;
82+
- }
83+
+ uint32_t last_read_u32 = (uint32_t)last_read_u64;
84+
+ uint64_t timestamp_u64 = (uint64_t)timestamp_u32 | (last_read_u64 & 0xFFFFFFFF00000000ULL);
85+
+
86+
+ if (timestamp_u32 < last_read_u32) {
87+
+ timestamp_u64 += 1ULL << 32;
88+
+ }
89+
+
90+
+ absolute_time_t target = { timestamp_u64 };
91+
+ bool missed = hardware_alarm_set_target(alarm_num, target);
92+
+
93+
+ if (missed) {
94+
+ us_ticker_fire_interrupt();
95+
}
96+
- absolute_time_t target = { _timestamp };
97+
- hardware_alarm_set_target(alarm_num, target);
98+
99+
core_util_critical_section_exit();
100+
}

0 commit comments

Comments
 (0)