* [PATCH v3] testsuite: App of gpio loopback/react benchmark @ 2020-08-03 6:04 chensong 2020-08-11 7:15 ` Jan Kiszka 0 siblings, 1 reply; 14+ messages in thread From: chensong @ 2020-08-03 6:04 UTC (permalink / raw) To: xenomai, jan.kiszka, greg 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 <jan.kiszka@siemens.com> CC: Greg Gallagher <greg@embeddedgreg.com> Signed-off-by: chensong <chensong@tj.kylinos.cn> --- 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 <chensong@tj.kylinos.cn> +// +// 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 + +*-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 <chensong@tj.kylinos.cn> + * + * 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; +} -- 2.7.4 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-03 6:04 [PATCH v3] testsuite: App of gpio loopback/react benchmark chensong @ 2020-08-11 7:15 ` Jan Kiszka 2020-08-11 15:36 ` Greg Gallagher 0 siblings, 1 reply; 14+ messages in thread From: Jan Kiszka @ 2020-08-11 7:15 UTC (permalink / raw) To: chensong, xenomai, greg 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 <jan.kiszka@siemens.com> > CC: Greg Gallagher <greg@embeddedgreg.com> > > Signed-off-by: chensong <chensong@tj.kylinos.cn> > --- > 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 <chensong@tj.kylinos.cn> > +// > +// 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 > + > +*-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 <chensong@tj.kylinos.cn> > + * > + * 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 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-11 7:15 ` Jan Kiszka @ 2020-08-11 15:36 ` Greg Gallagher 2020-08-12 3:36 ` Greg Gallagher 0 siblings, 1 reply; 14+ messages in thread From: Greg Gallagher @ 2020-08-11 15:36 UTC (permalink / raw) To: Jan Kiszka; +Cc: chensong, Xenomai@xenomai.org On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> 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 <jan.kiszka@siemens.com> > > CC: Greg Gallagher <greg@embeddedgreg.com> > > > > Signed-off-by: chensong <chensong@tj.kylinos.cn> > > --- > > 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 <chensong@tj.kylinos.cn> > > +// > > +// 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 <chensong@tj.kylinos.cn> > > + * > > + * 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 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-11 15:36 ` Greg Gallagher @ 2020-08-12 3:36 ` Greg Gallagher 2020-08-12 14:55 ` Jan Kiszka 0 siblings, 1 reply; 14+ messages in thread From: Greg Gallagher @ 2020-08-12 3:36 UTC (permalink / raw) To: Jan Kiszka; +Cc: chensong, Xenomai@xenomai.org On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher <greg@embeddedgreg.com> wrote: > > On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> 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 <jan.kiszka@siemens.com> > > > CC: Greg Gallagher <greg@embeddedgreg.com> > > > > > > Signed-off-by: chensong <chensong@tj.kylinos.cn> > > > --- > > > 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 <chensong@tj.kylinos.cn> > > > +// > > > +// 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 <chensong@tj.kylinos.cn> > > > + * > > > + * 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 :) ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-12 3:36 ` Greg Gallagher @ 2020-08-12 14:55 ` Jan Kiszka 2020-08-12 15:28 ` Jan Kiszka 0 siblings, 1 reply; 14+ messages in thread From: Jan Kiszka @ 2020-08-12 14:55 UTC (permalink / raw) To: Greg Gallagher, chensong; +Cc: Xenomai@xenomai.org On 12.08.20 05:36, Greg Gallagher wrote: > On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher <greg@embeddedgreg.com> wrote: >> >> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> 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 <jan.kiszka@siemens.com> >>>> CC: Greg Gallagher <greg@embeddedgreg.com> >>>> >>>> Signed-off-by: chensong <chensong@tj.kylinos.cn> >>>> --- >>>> 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 <chensong@tj.kylinos.cn> >>>> +// >>>> +// 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 <chensong@tj.kylinos.cn> >>>> + * >>>> + * 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. Thanks, Jan -- Siemens AG, Corporate Technology, CT RDA IOT SES-DE Corporate Competence Center Embedded Linux ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-12 14:55 ` Jan Kiszka @ 2020-08-12 15:28 ` Jan Kiszka 2020-08-12 15:43 ` Jan Kiszka 0 siblings, 1 reply; 14+ messages in thread From: Jan Kiszka @ 2020-08-12 15:28 UTC (permalink / raw) To: Greg Gallagher, chensong; +Cc: Xenomai@xenomai.org 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 >> <greg@embeddedgreg.com> wrote: >>> >>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> >>> 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 <jan.kiszka@siemens.com> >>>>> CC: Greg Gallagher <greg@embeddedgreg.com> >>>>> >>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn> >>>>> --- >>>>> 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 <chensong@tj.kylinos.cn> >>>>> +// >>>>> +// 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 <chensong@tj.kylinos.cn> >>>>> + * >>>>> + * 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. Jan -- Siemens AG, Corporate Technology, CT RDA IOT SES-DE Corporate Competence Center Embedded Linux ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-12 15:28 ` Jan Kiszka @ 2020-08-12 15:43 ` Jan Kiszka 2020-08-12 16:10 ` Greg Gallagher 0 siblings, 1 reply; 14+ messages in thread From: Jan Kiszka @ 2020-08-12 15:43 UTC (permalink / raw) To: Greg Gallagher, chensong; +Cc: Xenomai@xenomai.org 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 >>> <greg@embeddedgreg.com> wrote: >>>> >>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> >>>> 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 <jan.kiszka@siemens.com> >>>>>> CC: Greg Gallagher <greg@embeddedgreg.com> >>>>>> >>>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn> >>>>>> --- >>>>>> 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 <chensong@tj.kylinos.cn> >>>>>> +// >>>>>> +// 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 <chensong@tj.kylinos.cn> >>>>>> + * >>>>>> + * 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 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-12 15:43 ` Jan Kiszka @ 2020-08-12 16:10 ` Greg Gallagher 2020-08-12 16:14 ` Jan Kiszka 0 siblings, 1 reply; 14+ messages in thread From: Greg Gallagher @ 2020-08-12 16:10 UTC (permalink / raw) To: Jan Kiszka; +Cc: chensong, Xenomai@xenomai.org On Wed, Aug 12, 2020 at 11:43 AM Jan Kiszka <jan.kiszka@siemens.com> 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 > >>> <greg@embeddedgreg.com> wrote: > >>>> > >>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> > >>>> 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 <jan.kiszka@siemens.com> > >>>>>> CC: Greg Gallagher <greg@embeddedgreg.com> > >>>>>> > >>>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn> > >>>>>> --- > >>>>>> 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 <chensong@tj.kylinos.cn> > >>>>>> +// > >>>>>> +// 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 <chensong@tj.kylinos.cn> > >>>>>> + * > >>>>>> + * 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 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-12 16:10 ` Greg Gallagher @ 2020-08-12 16:14 ` Jan Kiszka 2020-08-13 1:57 ` chensong 0 siblings, 1 reply; 14+ messages in thread From: Jan Kiszka @ 2020-08-12 16:14 UTC (permalink / raw) To: Greg Gallagher; +Cc: chensong, Xenomai@xenomai.org On 12.08.20 18:10, Greg Gallagher wrote: > On Wed, Aug 12, 2020 at 11:43 AM Jan Kiszka <jan.kiszka@siemens.com> 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 >>>>> <greg@embeddedgreg.com> wrote: >>>>>> >>>>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> >>>>>> 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 <jan.kiszka@siemens.com> >>>>>>>> CC: Greg Gallagher <greg@embeddedgreg.com> >>>>>>>> >>>>>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn> >>>>>>>> --- >>>>>>>> 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 <chensong@tj.kylinos.cn> >>>>>>>> +// >>>>>>>> +// 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 <chensong@tj.kylinos.cn> >>>>>>>> + * >>>>>>>> + * 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. Different compiler warning defaults, probably. Jan -- Siemens AG, Corporate Technology, CT RDA IOT SES-DE Corporate Competence Center Embedded Linux ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-12 16:14 ` Jan Kiszka @ 2020-08-13 1:57 ` chensong 2020-08-13 8:54 ` Jan Kiszka 0 siblings, 1 reply; 14+ messages in thread From: chensong @ 2020-08-13 1:57 UTC (permalink / raw) To: Jan Kiszka, Greg Gallagher; +Cc: Xenomai@xenomai.org I read the code in cyclictest, looks like printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index, (unsigned long long)inner_diff, (unsigned long long)outer_diff); can fix the error reported in https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. Do i need to apply a new version? chensong On 2020年08月13日 00:14, Jan Kiszka wrote: > On 12.08.20 18:10, Greg Gallagher wrote: >> On Wed, Aug 12, 2020 at 11:43 AM Jan Kiszka <jan.kiszka@siemens.com> >> 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 >>>>>> <greg@embeddedgreg.com> wrote: >>>>>>> >>>>>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> >>>>>>> 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 <jan.kiszka@siemens.com> >>>>>>>>> CC: Greg Gallagher <greg@embeddedgreg.com> >>>>>>>>> >>>>>>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn> >>>>>>>>> --- >>>>>>>>> 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 <chensong@tj.kylinos.cn> >>>>>>>>> +// >>>>>>>>> +// 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 <chensong@tj.kylinos.cn> >>>>>>>>> + * >>>>>>>>> + * 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. > > Different compiler warning defaults, probably. > > Jan > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-13 1:57 ` chensong @ 2020-08-13 8:54 ` Jan Kiszka 2020-08-13 10:06 ` chensong 0 siblings, 1 reply; 14+ messages in thread From: Jan Kiszka @ 2020-08-13 8:54 UTC (permalink / raw) To: chensong, Greg Gallagher; +Cc: Xenomai@xenomai.org On 13.08.20 03:57, chensong wrote: > I read the code in cyclictest, looks like > > printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index, > (unsigned long long)inner_diff, > (unsigned long long)outer_diff); I even wonder if this is strictly correct as it ignores negative diffs. > > can fix the error reported in > https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. > > Do i need to apply a new version? No, I've converted your patch to long long types already. Please cross-check what is in next branch. Jan -- Siemens AG, Corporate Technology, CT RDA IOT SES-DE Corporate Competence Center Embedded Linux ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-13 8:54 ` Jan Kiszka @ 2020-08-13 10:06 ` chensong 2020-08-13 10:27 ` Jan Kiszka 0 siblings, 1 reply; 14+ messages in thread From: chensong @ 2020-08-13 10:06 UTC (permalink / raw) To: Jan Kiszka, Greg Gallagher; +Cc: Xenomai@xenomai.org On 2020年08月13日 16:54, Jan Kiszka wrote: > On 13.08.20 03:57, chensong wrote: >> I read the code in cyclictest, looks like >> >> printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index, >> (unsigned long long)inner_diff, >> (unsigned long long)outer_diff); > > I even wonder if this is strictly correct as it ignores negative diffs. > >> >> can fix the error reported in >> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. >> >> Do i need to apply a new version? > > No, I've converted your patch to long long types already. Please > cross-check what is in next branch. i'm a little confused here, cross-check what? negative diffs? in theory, it should be always positive, do you mean i keep checking if there is a negative diff? and what's next branch? what can i do about it? > > Jan > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-13 10:06 ` chensong @ 2020-08-13 10:27 ` Jan Kiszka 2020-08-13 10:41 ` chensong 0 siblings, 1 reply; 14+ messages in thread From: Jan Kiszka @ 2020-08-13 10:27 UTC (permalink / raw) To: chensong, Greg Gallagher; +Cc: Xenomai@xenomai.org On 13.08.20 12:06, chensong wrote: > > > On 2020年08月13日 16:54, Jan Kiszka wrote: >> On 13.08.20 03:57, chensong wrote: >>> I read the code in cyclictest, looks like >>> >>> printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index, >>> (unsigned long long)inner_diff, >>> (unsigned long long)outer_diff); >> >> I even wonder if this is strictly correct as it ignores negative diffs. >> >>> >>> can fix the error reported in >>> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. >>> >>> Do i need to apply a new version? >> >> No, I've converted your patch to long long types already. Please >> cross-check what is in next branch. > i'm a little confused here, cross-check what? negative diffs? in theory, > it should be always positive, do you mean i keep checking if there is a > negative diff? > > and what's next branch? what can i do about it? I've modified your patch in https://gitlab.denx.de/Xenomai/xenomai/-/commits/next. Please have a look if https://gitlab.denx.de/Xenomai/xenomai/-/commit/0bcfce8fdc8dee48fa7d85dcd3db0d0ecc6bed1a is fine for you as well. Thanks, Jan -- Siemens AG, Corporate Technology, CT RDA IOT SES-DE Corporate Competence Center Embedded Linux ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark 2020-08-13 10:27 ` Jan Kiszka @ 2020-08-13 10:41 ` chensong 0 siblings, 0 replies; 14+ messages in thread From: chensong @ 2020-08-13 10:41 UTC (permalink / raw) To: Jan Kiszka, Greg Gallagher; +Cc: Xenomai@xenomai.org On 2020年08月13日 18:27, Jan Kiszka wrote: > On 13.08.20 12:06, chensong wrote: >> >> >> On 2020年08月13日 16:54, Jan Kiszka wrote: >>> On 13.08.20 03:57, chensong wrote: >>>> I read the code in cyclictest, looks like >>>> >>>> printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index, >>>> (unsigned long long)inner_diff, >>>> (unsigned long long)outer_diff); >>> >>> I even wonder if this is strictly correct as it ignores negative diffs. >>> >>>> >>>> can fix the error reported in >>>> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. >>>> >>>> Do i need to apply a new version? >>> >>> No, I've converted your patch to long long types already. Please >>> cross-check what is in next branch. >> i'm a little confused here, cross-check what? negative diffs? in >> theory, it should be always positive, do you mean i keep checking if >> there is a negative diff? >> >> and what's next branch? what can i do about it? > > I've modified your patch in > https://gitlab.denx.de/Xenomai/xenomai/-/commits/next. Please have a > look if > https://gitlab.denx.de/Xenomai/xenomai/-/commit/0bcfce8fdc8dee48fa7d85dcd3db0d0ecc6bed1a > is fine for you as well. i'm fine with that, it's thoughtful, thanks a lot. chensong > > Thanks, > Jan > ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2020-08-13 10:41 UTC | newest] Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-08-03 6:04 [PATCH v3] testsuite: App of gpio loopback/react benchmark chensong 2020-08-11 7:15 ` Jan Kiszka 2020-08-11 15:36 ` Greg Gallagher 2020-08-12 3:36 ` Greg Gallagher 2020-08-12 14:55 ` Jan Kiszka 2020-08-12 15:28 ` Jan Kiszka 2020-08-12 15:43 ` Jan Kiszka 2020-08-12 16:10 ` Greg Gallagher 2020-08-12 16:14 ` Jan Kiszka 2020-08-13 1:57 ` chensong 2020-08-13 8:54 ` Jan Kiszka 2020-08-13 10:06 ` chensong 2020-08-13 10:27 ` Jan Kiszka 2020-08-13 10:41 ` chensong
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.