From 80d21739ccfd4f6e92e31da0c8208f59efd93bcb Mon Sep 17 00:00:00 2001 From: Josarn Date: Fri, 11 May 2018 02:44:58 +0200 Subject: [PATCH] tests/xtimer_configuration: Testing parameter --- tests/xtimer_configuration/Makefile | 18 ++ tests/xtimer_configuration/README.md | 71 ++++++++ tests/xtimer_configuration/main.c | 191 +++++++++++++++++++++ tests/xtimer_configuration/tests/01-run.py | 77 +++++++++ 4 files changed, 357 insertions(+) create mode 100644 tests/xtimer_configuration/Makefile create mode 100644 tests/xtimer_configuration/README.md create mode 100644 tests/xtimer_configuration/main.c create mode 100644 tests/xtimer_configuration/tests/01-run.py diff --git a/tests/xtimer_configuration/Makefile b/tests/xtimer_configuration/Makefile new file mode 100644 index 0000000000000..136fc03cbd04d --- /dev/null +++ b/tests/xtimer_configuration/Makefile @@ -0,0 +1,18 @@ +include ../Makefile.tests_common + +USEMODULE += xtimer + +# Port and pin configuration for probing with oscilloscope +# Port number should be found in port enum e.g in cpu/include/periph_cpu.h +FEATURES_REQUIRED += periph_gpio +CFLAGS += -DSLEEP_PIN=6 +CFLAGS += -DSLEEP_PORT=PORT_F + +CFLAGS += -DDEBUG_TIMER_PORT=PORTF +CFLAGS += -DDEBUG_TIMER_DDR=DDRF +CFLAGS += -DDEBUG_TIMER_PIN=PORTF4 + +include $(RIOTBASE)/Makefile.include + +test: + ./tests/01-run.py diff --git a/tests/xtimer_configuration/README.md b/tests/xtimer_configuration/README.md new file mode 100644 index 0000000000000..d9d2fc16a2f27 --- /dev/null +++ b/tests/xtimer_configuration/README.md @@ -0,0 +1,71 @@ +# xtimer_configuration test application + +This test tests `_xtimer_tsleep32()` and `xtimer_periodic_wakeup` against the +timings of of the xtimer. +And agains the timings of the external host (PC). + +This test can be used to adjust `XTIMER_SHOOT_OVERHEAD`, `XTIMER_OVERHEAD`, +`XTIMER_BACKOFF` and `XTIMER_ISR_BACKOFF`. +This is to ensure that the xtimer interrupt is only as long as neccessary. + +## Usage +``` +make BOARD= flash test +``` + +## Find right values + +1. Configure parameter in board.h as follow: +`XTIMER_ISR_BACKOFF` has to be twice `XTIMER_OVERHEAD` +``` +#define XTIMER_BACKOFF (100) +#define XTIMER_ISR_BACKOFF (100) +#define XTIMER_BACKOFF_OVERHEAD (1) +#define XTIMER_OVERHEAD (50) +#define XTIMER_SHOOT_OVERHEAD (0) +``` + +run the test. +If the output is as follows +``` +xxxx-xx-xx xx:xx:xx,xxx - INFO # 240876 243436 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 27 ticks +Increase value for XTIMER_SHOOT_OVERHEAD or XTIMER_OVERHEAD 27 +... +xxxx-xx-xx xx:xx:xx,xxx - INFO # 266798 266845 slept for 47 ticks (376 us) expected 47 ticks, diff 14 ticks +Increase value for XTIMER_BACKOFF_OVERHEAD 14 +``` + +Add the new value for `XTIMER_SHOOT_OVERHEAD` and `XTIMER_BACKOFF_OVERHEAD` to the board.h and set `XTIMER_OVERHEAD` to zero. + +``` +#define XTIMER_BACKOFF (100) +#define XTIMER_ISR_BACKOFF (100) +#define XTIMER_BACKOFF_OVERHEAD (14) +#define XTIMER_OVERHEAD (0) +#define XTIMER_SHOOT_OVERHEAD (27) +``` + +2. run test again + +Set `XTIMER_OVERHEAD` to new value. + +If the output is as follows + +``` +xxxx-xx-xx xx:xx:xx,xxx - INFO # 240876 243436 slept for 2560 ticks (20480 us) expected 2560 ticks, diff 33 ticks +Increase value for XTIMER_SHOOT_OVERHEAD or XTIMER_OVERHEAD 33 +``` +Set 33 as value for `XTIMER_OVERHEAD` and `XTIMER_BACKOFF` two times this value. +Also set `XTIMER_ISR_BACKOFF` to `XTIMER_BACKOFF_OVERHEAD`. + +``` +#define XTIMER_BACKOFF (66) +#define XTIMER_ISR_BACKOFF (14) +#define XTIMER_BACKOFF_OVERHEAD (14) +#define XTIMER_OVERHEAD (33) +#define XTIMER_SHOOT_OVERHEAD (27) +``` + +3. Run the test again and check if there are values which are far off. + +now run `tests/xtimer_usleep_short` and `tests/xtimer_usleep`. \ No newline at end of file diff --git a/tests/xtimer_configuration/main.c b/tests/xtimer_configuration/main.c new file mode 100644 index 0000000000000..f9fbe07a6ddc3 --- /dev/null +++ b/tests/xtimer_configuration/main.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2017 Inria + * 2017 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief xtimer_usleep test application + * + * @author Francisco Acosta + * @author Martine Lenders + * @} + */ + +#include +#include + +#include "xtimer.h" +#include "timex.h" +#include "thread.h" + +#include "Board.h" + +/* + * To use a pin to probe the sleep times enable the gpio module and define + * respective DSLEEP_PIN and DSLEEP_PORT. + * + * Port number should be found in port enum e.g in cpu/include/periph_cpu.h + * + * FEATURES_REQUIRED += periph_gpio + * CFLAGS += -DSLEEP_PIN=6 + * CFLAGS += -DSLEEP_PORT=PORT_F + * */ +#if defined(SLEEP_PORT) +#include "board.h" +#include "periph/gpio.h" +#endif + +#define RUNS (3U) +#define SLEEP_TIMES_NUMOF (sizeof(sleep_times) / sizeof(sleep_times[0])) + +#define CLOCK_CICLE ((1ul) << XTIMER_WIDTH) +static const uint32_t sleep_times[] = { + XTIMER_BACKOFF - 1, /* spin adjust XTIMER_BACKOFF_OVERHEAD*/ + XTIMER_BACKOFF + 1, /* no backoff test */ + XTIMER_PERIODIC_SPIN, /* for sure no backoff */ + CLOCK_CICLE, /* shoot with interrupt adjust*/ + 1000, + 2345, + 50000, + 100000, + 123456, +}; + +uint32_t start_kept[5 * SLEEP_TIMES_NUMOF]; +uint32_t test_times[5 * SLEEP_TIMES_NUMOF]; +volatile uint32_t end_times[SLEEP_TIMES_NUMOF]; + +static char stack[THREAD_STACKSIZE_MAIN]; +static void *_thread(void *arg) +{ + (void)arg; + uint8_t i = 0; + + puts("thread(): waiting going to sleep!"); + + while (1) { + thread_sleep(); + end_times[i++] = _xtimer_now(); + } + return NULL; +} + +int main(void) +{ + uint32_t start_test, testtime; + + printf("Running test %u times with %u distinct sleep times\n", RUNS, + (unsigned)SLEEP_TIMES_NUMOF); + puts("Please hit any key and then ENTER to continue"); + getchar(); + start_test = xtimer_now_usec(); + + /* Testing Periodic with fixed time + * Periodic wakeup has the fastest return time as the absolute value is set + * as timestamp. + */ + uint32_t periode, periode_ticks; + xtimer_ticks32_t start_periode; + + /* Testing normal usage relativ timestamp*/ + periode_ticks = 5 * XTIMER_PERIODIC_RELATIVE; + periode = _xtimer_usec_from_ticks(periode_ticks); + + start_periode = xtimer_now(); + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + start_kept[n] = start_periode.ticks32; + xtimer_periodic_wakeup(&start_periode, periode); + test_times[n] = _xtimer_now(); + } + + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + uint32_t diff = test_times[n] - start_kept[n]; + printf("%" PRIu32 " %" PRIu32 " slept for %" PRIu32 " ticks (%" PRIu32 + " us) expected %" PRIu32 " ticks, diff %" PRIu32 " ticks\n", + start_kept[n], test_times[n], diff, _xtimer_usec_from_ticks(diff), + periode_ticks, diff - periode_ticks); + } + + /* Testing ticks */ + /* will always have some ticks delay as absolute timestamp is calculated + * after calling multiple functions. + * _xtimer_tsleep32 + * _xtimer_tsleep + * _xtimer_set64 + * _xtimer_set + * */ + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + uint32_t start, end, diff; + + start = _xtimer_now(); + _xtimer_tsleep32(sleep_times[n]); + end = _xtimer_now(); + + diff = end - start; + printf("%" PRIu32 " %" PRIu32 " slept for %" PRIu32 " ticks (%" PRIu32 + " us) expected %" PRIu32 " ticks, diff %" PRIu32 " ticks\n", + start, end, diff, _xtimer_usec_from_ticks(diff), + sleep_times[n], diff - sleep_times[n]); + } + + /* Testing XTIMER_ISR_BACKOFF */ + /* When only one timer is triggered the delay is smaller as less functions + * are called untill absolute timestamp is calculated then for _xtimer_tsleep32. + * _xtimer_set_wakeup + * _xtimer_set + */ + xtimer_t xtimer[SLEEP_TIMES_NUMOF]; + uint32_t start[SLEEP_TIMES_NUMOF]; + uint32_t interval[SLEEP_TIMES_NUMOF]; + + kernel_pid_t pid_main = thread_getpid(); + + kernel_pid_t pid_1 = thread_create(stack, + sizeof(stack), + THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, + _thread, + NULL, + "second_thread"); + + interval[SLEEP_TIMES_NUMOF - 2] = _xtimer_usec_from_ticks(40000 + 15 + XTIMER_ISR_BACKOFF); + start[SLEEP_TIMES_NUMOF - 2] = _xtimer_now(); + xtimer_set_wakeup(&xtimer[SLEEP_TIMES_NUMOF - 2], interval[SLEEP_TIMES_NUMOF - 2], pid_main); + + interval[SLEEP_TIMES_NUMOF - 1] = _xtimer_usec_from_ticks(40000 + (XTIMER_ISR_BACKOFF * SLEEP_TIMES_NUMOF * 2) + 15 * SLEEP_TIMES_NUMOF); + start[SLEEP_TIMES_NUMOF - 1] = _xtimer_now(); + xtimer_set_wakeup(&xtimer[SLEEP_TIMES_NUMOF - 1], interval[SLEEP_TIMES_NUMOF - 1], pid_main); + + for (unsigned n = 0; n < (SLEEP_TIMES_NUMOF - 2); n++) { + interval[n] = _xtimer_usec_from_ticks(40000 + (XTIMER_ISR_BACKOFF * n * 2)); + start[n] = _xtimer_now(); + xtimer_set_wakeup(&xtimer[n], interval[n], pid_1); + } + + thread_sleep(); + end_times[SLEEP_TIMES_NUMOF - 2] = _xtimer_now(); + + thread_sleep(); + end_times[SLEEP_TIMES_NUMOF - 1] = _xtimer_now(); + + for (unsigned n = 0; n < SLEEP_TIMES_NUMOF; n++) { + uint32_t diff = end_times[n] - start[n]; + printf("%" PRIu32 " %" PRIu32 " slept for %" PRIu32 " ticks (%" PRIu32 + " us) expected %" PRIu32 " ticks, diff %" PRIu32 " ticks\n", + start[n], end_times[n], diff, _xtimer_usec_from_ticks(diff), + _xtimer_ticks_from_usec(interval[n]), diff - _xtimer_ticks_from_usec(interval[n])); + } + + testtime = xtimer_now_usec() - start_test; + printf("Test ran for %" PRIu32 " us\n", testtime); + + return 0; +} diff --git a/tests/xtimer_configuration/tests/01-run.py b/tests/xtimer_configuration/tests/01-run.py new file mode 100644 index 0000000000000..a99f6f8855e59 --- /dev/null +++ b/tests/xtimer_configuration/tests/01-run.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 + +# Copyright (C) 2017 Francisco Acosta +# 2017 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import os +import sys +import time + +US_PER_SEC = 1000000 +INTERNAL_JITTER = 0.05 +EXTERNAL_JITTER = 0.15 + + +class InvalidTimeout(Exception): + pass + + +def testfunc(child): + child.expect(u"Running test (\\d+) times with (\\d+) distinct sleep times") + RUNS = int(child.match.group(1)) + SLEEP_TIMES_NUMOF = int(child.match.group(2)) + try: + child.expect_exact(u"Please hit any key and then ENTER to continue") + child.sendline(u"a") + start_test = time.time() + for m in range(RUNS): + for n in range(SLEEP_TIMES_NUMOF): + child.expect(u"(\\d+) (\\d+) slept for (\\d+) ticks.* expected" + + " (\\d+) ticks.* diff (\\d+) ticks") + start = int(child.match.group(1)) + end = int(child.match.group(2)) + sleep_time = int(child.match.group(3)) + exp = int(child.match.group(4)) + lower_bound = exp - (exp * EXTERNAL_JITTER) + upper_bound = exp + (exp * EXTERNAL_JITTER) + time.sleep(0.001) + + #if end - start < exp: + # raise InvalidTimeout("To big values %d < %d, xtimer fires too early." + # % (sleep_time, exp)) + #elif sleep_time - exp > exp*(1.1): + # raise InvalidTimeout("Difference too big %d, reduce XTIMER_OVERHEAD." + # % (sleep_time-exp)) + if m == 0 and n == SLEEP_TIMES_NUMOF-1: + print("Increase value for XTIMER_SHOOT_OVERHEAD or " + "XTIMER_OVERHEAD %d" % (sleep_time-exp)) + elif m == 1 and n == 0: + print("Increase value for XTIMER_BACKOFF_OVERHEAD %d" % (sleep_time-exp)) + elif exp/10 < 0 and not (lower_bound < sleep_time < upper_bound): + raise InvalidTimeout("Invalid timeout %d, expected %d, xtimer " + + "compared to host clock has to much deviation" + % (sleep_time, exp)) + + testtime = (time.time() - start_test) * US_PER_SEC + child.expect(u"Test ran for (\\d+) us") + exp = int(child.match.group(1)) + lower_bound = exp - (exp * EXTERNAL_JITTER) + upper_bound = exp + (exp * EXTERNAL_JITTER) + if not (lower_bound < testtime < upper_bound): + raise InvalidTimeout("Host timer measured %d us (client measured %d us)" + % (testtime, exp)) + except InvalidTimeout as e: + print(e) + sys.exit(1) + + +if __name__ == "__main__": + sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner')) + from testrunner import run + sys.exit(run(testfunc))