pico_time_test.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /**
  2. * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <hardware/sync.h>
  10. #include "pico/stdlib.h"
  11. #include "pico/test.h"
  12. #include <inttypes.h>
  13. PICOTEST_MODULE_NAME("pico_time_test", "pico_time test harness");
  14. #define NUM_TIMEOUTS 500
  15. #define MAX_TIMERS_PER_POOL 250
  16. static_assert(PICO_TIME_DEFAULT_ALARM_POOL_MAX_TIMERS >= MAX_TIMERS_PER_POOL, "");
  17. #define TEST_LENGTH_US 2000000
  18. #define NUM_REPEATING_TIMERS 50
  19. static struct repeating_timer repeating_timers[NUM_REPEATING_TIMERS];
  20. static uint repeating_timer_callback_count[NUM_REPEATING_TIMERS];
  21. static struct timeout {
  22. alarm_id_t alarm_id;
  23. absolute_time_t target;
  24. absolute_time_t fired_at;
  25. uint pool;
  26. uint fired_count;
  27. bool cancelled;
  28. bool not_cancelled; // tried to cancel but it was done
  29. } timeouts[NUM_TIMEOUTS];
  30. int64_t timer_callback1(alarm_id_t id, void *user_data) {
  31. struct timeout *timeout = (struct timeout *)user_data;
  32. assert(timeout >= timeouts && timeout < (timeouts + NUM_TIMEOUTS));
  33. timeout->fired_at = get_absolute_time();
  34. timeout->fired_count++;
  35. // printf("%d %d %ld\n", timeout->pool, id, to_us_since_boot(timeout->target));
  36. return 0;
  37. }
  38. int sort_by_target(const void *a, const void *b) {
  39. const struct timeout *ta = (const struct timeout *)a;
  40. const struct timeout *tb = (const struct timeout *)b;
  41. int64_t delta = absolute_time_diff_us(tb->target, ta->target);
  42. if (delta < 0) return -1;
  43. else if (delta > 0) return 1;
  44. return 0;
  45. }
  46. static bool repeating_timer_callback(struct repeating_timer *t) {
  47. // check we get the right timer structure
  48. uint i = (uintptr_t)t->user_data;
  49. hard_assert(i == (t - repeating_timers));
  50. repeating_timer_callback_count[i]++;
  51. return true;
  52. }
  53. #ifndef PICO_HARDWARE_TIMER_RESOLUTION_US
  54. #define RESOLUTION_ALLOWANCE 0
  55. #else
  56. #define RESOLUTION_ALLOWANCE PICO_HARDWARE_TIMER_RESOLUTION_US
  57. #endif
  58. int issue_195_test(void);
  59. int main() {
  60. setup_default_uart();
  61. alarm_pool_init_default();
  62. PICOTEST_START();
  63. struct alarm_pool *pools[NUM_TIMERS];
  64. for(uint i=0; i<NUM_TIMERS; i++) {
  65. if (i == alarm_pool_hardware_alarm_num(alarm_pool_get_default())) {
  66. pools[i] = alarm_pool_get_default();
  67. } else {
  68. pools[i] = alarm_pool_create(i, MAX_TIMERS_PER_POOL);
  69. }
  70. PICOTEST_CHECK_AND_ABORT(pools[i], "failed to create timer pool");
  71. }
  72. // Check default config has valid data in it
  73. PICOTEST_START_SECTION("Alarm ordering test");
  74. absolute_time_t time_base = get_absolute_time();
  75. uint32_t init_ms = 1000;
  76. for(uint i = 0; i < NUM_TIMEOUTS; i++) {
  77. // printf("%d %p\n", i);
  78. absolute_time_t target;
  79. uint pool;
  80. if (1 == (i&127u)) {
  81. // want occasional duplicate time
  82. target = timeouts[i-1].target;
  83. pool = timeouts[i-1].pool;
  84. } else {
  85. target = delayed_by_us(time_base, init_ms + (rand() % TEST_LENGTH_US));
  86. pool = rand() % 4;
  87. }
  88. timeouts[i].target = target;
  89. timeouts[i].pool = pool;
  90. alarm_id_t id = alarm_pool_add_alarm_at(pools[pool], target, timer_callback1, timeouts + i, true);
  91. PICOTEST_CHECK_AND_ABORT(id >=0, "Failed to add timer");
  92. }
  93. PICOTEST_CHECK(absolute_time_diff_us(time_base, get_absolute_time()) < init_ms * 1000, "This is a flaky test :-(");
  94. uint64_t last_fired_at[NUM_TIMERS];
  95. uint64_t last_target[NUM_TIMERS];
  96. memset(&last_fired_at, 0, sizeof(last_fired_at));
  97. printf("Sleeping...\n");
  98. sleep_us(TEST_LENGTH_US + 250000);
  99. printf(" ...done\n");
  100. qsort(timeouts, NUM_TIMEOUTS, sizeof(struct timeout), sort_by_target);
  101. uint64_t max_jitter = 0;
  102. for(uint i = 0; i < NUM_TIMEOUTS; i++) {
  103. printf("%d %d %"PRIi64" : %"PRIi64"\n", timeouts[i].pool, timeouts[i].fired_count, to_us_since_boot(timeouts[i].fired_at), to_us_since_boot(timeouts[i].target));
  104. PICOTEST_CHECK(timeouts[i].fired_count, "Timer should have fired");
  105. PICOTEST_CHECK(timeouts[i].fired_count < 2, "Timer should only have fired once");
  106. uint64_t fired_at = to_us_since_boot(timeouts[i].fired_at);
  107. PICOTEST_CHECK(timeouts[i].fired_count != 1 || fired_at >= MAX(RESOLUTION_ALLOWANCE,
  108. to_us_since_boot(timeouts[i].target)) - RESOLUTION_ALLOWANCE, "Timer fired early");
  109. // we need to be in order unless the targets are the same in which case order is arbitrary
  110. PICOTEST_CHECK(timeouts[i].fired_count != 1 || fired_at > MAX(RESOLUTION_ALLOWANCE, last_fired_at[timeouts[i].pool]) - RESOLUTION_ALLOWANCE ||
  111. to_us_since_boot(timeouts[i].target) == last_target[timeouts[i].pool], "Timer fired out of order");
  112. last_fired_at[timeouts[i].pool] = fired_at;
  113. last_target[timeouts[i].pool] = to_us_since_boot(timeouts[i].target);
  114. if (timeouts[i].fired_count == 1) {
  115. uint64_t jitter = absolute_time_diff_us(timeouts[i].target, timeouts[i].fired_at);
  116. if (jitter > max_jitter) {
  117. max_jitter = jitter;
  118. }
  119. }
  120. }
  121. printf("MAX JITTER: %dus\n", (uint)max_jitter);
  122. PICOTEST_END_SECTION();
  123. PICOTEST_START_SECTION("Alarm completion or canceled");
  124. memset(timeouts, 0, sizeof(timeouts));
  125. absolute_time_t time_base = get_absolute_time();
  126. // this runs concurrently with the firing, so some are in the past
  127. uint approx_past_timeouts = 0;
  128. // uint32_t save = save_and_disable_interrupts();
  129. for(uint i = 0; i < NUM_TIMEOUTS; i++) {
  130. // printf("%d %p\n", i);
  131. absolute_time_t target = delayed_by_us(time_base, (rand() % TEST_LENGTH_US));
  132. if (absolute_time_diff_us(target, get_absolute_time()) >= 0) {
  133. approx_past_timeouts++;
  134. }
  135. uint pool = rand() % 4;
  136. timeouts[i].target = target;
  137. timeouts[i].pool = pool;
  138. alarm_id_t id = alarm_pool_add_alarm_at(pools[pool], target, timer_callback1, timeouts + i, true);
  139. timeouts[i].alarm_id = id;
  140. PICOTEST_CHECK_AND_ABORT(id >=0, "Failed to add timer");
  141. if (id && !(rand() & 6)) {
  142. uint j = rand() % (i + 1);
  143. if (timeouts[j].alarm_id && !timeouts[j].cancelled && !timeouts[j].not_cancelled) {
  144. // alarm_pool_dump(pools[pool]);
  145. // printf("removing %d\n", timeouts[j].alarm_id);
  146. if (alarm_pool_cancel_alarm(pools[timeouts[j].pool], timeouts[j].alarm_id)) {
  147. timeouts[j].cancelled = true;
  148. } else {
  149. timeouts[j].not_cancelled = true;
  150. }
  151. // printf("removed %d\n", timeouts[j].alarm_id);
  152. // alarm_pool_dump(pools[pool]);
  153. }
  154. }
  155. busy_wait_us_32(2000); // we want to overlap with the firing
  156. }
  157. printf("approx past timeouts %d/%d\n", approx_past_timeouts, NUM_TIMEOUTS);
  158. sleep_us(TEST_LENGTH_US - 2000 * NUM_TIMEOUTS / 4 + 250000);
  159. for(uint i = 0; i < NUM_TIMEOUTS/4; i++) {
  160. printf("%d %d %d/%d/%d %"PRIi64" : %"PRIi64"\n", timeouts[i].pool, (int)timeouts[i].alarm_id, timeouts[i].fired_count, timeouts[i].cancelled,
  161. timeouts[i].not_cancelled, to_us_since_boot(timeouts[i].fired_at), to_us_since_boot(timeouts[i].target));
  162. uint total = timeouts[i].fired_count + timeouts[i].cancelled;
  163. PICOTEST_CHECK( timeouts[i].not_cancelled ? timeouts[i].fired_count : true, "Timer that failed to cancel should have fired");
  164. PICOTEST_CHECK(total == 1, "Timer should have fired or been cancelled");
  165. }
  166. PICOTEST_END_SECTION();
  167. PICOTEST_START_SECTION("Repeating timertest");
  168. for(uint i=0;i<NUM_REPEATING_TIMERS;i++) {
  169. add_repeating_timer_us(500+ (rand() & 1023), repeating_timer_callback, (void *)(uintptr_t)i, repeating_timers + i);
  170. }
  171. sleep_ms(3000);
  172. uint callbacks = 0;
  173. for(uint i=0;i<NUM_REPEATING_TIMERS;i++) {
  174. PICOTEST_CHECK(cancel_repeating_timer(repeating_timers + i), "Cancelling repeating timer should succeed");
  175. PICOTEST_CHECK(repeating_timer_callback_count[i] > 1, "Each repeating timer should have been called back multiple times");
  176. callbacks += repeating_timer_callback_count[i];
  177. }
  178. uint callbacks2 = 0;
  179. for(uint i=0;i<NUM_REPEATING_TIMERS;i++) {
  180. PICOTEST_CHECK(!cancel_repeating_timer(repeating_timers + i), "Re-cancelling repeating timer should fail");
  181. callbacks2 += repeating_timer_callback_count[i];
  182. }
  183. PICOTEST_CHECK(callbacks == callbacks2, "No repeating timers should have been called back after being cancelled")
  184. PICOTEST_END_SECTION();
  185. PICOTEST_START_SECTION("end of time");
  186. PICOTEST_CHECK(absolute_time_diff_us(at_the_end_of_time, get_absolute_time()) < 0, "now should be before the end of time")
  187. PICOTEST_CHECK(absolute_time_diff_us(get_absolute_time(), at_the_end_of_time) > 0, "the end of time should be after now")
  188. PICOTEST_CHECK(absolute_time_diff_us(at_the_end_of_time, at_the_end_of_time) == 0, "the end of time should equal itself")
  189. absolute_time_t near_the_end_of_time;
  190. update_us_since_boot(&near_the_end_of_time, 0x7ffffeffffffffff);
  191. PICOTEST_CHECK(absolute_time_diff_us(near_the_end_of_time, at_the_end_of_time) > 0, "near the end of time should be before the end of time")
  192. PICOTEST_END_SECTION();
  193. if (issue_195_test()) {
  194. return -1;
  195. }
  196. PICOTEST_END_TEST();
  197. }
  198. #define ISSUE_195_TIMER_DELAY 50
  199. volatile int issue_195_counter;
  200. int64_t issue_195_callback(alarm_id_t id, void *user_data) {
  201. issue_195_counter++;
  202. return -ISSUE_195_TIMER_DELAY;
  203. }
  204. int issue_195_test(void) {
  205. PICOTEST_START_SECTION("Issue #195 race condition - without fix may hang on gcc 10.2.1 release builds");
  206. absolute_time_t t1 = get_absolute_time();
  207. int id = add_alarm_in_us(ISSUE_195_TIMER_DELAY, issue_195_callback, NULL, true);
  208. for(uint i=0;i<5000;i++) {
  209. sleep_us(100);
  210. sleep_us(100);
  211. uint delay = 9; // 9 seems to be the magic number (at least for reproducing on 10.2.1)
  212. sleep_us(delay);
  213. }
  214. absolute_time_t t2 = get_absolute_time();
  215. cancel_alarm(id);
  216. int expected_count = absolute_time_diff_us(t1, t2) / ISSUE_195_TIMER_DELAY;
  217. printf("Timer fires approx_expected=%d actual=%d\n", expected_count, issue_195_counter);
  218. PICOTEST_END_SECTION();
  219. return 0;
  220. }