Skip to content

Commit 969f589

Browse files
ta_set_timeout can fail to set an alarm (#2127)
* ta_set_timeout can fail to set an alarm If alarm_pool_irq_handler takes <1us between handling an alarm and calling ta_set_timeout then no alarms will be set as it will appear as if an earlier alarm is already armedi before the target time. Make sure ta_set_timeout always leaves with an alarm set by checking the armed status. Fixes #2118 * ta_disable_irq_handler should unarm its timer
1 parent ba7aa3f commit 969f589

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

src/rp2_common/pico_time_adapter/include/pico/time_adapter.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ static inline void ta_set_timeout(alarm_pool_timer_t *timer, uint alarm_num, int
4848
// 2. If we just passed the target time, then time_til_target will be high, meaning we will
4949
// likely not do the update, but this is OK since the caller who has the full 64 bits
5050
// must check if the target time has passed when we return anyway to avoid races.
51-
if (time_til_target < time_til_alarm) {
51+
// 3. We should never leave here without an alarm being armed
52+
if (time_til_target < time_til_alarm || (timer_hw_from_timer(timer)->armed & (1 << alarm_num)) == 0) {
5253
timer_hw_from_timer(timer)->alarm[alarm_num] = (uint32_t) target;
5354
}
5455
}
@@ -75,6 +76,7 @@ static inline void ta_enable_irq_handler(alarm_pool_timer_t *timer, uint alarm_n
7576

7677
static inline void ta_disable_irq_handler(alarm_pool_timer_t *timer, uint alarm_num, irq_handler_t irq_handler) {
7778
uint irq_num = timer_hardware_alarm_get_irq_num(timer, alarm_num);
79+
timer_hw_from_timer(timer)->armed = 1u << alarm_num; // disarm the timer
7880
hw_clear_bits(&timer_hw_from_timer(timer)->inte, 1u << alarm_num);
7981
irq_set_enabled(irq_num, true);
8082
irq_remove_handler(irq_num, irq_handler);

test/pico_time_test/pico_time_test.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <stdlib.h>
99
#include <string.h>
1010
#include <hardware/sync.h>
11+
#include "hardware/clocks.h"
1112
#include "pico/stdlib.h"
1213
#include "pico/test.h"
1314
// Include sys/types.h before inttypes.h to work around issue with
@@ -74,6 +75,7 @@ static bool repeating_timer_callback(struct repeating_timer *t) {
7475
int issue_195_test(void);
7576
int issue_1812_test(void);
7677
int issue_1953_test(void);
78+
int issue_2118_test(void);
7779

7880
int main() {
7981
setup_default_uart();
@@ -246,6 +248,8 @@ int main() {
246248

247249
issue_1953_test();
248250

251+
issue_2118_test();
252+
249253
PICOTEST_END_TEST();
250254
}
251255

@@ -325,3 +329,38 @@ int issue_1953_test(void) {
325329
PICOTEST_END_SECTION();
326330
return 0;
327331
}
332+
333+
static int counter_2118;
334+
static bool timer_callback_issue_2118(repeating_timer_t *rt) {
335+
counter_2118++;
336+
return true;
337+
}
338+
339+
int issue_2118_test(void) {
340+
PICOTEST_START_SECTION("Issue #2118 defect - failure to set an alarm");
341+
342+
// this problem only happens when running the clock fast as it requires the time between
343+
// alarm_pool_irq_handler handling an alarm and setting the next alarm to be <1us
344+
set_sys_clock_hz(200 * MHZ, true);
345+
setup_default_uart();
346+
347+
alarm_pool_t *pool = alarm_pool_create(2, 1);
348+
repeating_timer_t timer;
349+
alarm_pool_add_repeating_timer_ms(pool, -20, timer_callback_issue_2118, NULL, &timer);
350+
351+
int iterations = 0;
352+
while(iterations < 100) {
353+
iterations++;
354+
sleep_ms(20);
355+
}
356+
PICOTEST_CHECK(counter_2118 >= 100, "Repeating timer failure");
357+
358+
alarm_pool_destroy(pool);
359+
hard_assert(timer_hw->armed == 0); // check destroying the pool unarms its timer
360+
361+
set_sys_clock_hz(SYS_CLK_HZ, true);
362+
setup_default_uart();
363+
364+
PICOTEST_END_SECTION();
365+
return 0;
366+
}

0 commit comments

Comments
 (0)