diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c
index 47d28a6..b39ce2b 100644
--- a/example/generator/odp_generator.c
+++ b/example/generator/odp_generator.c
@@ -585,7 +585,7 @@ static void *gen_recv_thread(void *arg)
  */
 static void print_global_stats(int num_workers)
 {
-	uint64_t start, wait, diff;
+	odp_time_t start, wait, diff;
 	uint64_t pkts, pkts_prev = 0, pps, maximum_pps = 0;
 	int verbose_interval = 20;
 	odp_thrmask_t thrd_mask;
@@ -593,8 +593,8 @@ static void print_global_stats(int num_workers)
 	while (odp_thrmask_worker(&thrd_mask) < num_workers)
 		continue;
 
-	wait = odp_time_ns_to_cycles(verbose_interval * ODP_TIME_SEC);
-	start = odp_time_cycles();
+	wait = odp_time_from_ns(verbose_interval * ODP_TIME_SEC);
+	start = odp_time();
 
 	while (odp_thrmask_worker(&thrd_mask) == num_workers) {
 		if (args->appl.number != -1 &&
@@ -603,11 +603,11 @@ static void print_global_stats(int num_workers)
 			break;
 		}
 
-		diff = odp_time_diff_cycles(start, odp_time_cycles());
-		if (diff < wait)
+		diff = odp_time_diff(start, odp_time());
+		if (odp_time_cmp(diff, wait) > 0)
 			continue;
 
-		start = odp_time_cycles();
+		start = odp_time();
 
 		if (args->appl.mode == APPL_MODE_RCV) {
 			pkts = odp_atomic_load_u64(&counters.udp);
diff --git a/include/odp/api/time.h b/include/odp/api/time.h
index b0072fc..60800ba 100644
--- a/include/odp/api/time.h
+++ b/include/odp/api/time.h
@@ -28,14 +28,22 @@ extern "C" {
 #define ODP_TIME_MSEC 1000000ULL    /**< Millisecond in nsec */
 #define ODP_TIME_SEC  1000000000ULL /**< Second in nsec */
 
+/**
+ * @typedef odp_time_t
+ * ODP time stamp. Time stamp is global and can be shared between threads.
+ */
 
 /**
- * Current time in CPU cycles
- *
- * @return Current time in CPU cycles
+ * @def ODP_TIME_NULL
+ * Zero time stamp
  */
-uint64_t odp_time_cycles(void);
 
+/**
+ * Current global time stamp.
+ *
+ * @return Time stamp. It should be hi-resolution time.
+ */
+odp_time_t odp_time(void);
 
 /**
  * Time difference
@@ -43,29 +51,60 @@ uint64_t odp_time_cycles(void);
  * @param t1    First time stamp
  * @param t2    Second time stamp
  *
- * @return Difference of time stamps in CPU cycles
+ * @return Difference of time stamps
  */
-uint64_t odp_time_diff_cycles(uint64_t t1, uint64_t t2);
+odp_time_t odp_time_diff(odp_time_t t1, odp_time_t t2);
 
+/**
+ * Time sum
+ *
+ * @param t1    time stamp
+ * @param t2    time stamp
+ *
+ * @return Sum of time stamps
+ */
+odp_time_t odp_time_sum(odp_time_t t1, odp_time_t t2);
 
 /**
- * Convert CPU cycles to nanoseconds
+ * Convert time to nanoseconds
  *
- * @param cycles  Time in CPU cycles
+ * @param time  Time
  *
  * @return Time in nanoseconds
  */
-uint64_t odp_time_cycles_to_ns(uint64_t cycles);
-
+uint64_t odp_time_to_ns(odp_time_t time);
 
 /**
- * Convert nanoseconds to CPU cycles
+ * Convert nanoseconds to time
  *
  * @param ns      Time in nanoseconds
  *
- * @return Time in CPU cycles
+ * @return Time stamp
+ */
+odp_time_t odp_time_from_ns(uint64_t ns);
+
+/**
+ * Compare two times as absolute ranges
+ *
+ * @param t1    First time
+ * @param t2    Second time
+ *
+ * @retval -1 if t2 < t1, 0 if t1 = t2, 1 if t2 > t1
+ */
+int odp_time_cmp(odp_time_t t1, odp_time_t t2);
+
+/**
+ * Get printable value for an odp_time_t
+ *
+ * @param time time to be printed
+ * @return     uint64_t value that can be used to print/display this
+ *             time
+ *
+ * @note This routine is intended to be used for diagnostic purposes
+ * to enable applications to generate a printable value that represents
+ * an odp_time_t time.
  */
-uint64_t odp_time_ns_to_cycles(uint64_t ns);
+uint64_t odp_time_to_u64(odp_time_t time);
 
 /**
  * @}
diff --git a/test/performance/odp_pktio_perf.c b/test/performance/odp_pktio_perf.c
index 09e1f6f..6e001a3 100644
--- a/test/performance/odp_pktio_perf.c
+++ b/test/performance/odp_pktio_perf.c
@@ -106,7 +106,7 @@ struct tx_stats_s {
 	uint64_t tx_cnt;	/* Packets transmitted */
 	uint64_t alloc_failures;/* Packet allocation failures */
 	uint64_t enq_failures;	/* Enqueue failures */
-	uint64_t idle_cycles;	/* Idle cycle count in TX loop */
+	odp_time_t idle_cycles;	/* Idle cycle count in TX loop */
 };
 
 typedef union tx_stats_u {
@@ -305,12 +305,12 @@ static void *run_thread_tx(void *arg)
 	int thr_id;
 	odp_queue_t outq;
 	pkt_tx_stats_t *stats;
-	uint64_t burst_start_cycles, start_cycles, cur_cycles, send_duration;
-	uint64_t burst_gap_cycles;
+	odp_time_t start_cycles, cur_cycles, send_duration;
+	odp_time_t burst_start_cycles, burst_gap_cycles;
 	uint32_t batch_len;
 	int unsent_pkts = 0;
 	odp_event_t  tx_event[BATCH_LEN_MAX];
-	uint64_t idle_start = 0;
+	odp_time_t idle_start = ODP_TIME_NULL;
 
 	thread_args_t *targs = arg;
 
@@ -328,30 +328,33 @@ static void *run_thread_tx(void *arg)
 	if (outq == ODP_QUEUE_INVALID)
 		LOG_ABORT("Failed to get output queue for thread %d\n", thr_id);
 
-	burst_gap_cycles = odp_time_ns_to_cycles(
+	burst_gap_cycles = odp_time_from_ns(
 				ODP_TIME_SEC / (targs->pps / targs->batch_len));
-	send_duration = odp_time_ns_to_cycles(targs->duration * ODP_TIME_SEC);
+	send_duration = odp_time_from_ns(targs->duration * ODP_TIME_SEC);
 
 	odp_barrier_wait(&globals->tx_barrier);
 
-	cur_cycles     = odp_time_cycles();
+	cur_cycles     = odp_time();
 	start_cycles   = cur_cycles;
-	burst_start_cycles = odp_time_diff_cycles(burst_gap_cycles, cur_cycles);
-	while (odp_time_diff_cycles(start_cycles, cur_cycles) < send_duration) {
+	burst_start_cycles = odp_time_diff(burst_gap_cycles, cur_cycles);
+	while (odp_time_diff(start_cycles, cur_cycles) < send_duration) {
 		unsigned alloc_cnt = 0, tx_cnt;
 
-		if (odp_time_diff_cycles(burst_start_cycles, cur_cycles)
+		if (odp_time_diff(burst_start_cycles, cur_cycles)
 							< burst_gap_cycles) {
-			cur_cycles = odp_time_cycles();
-			if (idle_start == 0)
+			cur_cycles = odp_time();
+			if (!odp_time_cmp(ODP_TIME_NULL, idle_start))
 				idle_start = cur_cycles;
 			continue;
 		}
 
-		if (idle_start) {
-			stats->s.idle_cycles += odp_time_diff_cycles(
-							idle_start, cur_cycles);
-			idle_start = 0;
+		if (odp_time_cmp(ODP_TIME_NULL, idle_start)) {
+			odp_time_t diff = odp_time_diff(idle_start, cur_cycles);
+
+			stats->s.idle_cycles =
+				odp_time_sum(diff, stats->s.idle_cycles);
+
+			idle_start = ODP_TIME_NULL;
 		}
 
 		burst_start_cycles += burst_gap_cycles;
@@ -365,14 +368,14 @@ static void *run_thread_tx(void *arg)
 		stats->s.enq_failures += unsent_pkts;
 		stats->s.tx_cnt += tx_cnt;
 
-		cur_cycles = odp_time_cycles();
+		cur_cycles = odp_time();
 	}
 
 	VPRINT(" %02d: TxPkts %-8"PRIu64" EnqFail %-6"PRIu64
 	       " AllocFail %-6"PRIu64" Idle %"PRIu64"ms\n",
 	       thr_id, stats->s.tx_cnt,
 	       stats->s.enq_failures, stats->s.alloc_failures,
-	       odp_time_cycles_to_ns(stats->s.idle_cycles)/1000/1000);
+	       odp_time_to_ns(stats->s.idle_cycles) / (uint64_t)ODP_TIME_MSEC);
 
 	return NULL;
 }
@@ -591,13 +594,13 @@ static int setup_txrx_masks(odp_cpumask_t *thd_mask_tx,
  */
 static void busy_loop_ns(uint64_t wait_ns)
 {
-	uint64_t diff;
-	uint64_t start_time = odp_time_cycles();
-	uint64_t wait = odp_time_ns_to_cycles(wait_ns);
+	odp_time_t diff;
+	odp_time_t start_time = odp_time();
+	odp_time_t wait = odp_time_from_ns(wait_ns);
 
 	do {
-		diff = odp_time_diff_cycles(start_time, odp_time_cycles());
-	} while (diff < wait);
+		diff = odp_time_diff(start_time, odp_time());
+	} while (odp_time_cmp(diff, wait) > 0);
 }
 
 /*
diff --git a/test/validation/pktio/pktio.c b/test/validation/pktio/pktio.c
index 5c0799b..2134f12 100644
--- a/test/validation/pktio/pktio.c
+++ b/test/validation/pktio/pktio.c
@@ -332,18 +332,18 @@ static int destroy_inq(odp_pktio_t pktio)
 
 static odp_event_t queue_deq_wait_time(odp_queue_t queue, uint64_t ns)
 {
-	uint64_t start, now, diff;
+	odp_time_t start, now, diff;
 	odp_event_t ev;
 
-	start = odp_time_cycles();
+	start = odp_time();
 
 	do {
 		ev = odp_queue_deq(queue);
 		if (ev != ODP_EVENT_INVALID)
 			return ev;
-		now = odp_time_cycles();
-		diff = odp_time_diff_cycles(start, now);
-	} while (odp_time_cycles_to_ns(diff) < ns);
+		now = odp_time();
+		diff = odp_time_diff(start, now);
+	} while (odp_time_to_ns(diff) < ns);
 
 	return ODP_EVENT_INVALID;
 }
@@ -351,12 +351,12 @@ static odp_event_t queue_deq_wait_time(odp_queue_t queue, uint64_t ns)
 static odp_packet_t wait_for_packet(odp_queue_t queue,
 				    uint32_t seq, uint64_t ns)
 {
-	uint64_t start, now, diff;
+	odp_time_t start, now, diff;
 	odp_event_t ev;
 	odp_packet_t pkt = ODP_PACKET_INVALID;
 	uint64_t wait;
 
-	start = odp_time_cycles();
+	start = odp_time();
 	wait = odp_schedule_wait_time(ns);
 
 	do {
@@ -377,9 +377,9 @@ static odp_packet_t wait_for_packet(odp_queue_t queue,
 			odp_event_free(ev);
 		}
 
-		now = odp_time_cycles();
-		diff = odp_time_diff_cycles(start, now);
-	} while (odp_time_cycles_to_ns(diff) < ns);
+		now = odp_time();
+		diff = odp_time_diff(start, now);
+	} while (odp_time_to_ns(diff) < ns);
 
 	CU_FAIL("failed to receive transmitted packet");
 
diff --git a/test/validation/scheduler/scheduler.c b/test/validation/scheduler/scheduler.c
index 0c96dc3..96e0781 100644
--- a/test/validation/scheduler/scheduler.c
+++ b/test/validation/scheduler/scheduler.c
@@ -465,10 +465,11 @@ static void *schedule_common_(void *arg)
 			CU_ASSERT(from != ODP_QUEUE_INVALID);
 			if (locked) {
 				int cnt;
-				uint64_t cycles = 0;
+				odp_time_t cycles = ODP_TIME_NULL;
 				/* Do some work here to keep the thread busy */
 				for (cnt = 0; cnt < 1000; cnt++)
-					cycles += odp_time_cycles();
+					cycles = odp_time_sum(cycles,
+							      odp_time());
 
 				odp_spinlock_unlock(&globals->atomic_lock);
 			}
diff --git a/test/validation/time/time.c b/test/validation/time/time.c
index 4b81c2c..a81d7ff 100644
--- a/test/validation/time/time.c
+++ b/test/validation/time/time.c
@@ -16,43 +16,44 @@ void time_test_odp_cycles_diff(void)
 {
 	/* volatile to stop optimization of busy loop */
 	volatile int count = 0;
-	uint64_t diff, cycles1, cycles2;
+	odp_time_t diff, cycles1, cycles2;
 
-	cycles1 = odp_time_cycles();
+	cycles1 = odp_time();
 
 	while (count < BUSY_LOOP_CNT) {
 		count++;
 	};
 
-	cycles2 = odp_time_cycles();
-	CU_ASSERT(cycles2 > cycles1);
+	cycles2 = odp_time();
+	CU_ASSERT((odp_time_cmp(cycles1, cycles2) > 0));
 
-	diff = odp_time_diff_cycles(cycles1, cycles2);
-	CU_ASSERT(diff > 0);
+	diff = odp_time_diff(cycles1, cycles2);
+	CU_ASSERT(odp_time_cmp(ODP_TIME_NULL, diff) > 0);
 }
 
 /* check that a negative cycles difference gives a reasonable result */
 void time_test_odp_cycles_negative_diff(void)
 {
-	uint64_t diff, cycles1, cycles2;
+	odp_time_t diff, cycles1, cycles2;
 
 	cycles1 = 10;
 	cycles2 = 5;
-	diff = odp_time_diff_cycles(cycles1, cycles2);
-	CU_ASSERT(diff > 0);
+	diff = odp_time_diff(cycles1, cycles2);
+	CU_ASSERT(odp_time_cmp(ODP_TIME_NULL, diff) > 0);
 }
 
 /* check that related conversions come back to the same value */
 void time_test_odp_time_conversion(void)
 {
-	uint64_t ns1, ns2, cycles;
+	uint64_t ns1, ns2;
+	odp_time_t cycles;
 	uint64_t upper_limit, lower_limit;
 
 	ns1 = 100;
-	cycles = odp_time_ns_to_cycles(ns1);
-	CU_ASSERT(cycles > 0);
+	cycles = odp_time_from_ns(ns1);
+	CU_ASSERT(odp_time_cmp(ODP_TIME_NULL, cycles) > 0);
 
-	ns2 = odp_time_cycles_to_ns(cycles);
+	ns2 = odp_time_to_ns(cycles);
 
 	/* need to check within arithmetic tolerance that the same
 	 * value in ns is returned after conversions */
