diff options
author | Jere Leppänen <jere.leppanen@nokia.com> | 2023-02-09 11:20:16 +0200 |
---|---|---|
committer | Matias Elo <matias.elo@nokia.com> | 2023-03-07 09:58:03 +0200 |
commit | 6b248be5de9f68458737f5e28f203e2b5a69ded4 (patch) | |
tree | e11b20877a3ebd63ba4cdbc88cb85a533633c38d /example | |
parent | a5953871cba3f5722c59ef98b0632c997dc34fbe (diff) |
example: timer_accuracy: add periodic timer mode
Add a new mode (-m 3), which uses the periodic timer API. Add -P and
-M options, which allow specifying periodic timer pool parameters and
periodic timer multiplier.
Signed-off-by: Jere Leppänen <jere.leppanen@nokia.com>
Reviewed-by: Matias Elo <matias.elo@nokia.com>
Diffstat (limited to 'example')
-rw-r--r-- | example/timer/odp_timer_accuracy.c | 228 |
1 files changed, 194 insertions, 34 deletions
diff --git a/example/timer/odp_timer_accuracy.c b/example/timer/odp_timer_accuracy.c index 0952320bb..a241cf564 100644 --- a/example/timer/odp_timer_accuracy.c +++ b/example/timer/odp_timer_accuracy.c @@ -1,5 +1,5 @@ /* Copyright (c) 2018, Linaro Limited - * Copyright (c) 2019-2022, Nokia + * Copyright (c) 2019-2023, Nokia * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -18,11 +18,18 @@ #define MAX_FILENAME 128 +enum mode_e { + MODE_ONESHOT = 0, + MODE_RESTART_ABS, + MODE_RESTART_REL, + MODE_PERIODIC, +}; + typedef struct timer_ctx_t { odp_timer_t timer; odp_event_t event; uint64_t nsec; - + uint64_t count; } timer_ctx_t; typedef struct { @@ -58,7 +65,10 @@ typedef struct test_global_t { unsigned long long num; unsigned long long burst; unsigned long long burst_gap; - int mode; + odp_fract_u64_t freq; + unsigned long long max_multiplier; + unsigned long long multiplier; + enum mode_e mode; int clk_src; int init; int output; @@ -94,15 +104,20 @@ static void print_usage(void) " -r, --res_ns <nsec> Timeout resolution in nsec. Default: period / 10\n" " -R, --res_hz <hertz> Timeout resolution in hertz. Note: resolution can be set\n" " either in nsec or hertz (not both). Default: 0\n" - " -f, --first <nsec> First timer offset in nsec. Default: 300 msec\n" + " -f, --first <nsec> First timer offset in nsec. Default: 0 for periodic mode, otherwise 300 msec\n" " -x, --max_tmo <nsec> Maximum timeout in nsec. When 0, max tmo is calculated from other options. Default: 0\n" " -n, --num <number> Number of timeout periods. Default: 50\n" " -b, --burst <number> Number of timers per a timeout period. Default: 1\n" " -g, --burst_gap <nsec> Gap (in nsec) between timers within a burst. Default: 0\n" + " In periodic mode, first + burst * burst_gap must be less than period length.\n" " -m, --mode <number> Test mode select (default: 0):\n" - " 0: Set all timers at init phase.\n" - " 1: Set first burst of timers at init. Restart timers during test with absolute time.\n" - " 2: Set first burst of timers at init. Restart timers during test with relative time.\n" + " 0: One-shot. Start all timers at init phase.\n" + " 1: One-shot. Each period, restart timers with absolute time.\n" + " 2: One-shot. Each period, restart timers with relative time.\n" + " 3: Periodic.\n" + " -P, --periodic <freq_integer:freq_numer:freq_denom:max_multiplier>\n" + " Periodic timer pool parameters. Default: 5:0:0:1\n" + " -M, --multiplier Periodic timer multiplier. Default: 1\n" " -o, --output <file> Output file for measurement logs\n" " -e, --early_retry <num> When timer restart fails due to ODP_TIMER_TOO_NEAR, retry this many times\n" " with expiration time incremented by the period. Default: 0\n" @@ -126,6 +141,8 @@ static int parse_options(int argc, char *argv[], test_global_t *test_global) {"burst", required_argument, NULL, 'b'}, {"burst_gap", required_argument, NULL, 'g'}, {"mode", required_argument, NULL, 'm'}, + {"periodic", required_argument, NULL, 'P'}, + {"multiplier", required_argument, NULL, 'M'}, {"output", required_argument, NULL, 'o'}, {"early_retry", required_argument, NULL, 'e'}, {"clk_src", required_argument, NULL, 's'}, @@ -133,18 +150,23 @@ static int parse_options(int argc, char *argv[], test_global_t *test_global) {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; - const char *shortopts = "+p:r:R:f:x:n:b:g:m:o:e:s:ih"; + const char *shortopts = "+p:r:R:f:x:n:b:g:m:P:M:o:e:s:ih"; int ret = 0; test_global->opt.period_ns = 200 * ODP_TIME_MSEC_IN_NS; test_global->opt.res_ns = 0; test_global->opt.res_hz = 0; - test_global->opt.offset_ns = 300 * ODP_TIME_MSEC_IN_NS; + test_global->opt.offset_ns = UINT64_MAX; test_global->opt.max_tmo_ns = 0; test_global->opt.num = 50; test_global->opt.burst = 1; test_global->opt.burst_gap = 0; - test_global->opt.mode = 0; + test_global->opt.mode = MODE_ONESHOT; + test_global->opt.freq.integer = ODP_TIME_SEC_IN_NS / test_global->opt.period_ns; + test_global->opt.freq.numer = 0; + test_global->opt.freq.denom = 0; + test_global->opt.max_multiplier = 1; + test_global->opt.multiplier = 1; test_global->opt.clk_src = ODP_CLOCK_DEFAULT; test_global->opt.init = 0; test_global->opt.output = 0; @@ -184,6 +206,16 @@ static int parse_options(int argc, char *argv[], test_global_t *test_global) case 'm': test_global->opt.mode = atoi(optarg); break; + case 'P': + sscanf(optarg, "%" SCNu64 ":%" SCNu64 ":%" SCNu64 ":%llu", + &test_global->opt.freq.integer, + &test_global->opt.freq.numer, + &test_global->opt.freq.denom, + &test_global->opt.max_multiplier); + break; + case 'M': + test_global->opt.multiplier = strtoull(optarg, NULL, 0); + break; case 'o': test_global->opt.output = 1; /* filename is NULL terminated in anycase */ @@ -209,16 +241,28 @@ static int parse_options(int argc, char *argv[], test_global_t *test_global) } } + if (test_global->opt.mode == MODE_PERIODIC) + test_global->opt.period_ns = ODP_TIME_SEC_IN_NS / + odp_fract_u64_to_dbl(&test_global->opt.freq) / + test_global->opt.multiplier; + + if (test_global->opt.offset_ns == UINT64_MAX) { + if (test_global->opt.mode == MODE_PERIODIC) + test_global->opt.offset_ns = 0; + else + test_global->opt.offset_ns = 300 * ODP_TIME_MSEC_IN_NS; + } + /* Default resolution */ if (test_global->opt.res_ns == 0 && test_global->opt.res_hz == 0) test_global->opt.res_ns = test_global->opt.period_ns / 10; test_global->tot_timers = test_global->opt.num * test_global->opt.burst; - if (test_global->opt.mode) - test_global->alloc_timers = test_global->opt.burst; - else + if (test_global->opt.mode == MODE_ONESHOT) test_global->alloc_timers = test_global->tot_timers; + else + test_global->alloc_timers = test_global->opt.burst; return ret; } @@ -236,13 +280,16 @@ static int start_timers(test_global_t *test_global) uint64_t start_tick; uint64_t period_ns, res_ns, res_hz, start_ns, nsec, offset_ns; uint64_t max_res_ns, max_res_hz; + odp_fract_u64_t freq; + uint64_t max_multiplier, multiplier; + uint32_t max_timers; odp_event_t event; odp_timeout_t timeout; odp_timer_set_t ret; odp_time_t time; uint64_t i, j, idx, num_tmo, burst, burst_gap; uint64_t tot_timers, alloc_timers; - int mode; + enum mode_e mode; odp_timer_clk_src_t clk_src; mode = test_global->opt.mode; @@ -253,6 +300,9 @@ static int start_timers(test_global_t *test_global) burst_gap = test_global->opt.burst_gap; period_ns = test_global->opt.period_ns; test_global->period_ns = period_ns; + freq = test_global->opt.freq; + max_multiplier = test_global->opt.max_multiplier; + multiplier = test_global->opt.multiplier; /* Always init globals for destroy calls */ test_global->queue = ODP_QUEUE_INVALID; @@ -297,11 +347,20 @@ static int start_timers(test_global_t *test_global) return -1; } - if (timer_capa.max_timers && - test_global->alloc_timers > timer_capa.max_timers) { + max_timers = timer_capa.max_timers; + + if (mode == MODE_PERIODIC) { + if (timer_capa.periodic.max_pools < 1) { + printf("Error: Periodic timers not supported.\n"); + return -1; + } + max_timers = timer_capa.periodic.max_timers; + } + + if (max_timers && test_global->alloc_timers > max_timers) { printf("Error: Too many timers: %" PRIu64 ".\n" - " Max timers: %u\n", test_global->alloc_timers, - timer_capa.max_timers); + " Max timers: %u\n", + test_global->alloc_timers, max_timers); return -1; } @@ -339,11 +398,10 @@ static int start_timers(test_global_t *test_global) else timer_param.res_hz = res_hz; - if (mode == 0) { + if (mode == MODE_ONESHOT) { timer_param.min_tmo = offset_ns / 2; timer_param.max_tmo = offset_ns + ((num_tmo + 1) * period_ns); } else { - /* periodic mode */ timer_param.min_tmo = period_ns / 10; timer_param.max_tmo = offset_ns + (2 * period_ns); } @@ -358,6 +416,38 @@ static int start_timers(test_global_t *test_global) timer_param.max_tmo = test_global->opt.max_tmo_ns; } + if (mode == MODE_PERIODIC) { + int r; + odp_timer_periodic_capability_t capa; + + capa.base_freq_hz = freq; + capa.max_multiplier = max_multiplier; + capa.res_ns = res_ns; + + r = odp_timer_periodic_capability(test_global->opt.clk_src, &capa); + + if (r < 0) { + printf("Requested periodic timer capabilities are not supported.\n" + "Capabilities: min base freq %g Hz, max base freq %g Hz, " + "max res %" PRIu64 " Hz\n", + odp_fract_u64_to_dbl(&timer_capa.periodic.min_base_freq_hz), + odp_fract_u64_to_dbl(&timer_capa.periodic.max_base_freq_hz), + timer_capa.max_res.res_hz); + return -1; + } + + if (r == 0) { + printf("Requested base frequency is not met, which causes drift.\n" + "Using base frequency %g Hz\n", + odp_fract_u64_to_dbl(&capa.base_freq_hz)); + } + + timer_param.timer_type = ODP_TIMER_TYPE_PERIODIC; + timer_param.res_ns = res_ns; + timer_param.periodic.base_freq_hz = capa.base_freq_hz; + timer_param.periodic.max_multiplier = max_multiplier; + } + timer_param.num_timers = alloc_timers; timer_param.clk_src = clk_src; @@ -365,7 +455,7 @@ static int start_timers(test_global_t *test_global) printf(" clock source: %i\n", test_global->opt.clk_src); printf(" max res nsec: %" PRIu64 "\n", max_res_ns); printf(" max res hertz: %" PRIu64 "\n", max_res_hz); - printf(" max timers capa: %" PRIu32 "\n", timer_capa.max_timers); + printf(" max timers capa: %" PRIu32 "\n", max_timers); printf(" mode: %i\n", mode); printf(" restart retries: %i\n", test_global->opt.early_retry); if (test_global->opt.output) @@ -376,6 +466,13 @@ static int start_timers(test_global_t *test_global) printf(" resolution: %" PRIu64 " nsec\n", res_ns); else printf(" resolution: %" PRIu64 " hz\n", res_hz); + if (mode == MODE_PERIODIC) { + printf(" freq integer: %" PRIu64 "\n", freq.integer); + printf(" freq numer: %" PRIu64 "\n", freq.numer); + printf(" freq denom: %" PRIu64 "\n", freq.denom); + printf(" max_multiplier: %" PRIu64 "\n", max_multiplier); + printf(" multiplier: %" PRIu64 "\n", multiplier); + } printf(" min timeout: %" PRIu64 " nsec\n", timer_param.min_tmo); printf(" max timeout: %" PRIu64 " nsec\n", timer_param.max_tmo); printf(" num timeout: %" PRIu64 "\n", num_tmo); @@ -446,8 +543,8 @@ static int start_timers(test_global_t *test_global) test_global->start_ns = start_ns; test_global->period_tick = odp_timer_ns_to_tick(timer_pool, period_ns); - /* When mode is 1, set only one burst of timers initially */ - if (mode) + /* When mode is not one-shot, set only one burst of timers initially */ + if (mode != MODE_ONESHOT) num_tmo = 1; for (i = 0; i < num_tmo; i++) { @@ -455,14 +552,28 @@ static int start_timers(test_global_t *test_global) timer_ctx_t *ctx = &test_global->timer_ctx[idx]; odp_timer_start_t start_param; - nsec = offset_ns + (i * period_ns) + (j * burst_gap); - ctx->nsec = start_ns + nsec; - - start_param.tick_type = ODP_TIMER_TICK_ABS; - start_param.tick = start_tick + odp_timer_ns_to_tick(timer_pool, nsec); - start_param.tmo_ev = ctx->event; - - ret = odp_timer_start(ctx->timer, &start_param); + if (mode == MODE_PERIODIC) { + odp_timer_periodic_start_t start_param; + + nsec = offset_ns + (j * burst_gap); + ctx->nsec = start_ns + (nsec ? nsec : period_ns); + ctx->count = 0; + start_param.freq_multiplier = test_global->opt.multiplier; + start_param.first_tick = 0; + if (nsec) + start_param.first_tick = + start_tick + odp_timer_ns_to_tick(timer_pool, nsec); + start_param.tmo_ev = ctx->event; + ret = odp_timer_periodic_start(ctx->timer, &start_param); + } else { + nsec = offset_ns + (i * period_ns) + (j * burst_gap); + ctx->nsec = start_ns + nsec; + start_param.tick_type = ODP_TIMER_TICK_ABS; + start_param.tick = + start_tick + odp_timer_ns_to_tick(timer_pool, nsec); + start_param.tmo_ev = ctx->event; + ret = odp_timer_start(ctx->timer, &start_param); + } if (ret != ODP_TIMER_SUCCESS) { printf("Timer[%" PRIu64 "] set failed: %i\n", @@ -580,6 +691,41 @@ static void print_stat(test_global_t *test_global) printf("\n"); } +static void cancel_periodic_timers(test_global_t *test_global) +{ + uint64_t i, alloc_timers; + odp_timer_t timer; + odp_event_t ev; + + if (test_global->opt.mode != MODE_PERIODIC) + return; + + alloc_timers = test_global->alloc_timers; + + for (i = 0; i < alloc_timers; i++) { + timer = test_global->timer_ctx[i].timer; + + if (timer == ODP_TIMER_INVALID) + break; + + if (odp_timer_periodic_cancel(timer)) + printf("Failed to cancel periodic timer.\n"); + } + + while (alloc_timers) { + odp_timeout_t tmo; + timer_ctx_t *ctx; + + ev = odp_schedule(NULL, ODP_SCHED_WAIT); + tmo = odp_timeout_from_event(ev); + ctx = odp_timeout_user_ptr(tmo); + if (odp_timer_periodic_ack(ctx->timer, ev) != 2) + continue; + odp_event_free(ev); + alloc_timers--; + } +} + static void run_test(test_global_t *test_global) { uint64_t burst, num, num_tmo, next_tmo; @@ -592,7 +738,10 @@ static void run_test(test_global_t *test_global) timer_ctx_t *ctx; test_stat_t *stat = &test_global->stat; test_log_t *log = test_global->log; - int mode = test_global->opt.mode; + enum mode_e mode = test_global->opt.mode; + double period = ODP_TIME_SEC_IN_NS / + odp_fract_u64_to_dbl(&test_global->opt.freq) / + test_global->opt.multiplier; num = 0; next_tmo = 1; @@ -609,6 +758,11 @@ static void run_test(test_global_t *test_global) tmo = odp_timeout_from_event(ev); ctx = odp_timeout_user_ptr(tmo); tmo_ns = ctx->nsec; + if (mode == MODE_PERIODIC) { + /* round to nearest */ + tmo_ns += ctx->count * period + .5; + ctx->count++; + } if (log) log[i].tmo_ns = tmo_ns; @@ -638,7 +792,8 @@ static void run_test(test_global_t *test_global) stat->num_exact++; } - if (mode && next_tmo < num_tmo) { + if ((mode == MODE_RESTART_ABS || mode == MODE_RESTART_REL) && + next_tmo < num_tmo) { /* Reset timer for next period */ odp_timer_t tim; uint64_t nsec, tick; @@ -654,7 +809,7 @@ static void run_test(test_global_t *test_global) /* Depending on the option, retry when expiration * time is too early */ for (j = 0; j < retries + 1; j++) { - if (mode == 1) { + if (mode == MODE_RESTART_ABS) { /* Absolute time */ ctx->nsec += period_ns; nsec = ctx->nsec - start_ns; @@ -685,6 +840,9 @@ static void run_test(test_global_t *test_global) "%" PRIu64 "\n", ret, ctx->nsec); return; } + } else if (mode == MODE_PERIODIC) { + if (odp_timer_periodic_ack(ctx->timer, ev)) + printf("Failed to ack a periodic timer.\n"); } else { odp_event_free(ev); } @@ -698,6 +856,8 @@ static void run_test(test_global_t *test_global) } + cancel_periodic_timers(test_global); + /* Free current scheduler context. There should be no more events. */ while ((ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT)) != ODP_EVENT_INVALID) { |