On Wed, Aug 12, 2020 at 11:43 AM Jan Kiszka <[email protected]> wrote: > > On 12.08.20 17:28, Jan Kiszka wrote: > > On 12.08.20 16:55, Jan Kiszka wrote: > >> On 12.08.20 05:36, Greg Gallagher wrote: > >>> On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher > >>> <[email protected]> wrote: > >>>> > >>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <[email protected]> > >>>> wrote: > >>>>> > >>>>> On 03.08.20 08:04, chensong wrote: > >>>>>> This a tool to benchmark the latency of GPIO driver, > >>>>>> it's able to run 2 kinds of benchmark test: > >>>>>> > >>>>>> 1, loopback mode > >>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver > >>>>>> like gpio-bcm2835 and gpio-core.c, one is as output, > >>>>>> the other is as interrupt > >>>>>> 2) call write_rt to send a pulse from output > >>>>>> 3) call read_rt to get timestamps recorded in driver (inner loop) > >>>>>> 4) also record timespace in user space(outer_loop) > >>>>>> outer_loop is inner_loop plus overhead of event wakeup > >>>>>> 5) ftrace enable/disable > >>>>>> > >>>>>> 2, react mode > >>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver > >>>>>> like gpio-bcm2835 and gpio-core.c, one is as ourput, > >>>>>> the other is as interrupt > >>>>>> 2) call read_rt to wait for a pulse from latency box > >>>>>> 3) call write_rt to send a signal back to latency box > >>>>>> as a reaction > >>>>>> 4) latency box calculates the diff and makes the histogram > >>>>>> > >>>>>> e.g.: > >>>>>> 1) react mode: > >>>>>> gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000 > >>>>>> 2) loopback mode: > >>>>>> gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100 > >>>>>> -b 50 > >>>>>> > >>>>>> CC: Jan Kiszka <[email protected]> > >>>>>> CC: Greg Gallagher <[email protected]> > >>>>>> > >>>>>> Signed-off-by: chensong <[email protected]> > >>>>>> --- > >>>>>> configure.ac | 1 + > >>>>>> doc/asciidoc/man1/gpiobench.adoc | 70 +++++ > >>>>>> testsuite/Makefile.am | 3 +- > >>>>>> testsuite/gpiobench/Makefile.am | 18 ++ > >>>>>> testsuite/gpiobench/gpiobench.c | 654 > >>>>>> +++++++++++++++++++++++++++++++++++++++ > >>>>>> 5 files changed, 745 insertions(+), 1 deletion(-) > >>>>>> create mode 100644 doc/asciidoc/man1/gpiobench.adoc > >>>>>> create mode 100644 testsuite/gpiobench/Makefile.am > >>>>>> create mode 100644 testsuite/gpiobench/gpiobench.c > >>>>>> > >>>>>> diff --git a/configure.ac b/configure.ac > >>>>>> index 29fefab..164c449 100644 > >>>>>> --- a/configure.ac > >>>>>> +++ b/configure.ac > >>>>>> @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \ > >>>>>> testsuite/latency/Makefile \ > >>>>>> testsuite/switchtest/Makefile \ > >>>>>> testsuite/gpiotest/Makefile \ > >>>>>> + testsuite/gpiobench/Makefile \ > >>>>>> testsuite/spitest/Makefile \ > >>>>>> testsuite/smokey/Makefile \ > >>>>>> testsuite/smokey/arith/Makefile \ > >>>>>> diff --git a/doc/asciidoc/man1/gpiobench.adoc > >>>>>> b/doc/asciidoc/man1/gpiobench.adoc > >>>>>> new file mode 100644 > >>>>>> index 0000000..bd32ea8 > >>>>>> --- /dev/null > >>>>>> +++ b/doc/asciidoc/man1/gpiobench.adoc > >>>>>> @@ -0,0 +1,70 @@ > >>>>>> +// ** The above line should force tbl to be a preprocessor ** > >>>>>> +// Man page for gpiobench > >>>>>> +// > >>>>>> +// Copyright (C) 2020 song chen <[email protected]> > >>>>>> +// > >>>>>> +// You may distribute under the terms of the GNU General Public > >>>>>> +// License as specified in the file COPYING that comes with the > >>>>>> +// Xenomai distribution. > >>>>>> +// > >>>>>> +// > >>>>>> +GPIOBENCH(1) > >>>>>> +========== > >>>>>> +:doctype: manpage > >>>>>> +:revdate: 2020/08/03 > >>>>>> +:man source: Xenomai > >>>>>> +:man version: {xenover} > >>>>>> +:man manual: Xenomai Manual > >>>>>> + > >>>>>> +NAME > >>>>>> +----- > >>>>>> +gpiobench - Xenomai gpio latency benchmark > >>>>>> + > >>>>>> +SYNOPSIS > >>>>>> +--------- > >>>>>> +// The general command line > >>>>>> +*gpiobench* [ options ] > >>>>>> + > >>>>>> +DESCRIPTION > >>>>>> +------------ > >>>>>> +*gpiobench* is part of the Xenomai test suite. It is a gpio latency > >>>>>> +benchmark program. The system must run a suitable Xenomai > >>>>>> enabled kernel with > >>>>>> +the respective module (xeno_timerbench). > >>>>>> + > >>>>>> +OPTIONS > >>>>>> +-------- > >>>>>> +*gpiobench* accepts the following options: > >>>>>> + > >>>>>> +*-h <histogram-size>*:: > >>>>>> +default = 100, increase if your last bucket is full > >>>>>> + > >>>>>> +*-l <num-of-loops>*:: > >>>>>> +default=1000, number of loops to run the test > >>>>>> + > >>>>>> +*-q <quiet>*:: > >>>>>> +print only a summary on exit > >>>>>> + > >>>>>> +*-m <test-mode>*:: > >>>>>> +0 = loopback (default), 1 = react > >>>>>> + > >>>>>> +*-c <pin-controller>*:: > >>>>>> +name of pin controller > >>>> > >>>> Consider changing this to from pin controller to something a little > >>>> more generic. On the bcm2835 the gpios are > >>>>>> + > >>>>>> +*-o <output-pin>*:: > >>>>>> +number of gpio pin as output > >>>>>> + > >>>>>> +*-i <interrupt-pin>*:: > >>>>>> +number of gpin pin as interrupt > >>>>>> + > >>>>>> +*-p <priority>*:: > >>>>>> +default = 99, task priority > >>>>>> + > >>>>>> +*-b <bracktrace>*:: > >>>>>> +default = 1000, send break trace command when latency > breaktrace > >>>>>> + > >>>>>> + > >>>>>> + > >>>>>> +AUTHOR > >>>>>> +------- > >>>>>> +*gpiobench* was written by song chen. This man page > >>>>>> +was written by song chen. > >>>>>> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am > >>>>>> index 76d108e..4932f6d 100644 > >>>>>> --- a/testsuite/Makefile.am > >>>>>> +++ b/testsuite/Makefile.am > >>>>>> @@ -1,5 +1,5 @@ > >>>>>> > >>>>>> -SUBDIRS = latency smokey > >>>>>> +SUBDIRS = latency smokey gpiobench > >>>>>> > >>>>>> if XENO_COBALT > >>>>>> SUBDIRS += \ > >>>>>> @@ -13,6 +13,7 @@ endif > >>>>>> DIST_SUBDIRS = \ > >>>>>> clocktest \ > >>>>>> gpiotest \ > >>>>>> + gpiobench \ > >>>>>> latency \ > >>>>>> smokey \ > >>>>>> spitest \ > >>>>>> diff --git a/testsuite/gpiobench/Makefile.am > >>>>>> b/testsuite/gpiobench/Makefile.am > >>>>>> new file mode 100644 > >>>>>> index 0000000..cca3395 > >>>>>> --- /dev/null > >>>>>> +++ b/testsuite/gpiobench/Makefile.am > >>>>>> @@ -0,0 +1,18 @@ > >>>>>> +testdir = @XENO_TEST_DIR@ > >>>>>> + > >>>>>> +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC) > >>>>>> + > >>>>>> +test_PROGRAMS = gpiobench > >>>>>> + > >>>>>> +gpiobench_SOURCES = gpiobench.c > >>>>>> + > >>>>>> +gpiobench_CPPFLAGS = \ > >>>>>> + $(XENO_USER_CFLAGS) \ > >>>>>> + -I$(top_srcdir)/include > >>>>>> + > >>>>>> +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS) > >>>>>> + > >>>>>> +gpiobench_LDADD = \ > >>>>>> + @XENO_CORE_LDADD@ \ > >>>>>> + @XENO_USER_LDADD@ \ > >>>>>> + -lpthread -lrt -lm > >>>>>> diff --git a/testsuite/gpiobench/gpiobench.c > >>>>>> b/testsuite/gpiobench/gpiobench.c > >>>>>> new file mode 100644 > >>>>>> index 0000000..7f19837 > >>>>>> --- /dev/null > >>>>>> +++ b/testsuite/gpiobench/gpiobench.c > >>>>>> @@ -0,0 +1,654 @@ > >>>>>> +/* > >>>>>> + * Copyright (C) 2020 Song Chen <[email protected]> > >>>>>> + * > >>>>>> + * Permission is hereby granted, free of charge, to any person > >>>>>> obtaining > >>>>>> + * a copy of this software and associated documentation files (the > >>>>>> + * "Software"), to deal in the Software without restriction, > >>>>>> including > >>>>>> + * without limitation the rights to use, copy, modify, merge, > >>>>>> publish, > >>>>>> + * distribute, sublicense, and/or sell copies of the Software, > >>>>>> and to > >>>>>> + * permit persons to whom the Software is furnished to do so, > >>>>>> subject to > >>>>>> + * the following conditions: > >>>>>> + * > >>>>>> + * The above copyright notice and this permission notice shall be > >>>>>> included > >>>>>> + * in all copies or substantial portions of the Software. > >>>>>> + * > >>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > >>>>>> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE > >>>>>> WARRANTIES OF > >>>>>> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND > >>>>>> NONINFRINGEMENT. > >>>>>> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE > >>>>>> FOR ANY > >>>>>> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF > >>>>>> CONTRACT, > >>>>>> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE > >>>>>> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > >>>>>> + */ > >>>>>> +#include <stdlib.h> > >>>>>> +#include <math.h> > >>>>>> +#include <stdio.h> > >>>>>> +#include <string.h> > >>>>>> +#include <errno.h> > >>>>>> +#include <error.h> > >>>>>> +#include <signal.h> > >>>>>> +#include <sched.h> > >>>>>> +#include <time.h> > >>>>>> +#include <sys/time.h> > >>>>>> +#include <unistd.h> > >>>>>> +#include <pthread.h> > >>>>>> +#include <semaphore.h> > >>>>>> +#include <sys/timerfd.h> > >>>>>> +#include <xeno_config.h> > >>>>>> +#include <rtdm/testing.h> > >>>>>> +#include <rtdm/gpio.h> > >>>>>> +#include <boilerplate/trace.h> > >>>>>> +#include <xenomai/init.h> > >>>>>> +#include <sys/mman.h> > >>>>>> +#include <getopt.h> > >>>>>> + > >>>>>> +#define NS_PER_MS (1000000) > >>>>>> +#define NS_PER_S (1000000000) > >>>>>> + > >>>>>> +#define DEFAULT_PRIO 99 > >>>>>> +#define VERSION_STRING "0.1" > >>>>>> +#define GPIO_HIGH 1 > >>>>>> +#define GPIO_LOW 0 > >>>>>> +#define MAX_HIST 100 > >>>>>> +#define MAX_CYCLES 1000000 > >>>>>> +#define DEFAULT_LIMIT 1000 > >>>>>> +#define DEV_PATH "/dev/rtdm/" > >>>>>> +#define TRACING_ON "/sys/kernel/debug/tracing/tracing_on" > >>>>>> +#define TRACING_EVENTS "/sys/kernel/debug/tracing/events/enable" > >>>>>> +#define TRACE_MARKER "/sys/kernel/debug/tracing/trace_marker" > >>>>>> +#define ON "1" > >>>>>> +#define OFF "0" > >>>>>> + > >>>>>> +enum { > >>>>>> + MODE_LOOPBACK, > >>>>>> + MODE_REACT, > >>>>>> + MODE_ALL > >>>>>> +}; > >>>>>> + > >>>>>> +/* Struct for statistics */ > >>>>>> +struct test_stat { > >>>>>> + long inner_min; > >>>>>> + long inner_max; > >>>>>> + double inner_avg; > >>>>>> + long *inner_hist_array; > >>>>>> + long inner_hist_overflow; > >>>>>> + long outer_min; > >>>>>> + long outer_max; > >>>>>> + double outer_avg; > >>>>>> + long *outer_hist_array; > >>>>>> + long outer_hist_overflow; > >>>>>> +}; > >>>>>> + > >>>>>> +/* Struct for information */ > >>>>>> +struct test_info { > >>>>>> + unsigned long max_cycles; > >>>>>> + unsigned long total_cycles; > >>>>>> + unsigned long max_histogram; > >>>>>> + int mode; > >>>>>> + int prio; > >>>>>> + int quiet; > >>>>>> + int tracelimit; > >>>>>> + int fd_dev_intr; > >>>>>> + int fd_dev_out; > >>>>>> + char pin_controller[32]; > >>>>>> + pthread_t gpio_task; > >>>>>> + int gpio_intr; > >>>>>> + int gpio_out; > >>>>>> + struct test_stat ts; > >>>>>> +}; > >>>>>> + > >>>>>> +struct test_info ti; > >>>>>> +/* Print usage information */ > >>>>>> +static void display_help(void) > >>>>>> +{ > >>>>>> + printf("gpiobench V %s\n", VERSION_STRING); > >>>>>> + printf("Usage:\n" > >>>>>> + "gpiobench <options>\n\n" > >>>>>> + > >>>>>> + "-b --breaktrace=USEC send break trace command > >>>>>> when latency > USEC\n" > >>>>>> + " default=1000\n" > >>>>>> + "-h --histogram=US dump a latency histogram > >>>>>> to stdout after the run\n" > >>>>>> + " US is the max time to be > >>>>>> tracked in microseconds,\n" > >>>>>> + " default=100\n" > >>>>>> + "-l --loops number of loops, > >>>>>> default=1000\n" > >>>>>> + "-p --prio priority of highest prio > >>>>>> thread, defaults=99\n" > >>>>>> + "-q --quiet print only a summary on > >>>>>> exit\n" > >>>>>> + "-o --output gpio port number for > >>>>>> output, no default value,\n" > >>>>>> + " must be specified\n" > >>>>>> + "-i --intr gpio port number as an > >>>>>> interrupt, no default value,\n" > >>>>>> + " must be specified\n" > >>>>>> + "-c --pinctrl gpio pin controller's > >>>>>> name, no default value,\n" > >>>>>> + " must be specified\n" > >>>>>> + "-m --testmode 0 is loopback mode\n" > >>>>>> + " 1 is react mode which > >>>>>> works with a latency box,\n" > >>>>>> + " default=0\n\n" > >>>>>> + > >>>>>> + "e.g. gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n" > >>>>>> + ); > >>>>>> +} > >>>>>> + > >>>>>> +static void process_options(int argc, char *argv[]) > >>>>>> +{ > >>>>>> + int c = 0; > >>>>>> + static const char optstring[] = "h:p:m:l:c:b:i:o:q"; > >>>>>> + > >>>>>> + struct option long_options[] = { > >>>>>> + { "bracetrace", required_argument, 0, 'b'}, > >>>>>> + { "histogram", required_argument, 0, 'h'}, > >>>>>> + { "loops", required_argument, 0, 'l'}, > >>>>>> + { "prio", required_argument, 0, 'p'}, > >>>>>> + { "quiet", no_argument, 0, 'q'}, > >>>>>> + { "output", required_argument, 0, 'o'}, > >>>>>> + { "intr", required_argument, 0, 'i'}, > >>>>>> + { "pinctrl", required_argument, 0, 'c'}, > >>>>>> + { "testmode", required_argument, 0, 'm'}, > >>>>>> + { 0, 0, 0, 0}, > >>>>>> + }; > >>>>>> + > >>>>>> + while ((c = getopt_long(argc, argv, optstring, long_options, > >>>>>> + NULL)) != -1) { > >>>>>> + switch (c) { > >>>>>> + case 'h': > >>>>>> + ti.max_histogram = atoi(optarg); > >>>>>> + break; > >>>>>> + > >>>>>> + case 'p': > >>>>>> + ti.prio = atoi(optarg); > >>>>>> + break; > >>>>>> + > >>>>>> + case 'l': > >>>>>> + ti.max_cycles = atoi(optarg); > >>>>>> + break; > >>>>>> + > >>>>>> + case 'q': > >>>>>> + ti.quiet = 1; > >>>>>> + break; > >>>>>> + > >>>>>> + case 'b': > >>>>>> + ti.tracelimit = atoi(optarg); > >>>>>> + break; > >>>>>> + > >>>>>> + case 'i': > >>>>>> + ti.gpio_intr = atoi(optarg); > >>>>>> + break; > >>>>>> + > >>>>>> + case 'o': > >>>>>> + ti.gpio_out = atoi(optarg); > >>>>>> + break; > >>>>>> + > >>>>>> + case 'c': > >>>>>> + strcpy(ti.pin_controller, optarg); > >>>>>> + break; > >>>>>> + > >>>>>> + case 'm': > >>>>>> + ti.mode = atoi(optarg) >= > >>>>>> + MODE_REACT ? MODE_REACT : > >>>>>> MODE_LOOPBACK; > >>>>>> + break; > >>>>>> + > >>>>>> + default: > >>>>>> + display_help(); > >>>>>> + exit(2); > >>>>>> + } > >>>>>> + } > >>>>>> + > >>>>>> + if ((ti.gpio_out == -1) || (ti.gpio_intr == -1) > >>>>>> + || (strlen(ti.pin_controller) == 0)) { > >>>>>> + display_help(); > >>>>>> + exit(2); > >>>>>> + } > >>>>>> + > >>>>>> + ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio; > >>>>>> + ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES : > >>>>>> ti.max_cycles; > >>>>>> + > >>>>>> + ti.max_histogram = ti.max_histogram > MAX_HIST ? > >>>>>> + MAX_HIST : ti.max_histogram; > >>>>>> + ti.ts.inner_hist_array = calloc(ti.max_histogram, > >>>>>> sizeof(long)); > >>>>>> + ti.ts.outer_hist_array = calloc(ti.max_histogram, > >>>>>> sizeof(long)); > >>>>>> +} > >>>>>> + > >>>>>> +static int thread_msleep(unsigned int ms) > >>>>>> +{ > >>>>>> + struct timespec ts = { > >>>>>> + .tv_sec = (ms * NS_PER_MS) / NS_PER_S, > >>>>>> + .tv_nsec = (ms * NS_PER_MS) % NS_PER_S, > >>>>>> + }; > >>>>>> + > >>>>>> + return -nanosleep(&ts, NULL); > >>>>>> +} > >>>>>> + > >>>>>> +static inline int64_t calc_us(struct timespec t) > >>>>>> +{ > >>>>>> + return (t.tv_sec * NS_PER_S + t.tv_nsec); > >>>>>> +} > >>>>>> + > >>>>>> +static int setevent(char *event, char *val) > >>>>>> +{ > >>>>>> + int fd; > >>>>>> + int ret; > >>>>>> + > >>>>>> + fd = open(event, O_WRONLY); > >>>>>> + if (fd < 0) { > >>>>>> + printf("unable to open %s\n", event); > >>>>>> + return -1; > >>>>>> + } > >>>>>> + > >>>>>> + ret = write(fd, val, strlen(val)); > >>>>>> + if (ret < 0) { > >>>>>> + printf("unable to write %s to %s\n", val, event); > >>>>>> + close(fd); > >>>>>> + return -1; > >>>>>> + } > >>>>>> + > >>>>>> + close(fd); > >>>>>> + return 0; > >>>>>> +} > >>>>>> + > >>>>>> +static void tracing(char *enable) > >>>>>> +{ > >>>>>> + setevent(TRACING_EVENTS, enable); > >>>>>> + setevent(TRACING_ON, enable); > >>>>>> +} > >>>>>> + > >>>>>> +#define write_check(__fd, __buf, __len) \ > >>>>>> + do { \ > >>>>>> + int __ret = write(__fd, __buf, __len); \ > >>>>>> + (void)__ret; \ > >>>>>> + } while (0) > >>>>>> + > >>>>>> +#define TRACEBUFSIZ 1024 > >>>>>> +static __thread char tracebuf[TRACEBUFSIZ]; > >>>>>> + > >>>>>> +static void tracemark(char *fmt, ...) > >>>>>> __attribute__((format(printf, 1, 2))); > >>>>>> +static void tracemark(char *fmt, ...) > >>>>>> +{ > >>>>>> + va_list ap; > >>>>>> + int len; > >>>>>> + int tracemark_fd; > >>>>>> + > >>>>>> + tracemark_fd = open(TRACE_MARKER, O_WRONLY); > >>>>>> + if (tracemark_fd == -1) { > >>>>>> + printf("unable to open trace_marker file: %s\n", > >>>>>> TRACE_MARKER); > >>>>>> + return; > >>>>>> + } > >>>>>> + > >>>>>> + /* bail out if we're not tracing */ > >>>>>> + /* or if the kernel doesn't support trace_mark */ > >>>>>> + if (tracemark_fd < 0) > >>>>>> + return; > >>>>>> + > >>>>>> + va_start(ap, fmt); > >>>>>> + len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap); > >>>>>> + va_end(ap); > >>>>>> + write_check(tracemark_fd, tracebuf, len); > >>>>>> + > >>>>>> + close(tracemark_fd); > >>>>>> +} > >>>>>> + > >>>>>> +static int rw_gpio(int value, int index) > >>>>>> +{ > >>>>>> + int ret; > >>>>>> + struct timespec timestamp; > >>>>>> + struct rtdm_gpio_readout rdo; > >>>>>> + uint64_t gpio_write, gpio_read, inner_diff, outer_diff; > >>>>>> + > >>>>>> + clock_gettime(CLOCK_MONOTONIC, ×tamp); > >>>>>> + gpio_write = calc_us(timestamp); > >>>>>> + > >>>>>> + ret = write(ti.fd_dev_out, &value, sizeof(value)); > >>>>>> + if (ret < 0) { > >>>>>> + printf("write GPIO, failed\n"); > >>>>>> + return ret; > >>>>>> + } > >>>>>> + > >>>>>> + ret = read(ti.fd_dev_intr, &rdo, sizeof(struct > >>>>>> rtdm_gpio_readout)); > >>>>>> + if (ret < 0) { > >>>>>> + printf("read GPIO, failed\n"); > >>>>>> + return ret; > >>>>>> + } > >>>>>> + > >>>>>> + clock_gettime(CLOCK_MONOTONIC, ×tamp); > >>>>>> + gpio_read = calc_us(timestamp); > >>>>>> + > >>>>>> + inner_diff = (rdo.timestamp - gpio_write) / 1000; > >>>>>> + outer_diff = (gpio_read - gpio_write) / 1000; > >>>>>> + > >>>>>> + if (inner_diff < ti.ts.inner_min) > >>>>>> + ti.ts.inner_min = inner_diff; > >>>>>> + if (inner_diff > ti.ts.inner_max) > >>>>>> + ti.ts.inner_max = inner_diff; > >>>>>> + ti.ts.inner_avg += (double) inner_diff; > >>>>>> + if (inner_diff >= ti.max_histogram) > >>>>>> + ti.ts.inner_hist_overflow++; > >>>>>> + else > >>>>>> + ti.ts.inner_hist_array[inner_diff]++; > >>>>>> + > >>>>>> + if (outer_diff < ti.ts.outer_min) > >>>>>> + ti.ts.outer_min = outer_diff; > >>>>>> + if (inner_diff > ti.ts.outer_max) > >>>>>> + ti.ts.outer_max = outer_diff; > >>>>>> + ti.ts.outer_avg += (double) outer_diff; > >>>>>> + if (outer_diff >= ti.max_histogram) > >>>>>> + ti.ts.outer_hist_overflow++; > >>>>>> + else > >>>>>> + ti.ts.outer_hist_array[outer_diff]++; > >>>>>> + > >>>>>> + if (ti.quiet == 0) > >>>>>> + printf("index: %d, inner_diff: %8ld, outer_diff: > >>>>>> %8ld\n", > >>>>>> + index, inner_diff, outer_diff); > >>>>>> + > >>>>>> + return outer_diff; > >>>>>> +} > >>>>>> + > >>>>>> +static void *run_gpiobench_loop(void *cookie) > >>>>>> +{ > >>>>>> + int i, ret; > >>>>>> + > >>>>>> + printf("----rt task, gpio loop, test run----\n"); > >>>>>> + > >>>>>> + for (i = 0; i < ti.max_cycles; i++) { > >>>>>> + ti.total_cycles = i; > >>>>>> + /* send a high level pulse from gpio output pin and > >>>>>> + * receive an interrupt from the other gpio pin, > >>>>>> + * measuring the time elapsed between the two events > >>>>>> + */ > >>>>>> + ret = rw_gpio(GPIO_HIGH, i); > >>>>>> + if (ret < 0) { > >>>>>> + printf("RW GPIO, failed\n"); > >>>>>> + break; > >>>>>> + } else if (ret > ti.tracelimit) { > >>>>>> + tracemark("hit latency threshold (%d > %d), > >>>>>> index: %d", > >>>>>> + ret, ti.tracelimit, i); > >>>>>> + break; > >>>>>> + } > >>>>>> + > >>>>>> + /*take a break, nanosleep here will not jeopardize > >>>>>> the latency*/ > >>>>>> + thread_msleep(10); > >>>>>> + > >>>>>> + ret = rw_gpio(GPIO_LOW, i); > >>>>>> + /* send a low level pulse from gpio output pin and > >>>>>> + * receive an interrupt from the other gpio pin, > >>>>>> + * measuring the time elapsed between the two events > >>>>>> + */ > >>>>>> + if (ret < 0) { > >>>>>> + printf("RW GPIO, failed\n"); > >>>>>> + break; > >>>>>> + } else if (ti.tracelimit && ret > ti.tracelimit) { > >>>>>> + tracemark("hit latency threshold (%d > %d), > >>>>>> index: %d", > >>>>>> + ret, ti.tracelimit, i); > >>>>>> + break; > >>>>>> + } > >>>>>> + > >>>>>> + /*take a break, nanosleep here will not jeopardize > >>>>>> the latency*/ > >>>>>> + thread_msleep(10); > >>>>>> + } > >>>>>> + > >>>>>> + ti.ts.inner_avg /= (ti.total_cycles * 2); > >>>>>> + ti.ts.outer_avg /= (ti.total_cycles * 2); > >>>>>> + > >>>>>> + return NULL; > >>>>>> +} > >>>>>> + > >>>>>> +static void *run_gpiobench_react(void *cookie) > >>>>>> +{ > >>>>>> + int value, ret, i; > >>>>>> + struct rtdm_gpio_readout rdo; > >>>>>> + > >>>>>> + printf("----rt task, gpio react, test run----\n"); > >>>>>> + > >>>>>> + for (i = 0; i < ti.max_cycles; i++) { > >>>>>> + /* received a pulse from latency box from one of > >>>>>> + * the gpio pin pair > >>>>>> + */ > >>>>>> + ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo)); > >>>>>> + if (ret < 0) { > >>>>>> + printf("RW GPIO read, failed\n"); > >>>>>> + break; > >>>>>> + } > >>>>>> + > >>>>>> + if (ti.quiet == 0) > >>>>>> + printf("idx: %d, received signal from > >>>>>> latency box\n", > >>>>>> + i); > >>>>>> + > >>>>>> + /* send a signal back from the other gpio pin > >>>>>> + * to latency box as the acknowledge, > >>>>>> + * latency box will measure the time elapsed > >>>>>> + * between the two events > >>>>>> + */ > >>>>>> + value = GPIO_HIGH; > >>>>>> + ret = write(ti.fd_dev_out, &value, sizeof(value)); > >>>>>> + if (ret < 0) { > >>>>>> + printf("RW GPIO write, failed\n"); > >>>>>> + break; > >>>>>> + } > >>>>>> + > >>>>>> + if (ti.quiet == 0) > >>>>>> + printf("idx: %d, sent reaction to latency > >>>>>> box\n", i); > >>>>>> + } > >>>>>> + > >>>>>> + return NULL; > >>>>>> +} > >>>>>> + > >>>>>> +static void setup_sched_parameters(pthread_attr_t *attr, int prio) > >>>>>> +{ > >>>>>> + struct sched_param p; > >>>>>> + int ret; > >>>>>> + > >>>>>> + ret = pthread_attr_init(attr); > >>>>>> + if (ret) { > >>>>>> + printf("pthread_attr_init(), failed\n"); > >>>>>> + return; > >>>>>> + } > >>>>>> + > >>>>>> + ret = pthread_attr_setinheritsched(attr, > >>>>>> PTHREAD_EXPLICIT_SCHED); > >>>>>> + if (ret) { > >>>>>> + printf("pthread_attr_setinheritsched(), failed\n"); > >>>>>> + return; > >>>>>> + } > >>>>>> + > >>>>>> + ret = pthread_attr_setschedpolicy(attr, > >>>>>> + prio ? SCHED_FIFO : SCHED_OTHER); > >>>>>> + if (ret) { > >>>>>> + printf("pthread_attr_setschedpolicy(), failed\n"); > >>>>>> + return; > >>>>>> + } > >>>>>> + > >>>>>> + p.sched_priority = prio; > >>>>>> + ret = pthread_attr_setschedparam(attr, &p); > >>>>>> + if (ret) { > >>>>>> + printf("pthread_attr_setschedparam(), failed\n"); > >>>>>> + return; > >>>>>> + } > >>>>>> +} > >>>>>> + > >>>>>> +static void init_ti(void) > >>>>>> +{ > >>>>>> + memset(&ti, 0, sizeof(struct test_info)); > >>>>>> + ti.prio = DEFAULT_PRIO; > >>>>>> + ti.max_cycles = MAX_CYCLES; > >>>>>> + ti.total_cycles = MAX_CYCLES; > >>>>>> + ti.max_histogram = MAX_HIST; > >>>>>> + ti.tracelimit = DEFAULT_LIMIT; > >>>>>> + ti.quiet = 0; > >>>>>> + ti.gpio_out = -1; > >>>>>> + ti.gpio_intr = -1; > >>>>>> + ti.mode = MODE_LOOPBACK; > >>>>>> + > >>>>>> + ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT; > >>>>>> + ti.ts.inner_max = ti.ts.outer_max = 0; > >>>>>> + ti.ts.inner_avg = ti.ts.outer_avg = 0.0; > >>>>>> +} > >>>>>> + > >>>>>> +static void print_hist(void) > >>>>>> +{ > >>>>>> + int i; > >>>>>> + > >>>>>> + printf("\n"); > >>>>>> + printf("# Inner Loop Histogram\n"); > >>>>>> + printf("# Inner Loop latency is the latency in kernel space\n" > >>>>>> + "# between gpio_set_value and irq handler\n"); > >>>>>> + > >>>>>> + for (i = 0; i < ti.max_histogram; i++) { > >>>>>> + unsigned long curr_latency = ti.ts.inner_hist_array[i]; > >>>>>> + > >>>>>> + printf("%06d ", i); > >>>>>> + printf("%06lu\n", curr_latency); > >>>>>> + } > >>>>>> + > >>>>>> + printf("# Total:"); > >>>>>> + printf(" %09lu", ti.total_cycles); > >>>>>> + printf("\n"); > >>>>>> + > >>>>>> + printf("# Min Latencies:"); > >>>>>> + printf(" %05lu", ti.ts.inner_min); > >>>>>> + printf("\n"); > >>>>>> + printf("# Avg Latencies:"); > >>>>>> + printf(" %05lf", ti.ts.inner_avg); > >>>>>> + printf("\n"); > >>>>>> + printf("# Max Latencies:"); > >>>>>> + printf(" %05lu", ti.ts.inner_max); > >>>>>> + printf("\n"); > >>>>>> + > >>>>>> + printf("\n"); > >>>>>> + printf("\n"); > >>>>>> + > >>>>>> + printf("# Outer Loop Histogram\n"); > >>>>>> + printf("# Outer Loop latency is the latency in user space\n" > >>>>>> + "# between write and read\n" > >>>>>> + "# Technically, outer loop latency is inner loop > >>>>>> latercy\n" > >>>>>> + "# plus overhead of event wakeup\n"); > >>>>>> + > >>>>>> + for (i = 0; i < ti.max_histogram; i++) { > >>>>>> + unsigned long curr_latency = ti.ts.outer_hist_array[i]; > >>>>>> + > >>>>>> + printf("%06d ", i); > >>>>>> + printf("%06lu\n", curr_latency); > >>>>>> + } > >>>>>> + > >>>>>> + printf("# Total:"); > >>>>>> + printf(" %09lu", ti.total_cycles); > >>>>>> + printf("\n"); > >>>>>> + > >>>>>> + printf("# Min Latencies:"); > >>>>>> + printf(" %05lu", ti.ts.outer_min); > >>>>>> + printf("\n"); > >>>>>> + printf("# Avg Latencies:"); > >>>>>> + printf(" %05lf", ti.ts.outer_avg); > >>>>>> + printf("\n"); > >>>>>> + printf("# Max Latencies:"); > >>>>>> + printf(" %05lu", ti.ts.outer_max); > >>>>>> + printf("\n"); > >>>>>> +} > >>>>>> + > >>>>>> +static void cleanup(void) > >>>>>> +{ > >>>>>> + int ret; > >>>>>> + > >>>>>> + if (ti.tracelimit < DEFAULT_LIMIT) > >>>>>> + tracing(OFF); > >>>>>> + > >>>>>> + ret = close(ti.fd_dev_out); > >>>>>> + if (ret < 0) > >>>>>> + printf("can't close gpio_out device\n"); > >>>>>> + > >>>>>> + ret = close(ti.fd_dev_intr); > >>>>>> + if (ret < 0) > >>>>>> + printf("can't close gpio_intr device\n"); > >>>>>> + > >>>>>> + if (ti.mode == MODE_LOOPBACK) > >>>>>> + print_hist(); > >>>>>> + > >>>>>> +} > >>>>>> + > >>>>>> +static void cleanup_and_exit(int sig) > >>>>>> +{ > >>>>>> + printf("Signal %d received\n", sig); > >>>>>> + cleanup(); > >>>>>> + exit(0); > >>>>>> +} > >>>>>> + > >>>>>> +int main(int argc, char **argv) > >>>>>> +{ > >>>>>> + struct sigaction sa __attribute__((unused)); > >>>>>> + int ret = 0; > >>>>>> + pthread_attr_t tattr; > >>>>>> + int trigger, value; > >>>>>> + char dev_name[64]; > >>>>>> + > >>>>>> + init_ti(); > >>>>>> + > >>>>>> + process_options(argc, argv); > >>>>>> + > >>>>>> + ret = mlockall(MCL_CURRENT|MCL_FUTURE); > >>>>>> + if (ret) { > >>>>>> + printf("mlockall failed\n"); > >>>>>> + goto out; > >>>>>> + } > >>>>>> + > >>>>>> + sprintf(dev_name, "%s%s/gpio%d", > >>>>>> + DEV_PATH, ti.pin_controller, ti.gpio_out); > >>>>>> + ti.fd_dev_out = open(dev_name, O_RDWR); > >>>>>> + if (ti.fd_dev_out < 0) { > >>>>>> + printf("can't open %s\n", dev_name); > >>>>>> + goto out; > >>>>>> + } > >>>>>> + > >>>>>> + if (ti.gpio_out) { > >>>>>> + value = 0; > >>>>>> + ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value); > >>>>>> + if (ret) { > >>>>>> + printf("ioctl gpio port output, failed\n"); > >>>>>> + goto out; > >>>>>> + } > >>>>>> + } > >>>>>> + > >>>>>> + sprintf(dev_name, "%s%s/gpio%d", > >>>>>> + DEV_PATH, ti.pin_controller, ti.gpio_intr); > >>>>>> + ti.fd_dev_intr = open(dev_name, O_RDWR); > >>>>>> + if (ti.fd_dev_intr < 0) { > >>>>>> + printf("can't open %s\n", dev_name); > >>>>>> + goto out; > >>>>>> + } > >>>>>> + > >>>>>> + if (ti.gpio_intr) { > >>>>>> + trigger = > >>>>>> GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING; > >>>>>> + value = 1; > >>>>>> + > >>>>>> + ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN, > >>>>>> &trigger); > >>>>>> + if (ret) { > >>>>>> + printf("ioctl gpio port interrupt, failed\n"); > >>>>>> + goto out; > >>>>>> + } > >>>>>> + > >>>>>> + �� ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value); > >>>>>> + if (ret) { > >>>>>> + printf("ioctl gpio port ts, failed\n"); > >>>>>> + goto out; > >>>>>> + } > >>>>>> + } > >>>>>> + > >>>>>> + if (ti.tracelimit < DEFAULT_LIMIT) > >>>>>> + tracing(ON); > >>>>>> + > >>>>>> + signal(SIGTERM, cleanup_and_exit); > >>>>>> + signal(SIGINT, cleanup_and_exit); > >>>>>> + setup_sched_parameters(&tattr, ti.prio); > >>>>>> + > >>>>>> + if (ti.mode == MODE_LOOPBACK) > >>>>>> + ret = pthread_create(&ti.gpio_task, &tattr, > >>>>>> + run_gpiobench_loop, NULL); > >>>>>> + else > >>>>>> + ret = pthread_create(&ti.gpio_task, &tattr, > >>>>>> + run_gpiobench_react, NULL); > >>>>>> + > >>>>>> + if (ret) { > >>>>>> + printf("pthread_create(gpiotask), failed\n"); > >>>>>> + goto out; > >>>>>> + } > >>>>>> + > >>>>>> + pthread_join(ti.gpio_task, NULL); > >>>>>> + pthread_attr_destroy(&tattr); > >>>>>> + > >>>>>> +out: > >>>>>> + cleanup(); > >>>>>> + return 0; > >>>>>> +} > >>>>>> > >>>>> > >>>>> Looks good to me. Greg, any comments from your side? > >>>>> > >>>>> Jan > >>>>> > >>>>> -- > >>>>> Siemens AG, Corporate Technology, CT RDA IOT SES-DE > >>>>> Corporate Competence Center Embedded Linux > >>>> > >>>> Hi, > >>>> I'll run this on the zynq platform for testing tonight. It looks > >>>> good, my only concern is the 'ti.pin_controller' name may be a little > >>>> bit misleading, but I want to confirm that assumption tonight. > >>>> > >>>> Thanks > >>>> > >>>> Greg > >>> > >>> Tested well on zynq. After looking at gpio-core again, the name > >>> pinctrl makes sense. Looks good to me :) > >>> > >> > >> Perfect - applied to next. > >> > > > > Had to fix > > https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. Was > > trivial enough to force-push a new version. > > > > ...but it wasn't trivial. I migrated from uint64_t to long long, please > have a look at the result in next. Should be tested again on both 64 and > 32-bit targets. > > Jan > > -- > Siemens AG, Corporate Technology, CT RDA IOT SES-DE > Corporate Competence Center Embedded Linux
Don't know why i didn't hit it in my testing, I can run it again on ARM64/ARM platforms. I need to do an ipipe release on both soon anyway. Thanks Greg
