mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #8040 from haukepetersen/add_bench_periphgpiototest
tests/periph_gpio: added benchmark capabilities
This commit is contained in:
commit
f59e8247b0
@ -688,6 +688,10 @@ ifneq (,$(filter fatfs_vfs,$(USEMODULE)))
|
||||
USEMODULE += vfs
|
||||
endif
|
||||
|
||||
ifneq (,$(filter benchmark,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
# always select gpio (until explicit dependencies are sorted out)
|
||||
FEATURES_OPTIONAL += periph_gpio
|
||||
|
||||
|
1
sys/benchmark/Makefile
Normal file
1
sys/benchmark/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
32
sys/benchmark/benchmark.c
Normal file
32
sys/benchmark/benchmark.c
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup sys_benchmark
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Utility functions for the benchmark module
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "benchmark.h"
|
||||
|
||||
void benchmark_print_time(uint32_t time, unsigned long runs, const char *name)
|
||||
{
|
||||
uint32_t full = (time / runs);
|
||||
uint32_t div = (time - (full * runs)) / (runs / 1000);
|
||||
|
||||
printf("%11s: %9" PRIu32 "us --- %2" PRIu32 ".%03" PRIu32 "us per call\n",
|
||||
name, time, full, div);
|
||||
}
|
76
sys/include/benchmark.h
Normal file
76
sys/include/benchmark.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_benchmark Benchmark
|
||||
* @ingroup sys
|
||||
* @brief Framework for running simple runtime benchmarks
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Interface for running simple benchmarks
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef BENCHMARK_H
|
||||
#define BENCHMARK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "xtimer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Prepare the current scope for running BENCHMARK_x() macros
|
||||
*/
|
||||
#define BENCHMARK_SETUP() \
|
||||
unsigned state; \
|
||||
unsigned time
|
||||
|
||||
/**
|
||||
* @brief Measure the runtime of a given function call
|
||||
*
|
||||
* As we are doing a time sensitive measurement here, there is no way around
|
||||
* using a preprocessor function, as going with a function pointer or similar
|
||||
* would influence the measured runtime...
|
||||
*
|
||||
* @note BENCHMARK_SETUP() needs to be called in the same scope
|
||||
*
|
||||
* @param[in] name name for labeling the output
|
||||
* @param[in] runs number of times to run @p func
|
||||
* @param[in] func function call to benchmark
|
||||
*/
|
||||
#define BENCHMARK_FUNC(name, runs, func) \
|
||||
state = irq_disable(); \
|
||||
time = xtimer_now_usec(); \
|
||||
for (unsigned long i = 0; i < runs; i++) { \
|
||||
func; \
|
||||
} \
|
||||
time = (xtimer_now_usec() - time); \
|
||||
irq_restore(state); \
|
||||
benchmark_print_time(time, runs, name)
|
||||
|
||||
/**
|
||||
* @brief Output the given time as well as the time per run on STDIO
|
||||
*
|
||||
* @param[in] time overall runtime in us
|
||||
* @param[in] runs number of runs
|
||||
* @param[in] name name to label the output
|
||||
*/
|
||||
void benchmark_print_time(uint32_t time, unsigned long runs, const char *name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BENCHMARK_H */
|
||||
/** @} */
|
@ -3,5 +3,9 @@ include ../Makefile.tests_common
|
||||
FEATURES_REQUIRED = periph_gpio
|
||||
|
||||
USEMODULE += shell
|
||||
USEMODULE += benchmark
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
bench:
|
||||
tests/02-bench.py
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014,2017 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
@ -11,7 +11,7 @@
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Manual test application for GPIO peripheral drivers
|
||||
* @brief Test application for GPIO peripheral drivers
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
@ -21,9 +21,13 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "irq.h"
|
||||
#include "shell.h"
|
||||
#include "benchmark.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#define BENCH_RUNS_DEFAULT (1000UL * 1000)
|
||||
|
||||
static void cb(void *arg)
|
||||
{
|
||||
printf("INT: external interrupt from pin %i\n", (int)arg);
|
||||
@ -203,6 +207,31 @@ static int toggle(int argc, char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bench(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
printf("usage: %s <port> <pin> [# of runs]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
gpio_t pin = GPIO_PIN(atoi(argv[1]), atoi(argv[2]));
|
||||
unsigned long runs = BENCH_RUNS_DEFAULT;
|
||||
if (argc > 3) {
|
||||
runs = (unsigned long)atol(argv[3]);
|
||||
}
|
||||
|
||||
puts("\nGPIO driver run-time performance benchmark\n");
|
||||
BENCHMARK_SETUP();
|
||||
BENCHMARK_FUNC("nop loop", runs, __asm__ volatile("nop"));
|
||||
BENCHMARK_FUNC("gpio_set", runs, gpio_set(pin));
|
||||
BENCHMARK_FUNC("gpio_clear", runs, gpio_clear(pin));
|
||||
BENCHMARK_FUNC("gpio_toggle", runs, gpio_toggle(pin));
|
||||
BENCHMARK_FUNC("gpio_read", runs, (void)gpio_read(pin));
|
||||
BENCHMARK_FUNC("gpio_write", runs, gpio_write(pin, 1));
|
||||
puts("\n --- DONE ---");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const shell_command_t shell_commands[] = {
|
||||
{ "init_out", "init as output (push-pull mode)", init_out },
|
||||
{ "init_in", "init as input w/o pull resistor", init_in },
|
||||
@ -215,6 +244,7 @@ static const shell_command_t shell_commands[] = {
|
||||
{ "set", "set pin to HIGH", set },
|
||||
{ "clear", "set pin to LOW", clear },
|
||||
{ "toggle", "toggle pin", toggle },
|
||||
{ "bench", "run a set of predefined benchmarks", bench },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
38
tests/periph_gpio/tests/02-bench.py
Executable file
38
tests/periph_gpio/tests/02-bench.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2017 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
child.expect_exact("GPIO peripheral driver test")
|
||||
child.expect_exact(">")
|
||||
|
||||
for pin in range(0, 8):
|
||||
child.sendline("bench 0 {}".format(pin))
|
||||
child.expect(r" *nop loop: +(\d+)us --- +(\d+\.\d+)us per call")
|
||||
child.expect(r" *gpio_set: +(\d+)us --- +(\d+\.\d+)us per call")
|
||||
child.expect(r" *gpio_clear: +(\d+)us --- +(\d+\.\d+)us per call")
|
||||
child.expect(r" *gpio_toggle: +(\d+)us --- +(\d+\.\d+)us per call")
|
||||
child.expect(r" *gpio_read: +(\d+)us --- +(\d+\.\d+)us per call")
|
||||
child.expect(r" *gpio_write: +(\d+)us --- +(\d+\.\d+)us per call")
|
||||
child.expect_exact(" --- DONE ---")
|
||||
child.expect_exact(">")
|
||||
|
||||
# TODO do some automated verification here? E.g. all pins should have the
|
||||
# same timing?
|
||||
# TODO parse output data and put in some unified format?
|
||||
|
||||
print("Benchmark was successful")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
|
||||
from testrunner import run
|
||||
sys.exit(run(testfunc, timeout=10))
|
Loading…
Reference in New Issue
Block a user