From ed13a3ae27db05b3e2ab87f51cbe003af6ec32dd Mon Sep 17 00:00:00 2001 From: Juha Niskanen Date: Tue, 14 Aug 2018 06:45:16 -0600 Subject: [PATCH] examples/calib_udelay: Add tool for calibrating CONFIG_BOARD_LOOPSPERMSEC --- examples/buttons/buttons_main.c | 4 +- examples/calib_udelay/.gitignore | 11 + examples/calib_udelay/Kconfig | 24 ++ examples/calib_udelay/Make.defs | 39 +++ examples/calib_udelay/Makefile | 56 ++++ examples/calib_udelay/calib_udelay_main.c | 328 ++++++++++++++++++++++ 6 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 examples/calib_udelay/.gitignore create mode 100644 examples/calib_udelay/Kconfig create mode 100644 examples/calib_udelay/Make.defs create mode 100644 examples/calib_udelay/Makefile create mode 100644 examples/calib_udelay/calib_udelay_main.c diff --git a/examples/buttons/buttons_main.c b/examples/buttons/buttons_main.c index e32e21332..19401a555 100644 --- a/examples/buttons/buttons_main.c +++ b/examples/buttons/buttons_main.c @@ -180,11 +180,13 @@ static int button_daemon(int argc, char *argv[]) #ifdef CONFIG_EXAMPLES_BUTTONS_NAMES btn_buttonset_t oldsample = 0; - int i; #endif int ret; int fd; + int i; + + UNUSED(i); /* Indicate that we are running */ diff --git a/examples/calib_udelay/.gitignore b/examples/calib_udelay/.gitignore new file mode 100644 index 000000000..fa1ec7579 --- /dev/null +++ b/examples/calib_udelay/.gitignore @@ -0,0 +1,11 @@ +/Make.dep +/.depend +/.built +/*.asm +/*.obj +/*.rel +/*.lst +/*.sym +/*.adb +/*.lib +/*.src diff --git a/examples/calib_udelay/Kconfig b/examples/calib_udelay/Kconfig new file mode 100644 index 000000000..19689301f --- /dev/null +++ b/examples/calib_udelay/Kconfig @@ -0,0 +1,24 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config EXAMPLES_CALIB_UDELAY + bool "Calibration tool for udelay" + default n + select CLOCK_MONOTONIC + ---help--- + +if EXAMPLES_CALIB_UDELAY + +config EXAMPLES_CALIB_UDELAY_NUM_MEASUREMENTS + int "Number of measurements to take for each loop iteration count" + default 3 + ---help--- + +config EXAMPLES_CALIB_UDELAY_NUM_RESULTS + int "Number of results to generate for calibration slope" + default 20 + ---help--- + +endif diff --git a/examples/calib_udelay/Make.defs b/examples/calib_udelay/Make.defs new file mode 100644 index 000000000..1fc121ba5 --- /dev/null +++ b/examples/calib_udelay/Make.defs @@ -0,0 +1,39 @@ +############################################################################ +# apps/examples/calib_udelay/Make.defs +# Adds selected applications to apps/ build +# +# Copyright (C) 2018 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# 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. +# +############################################################################ + +ifeq ($(CONFIG_EXAMPLES_CALIB_UDELAY),y) +CONFIGURED_APPS += examples/calib_udelay +endif diff --git a/examples/calib_udelay/Makefile b/examples/calib_udelay/Makefile new file mode 100644 index 000000000..09d43b0d0 --- /dev/null +++ b/examples/calib_udelay/Makefile @@ -0,0 +1,56 @@ +############################################################################ +# apps/examples/calib_udelay/Makefile +# +# Copyright (C) 2018 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# 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. +# +############################################################################ + +-include $(TOPDIR)/Make.defs + +# built-in application info + +CONFIG_EXAMPLES_CALIB_UDELAY_APPNAME ?= "calib_udelay" +CONFIG_EXAMPLES_CALIB_UDELAY_STACKSIZE ?= 2048 +CONFIG_EXAMPLES_CALIB_UDELAY_PRIORITY ?= 100 +CONFIG_EXAMPLES_CALIB_UDELAY_PROGNAME ?= "calib_udelay" + +APPNAME = $(CONFIG_EXAMPLES_CALIB_UDELAY_APPNAME) +PRIORITY = $(CONFIG_EXAMPLES_CALIB_UDELAY_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_CALIB_UDELAY_STACKSIZE) +PROGNAME = $(CONFIG_EXAMPLES_CALIB_UDELAY_PROGNAME) + +# Udelay calibration tool + +ASRCS = +CSRCS = +MAINSRC = calib_udelay_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/calib_udelay/calib_udelay_main.c b/examples/calib_udelay/calib_udelay_main.c new file mode 100644 index 000000000..3fb08afbc --- /dev/null +++ b/examples/calib_udelay/calib_udelay_main.c @@ -0,0 +1,328 @@ +/**************************************************************************** + * examples/calib_udelay/calib_udelay_main.c + * + * Copyright (C) 2017 Haltian Ltd All rights reserved. + * Author: Jussi Kivilinna + * + * 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_EXAMPLES_CALIB_UDELAY_NUM_MEASUREMENTS +# define CONFIG_EXAMPLES_CALIB_UDELAY_NUM_MEASUREMENTS 3 +#endif + +#ifndef CONFIG_EXAMPLES_CALIB_UDELAY_NUM_RESULTS +# define CONFIG_EXAMPLES_CALIB_UDELAY_NUM_RESULTS 20 +#endif + +#define DELAY_TEST_ITERS 100000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct measurement_s +{ + int count; + uint64_t nsecs; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint64_t gettime_nsecs(void) +{ + struct timespec ts; + uint64_t nsecs; + + (void)clock_gettime(CLOCK_MONOTONIC, &ts); + + nsecs = ts.tv_sec; + nsecs *= 1000 * 1000 * 1000; + nsecs += ts.tv_nsec; + + return nsecs; +} + +static int compare_measurements(const void *va, const void *vb) +{ + const struct measurement_s *a = va; + const struct measurement_s *b = vb; + + if (a->nsecs == b->nsecs) + { + return 0; + } + else if (a->nsecs < b->nsecs) + { + return -1; + } + else + { + return 1; + } +} + +static void __attribute__((noinline)) calib_udelay_test(uint32_t count) +{ + volatile int i; + + while (count > 0) + { + for (i = 0; i < DELAY_TEST_ITERS; i++) + { + } + + count--; + } +} + +static void perform_measurements(int loop_count, + FAR struct measurement_s *measurements, + int num_measurements) +{ + int n; + + memset(measurements, 0, sizeof(*measurements) * num_measurements); + + for (n = 0; n < num_measurements; n++) + { + measurements[n].nsecs = gettime_nsecs(); + calib_udelay_test(loop_count); + measurements[n].nsecs = gettime_nsecs() - measurements[n].nsecs; + measurements[n].count = loop_count; + } + + qsort(measurements, num_measurements, sizeof(measurements[0]), + compare_measurements); +} + +static double getx(FAR struct measurement_s *point) +{ + return point->count * (double)DELAY_TEST_ITERS; +} + +static double gety(struct measurement_s *point) +{ + return point->nsecs; +} + +static int linreg(FAR struct measurement_s *point, int num_points, + FAR double *m, FAR double *b, FAR double *r2) +{ + double sumx = 0.0; + double sumx2 = 0.0; + double sumxy = 0.0; + double sumy = 0.0; + double sumy2 = 0.0; + double x; + double y; + double denom; + double inv_denom; + int i; + + for (i = 0; i < num_points; i++) + { + x = getx(point + i); + y = gety(point + i); + sumx += x; + sumx2 += x * x; + sumxy += x * y; + sumy += y; + sumy2 += y * y; + } + + denom = num_points * sumx2 - sumx * sumx; + if (denom == 0) + { + *m = 0; + *b = 0; + + if (r2) + { + *r2 = 0; + } + + return ERROR; + } + + inv_denom = 1.0 / denom; + + *m = ((num_points * sumxy) - (sumx * sumy)) * inv_denom; + *b = ((sumy * sumx2) - (sumx * sumxy)) * inv_denom; + + if (r2) + { + double term1 = ((num_points * sumxy) - (sumx * sumy)); + double term2 = ((num_points * sumx2) - (sumx * sumx)); + double term3 = ((num_points * sumy2) - (sumy * sumy)); + double term23 = (term2 * term3); + + *r2 = 1.0; + if (fabs(term23) > 1e-10) + { + *r2 = (term1 * term1) / term23; + } + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * calib_udelay_main + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +int main(int argc, FAR char *argv[]) +#else +int calib_udelay_main(int argc, char *argv[]) +#endif +{ + const int num_measurements = CONFIG_EXAMPLES_CALIB_UDELAY_NUM_MEASUREMENTS; + const int num_results = CONFIG_EXAMPLES_CALIB_UDELAY_NUM_RESULTS; + const int min_timer_resolution_steps = 3; + const int calibration_step_multiplier = 10; + struct measurement_s measurements[num_measurements]; + struct measurement_s result; + struct measurement_s results[num_results]; + double iters_per_nsec; + double iters_per_msec; + uint64_t timer_resolution; + double slope_m, slope_b, slope_r2; + int min_step; + int loop_count; + double duration; + int i; + + printf("\n"); + + sched_lock(); + + printf("Calibrating timer for main calibration...\n"); + usleep(200 * 1000); + + /* Find out timer resolution. */ + + loop_count = 0; + do + { + loop_count++; + perform_measurements(loop_count, measurements, num_measurements); + result = measurements[0]; + } + while (result.nsecs == 0); + + timer_resolution = result.nsecs; + + /* Find first loop count where timer steps five times. */ + + do + { + loop_count++; + perform_measurements(loop_count, measurements, num_measurements); + result = measurements[0]; + } + while (result.nsecs < timer_resolution * min_timer_resolution_steps); + + min_step = result.count; + + /* Get calibration slope for loop function. */ + + for (duration = 0, loop_count = min_step, i = 0; i < num_results; i++, + loop_count += min_step * calibration_step_multiplier) + { + duration += (double)num_measurements * loop_count * + timer_resolution * min_timer_resolution_steps / min_step; + } + + printf("Performing main calibration for udelay. This will take approx. %.3f seconds.\n", + duration * 1e-9); + usleep(200 * 1000); + + for (loop_count = min_step, i = 0; i < num_results; i++, + loop_count += min_step * calibration_step_multiplier) + { + perform_measurements(loop_count, measurements, num_measurements); + results[i] = measurements[0]; + } + + if (linreg(results, num_results, &slope_m, &slope_b, &slope_r2) == OK) + { + printf("Calibration slope for udelay:\n" + " Y = m*X + b, where\n" + " X is loop iterations,\n" + " Y is time in nanoseconds,\n" + " b is base overhead,\n" + " m is nanoseconds per loop iteration.\n\n" + + " m = %.08f nsec/iter\n" + " b = %.08f nsec\n\n" + + " Correlation coefficient, R² = %.4f\n\n", + slope_m, slope_b, slope_r2); + + iters_per_nsec = (1.0 / slope_m); + iters_per_msec = iters_per_nsec * 1000 * 1000; + + printf("Without overhead, %.8f iterations per nanosecond and %.2f " + "iterations per millisecond.\n\n", + iters_per_nsec, iters_per_msec); + + printf("Recommended setting for CONFIG_BOARD_LOOPSPERMSEC:\n" + " CONFIG_BOARD_LOOPSPERMSEC=%.0f\n", ceil(iters_per_msec)); + } + else + { + printf("cannot solve\n"); + } + + sched_unlock(); + return 0; +}