/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (C) 2014, 2016 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "tmacros.h" const char rtems_test_name[] = "SPCPUCOUNTER 1"; #define NS_PER_TICK 1000000 #define N 10 typedef struct { rtems_counter_ticks delay_ns_t[N][2]; rtems_counter_ticks delay_ticks_t[N][2]; rtems_counter_ticks overhead_t[N][5]; rtems_counter_ticks overhead_delta; } test_context; static test_context test_instance; static rtems_interval sync_with_clock_tick(void) { rtems_interval start = rtems_clock_get_ticks_since_boot(); rtems_interval current; do { current = rtems_clock_get_ticks_since_boot(); } while (current == start); return current; } static void test_converter(void) { CPU_Counter_ticks frequency; CPU_Counter_ticks frequency2; uint64_t ns; int64_t sbt; frequency = rtems_counter_nanoseconds_to_ticks(1000000000); ns = rtems_counter_ticks_to_nanoseconds(frequency); printf("CPU counter frequency: %" PRIu32 "Hz\n", frequency); printf("nanoseconds for frequency count ticks: %" PRIu64 "\n", ns); rtems_test_assert(ns == 1000000000); sbt = rtems_counter_ticks_to_sbintime(frequency); rtems_test_assert(sbt == (INT64_C(1) << 32)); frequency2 = rtems_counter_sbintime_to_ticks(sbt); rtems_test_assert(frequency == frequency2); } static void test_delay_nanoseconds(test_context *ctx) { int i; for (i = 0; i < N; ++i) { rtems_counter_ticks t0; rtems_counter_ticks t1; rtems_interval tick; tick = sync_with_clock_tick(); t0 = rtems_counter_read(); rtems_counter_delay_nanoseconds(NS_PER_TICK); t1 = rtems_counter_read(); ctx->delay_ns_t[i][0] = t0; ctx->delay_ns_t[i][1] = t1; rtems_test_assert(tick < rtems_clock_get_ticks_since_boot()); } } static void test_delay_ticks(test_context *ctx) { rtems_counter_ticks ticks = rtems_counter_nanoseconds_to_ticks(NS_PER_TICK); int i; for (i = 0; i < N; ++i) { rtems_counter_ticks t0; rtems_counter_ticks t1; rtems_interval tick; tick = sync_with_clock_tick(); t0 = rtems_counter_read(); rtems_counter_delay_ticks(ticks); t1 = rtems_counter_read(); ctx->delay_ticks_t[i][0] = t0; ctx->delay_ticks_t[i][1] = t1; rtems_test_assert(tick < rtems_clock_get_ticks_since_boot()); } } static void test_overheads(test_context *ctx) { int i; for (i = 0; i < N; ++i) { rtems_counter_ticks t0; rtems_counter_ticks t1; rtems_counter_ticks t2; rtems_counter_ticks t3; rtems_counter_ticks t4; rtems_counter_ticks d; t0 = rtems_counter_read(); t1 = rtems_counter_read(); d = rtems_counter_difference(t1, t0); t2 = rtems_counter_read(); rtems_counter_delay_nanoseconds(0); t3 = rtems_counter_read(); rtems_counter_delay_ticks(0); t4 = rtems_counter_read(); ctx->overhead_t[i][0] = t0; ctx->overhead_t[i][1] = t1; ctx->overhead_t[i][2] = t2; ctx->overhead_t[i][3] = t3; ctx->overhead_t[i][4] = t4; ctx->overhead_delta = d; } } static void report_overhead( const char *name, rtems_counter_ticks t1, rtems_counter_ticks t0 ) { rtems_counter_ticks d; uint64_t ns; d = rtems_counter_difference(t1, t0); ns = rtems_counter_ticks_to_nanoseconds(d); printf( "overhead %s: %" PRIu64 " ticks, %" PRIu64 "ns\n", name, (uint64_t) d, ns ); } static uint64_t large_delta_to_ns(rtems_counter_ticks d) { uint64_t ns; ns = rtems_counter_ticks_to_nanoseconds(d); /* Special case for CPU counters using the clock driver counter */ if (ns < rtems_configuration_get_nanoseconds_per_tick()) { printf( "warning: the RTEMS counter seems to be unable to\n" " measure intervals greater than the clock tick interval\n" ); ns += rtems_configuration_get_nanoseconds_per_tick(); } return ns; } static void test_report(test_context *ctx) { double ns_per_tick = NS_PER_TICK; rtems_counter_ticks d; uint64_t ns; size_t i; printf("test delay nanoseconds (%i times)\n", N); for (i = 0; i < N; ++i) { d = rtems_counter_difference(ctx->delay_ns_t[i][1], ctx->delay_ns_t[i][0]); ns = large_delta_to_ns(d); printf( "ns busy wait duration: %" PRIu64 "ns\n" "ns busy wait relative to clock tick: %f\n", ns, (ns - ns_per_tick) / ns_per_tick ); } printf("test delay ticks (%i times)\n", N); for (i = 0; i < N; ++i) { d = rtems_counter_difference( ctx->delay_ticks_t[i][1], ctx->delay_ticks_t[i][0] ); ns = large_delta_to_ns(d); printf( "ticks busy wait duration: %" PRIu64 "ns\n" "ticks busy wait relative to clock tick: %f\n", ns, (ns - ns_per_tick) / ns_per_tick ); } printf("test overheads (%i times)\n", N); for (i = 0; i < N; ++i) { report_overhead("read", ctx->overhead_t[i][1], ctx->overhead_t[i][0]); report_overhead("difference", ctx->overhead_t[i][2], ctx->overhead_t[i][1]); report_overhead("delay ns", ctx->overhead_t[i][3], ctx->overhead_t[i][2]); report_overhead("delay ticks", ctx->overhead_t[i][4], ctx->overhead_t[i][3]); } } static void Init(rtems_task_argument arg) { test_context *ctx = &test_instance; rtems_print_printer_fprintf_putc(&rtems_test_printer); TEST_BEGIN(); test_delay_nanoseconds(ctx); test_delay_ticks(ctx); test_overheads(ctx); test_converter(); test_report(ctx); TEST_END(); rtems_test_exit(0); } #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER #define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER #define CONFIGURE_MICROSECONDS_PER_TICK (NS_PER_TICK / 1000) #define CONFIGURE_MAXIMUM_TASKS 1 #define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT #define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION #define CONFIGURE_RTEMS_INIT_TASKS_TABLE #define CONFIGURE_INIT #include