API Reference Manual  1.45.0
odp_timer_accuracy.c

ODP timer accuracy test application

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) 2018 Linaro Limited
* Copyright (c) 2019-2023 Nokia
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <odp_api.h>
#include <odp/helper/odph_api.h>
#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
#define MAX_QUEUES 1024
#define MAX_FILENAME 128
enum mode_e {
MODE_ONESHOT = 0,
MODE_RESTART_ABS,
MODE_RESTART_REL,
MODE_PERIODIC,
};
typedef struct test_opt_t {
int cpu_count;
unsigned long long period_ns;
long long res_ns;
unsigned long long res_hz;
unsigned long long offset_ns;
unsigned long long max_tmo_ns;
unsigned long long num;
unsigned long long num_warmup;
unsigned long long burst;
unsigned long long burst_gap;
unsigned long long max_multiplier;
unsigned long long multiplier;
enum mode_e mode;
int clk_src;
odp_queue_type_t queue_type;
int num_queue;
int groups;
int init;
int output;
int early_retry;
uint64_t warmup_timers;
uint64_t tot_timers;
uint64_t alloc_timers;
char filename[MAX_FILENAME];
} test_opt_t;
typedef struct timer_ctx_t {
odp_timer_t timer;
odp_event_t event;
uint64_t nsec;
uint64_t count;
uint64_t first_period;
int tmo_tick;
int64_t first_tmo_diff;
int64_t nsec_final;
} timer_ctx_t;
typedef struct {
uint64_t nsec_before_sum;
uint64_t nsec_before_min;
uint64_t nsec_before_min_idx;
uint64_t nsec_before_max;
uint64_t nsec_before_max_idx;
uint64_t nsec_after_sum;
uint64_t nsec_after_min;
uint64_t nsec_after_min_idx;
uint64_t nsec_after_max;
uint64_t nsec_after_max_idx;
uint64_t num_before;
uint64_t num_exact;
uint64_t num_after;
uint64_t num_too_near;
} test_stat_t;
typedef struct test_log_t {
uint64_t tmo_ns;
int64_t diff_ns;
int tid;
} test_log_t;
typedef struct test_global_t {
test_opt_t opt;
test_stat_t stat[MAX_WORKERS];
odp_queue_t queue[MAX_QUEUES];
odp_schedule_group_t group[MAX_WORKERS];
odp_timer_pool_t timer_pool;
odp_pool_t timeout_pool;
timer_ctx_t *timer_ctx;
double res_ns;
uint64_t start_tick;
uint64_t start_ns;
uint64_t period_tick;
double period_dbl;
odp_fract_u64_t base_freq;
test_log_t *log;
FILE *file;
odp_barrier_t barrier;
odp_atomic_u64_t last_events;
} test_global_t;
static void print_usage(void)
{
printf("\n"
"Timer accuracy test application.\n"
"\n"
"OPTIONS:\n"
" -c, --count <num> CPU count, 0=all available, default=1\n"
" -p, --period <nsec> Timeout period in nsec. Not used in periodic mode. Default: 200 msec\n"
" -r, --res_ns <nsec> Timeout resolution in nsec. Default value is 0. Special values:\n"
" 0: Use period / 10 as the resolution\n"
" -1: In periodic mode, use resolution from capabilities\n"
" -R, --res_hz <hertz> Timeout resolution in hertz. Set resolution either with -r (nsec) or -R (hertz),\n"
" and leave other to 0. Default: 0 (not used)\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. Not used in periodic mode.\n"
" When 0, max tmo is calculated from other options. Default: 0\n"
" -n, --num <number> Number of timeout periods. Default: 50\n"
" -w, --warmup <number> Number of warmup periods. Default: 0\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: 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 (5 Hz)\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"
" -s, --clk_src Clock source select (default 0):\n"
" 0: ODP_CLOCK_DEFAULT\n"
" 1: ODP_CLOCK_SRC_1, ...\n"
" -t, --queue_type Queue sync type. Default is 0 (PARALLEL).\n"
" 0: PARALLEL\n"
" 1: ATOMIC\n"
" 2: ORDERED\n"
" -q, --num_queue Number of queues. Default is 1.\n"
" -G, --sched_groups Use dedicated schedule group for each worker.\n"
" -i, --init Set global init parameters. Default: init params not set.\n"
" -h, --help Display help and exit.\n\n");
}
static int parse_options(int argc, char *argv[], test_opt_t *test_opt)
{
int opt, long_index;
const struct option longopts[] = {
{"count", required_argument, NULL, 'c'},
{"period", required_argument, NULL, 'p'},
{"res_ns", required_argument, NULL, 'r'},
{"res_hz", required_argument, NULL, 'R'},
{"first", required_argument, NULL, 'f'},
{"max_tmo", required_argument, NULL, 'x'},
{"num", required_argument, NULL, 'n'},
{"warmup", required_argument, NULL, 'w'},
{"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'},
{"queue_type", required_argument, NULL, 't'},
{"num_queue", required_argument, NULL, 'q'},
{"sched_groups", no_argument, NULL, 'G'},
{"init", no_argument, NULL, 'i'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
const char *shortopts = "+c:p:r:R:f:x:n:w:b:g:m:P:M:o:e:s:t:q:Gih";
int ret = 0;
memset(test_opt, 0, sizeof(*test_opt));
test_opt->cpu_count = 1;
test_opt->period_ns = 200 * ODP_TIME_MSEC_IN_NS;
test_opt->res_ns = 0;
test_opt->res_hz = 0;
test_opt->offset_ns = UINT64_MAX;
test_opt->max_tmo_ns = 0;
test_opt->num = 50;
test_opt->num_warmup = 0;
test_opt->burst = 1;
test_opt->burst_gap = 0;
test_opt->mode = MODE_ONESHOT;
test_opt->freq.integer = ODP_TIME_SEC_IN_NS / test_opt->period_ns;
test_opt->freq.numer = 0;
test_opt->freq.denom = 0;
test_opt->max_multiplier = 1;
test_opt->multiplier = 1;
test_opt->clk_src = ODP_CLOCK_DEFAULT;
test_opt->queue_type = ODP_SCHED_SYNC_PARALLEL;
test_opt->groups = 0;
test_opt->num_queue = 1;
test_opt->init = 0;
test_opt->output = 0;
test_opt->early_retry = 0;
while (1) {
opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
if (opt == -1)
break; /* No more options */
switch (opt) {
case 'c':
test_opt->cpu_count = atoi(optarg);
break;
case 'p':
test_opt->period_ns = strtoull(optarg, NULL, 0);
break;
case 'r':
test_opt->res_ns = strtoll(optarg, NULL, 0);
break;
case 'R':
test_opt->res_hz = strtoull(optarg, NULL, 0);
break;
case 'f':
test_opt->offset_ns = strtoull(optarg, NULL, 0);
break;
case 'x':
test_opt->max_tmo_ns = strtoull(optarg, NULL, 0);
break;
case 'n':
test_opt->num = strtoull(optarg, NULL, 0);
break;
case 'w':
test_opt->num_warmup = strtoull(optarg, NULL, 0);
break;
case 'b':
test_opt->burst = strtoull(optarg, NULL, 0);
break;
case 'g':
test_opt->burst_gap = strtoull(optarg, NULL, 0);
break;
case 'm':
test_opt->mode = atoi(optarg);
break;
case 'P':
sscanf(optarg, "%" SCNu64 ":%" SCNu64 ":%" SCNu64 ":%llu",
&test_opt->freq.integer, &test_opt->freq.numer,
&test_opt->freq.denom, &test_opt->max_multiplier);
break;
case 'M':
test_opt->multiplier = strtoull(optarg, NULL, 0);
break;
case 'o':
test_opt->output = 1;
if (strlen(optarg) >= MAX_FILENAME) {
printf("Filename too long\n");
return -1;
}
odph_strcpy(test_opt->filename, optarg, MAX_FILENAME);
break;
case 'e':
test_opt->early_retry = atoi(optarg);
break;
case 's':
test_opt->clk_src = atoi(optarg);
break;
case 't':
switch (atoi(optarg)) {
case 1:
test_opt->queue_type = ODP_SCHED_SYNC_ATOMIC;
break;
case 2:
test_opt->queue_type = ODP_SCHED_SYNC_ORDERED;
break;
default:
test_opt->queue_type = ODP_SCHED_SYNC_PARALLEL;
break;
}
break;
case 'q':
test_opt->num_queue = atoi(optarg);
break;
case 'G':
test_opt->groups = 1;
break;
case 'i':
test_opt->init = 1;
break;
case 'h':
print_usage();
ret = -1;
break;
default:
print_usage();
ret = -1;
break;
}
}
if (test_opt->mode == MODE_PERIODIC) {
if ((test_opt->freq.integer == 0 && test_opt->freq.numer == 0) ||
(test_opt->freq.numer != 0 && test_opt->freq.denom == 0)) {
printf("Bad frequency\n");
return -1;
}
test_opt->period_ns =
if (test_opt->offset_ns == UINT64_MAX)
test_opt->offset_ns = 0;
} else {
if (test_opt->res_ns < 0) {
printf("Resolution (res_ns) must be >= 0 with single shot timer\n");
return -1;
}
if (test_opt->offset_ns == UINT64_MAX)
test_opt->offset_ns = 300 * ODP_TIME_MSEC_IN_NS;
}
test_opt->warmup_timers = test_opt->num_warmup * test_opt->burst;
test_opt->tot_timers =
test_opt->warmup_timers + test_opt->num * test_opt->burst;
if (test_opt->mode == MODE_ONESHOT)
test_opt->alloc_timers = test_opt->tot_timers;
else
test_opt->alloc_timers = test_opt->burst;
return ret;
}
static int single_shot_params(test_global_t *test_global, odp_timer_pool_param_t *timer_param,
{
uint64_t res_ns, res_hz;
uint64_t max_res_ns, max_res_hz;
uint64_t period_ns = test_global->opt.period_ns;
uint64_t num_tmo = test_global->opt.num + test_global->opt.num_warmup;
uint64_t offset_ns = test_global->opt.offset_ns;
enum mode_e mode = test_global->opt.mode;
max_res_ns = timer_capa->max_res.res_ns;
max_res_hz = timer_capa->max_res.res_hz;
/* Default resolution */
if (test_global->opt.res_ns == 0 && test_global->opt.res_hz == 0) {
res_ns = test_global->opt.period_ns / 10;
res_hz = 0;
} else if (test_global->opt.res_ns) {
res_ns = test_global->opt.res_ns;
res_hz = 0;
} else {
res_ns = 0;
res_hz = test_global->opt.res_hz;
}
if (res_ns && res_ns < max_res_ns) {
printf("Resolution %" PRIu64 " nsec too high. Highest resolution %" PRIu64 " nsec. "
"Default resolution is period / 10.\n\n",
res_ns, max_res_ns);
return -1;
}
if (res_hz && res_hz > max_res_hz) {
printf("Resolution %" PRIu64 " hz too high. Highest resolution %" PRIu64 " hz. "
"Default resolution is period / 10.\n\n",
res_hz, max_res_hz);
return -1;
}
if (res_ns)
timer_param->res_ns = res_ns;
else
timer_param->res_hz = res_hz;
if (mode == MODE_ONESHOT) {
timer_param->min_tmo = offset_ns / 2;
timer_param->max_tmo = offset_ns + ((num_tmo + 1) * period_ns);
} else {
timer_param->min_tmo = period_ns / 10;
timer_param->max_tmo = offset_ns + (2 * period_ns);
}
if (test_global->opt.max_tmo_ns) {
if (test_global->opt.max_tmo_ns < timer_param->max_tmo) {
printf("Max tmo is too small. Must be at least %" PRIu64 " nsec.\n",
timer_param->max_tmo);
return -1;
}
timer_param->max_tmo = test_global->opt.max_tmo_ns;
}
printf(" period: %" PRIu64 " nsec\n", period_ns);
printf(" max res nsec: %" PRIu64 "\n", max_res_ns);
printf(" max res hertz: %" PRIu64 "\n", max_res_hz);
test_global->period_dbl = period_ns;
return 0;
}
static int periodic_params(test_global_t *test_global, odp_timer_pool_param_t *timer_param,
{
int ret;
uint64_t res_ns;
double freq_dbl, min_freq, max_freq;
double opt_freq = odp_fract_u64_to_dbl(&test_global->opt.freq);
odp_fract_u64_t freq = test_global->opt.freq;
uint64_t res_hz = test_global->opt.res_hz;
uint64_t max_multiplier = test_global->opt.max_multiplier;
uint64_t multiplier = test_global->opt.multiplier;
if (res_hz) {
res_ns = ODP_TIME_SEC_IN_NS / res_hz;
} else {
res_ns = test_global->opt.res_ns;
/* Default resolution */
if (res_ns == 0)
res_ns = ODP_TIME_SEC_IN_NS / (10 * multiplier * opt_freq);
}
if (res_ns == 0) {
printf("Too high resolution\n");
return -1;
}
/* Resolution from capa */
if (test_global->opt.res_ns < 0)
res_ns = 0;
capa.base_freq_hz = freq;
capa.max_multiplier = max_multiplier;
capa.res_ns = res_ns;
ret = odp_timer_periodic_capability(test_global->opt.clk_src, &capa);
if (ret < 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", min_freq, max_freq, timer_capa->max_res.res_hz);
return -1;
}
if (ret == 0) {
printf("Requested base frequency is not met. Using %.2f Hz instead of %.2f Hz.\n",
freq = capa.base_freq_hz;
}
if (res_ns == 0)
res_ns = capa.res_ns;
freq_dbl = odp_fract_u64_to_dbl(&freq);
test_global->base_freq = freq;
test_global->period_dbl = ODP_TIME_SEC_IN_NS / (multiplier * freq_dbl);
/* Min/max tmo are ignored, leave those to default values */
timer_param->periodic.base_freq_hz = freq;
timer_param->periodic.max_multiplier = max_multiplier;
if (res_hz)
timer_param->res_hz = res_hz;
else
timer_param->res_ns = res_ns;
printf(" min freq capa: %.2f hz\n", min_freq);
printf(" max freq capa: %.2f hz\n", max_freq);
printf(" freq option: %.2f hz\n", opt_freq);
printf(" freq: %.2f hz\n", freq_dbl);
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(" timer freq: %.2f hz\n", multiplier * freq_dbl);
printf(" timer period: %.2f nsec\n", test_global->period_dbl);
printf(" resolution capa: %" PRIu64 " nsec\n", capa.res_ns);
return 0;
}
static int create_timers(test_global_t *test_global)
{
odp_pool_t pool;
odp_pool_param_t pool_param;
odp_timer_pool_t timer_pool;
odp_timer_t timer;
odp_queue_t *queue;
odp_queue_param_t queue_param;
uint64_t offset_ns;
uint32_t max_timers;
odp_event_t event;
odp_timeout_t timeout;
uint64_t i, num_tmo, num_warmup, burst, burst_gap;
uint64_t tot_timers, alloc_timers;
enum mode_e mode;
int ret;
mode = test_global->opt.mode;
alloc_timers = test_global->opt.alloc_timers;
tot_timers = test_global->opt.tot_timers;
num_warmup = test_global->opt.num_warmup;
num_tmo = num_warmup + test_global->opt.num;
burst = test_global->opt.burst;
burst_gap = test_global->opt.burst_gap;
offset_ns = test_global->opt.offset_ns;
queue = test_global->queue;
group = test_global->group;
/* Always init globals for destroy calls */
test_global->timer_pool = ODP_TIMER_POOL_INVALID;
test_global->timeout_pool = ODP_POOL_INVALID;
for (i = 0; i < alloc_timers; i++) {
test_global->timer_ctx[i].timer = ODP_TIMER_INVALID;
test_global->timer_ctx[i].event = ODP_EVENT_INVALID;
}
if (test_global->opt.groups) {
/* Create groups */
for (i = 0; i < (uint64_t)test_global->opt.cpu_count; i++) {
group[i] = odp_schedule_group_create(NULL, &zero);
if (group[i] == ODP_SCHED_GROUP_INVALID) {
printf("Group create failed.\n");
return -1;
}
}
}
odp_queue_param_init(&queue_param);
queue_param.type = ODP_QUEUE_TYPE_SCHED;
queue_param.sched.sync = test_global->opt.queue_type;
for (i = 0; i < (uint64_t)test_global->opt.num_queue; i++) {
if (test_global->opt.groups)
queue_param.sched.group = group[i % test_global->opt.cpu_count];
queue[i] = odp_queue_create(NULL, &queue_param);
if (queue[i] == ODP_QUEUE_INVALID) {
printf("Queue create failed.\n");
return -1;
}
}
odp_pool_param_init(&pool_param);
pool_param.type = ODP_POOL_TIMEOUT;
pool_param.tmo.num = alloc_timers;
pool = odp_pool_create("timeout pool", &pool_param);
if (pool == ODP_POOL_INVALID) {
printf("Timeout pool create failed.\n");
return -1;
}
test_global->timeout_pool = pool;
clk_src = test_global->opt.clk_src;
if (odp_timer_capability(clk_src, &timer_capa)) {
printf("Timer capa failed\n");
return -1;
}
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->opt.alloc_timers > max_timers) {
printf("Error: Too many timers: %" PRIu64 ".\n"
" Max timers: %u\n",
test_global->opt.alloc_timers, max_timers);
return -1;
}
printf("\nTest parameters:\n");
printf(" clock source: %i\n", clk_src);
printf(" max timers capa: %" PRIu32 "\n", max_timers);
printf(" mode: %i\n", mode);
printf(" queue type: %i\n", test_global->opt.queue_type);
printf(" num queue: %i\n", test_global->opt.num_queue);
printf(" sched groups: %s\n", test_global->opt.groups ? "yes" : "no");
if (mode == MODE_PERIODIC)
ret = periodic_params(test_global, &timer_param, &timer_capa);
else
ret = single_shot_params(test_global, &timer_param, &timer_capa);
if (ret)
return ret;
if (timer_param.res_hz) {
test_global->res_ns = 1000000000.0 / timer_param.res_hz;
printf(" resolution: %" PRIu64 " Hz\n", timer_param.res_hz);
} else {
test_global->res_ns = timer_param.res_ns;
printf(" resolution: %" PRIu64 " nsec\n", timer_param.res_ns);
}
timer_param.num_timers = alloc_timers;
timer_param.clk_src = clk_src;
printf(" restart retries: %i\n", test_global->opt.early_retry);
if (test_global->opt.output)
printf(" log file: %s\n", test_global->opt.filename);
printf(" start offset: %" PRIu64 " nsec\n", offset_ns);
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);
printf(" num warmup: %" PRIu64 "\n", num_warmup);
printf(" burst size: %" PRIu64 "\n", burst);
printf(" burst gap: %" PRIu64 "\n", burst_gap);
printf(" total timers: %" PRIu64 "\n", tot_timers);
printf(" warmup timers: %" PRIu64 "\n", test_global->opt.warmup_timers);
printf(" alloc timers: %" PRIu64 "\n", alloc_timers);
printf(" warmup time: %.2f sec\n",
(offset_ns + (num_warmup * test_global->period_dbl)) / 1000000000.0);
printf(" test run time: %.2f sec\n\n",
(offset_ns + (num_tmo * test_global->period_dbl)) / 1000000000.0);
timer_pool = odp_timer_pool_create("timer_accuracy", &timer_param);
if (timer_pool == ODP_TIMER_POOL_INVALID) {
printf("Timer pool create failed\n");
return -1;
}
if (odp_timer_pool_start_multi(&timer_pool, 1) != 1) {
ODPH_ERR("Timer pool start failed\n");
return -1;
}
odp_timer_pool_print(timer_pool);
/* Spend some time so that current tick would not be zero */
test_global->timer_pool = timer_pool;
for (i = 0; i < alloc_timers; i++) {
timer_ctx_t *ctx = &test_global->timer_ctx[i];
timer = odp_timer_alloc(timer_pool, queue[i % test_global->opt.num_queue], ctx);
if (timer == ODP_TIMER_INVALID) {
printf("Timer alloc failed.\n");
return -1;
}
ctx->timer = timer;
timeout = odp_timeout_alloc(pool);
if (timeout == ODP_TIMEOUT_INVALID) {
printf("Timeout alloc failed\n");
return -1;
}
ctx->event = odp_timeout_to_event(timeout);
}
/* Run scheduler few times to ensure that (software) timer is active */
for (i = 0; i < 1000; i++) {
if (event != ODP_EVENT_INVALID) {
printf("Spurious event received\n");
return -1;
}
}
return 0;
}
static int start_timers(test_global_t *test_global)
{
odp_timer_pool_t timer_pool;
uint64_t start_tick;
uint64_t period_ns, start_ns, nsec, offset_ns;
odp_time_t time;
uint64_t i, j, idx, num_tmo, num_warmup, burst, burst_gap;
enum mode_e mode;
mode = test_global->opt.mode;
num_warmup = test_global->opt.num_warmup;
num_tmo = num_warmup + test_global->opt.num;
burst = test_global->opt.burst;
burst_gap = test_global->opt.burst_gap;
period_ns = test_global->opt.period_ns;
offset_ns = test_global->opt.offset_ns;
timer_pool = test_global->timer_pool;
idx = 0;
/* Record test start time and tick. Memory barriers forbid compiler and out-of-order
* CPU to move samples apart. */
start_tick = odp_timer_current_tick(timer_pool);
time = odp_time_global();
start_ns = odp_time_to_ns(time);
test_global->start_tick = start_tick;
test_global->start_ns = start_ns;
test_global->period_tick = odp_timer_ns_to_tick(timer_pool, period_ns);
/* 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++) {
for (j = 0; j < burst; j++) {
timer_ctx_t *ctx = &test_global->timer_ctx[idx];
odp_timer_start_t start_param;
if (mode == MODE_PERIODIC) {
nsec = offset_ns + (j * burst_gap);
/* By default, timer starts one period after current time. Round
* floating point to closest integer number. */
ctx->nsec = start_ns + test_global->period_dbl + 0.5;
if (nsec)
ctx->nsec = start_ns + nsec;
ctx->count = 0;
ctx->first_period = start_tick +
test_global->period_dbl + 0.5);
periodic_start.freq_multiplier = test_global->opt.multiplier;
periodic_start.first_tick = 0;
if (nsec)
periodic_start.first_tick =
start_tick + odp_timer_ns_to_tick(timer_pool, nsec);
periodic_start.tmo_ev = ctx->event;
retval = odp_timer_periodic_start(ctx->timer, &periodic_start);
} else {
nsec = offset_ns + (i * period_ns) + (j * burst_gap);
ctx->nsec = start_ns + nsec;
start_param.tick =
start_tick + odp_timer_ns_to_tick(timer_pool, nsec);
start_param.tmo_ev = ctx->event;
retval = odp_timer_start(ctx->timer, &start_param);
}
if (retval != ODP_TIMER_SUCCESS) {
printf("Timer[%" PRIu64 "] set failed: %i\n", idx, retval);
return -1;
}
idx++;
}
}
printf("\nStarting timers took %" PRIu64 " nsec\n", odp_time_global_ns() - start_ns);
return 0;
}
static int destroy_timers(test_global_t *test_global)
{
uint64_t i, alloc_timers;
odp_timer_t timer;
int ret = 0;
alloc_timers = test_global->opt.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_free(timer)) {
printf("Timer free failed: %" PRIu64 "\n", i);
ret = -1;
}
}
if (test_global->timer_pool != ODP_TIMER_POOL_INVALID)
odp_timer_pool_destroy(test_global->timer_pool);
if (test_global->timeout_pool != ODP_POOL_INVALID) {
if (odp_pool_destroy(test_global->timeout_pool)) {
printf("Pool destroy failed.\n");
ret = -1;
}
}
for (i = 0; i < (uint64_t)test_global->opt.num_queue; i++) {
if (odp_queue_destroy(test_global->queue[i])) {
printf("Queue destroy failed.\n");
ret = -1;
}
}
if (test_global->opt.groups) {
for (i = 0; i < (uint64_t)test_global->opt.cpu_count; i++) {
if (odp_schedule_group_destroy(test_global->group[i])) {
printf("Group destroy failed.\n");
ret = -1;
}
}
}
return ret;
}
static void print_nsec_error(const char *str, int64_t nsec, double res_ns,
int tid, int idx)
{
printf(" %s: %12" PRIi64 " / %.3fx resolution",
str, nsec, (double)nsec / res_ns);
if (tid >= 0)
printf(", thread %d", tid);
if (idx >= 0)
printf(", event %d", idx);
printf("\n");
}
static void print_stat(test_global_t *test_global)
{
test_stat_t test_stat;
test_stat_t *stat = &test_stat;
uint64_t tot_timers;
test_stat_t *s = test_global->stat;
test_log_t *log = test_global->log;
double res_ns = test_global->res_ns;
uint64_t ave_after = 0;
uint64_t ave_before = 0;
uint64_t nsec_before_min_tid = 0;
uint64_t nsec_before_max_tid = 0;
uint64_t nsec_after_min_tid = 0;
uint64_t nsec_after_max_tid = 0;
memset(stat, 0, sizeof(*stat));
stat->nsec_before_min = UINT64_MAX;
stat->nsec_after_min = UINT64_MAX;
for (int i = 1; i < test_global->opt.cpu_count + 1; i++) {
stat->nsec_before_sum += s[i].nsec_before_sum;
stat->nsec_after_sum += s[i].nsec_after_sum;
stat->num_before += s[i].num_before;
stat->num_exact += s[i].num_exact;
stat->num_after += s[i].num_after;
stat->num_too_near += s[i].num_too_near;
if (s[i].nsec_before_min < stat->nsec_before_min) {
stat->nsec_before_min = s[i].nsec_before_min;
stat->nsec_before_min_idx = s[i].nsec_before_min_idx;
nsec_before_min_tid = i;
}
if (s[i].nsec_after_min < stat->nsec_after_min) {
stat->nsec_after_min = s[i].nsec_after_min;
stat->nsec_after_min_idx = s[i].nsec_after_min_idx;
nsec_after_min_tid = i;
}
if (s[i].nsec_before_max > stat->nsec_before_max) {
stat->nsec_before_max = s[i].nsec_before_max;
stat->nsec_before_max_idx = s[i].nsec_before_max_idx;
nsec_before_max_tid = i;
}
if (s[i].nsec_after_max > stat->nsec_after_max) {
stat->nsec_after_max = s[i].nsec_after_max;
stat->nsec_after_max_idx = s[i].nsec_after_max_idx;
nsec_after_max_tid = i;
}
}
if (stat->num_after)
ave_after = stat->nsec_after_sum / stat->num_after;
else
stat->nsec_after_min = 0;
if (stat->num_before)
ave_before = stat->nsec_before_sum / stat->num_before;
else
stat->nsec_before_min = 0;
tot_timers = stat->num_before + stat->num_after + stat->num_exact;
if (log) {
FILE *file = test_global->file;
fprintf(file, " Timer thread tmo(ns) diff(ns)\n");
for (uint64_t i = 0; i < tot_timers; i++) {
fprintf(file, "%8" PRIu64 " %7u %12" PRIu64 " %10"
PRIi64 "\n", i, log[i].tid, log[i].tmo_ns, log[i].diff_ns);
}
fprintf(file, "\n");
}
printf("\nTest results:\n");
printf(" num after: %12" PRIu64 " / %.2f%%\n",
stat->num_after, 100.0 * stat->num_after / tot_timers);
printf(" num before: %12" PRIu64 " / %.2f%%\n",
stat->num_before, 100.0 * stat->num_before / tot_timers);
printf(" num exact: %12" PRIu64 " / %.2f%%\n",
stat->num_exact, 100.0 * stat->num_exact / tot_timers);
printf(" num retry: %12" PRIu64 " / %.2f%%\n",
stat->num_too_near, 100.0 * stat->num_too_near / tot_timers);
printf(" error after (nsec):\n");
print_nsec_error("min", stat->nsec_after_min, res_ns, nsec_after_min_tid,
stat->nsec_after_min_idx);
print_nsec_error("max", stat->nsec_after_max, res_ns, nsec_after_max_tid,
stat->nsec_after_max_idx);
print_nsec_error("ave", ave_after, res_ns, -1, -1);
printf(" error before (nsec):\n");
print_nsec_error("min", stat->nsec_before_min, res_ns, nsec_before_min_tid,
stat->nsec_before_min_idx);
print_nsec_error("max", stat->nsec_before_max, res_ns, nsec_before_max_tid,
stat->nsec_before_max_idx);
print_nsec_error("ave", ave_before, res_ns, -1, -1);
if (test_global->opt.mode == MODE_PERIODIC && !test_global->opt.offset_ns) {
int idx = 0;
int64_t max = 0;
for (int i = 0; i < (int)test_global->opt.alloc_timers; i++) {
timer_ctx_t *t = &test_global->timer_ctx[i];
int64_t v = t->first_tmo_diff;
if (ODPH_ABS(v) > ODPH_ABS(max)) {
max = v;
idx = i;
}
}
printf(" first timeout difference to one period, based on %s (nsec):\n",
test_global->timer_ctx[idx].tmo_tick ? "timeout tick" : "time");
print_nsec_error("max", max, res_ns, -1, -1);
}
int64_t max = 0;
for (int i = 0; i < (int)test_global->opt.alloc_timers; i++) {
timer_ctx_t *t = &test_global->timer_ctx[i];
int64_t v = t->nsec_final;
if (ODPH_ABS(v) > ODPH_ABS(max))
max = v;
}
printf(" final timeout error (nsec):\n");
print_nsec_error("max", max, res_ns, -1, -1);
printf("\n");
}
static void cancel_periodic_timers(test_global_t *test_global)
{
uint64_t i, alloc_timers;
odp_timer_t timer;
alloc_timers = test_global->opt.alloc_timers;
for (i = 0; i < alloc_timers; i++) {
timer = test_global->timer_ctx[i].timer;
if (timer == ODP_TIMER_INVALID)
break;
printf("Failed to cancel periodic timer.\n");
}
}
static int run_test(void *arg)
{
test_global_t *test_global = (test_global_t *)arg;
odp_time_t time;
uint64_t time_ns, diff_ns;
uint64_t tmo_ns;
timer_ctx_t *ctx;
test_log_t *log = test_global->log;
enum mode_e mode = test_global->opt.mode;
uint64_t tot_timers = test_global->opt.tot_timers;
double period_dbl = test_global->period_dbl;
odp_timer_pool_t tp = test_global->timer_pool;
int tid = odp_thread_id();
if (tid > test_global->opt.cpu_count) {
printf("Error: tid %d is larger than cpu_count %d.\n", tid,
test_global->opt.cpu_count);
return 0;
}
test_stat_t *stat = &test_global->stat[tid];
memset(stat, 0, sizeof(*stat));
stat->nsec_before_min = UINT64_MAX;
stat->nsec_after_min = UINT64_MAX;
if (test_global->opt.groups) {
odp_thrmask_set(&mask, tid);
group = test_global->group[tid - 1];
if (odp_schedule_group_join(group, &mask)) {
printf("odp_schedule_group_join() failed\n");
return 0;
}
}
odp_barrier_wait(&test_global->barrier);
while (1) {
ev = odp_schedule(NULL, wait);
if (ev == ODP_EVENT_INVALID) {
if (mode == MODE_PERIODIC) {
if (odp_atomic_load_u64(&test_global->last_events) >=
test_global->opt.alloc_timers)
break;
} else if (odp_atomic_load_u64(&test_global->events) >= tot_timers) {
break;
}
continue;
}
time_ns = odp_time_to_ns(time);
tmo_ns = ctx->nsec;
if (mode == MODE_PERIODIC) {
if (!ctx->count && !test_global->opt.offset_ns) {
/*
* If first_tick is zero, the API allows the implementation to
* place the timer where it can, so we have to adjust our
* expectation of the timeout time.
*/
uint64_t tmo_tick = odp_timeout_tick(tmo);
if (tmo_tick) {
/*
* Adjust by the difference between one period after start
* time and the timeout tick.
*/
ctx->tmo_tick = 1;
ctx->first_tmo_diff =
(int64_t)odp_timer_tick_to_ns(tp, tmo_tick) -
(int64_t)odp_timer_tick_to_ns(tp, ctx->first_period);
tmo_ns += ctx->first_tmo_diff;
} else {
/*
* Timeout tick is not provided, so the best we can do is
* to just take the current time as a baseline.
*/
ctx->first_tmo_diff = (int64_t)time_ns - (int64_t)tmo_ns;
tmo_ns = ctx->nsec = time_ns;
}
ctx->nsec = tmo_ns;
}
/* round to closest integer number */
tmo_ns += ctx->count * period_dbl + 0.5;
ctx->count++;
}
uint64_t events = odp_atomic_fetch_inc_u64(&test_global->events);
if (events >= test_global->opt.warmup_timers && events < tot_timers) {
uint64_t i = events - test_global->opt.warmup_timers;
ctx->nsec_final = (int64_t)time_ns - (int64_t)tmo_ns;
if (log) {
log[i].tmo_ns = tmo_ns;
log[i].tid = tid;
}
if (time_ns > tmo_ns) {
diff_ns = time_ns - tmo_ns;
stat->num_after++;
stat->nsec_after_sum += diff_ns;
if (diff_ns < stat->nsec_after_min) {
stat->nsec_after_min = diff_ns;
stat->nsec_after_min_idx = i;
}
if (diff_ns > stat->nsec_after_max) {
stat->nsec_after_max = diff_ns;
stat->nsec_after_max_idx = i;
}
if (log)
log[i].diff_ns = diff_ns;
} else if (time_ns < tmo_ns) {
diff_ns = tmo_ns - time_ns;
stat->num_before++;
stat->nsec_before_sum += diff_ns;
if (diff_ns < stat->nsec_before_min) {
stat->nsec_before_min = diff_ns;
stat->nsec_before_min_idx = i;
}
if (diff_ns > stat->nsec_before_max) {
stat->nsec_before_max = diff_ns;
stat->nsec_before_max_idx = i;
}
if (log)
log[i].diff_ns = -diff_ns;
} else {
stat->num_exact++;
}
}
if ((mode == MODE_RESTART_ABS || mode == MODE_RESTART_REL) &&
events < tot_timers - 1) {
/* Reset timer for next period */
uint64_t nsec, tick;
unsigned int j;
unsigned int retries = test_global->opt.early_retry;
uint64_t start_ns = test_global->start_ns;
uint64_t period_ns = test_global->opt.period_ns;
odp_timer_start_t start_param;
tim = ctx->timer;
/* Depending on the option, retry when expiration
* time is too early */
for (j = 0; j < retries + 1; j++) {
if (mode == MODE_RESTART_ABS) {
/* Absolute time */
ctx->nsec += period_ns;
nsec = ctx->nsec - start_ns;
tick = test_global->start_tick +
} else {
/* Relative time */
tick = test_global->period_tick;
time = odp_time_local();
time_ns = odp_time_to_ns(time);
ctx->nsec = time_ns + period_ns;
}
start_param.tmo_ev = ev;
start_param.tick = tick;
ret = odp_timer_start(tim, &start_param);
if (ret == ODP_TIMER_TOO_NEAR) {
if (events >= test_global->opt.warmup_timers)
stat->num_too_near++;
} else {
break;
}
}
if (ret != ODP_TIMER_SUCCESS) {
printf("Timer set failed: %i. Timeout nsec "
"%" PRIu64 "\n", ret, ctx->nsec);
return 0;
}
} else if (mode == MODE_PERIODIC) {
int ret = odp_timer_periodic_ack(ctx->timer, ev);
if (ret < 0)
printf("Failed to ack a periodic timer.\n");
if (ret == 2)
odp_atomic_inc_u64(&test_global->last_events);
if (ret == 2 || ret < 0)
} else {
}
}
if (test_global->opt.groups) {
if (odp_schedule_group_leave(group, &mask))
printf("odp_schedule_group_leave() failed\n");
}
return 0;
}
int main(int argc, char *argv[])
{
odp_instance_t instance;
odp_init_t init;
test_opt_t test_opt;
test_global_t *test_global;
odph_helper_options_t helper_options;
odp_init_t *init_ptr = NULL;
int ret = 0;
/* Let helper collect its own arguments (e.g. --odph_proc) */
argc = odph_parse_options(argc, argv);
if (odph_options(&helper_options)) {
ODPH_ERR("Reading ODP helper options failed.\n");
exit(EXIT_FAILURE);
}
if (parse_options(argc, argv, &test_opt))
return -1;
/* List features not to be used (may optimize performance) */
init.not_used.feat.cls = 1;
init.not_used.feat.crypto = 1;
init.not_used.feat.ipsec = 1;
init.not_used.feat.tm = 1;
init.mem_model = helper_options.mem_model;
if (test_opt.init)
init_ptr = &init;
/* Init ODP before calling anything else */
if (odp_init_global(&instance, init_ptr, NULL)) {
printf("Global init failed.\n");
return -1;
}
/* Init this thread */
printf("Local init failed.\n");
return -1;
}
/* Configure scheduler */
uint64_t size = sizeof(test_global_t);
shm = odp_shm_reserve("timer_accuracy", size,
ODP_CACHE_LINE_SIZE, ODP_SHM_SINGLE_VA);
if (shm == ODP_SHM_INVALID) {
printf("Shm alloc failed.\n");
return -1;
}
test_global = odp_shm_addr(shm);
memset(test_global, 0, size);
memcpy(&test_global->opt, &test_opt, sizeof(test_opt_t));
size = test_global->opt.alloc_timers * sizeof(timer_ctx_t);
shm_ctx = odp_shm_reserve("timer_accuracy_ctx", size,
ODP_CACHE_LINE_SIZE, ODP_SHM_SINGLE_VA);
if (shm_ctx == ODP_SHM_INVALID) {
printf("Timer context alloc failed.\n");
ret = -1;
goto quit;
}
test_global->timer_ctx = odp_shm_addr(shm_ctx);
memset(test_global->timer_ctx, 0, size);
if (test_global->opt.output) {
test_global->file = fopen(test_global->opt.filename, "w");
if (test_global->file == NULL) {
printf("Failed to open output file %s: %s\n",
test_global->opt.filename, strerror(errno));
ret = -1;
goto quit;
}
size = (test_global->opt.tot_timers - test_global->opt.warmup_timers) *
sizeof(test_log_t);
shm_log = odp_shm_reserve("timer_accuracy_log", size, sizeof(test_log_t),
if (shm_log == ODP_SHM_INVALID) {
printf("Test log alloc failed.\n");
ret = -1;
goto quit;
}
test_global->log = odp_shm_addr(shm_log);
memset(test_global->log, 0, size);
}
odph_thread_t thread_tbl[MAX_WORKERS];
int num_workers;
odp_cpumask_t cpumask;
char cpumaskstr[ODP_CPUMASK_STR_SIZE];
odph_thread_common_param_t thr_common;
odph_thread_param_t thr_param;
memset(thread_tbl, 0, sizeof(thread_tbl));
num_workers = MAX_WORKERS;
if (test_global->opt.cpu_count && test_global->opt.cpu_count < MAX_WORKERS)
num_workers = test_global->opt.cpu_count;
num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
test_global->opt.cpu_count = num_workers;
odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
printf("num worker threads: %i\n", num_workers);
printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
printf("cpu mask: %s\n", cpumaskstr);
ret = create_timers(test_global);
if (ret)
goto quit;
odp_barrier_init(&test_global->barrier, num_workers + 1);
odp_atomic_init_u64(&test_global->events, 0);
odp_atomic_init_u64(&test_global->last_events, 0);
odph_thread_param_init(&thr_param);
thr_param.start = run_test;
thr_param.arg = (void *)test_global;
thr_param.thr_type = ODP_THREAD_WORKER;
odph_thread_common_param_init(&thr_common);
thr_common.instance = instance;
thr_common.cpumask = &cpumask;
thr_common.share_param = 1;
odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
odp_barrier_wait(&test_global->barrier);
ret = start_timers(test_global);
if (ret)
goto quit;
if (test_global->opt.mode == MODE_PERIODIC) {
while (odp_atomic_load_u64(&test_global->events) < test_global->opt.tot_timers)
cancel_periodic_timers(test_global);
}
odph_thread_join(thread_tbl, num_workers);
print_stat(test_global);
quit:
if (test_global->file)
fclose(test_global->file);
if (destroy_timers(test_global))
ret = -1;
if (shm_log != ODP_SHM_INVALID && odp_shm_free(shm_log))
ret = -1;
if (shm_ctx != ODP_SHM_INVALID && odp_shm_free(shm_ctx))
ret = -1;
if (odp_shm_free(shm))
ret = -1;
if (odp_term_local()) {
printf("Term local failed.\n");
ret = -1;
}
if (odp_term_global(instance)) {
printf("Term global failed.\n");
ret = -1;
}
return ret;
}
void odp_atomic_init_u64(odp_atomic_u64_t *atom, uint64_t val)
Initialize atomic uint64 variable.
void odp_atomic_inc_u64(odp_atomic_u64_t *atom)
Increment atomic uint64 variable.
uint64_t odp_atomic_fetch_inc_u64(odp_atomic_u64_t *atom)
Fetch and increment atomic uint64 variable.
uint64_t odp_atomic_load_u64(odp_atomic_u64_t *atom)
Load value of atomic uint64 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_mb_full(void)
Full memory barrier.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int odp_cpumask_first(const odp_cpumask_t *mask)
Find first set CPU in mask.
int32_t odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int32_t size)
Format a string from CPU mask.
#define ODP_CPUMASK_STR_SIZE
The maximum number of characters needed to record any CPU mask as a string (output of odp_cpumask_to_...
void odp_event_free(odp_event_t event)
Free event.
#define ODP_EVENT_INVALID
Invalid event.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_TIMEOUT
Timeout pool.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_queue_type_t
Queue type.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
#define ODP_SCHED_SYNC_PARALLEL
Parallel scheduled queues.
int odp_schedule_group_t
Scheduler thread group.
int odp_schedule_group_join(odp_schedule_group_t group, const odp_thrmask_t *mask)
Join a schedule group.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
#define ODP_SCHED_SYNC_ORDERED
Ordered queue synchronization.
int odp_schedule_group_destroy(odp_schedule_group_t group)
Schedule group destroy.
int odp_schedule_group_leave(odp_schedule_group_t group, const odp_thrmask_t *mask)
Leave a schedule group.
#define ODP_SCHED_GROUP_INVALID
Invalid scheduler group.
#define ODP_SCHED_NO_WAIT
Do not wait.
int odp_schedule_default_prio(void)
Default scheduling priority level.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
odp_schedule_group_t odp_schedule_group_create(const char *name, const odp_thrmask_t *mask)
Schedule group create.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
#define ODP_SHM_SINGLE_VA
Single virtual address.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
double odp_fract_u64_to_dbl(const odp_fract_u64_t *fract)
Convert fractional number (u64) to double.
void odp_sys_info_print(void)
Print system info.
void odp_thrmask_set(odp_thrmask_t *mask, int thr)
Add thread to mask.
int odp_thread_id(void)
Get thread identifier.
void odp_thrmask_zero(odp_thrmask_t *mask)
Clear entire thread mask.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
uint64_t odp_time_to_ns(odp_time_t time)
Convert time to nanoseconds.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
odp_time_t odp_time_global(void)
Current global time.
odp_time_t odp_time_local(void)
Current local time.
odp_time_t odp_time_global_strict(void)
Current global time (strict)
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
uint64_t odp_time_global_ns(void)
Current global time in nanoseconds.
uint64_t odp_timer_tick_to_ns(odp_timer_pool_t timer_pool, uint64_t ticks)
Convert timer ticks to nanoseconds.
int odp_timer_pool_start_multi(odp_timer_pool_t timer_pool[], int num)
Start timer pools.
void odp_timer_pool_print(odp_timer_pool_t timer_pool)
Print timer pool debug information.
odp_timeout_t odp_timeout_alloc(odp_pool_t pool)
Timeout alloc.
uint64_t odp_timeout_tick(odp_timeout_t tmo)
Timeout expiration tick.
int odp_timer_free(odp_timer_t timer)
Free a timer.
int odp_timer_periodic_start(odp_timer_t timer, const odp_timer_periodic_start_t *start_param)
Start a periodic timer.
odp_timeout_t odp_timeout_from_event(odp_event_t ev)
Get timeout handle from a ODP_EVENT_TIMEOUT type event.
#define ODP_TIMER_POOL_INVALID
Invalid timer pool handle.
int odp_timer_periodic_capability(odp_timer_clk_src_t clk_src, odp_timer_periodic_capability_t *capa)
Periodic timer capability.
odp_timer_pool_t odp_timer_pool_create(const char *name, const odp_timer_pool_param_t *params)
Create a timer pool.
uint64_t odp_timer_current_tick(odp_timer_pool_t timer_pool)
Current tick value.
int odp_timer_capability(odp_timer_clk_src_t clk_src, odp_timer_capability_t *capa)
Query timer capabilities per clock source.
uint64_t odp_timer_ns_to_tick(odp_timer_pool_t timer_pool, uint64_t ns)
Convert nanoseconds to timer ticks.
int odp_timer_periodic_ack(odp_timer_t timer, odp_event_t tmo_ev)
Acknowledge timeout from a periodic timer.
odp_timer_clk_src_t
Clock sources for timer pools.
int odp_timer_start(odp_timer_t timer, const odp_timer_start_t *start_param)
Start a timer.
odp_timer_retval_t
Return values for timer start, restart and cancel calls.
int odp_timer_periodic_cancel(odp_timer_t timer)
Cancel a periodic timer.
odp_event_t odp_timeout_to_event(odp_timeout_t tmo)
Convert timeout handle to event handle.
#define ODP_TIMEOUT_INVALID
Invalid timeout handle.
odp_timer_t odp_timer_alloc(odp_timer_pool_t timer_pool, odp_queue_t queue, const void *user_ptr)
Allocate a timer.
#define ODP_CLOCK_DEFAULT
The default clock source.
#define ODP_TIMER_INVALID
Invalid timer handle.
void odp_timer_pool_param_init(odp_timer_pool_param_t *param)
Initialize timer pool parameters.
void odp_timer_pool_destroy(odp_timer_pool_t timer_pool)
Destroy a timer pool.
void * odp_timeout_user_ptr(odp_timeout_t tmo)
Return user pointer for the timeout.
@ ODP_TIMER_SUCCESS
Timer operation succeeded.
@ ODP_TIMER_TOO_NEAR
Timer operation failed, too near to the current time.
@ ODP_TIMER_TYPE_PERIODIC
Periodic timer.
@ ODP_TIMER_TICK_REL
Relative ticks.
@ ODP_TIMER_TICK_ABS
Absolute ticks.
The OpenDataPlane API.
Unsigned 64 bit fractional number.
uint64_t integer
Integer part.
uint64_t denom
Denominator of the fraction part.
uint64_t numer
Numerator of the fraction part.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
Pool parameters.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@124 tmo
Parameters for timeout pools.
odp_pool_type_t type
Pool type.
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
odp_queue_type_t type
Queue type.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
odp_fract_u64_t min_base_freq_hz
Minimum supported base frequency value.
uint32_t max_timers
Maximum number of single shot timers in a pool.
uint32_t max_pools
Maximum number of timer pools for single shot timers (per clock source)
struct odp_timer_capability_t::@153 periodic
Periodic timer capabilities.
odp_fract_u64_t max_base_freq_hz
Maximum supported base frequency value.
odp_timer_res_capability_t max_res
Maximum resolution.
Periodic timer capability.
odp_fract_u64_t base_freq_hz
Periodic timer pool base frequency in hertz.
uint64_t res_ns
Timeout resolution in nanoseconds.
uint64_t max_multiplier
Maximum base frequency multiplier.
Periodic timer start parameters.
odp_event_t tmo_ev
Timeout event.
uint64_t freq_multiplier
Base frequency multiplier.
uint64_t first_tick
First expiration time.
Timer pool parameters.
uint64_t res_ns
Timeout resolution in nanoseconds.
uint64_t res_hz
Timeout resolution in hertz.
struct odp_timer_pool_param_t::@154 periodic
Periodic timer parameters.
odp_timer_type_t timer_type
Timer type.
uint64_t max_multiplier
Maximum base frequency multiplier.
odp_fract_u64_t base_freq_hz
Timer pool base frequency in hertz.
uint64_t min_tmo
Minimum relative timeout in nanoseconds.
uint32_t num_timers
Number of timers in the pool.
odp_timer_clk_src_t clk_src
Clock source for timers.
uint64_t max_tmo
Maximum relative timeout in nanoseconds.
uint64_t res_hz
Timeout resolution in hertz.
uint64_t res_ns
Timeout resolution in nanoseconds.
Timer start parameters.
uint64_t tick
Expiration time in ticks.
odp_event_t tmo_ev
Timeout event.
odp_timer_tick_type_t tick_type
Tick type.
struct odp_feature_t::@145 feat
Individual feature bits.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()