Skip to content

Commit 2ec6917

Browse files
committed
RP2040: us_ticker: don't modify the system uptime
There is only one user of this API, the mbed timer queue. It sets targets a maximum of (2**32)//16*7 microseconds in the future. Assuming there are no other timer events to be scheduled, after 3 instances of this at 5637s uptime the 64-bit uptime gets wrapped. Never modify the system uptime because that makes it unusable when the 64-bit uptime that should never wrap, unexpectedly does. With the uptime proceeding as normal into large 64-bit values the 32-bit timestamp needs special handling. It's ambiguous what the 32-bit timestamp means because time advances while the ticker functions are being called, so the 64-bit time could wrap between calculating the next 32-bit timestamp and setting it as the target time. The only way to avoid this is to know for certain what the caller meant by keeping track of the last 32-bit value that was read. This relies on there only being caller but other mbed timer implementations already keep track of the last call to us_ticker_read() to handle ambiguity in timestamp values. Track the last read of the full 64-bit time too, so that we can always prepare the correct 64-bit time value. Avoid reading the current time repeatedly because it could change between calls. If the timestamp is in the near future it's possible that it has been set too late and the time has been missed. Force a timer interrupt when this happens.
1 parent e67b7f8 commit 2ec6917

File tree

1 file changed

+21
-18
lines changed

1 file changed

+21
-18
lines changed

targets/TARGET_RASPBERRYPI/TARGET_RP2040/us_ticker.c

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const ticker_info_t* us_ticker_get_info()
5454
}
5555

5656
static const uint8_t alarm_num = 0;
57+
static uint64_t last_read_u64 = 0;
5758

5859
static void us_ticker_irq_handler_internal(uint alarm_src) {
5960
if (alarm_num == alarm_src) {
@@ -69,30 +70,32 @@ void us_ticker_init(void)
6970

7071
uint32_t us_ticker_read()
7172
{
72-
return time_us_32();
73+
uint64_t now_u64 = time_us_64();
74+
75+
core_util_critical_section_enter();
76+
last_read_u64 = now_u64;
77+
core_util_critical_section_exit();
78+
79+
return now_u64;
7380
}
7481

75-
void us_ticker_set_interrupt(timestamp_t timestamp)
82+
void us_ticker_set_interrupt(timestamp_t timestamp_u32)
7683
{
7784
core_util_critical_section_enter();
7885

79-
uint64_t _timestamp = (uint64_t)timestamp;
80-
81-
if (timestamp < time_us_32()) {
82-
//32 bit timestamp has been wrapped
83-
//We need to provide a 64 bit timestamp able to fire the irq for this round
84-
_timestamp = (((time_us_64() >> 32) + 1) << 32) + timestamp;
85-
} else {
86-
//Then, at the next round, wrap the 64 bit timer to follow the 32 bit one
87-
if ((time_us_64() >> 32) > 0) {
88-
uint64_t current_time = time_us_64();
89-
uint64_t wrapped_time = current_time - 0xFFFFFFFF;
90-
timer_hw->timelw = (uint32_t)wrapped_time;
91-
timer_hw->timehw = 0;
92-
}
86+
uint32_t last_read_u32 = (uint32_t)last_read_u64;
87+
uint64_t timestamp_u64 = (uint64_t)timestamp_u32 | (last_read_u64 & 0xFFFFFFFF00000000ULL);
88+
89+
if (timestamp_u32 < last_read_u32) {
90+
timestamp_u64 += 1ULL << 32;
91+
}
92+
93+
absolute_time_t target = { timestamp_u64 };
94+
bool missed = hardware_alarm_set_target(alarm_num, target);
95+
96+
if (missed) {
97+
us_ticker_fire_interrupt();
9398
}
94-
absolute_time_t target = { _timestamp };
95-
hardware_alarm_set_target(alarm_num, target);
9699

97100
core_util_critical_section_exit();
98101
}

0 commit comments

Comments
 (0)