aboutsummaryrefslogtreecommitdiff
path: root/example
diff options
context:
space:
mode:
authorJere Leppänen <jere.leppanen@nokia.com>2023-02-09 11:20:16 +0200
committerMatias Elo <matias.elo@nokia.com>2023-03-07 09:58:03 +0200
commit6b248be5de9f68458737f5e28f203e2b5a69ded4 (patch)
treee11b20877a3ebd63ba4cdbc88cb85a533633c38d /example
parenta5953871cba3f5722c59ef98b0632c997dc34fbe (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.c228
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) {