1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/tests/bench/periph_gpio_ll/main.c

295 lines
9.0 KiB
C

/*
* Copyright (C) 2021 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Test application for the Peripheral GPIO Low-Level API
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
*
* @}
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mutex.h"
#include "periph/gpio.h"
#include "periph/gpio_ll.h"
#include "test_utils/expect.h"
#include "ztimer.h"
#include "timex.h"
#ifndef COMPENSATE_OVERHEAD
#define COMPENSATE_OVERHEAD 1
#endif
static gpio_port_t port_out = GPIO_PORT(PORT_OUT);
static void print_summary_compensated(uint_fast16_t loops, uint32_t duration,
uint32_t duration_uncompensated)
{
printf("%" PRIuFAST16 " iterations took %" PRIu32 " us "
"(%" PRIu32 " us uncompensated)\n",
loops, duration, duration_uncompensated);
printf("Two square waves pins at %12" PRIu32 " Hz "
"(%12" PRIu32 " Hz uncompensated)\n",
(uint32_t)((uint64_t)US_PER_SEC * loops / duration),
(uint32_t)((uint64_t)US_PER_SEC * loops / duration_uncompensated));
#ifdef CLOCK_CORECLOCK
uint64_t divisor = (uint64_t)US_PER_SEC * loops / CLOCK_CORECLOCK;
uint32_t cycles = (duration + divisor / 2) / divisor;
uint32_t cycles_uncompensated = (duration_uncompensated + divisor / 2)
/ divisor;
printf("~%" PRIu32 " CPU cycles per square wave period "
"(~%" PRIu32 " cycles uncompensated)\n",
cycles, cycles_uncompensated);
if (cycles <= 2) {
puts(":-D");
}
else if (cycles <= 4) {
puts(":-)");
}
else if (cycles <= 8) {
puts(":-|");
}
else if (cycles <= 16) {
puts(":-(");
}
else {
puts(":'-(");
}
#endif
}
static void print_summary_uncompensated(uint_fast16_t loops, uint32_t duration)
{
printf("%" PRIuFAST16 " iterations took %" PRIu32 " us\n",
loops, duration);
printf("Two square waves pins at %12" PRIu32 " Hz\n",
(uint32_t)((uint64_t)US_PER_SEC * loops / duration));
#ifdef CLOCK_CORECLOCK
uint64_t divisor = (uint64_t)US_PER_SEC * loops / CLOCK_CORECLOCK;
uint32_t cycles = (duration + divisor / 2) / divisor;
printf("~%" PRIu32 " CPU cycles per square wave period\n", cycles);
if (cycles <= 2) {
puts(":-D");
}
else if (cycles <= 4) {
puts(":-)");
}
else if (cycles <= 8) {
puts(":-|");
}
else if (cycles <= 16) {
puts(":-(");
}
else {
puts(":'-(");
}
#endif
}
int main(void)
{
static const uint_fast16_t loops = 50000;
uint32_t loop_overhead = 0;
uword_t mask_both = (1U << PIN_OUT_0) | (1U << PIN_OUT_1);
puts("\n"
"Benchmarking GPIO APIs\n"
"======================");
if (COMPENSATE_OVERHEAD) {
puts("\n"
"estimating loop overhead for compensation\n"
"-----------------------------------------");
uint32_t start = ztimer_now(ZTIMER_USEC);
for (uint_fast16_t i = loops; i > 0; i--) {
__asm__ volatile ("" : : : );
}
loop_overhead = ztimer_now(ZTIMER_USEC) - start;
printf("%" PRIu32 " us for %" PRIuFAST16 " iterations\n",
loop_overhead, loops);
}
{
puts("\n"
"periph/gpio: Using 2x gpio_set() and 2x gpio_clear()\n"
"---------------------------------------------------");
gpio_t p0 = GPIO_PIN(PORT_OUT, PIN_OUT_0);
gpio_t p1 = GPIO_PIN(PORT_OUT, PIN_OUT_1);
gpio_init(p0, GPIO_OUT);
gpio_init(p1, GPIO_OUT);
uint32_t start = ztimer_now(ZTIMER_USEC);
for (uint_fast16_t i = loops; i > 0; i--) {
gpio_set(p0);
gpio_set(p1);
gpio_clear(p0);
gpio_clear(p1);
}
uint32_t duration = ztimer_now(ZTIMER_USEC) - start;
if (COMPENSATE_OVERHEAD) {
print_summary_compensated(loops, duration - loop_overhead,
duration);
}
else {
print_summary_uncompensated(loops, duration);
}
}
{
puts("\n"
"periph/gpio_ll: Using gpio_ll_set() and gpio_ll_clear()\n"
"-------------------------------------------------------");
gpio_conf_t conf = {
.state = GPIO_OUTPUT_PUSH_PULL,
.slew_rate = GPIO_SLEW_FASTEST
};
expect(0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
expect(0 == gpio_ll_init(port_out, PIN_OUT_1, &conf));
uint32_t start = ztimer_now(ZTIMER_USEC);
for (uint_fast16_t i = loops; i > 0; i--) {
gpio_ll_set(port_out, (1UL << PIN_OUT_0) | (1UL << PIN_OUT_1));
gpio_ll_clear(port_out, (1UL << PIN_OUT_0) | (1UL << PIN_OUT_1));
}
uint32_t duration = ztimer_now(ZTIMER_USEC) - start;
if (COMPENSATE_OVERHEAD) {
print_summary_compensated(loops, duration - loop_overhead,
duration);
}
else {
print_summary_uncompensated(loops, duration);
}
}
{
puts("\n"
"periph/gpio: Using 4x gpio_toggle()\n"
"-----------------------------------");
gpio_t p0 = GPIO_PIN(PORT_OUT, PIN_OUT_0);
gpio_t p1 = GPIO_PIN(PORT_OUT, PIN_OUT_1);
gpio_init(p0, GPIO_OUT);
gpio_init(p1, GPIO_OUT);
uint32_t start = ztimer_now(ZTIMER_USEC);
for (uint_fast16_t i = loops; i > 0; i--) {
gpio_toggle(p0);
gpio_toggle(p1);
gpio_toggle(p0);
gpio_toggle(p1);
}
uint32_t duration = ztimer_now(ZTIMER_USEC) - start;
if (COMPENSATE_OVERHEAD) {
print_summary_compensated(loops, duration - loop_overhead,
duration);
}
else {
print_summary_uncompensated(loops, duration);
}
}
{
puts("\n"
"periph/gpio_ll: Using 2x gpio_ll_toggle()\n"
"-----------------------------------------");
gpio_conf_t conf = {
.state = GPIO_OUTPUT_PUSH_PULL,
.slew_rate = GPIO_SLEW_FASTEST
};
expect(0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
expect(0 == gpio_ll_init(port_out, PIN_OUT_1, &conf));
uint32_t start = ztimer_now(ZTIMER_USEC);
for (uint_fast16_t i = loops; i > 0; i--) {
gpio_ll_toggle(port_out, mask_both);
gpio_ll_toggle(port_out, mask_both);
}
uint32_t duration = ztimer_now(ZTIMER_USEC) - start;
if (COMPENSATE_OVERHEAD) {
print_summary_compensated(loops, duration - loop_overhead,
duration);
}
else {
print_summary_uncompensated(loops, duration);
}
}
{
puts("\n"
"periph/gpio: Using 4x gpio_write()\n"
"----------------------------------");
gpio_t p0 = GPIO_PIN(PORT_OUT, PIN_OUT_0);
gpio_t p1 = GPIO_PIN(PORT_OUT, PIN_OUT_1);
gpio_init(p0, GPIO_OUT);
gpio_init(p1, GPIO_OUT);
uint32_t start = ztimer_now(ZTIMER_USEC);
for (uint_fast16_t i = loops; i > 0; i--) {
gpio_write(p0, 1);
gpio_write(p1, 1);
gpio_write(p0, 0);
gpio_write(p1, 0);
}
uint32_t duration = ztimer_now(ZTIMER_USEC) - start;
if (COMPENSATE_OVERHEAD) {
print_summary_compensated(loops, duration - loop_overhead,
duration);
}
else {
print_summary_uncompensated(loops, duration);
}
}
{
puts("\n"
"periph/gpio_ll: Using 2x gpio_ll_write()\n"
"----------------------------------------");
gpio_conf_t conf = {
.state = GPIO_OUTPUT_PUSH_PULL,
.slew_rate = GPIO_SLEW_FASTEST
};
expect(0 == gpio_ll_init(port_out, PIN_OUT_0, &conf));
expect(0 == gpio_ll_init(port_out, PIN_OUT_1, &conf));
uword_t both_high = gpio_ll_prepare_write(port_out, mask_both,
mask_both);
uword_t both_low = gpio_ll_prepare_write(port_out, mask_both, 0);
uint32_t start = ztimer_now(ZTIMER_USEC);
for (uint_fast16_t i = loops; i > 0; i--) {
gpio_ll_write(port_out, both_high);
gpio_ll_write(port_out, both_low);
}
uint32_t duration = ztimer_now(ZTIMER_USEC) - start;
if (COMPENSATE_OVERHEAD) {
print_summary_compensated(loops, duration - loop_overhead,
duration);
}
else {
print_summary_uncompensated(loops, duration);
}
}
puts("\n\nTEST SUCCEEDED");
return 0;
}