@@ -265,7 +265,12 @@ static void alarm_pool_irq_handler(void) {
265
265
// need to wait
266
266
alarm_pool_entry_t * earliest_entry = & pool -> entries [earliest_index ];
267
267
earliest_target = earliest_entry -> target ;
268
- ta_set_timeout (timer , timer_alarm_num , earliest_target );
268
+ // we are leaving a timeout every 2^32 microseconds anyway if there is no valid target, so we can choose any value.
269
+ // best_effort_wfe_or_timeout now relies on it being the last value set, and arguably this is the
270
+ // best value anyway, as it is the furthest away from the last fire.
271
+ if (earliest_target != -1 ) {
272
+ ta_set_timeout (timer , timer_alarm_num , earliest_target );
273
+ }
269
274
// check we haven't now past the target time; if not we don't want to loop again
270
275
} while ((earliest_target - now ) <= 0 );
271
276
}
@@ -439,26 +444,35 @@ bool best_effort_wfe_or_timeout(absolute_time_t timeout_timestamp) {
439
444
return time_reached (timeout_timestamp );
440
445
} else {
441
446
alarm_id_t id ;
442
- id = add_alarm_at (timeout_timestamp , sleep_until_callback , NULL , false);
443
- if (id <= 0 ) {
444
- tight_loop_contents ();
447
+ // note that as of SDK2.0.0 calling add_alarm_at always causes a SEV. Whwat we really
448
+ // want to do is cause an IRQ at the specified time in the future if there is not
449
+ // an IRQ already happening before then. The problem is that the IRQ may be happening on the
450
+ // other core, so taking an IRQ is the only way to get the state protection.
451
+ //
452
+ // Therefore, we make a compromise; we will set the alarm, if we won't wake up before the right time
453
+ // already. This means that repeated calls to this function with the same timeout will work correctly
454
+ // after the first one! This is fine, because we ask callers to use a polling loop on another
455
+ // event variable when using this function
456
+ if (ta_wakes_up_on_or_before (alarm_pool_get_default ()-> timer , alarm_pool_get_default ()-> timer_alarm_num ,
457
+ (int64_t )to_us_since_boot (timeout_timestamp ))) {
458
+ __wfe ();
445
459
return time_reached (timeout_timestamp );
446
460
} else {
447
- // the above alarm add now may force an IRQ which will wake us up,
448
- // so we want to consume one __wfe.. we do an explicit __sev
449
- // just to make sure there is one
450
- __sev (); // make sure there is an event sow ee don't block
451
- __wfe ();
452
- if (!time_reached (timeout_timestamp ))
453
- {
454
- // ^ at the point above the timer hadn't fired, so it is safe
455
- // to wait; the event will happen due to IRQ at some point between
456
- // then and the correct wakeup time
457
- __wfe ();
461
+ id = add_alarm_at (timeout_timestamp , sleep_until_callback , NULL , false);
462
+ if (id <= 0 ) {
463
+ tight_loop_contents ();
464
+ return time_reached (timeout_timestamp );
465
+ } else {
466
+ if (!time_reached (timeout_timestamp )) {
467
+ // ^ at the point above the timer hadn't fired, so it is safe
468
+ // to wait; the event will happen due to IRQ at some point between
469
+ // then and the correct wakeup time
470
+ __wfe ();
471
+ }
472
+ // we need to clean up if it wasn't us that caused the wfe; if it was this will be a noop.
473
+ cancel_alarm (id );
474
+ return time_reached (timeout_timestamp );
458
475
}
459
- // we need to clean up if it wasn't us that caused the wfe; if it was this will be a noop.
460
- cancel_alarm (id );
461
- return time_reached (timeout_timestamp );
462
476
}
463
477
}
464
478
#else
0 commit comments