|
1 | 1 | /* |
2 | 2 | * Copyright (C) 2015 Freie Universität Berlin |
| 3 | + * 2018 Eistec AB |
| 4 | + * 2024 HAW Hamburg |
3 | 5 | * |
4 | 6 | * This file is subject to the terms and conditions of the GNU Lesser |
5 | 7 | * General Public License v2.1. See the file LICENSE in the top level |
|
14 | 16 | * @brief Peripheral timer test application |
15 | 17 | * |
16 | 18 | * @author Hauke Petersen <hauke.petersen@fu-berlin.de> |
| 19 | + * Joakim Nohlgård <joakim.nohlgard@eistec.se> |
| 20 | + * Bennet Blischke <bennet.blischke@haw-hamburg.de> |
17 | 21 | * |
18 | 22 | * @} |
19 | 23 | */ |
|
25 | 29 | #include "atomic_utils.h" |
26 | 30 | #include "architecture.h" |
27 | 31 | #include "clk.h" |
| 32 | +#include "mutex.h" |
28 | 33 | #include "periph/timer.h" |
29 | 34 | #include "test_utils/expect.h" |
30 | 35 | #include "time_units.h" |
|
51 | 56 | * e.g. when the timer was about to tick anyway */ |
52 | 57 | #define MINIMUM_TICKS 2 |
53 | 58 |
|
| 59 | +#ifndef TEST_ITERATIONS |
| 60 | +#define TEST_ITERATIONS (10000ul) |
| 61 | +#endif |
| 62 | + |
54 | 63 | static uint8_t fired; |
55 | 64 | static uint32_t sw_count; |
56 | 65 | static uint32_t timeouts[TIMER_CHANNEL_NUMOF]; |
57 | 66 | static unsigned args[TIMER_CHANNEL_NUMOF]; |
58 | 67 |
|
| 68 | +typedef struct { |
| 69 | + unsigned long counter; |
| 70 | + tim_t dev; |
| 71 | + mutex_t mtx; |
| 72 | +} test_ctx_t; |
| 73 | + |
| 74 | +static void cb_incr(void *arg, int chan) |
| 75 | +{ |
| 76 | + (void)chan; |
| 77 | + test_ctx_t *ctx = arg; |
| 78 | + |
| 79 | + ctx->counter++; |
| 80 | + if (ctx->counter < TEST_ITERATIONS) { |
| 81 | + /* Rescheduling the timer like this will trigger a bug in the lptmr |
| 82 | + * implementation in Kinetis */ |
| 83 | + timer_set(ctx->dev, chan, 20000u); |
| 84 | + timer_set(ctx->dev, chan, 0); |
| 85 | + } |
| 86 | + mutex_unlock(&ctx->mtx); |
| 87 | +} |
| 88 | + |
59 | 89 | static void cb(void *arg, int chan) |
60 | 90 | { |
61 | 91 | timeouts[chan] = sw_count; |
@@ -207,6 +237,53 @@ static int test_timer(unsigned num, uint32_t timer_freq) |
207 | 237 | return 1; |
208 | 238 | } |
209 | 239 |
|
| 240 | + |
| 241 | +/* This test is designed to catch an implementation bug where a timer callback is |
| 242 | + * called directly from inside timer_set if the given timeout=0, leading to a |
| 243 | + * stack overflow if timer_set is called from within the callback of the same |
| 244 | + * timer. |
| 245 | + * |
| 246 | + * The test will attempt to initialize each timer in the system and set a non-zero |
| 247 | + * timeout at first. The callback function provided will then attempt to set a new |
| 248 | + * timeout=0 until we have called the callback TEST_ITERATIONS times (default 10000). |
| 249 | + * The expected behavior is that the timer will trigger again as soon as the timer |
| 250 | + * callback function returns. If the timer driver implementation is broken, then |
| 251 | + * the callback will be called again by timer_set, causing a stack overflow after a |
| 252 | + * number of iterations. */ |
| 253 | +static int test_timer_timeout(unsigned num, uint32_t timer_freq) |
| 254 | +{ |
| 255 | + /* initialize and halt timer */ |
| 256 | + unsigned long switches = 0; |
| 257 | + test_ctx_t ctx = { |
| 258 | + .counter = 0, |
| 259 | + .dev = TIMER_DEV(num), |
| 260 | + .mtx = MUTEX_INIT_LOCKED |
| 261 | + }; |
| 262 | + |
| 263 | + printf(" - Testing timeout=0 in callback:\n"); |
| 264 | + |
| 265 | + if (timer_init(ctx.dev, timer_freq, cb_incr, &ctx) < 0) { |
| 266 | + printf(" TIMER_DEV(%u) init failed.\n", num); |
| 267 | + return 0; |
| 268 | + } |
| 269 | + /* Send the initial trigger for the timer */ |
| 270 | + timer_set(ctx.dev, 0, 100); |
| 271 | + /* Wait until we have executed the zero timeout callback enough times */ |
| 272 | + while (ctx.counter < TEST_ITERATIONS) { |
| 273 | + mutex_lock(&ctx.mtx); |
| 274 | + ++switches; |
| 275 | + } |
| 276 | + |
| 277 | + /* verify results */ |
| 278 | + if (ctx.counter != TEST_ITERATIONS) { |
| 279 | + printf(" TIMER_DEV(%u) counter mismatch, expected: %lu, actual: %lu\n", |
| 280 | + num, TEST_ITERATIONS, ctx.counter); |
| 281 | + return 0; |
| 282 | + } |
| 283 | + printf(" OK (timer timeout successfull)\n"); |
| 284 | + return 1; |
| 285 | +} |
| 286 | + |
210 | 287 | static uword_t query_freq_numof(tim_t dev) |
211 | 288 | { |
212 | 289 | if (IS_USED(MODULE_PERIPH_TIMER_QUERY_FREQS)) { |
@@ -271,7 +348,11 @@ int main(void) |
271 | 348 | * complete */ |
272 | 349 | end = MIN(end, 3); |
273 | 350 | for (uword_t j = 0; j < end; j++) { |
274 | | - if (!test_timer(i, query_freq(TIMER_DEV(i), j))) { |
| 351 | + uint32_t freq = query_freq(TIMER_DEV(i), j); |
| 352 | + if (!test_timer(i, freq)) { |
| 353 | + failed = 1; |
| 354 | + } |
| 355 | + if (!test_timer_timeout(i, freq)) { |
275 | 356 | failed = 1; |
276 | 357 | } |
277 | 358 | } |
|
0 commit comments