diff --git a/Makefile b/Makefile index 224d698fedcdb1135d80f62c6830772c66bc7863..f3b6d5b11e9fa797ba5a02f99f2bce02aa9eb5ab 100644 --- a/Makefile +++ b/Makefile @@ -121,13 +121,15 @@ LDSCRIPT= $(STARTUPLD)/STM32F411xE.ld # C sources that can be compiled in ARM or THUMB mode depending on the global # setting. -CSRC = $(ALLCSRC) \ - $(SRCDIR)/crc32.c \ - $(SRCDIR)/main.c \ - $(SRCDIR)/random.c \ - $(SRCDIR)/serial.c \ - $(SRCDIR)/usb.c \ - $(SRCDIR)/tests.c +CSRC = $(ALLCSRC) \ + $(SRCDIR)/crc32.c \ + $(SRCDIR)/benchmarks.c \ + $(SRCDIR)/benchmarks/memory.c \ + $(SRCDIR)/main.c \ + $(SRCDIR)/random.c \ + $(SRCDIR)/serial.c \ + $(SRCDIR)/tests.c \ + $(SRCDIR)/usb.c # C++ sources that can be compiled in ARM or THUMB mode depending on the global # setting. diff --git a/src/benchmarks.c b/src/benchmarks.c new file mode 100644 index 0000000000000000000000000000000000000000..549024721c4db8d58368ad35cdbd578b2efd2351 --- /dev/null +++ b/src/benchmarks.c @@ -0,0 +1,40 @@ +// This file is part of the execution-time evaluation for the qronos observer abstractions. +// Copyright (C) 2022-2023 Tim Rheinfels <tim.rheinfels@fau.de> +// See https://gitlab.cs.fau.de/qronos-state-abstractions/execution-time +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +/// +/// @file benchmarks.c +/// +/// @brief Provides the implementation for running all execution time benchmarks +/// +/// @author Tim Rheinfels <tim.rheinfels@fau.de> +/// + +#include "benchmarks.h" + +#include <benchmarks/campaign.h> + +void benchmarks_run(void) +{ + + // Basic campaigns + BENCHMARKS_CAMPAIGN("overhead", {}, {}, {}, 10u, 10u); + BENCHMARKS_CAMPAIGN("chSysPolledDelayX_10", chSysPolledDelayX(10u), {}, {}, 10u, 10u); + BENCHMARKS_CAMPAIGN("chSysPolledDelayX_100", chSysPolledDelayX(100u), {}, {}, 10u, 10u); + BENCHMARKS_CAMPAIGN("chSysPolledDelayX_1000", chSysPolledDelayX(1000u), {}, {}, 10u, 10u); + BENCHMARKS_CAMPAIGN("chSysPolledDelayX_10000", chSysPolledDelayX(10000u), {}, {}, 10u, 10u); + +} diff --git a/src/benchmarks.h b/src/benchmarks.h new file mode 100644 index 0000000000000000000000000000000000000000..5da4fe9bfeda347dbb506ab6443525187286c43e --- /dev/null +++ b/src/benchmarks.h @@ -0,0 +1,34 @@ +// This file is part of the execution-time evaluation for the qronos observer abstractions. +// Copyright (C) 2022-2023 Tim Rheinfels <tim.rheinfels@fau.de> +// See https://gitlab.cs.fau.de/qronos-state-abstractions/execution-time +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +/// +/// @file benchmarks.h +/// +/// @brief Provides the interface for running all execution time benchmarks +/// +/// @author Tim Rheinfels <tim.rheinfels@fau.de> +/// + +#ifndef BENCHMARKS_H +#define BENCHMARKS_H + +/// +/// @brief Runs all execution time benchmarks outputting the results via serial ove r USB +/// +void benchmarks_run(void); + +#endif // BENCHMARKS_H diff --git a/src/benchmarks/campaign.h b/src/benchmarks/campaign.h new file mode 100644 index 0000000000000000000000000000000000000000..9c245adc57739bf680fdf48c20c1bb808489c9ab --- /dev/null +++ b/src/benchmarks/campaign.h @@ -0,0 +1,125 @@ +// This file is part of the execution-time evaluation for the qronos observer abstractions. +// Copyright (C) 2022-2023 Tim Rheinfels <tim.rheinfels@fau.de> +// See https://gitlab.cs.fau.de/qronos-state-abstractions/execution-time +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +/// +/// @file benchmarks/campaign.h +/// +/// @brief Provides the interface for execution time measurement campaigns +/// +/// @author Tim Rheinfels <tim.rheinfels@fau.de> +/// + +#ifndef BENCHMARKS_CAMPAIGN_H +#define BENCHMARKS_CAMPAIGN_H + +#include <ch.h> +#include <hal.h> + +#include <serial.h> + +/// +/// @brief Performs a single execution time measurement called @p name by counting the number of @p cycles the execution of @p func takes +/// +/// @param[in] name Name of the measurement +/// @param[out] cycles Variable of type rtcnt_t to store the measurement in +/// @param[in] func Code to execute +/// +#define _BENCHMARKS_MEASUREMENT(name, cycles, func) { \ + /* Disable interrupts */ \ + chSysDisable(); \ + /* Flush caches */ \ + FLASH->ACR &= ~FLASH_ACR_DCEN; \ + FLASH->ACR &= ~FLASH_ACR_ICEN; \ + FLASH->ACR &= ~FLASH_ACR_PRFTEN; \ + FLASH->ACR |= FLASH_ACR_DCRST; \ + FLASH->ACR |= FLASH_ACR_ICRST; \ + FLASH->ACR |= FLASH_ACR_DCEN; \ + FLASH->ACR |= FLASH_ACR_ICEN; \ + FLASH->ACR |= FLASH_ACR_PRFTEN; \ + /* NOP slide for consistent pipeline state */ \ + asm volatile("nop"); \ + asm volatile("nop"); \ + asm volatile("nop"); \ + asm volatile("nop"); \ + asm volatile("nop"); \ + asm volatile("nop"); \ + asm volatile("nop"); \ + asm volatile("nop"); \ + asm volatile("nop"); \ + asm volatile("nop"); \ + /* Get start time */ \ + rtcnt_t start = chSysGetRealtimeCounterX(); \ + /* Just a label for inspecting disassembly */ \ + asm volatile(".in_et_" name ":"); \ + /* Run actual code */ \ + (void) (func); \ + /* Another label marking the end */ \ + asm volatile(".out_et_" name ":"); \ + /* Get end time */ \ + rtcnt_t end = chSysGetRealtimeCounterX(); \ + /* Reenable interrupts */ \ + chSysEnable(); \ + /* Store measurement */ \ + (cycles) = end - start; \ + } + +/// +/// @brief Runs the execution time campaign called @p name calling @p func +/// +/// @param[in] name Campaign's name +/// @param[in] func Code to measure +/// @param[in] setup Code called before all @p N tests +/// @param[in] teardown Code called after all @p N tests +/// @param[in] N Number of tests to run +/// @param[in] M Number of repetitions per test +/// +#define BENCHMARKS_CAMPAIGN(name, func, setup, teardown, N, M) { \ + serial_printf("{\n"); \ + serial_printf(" \"name\": \"%s\",\n", (name)); \ + serial_printf(" \"test_count\": %u,\n", (N)); \ + serial_printf(" \"repetition_count\": %u,\n", (M)); \ + serial_printf(" \"tests\": [\n"); \ + for(size_t i = 0u; i < (N); ++i) { \ + serial_printf(" {\n"); \ + serial_printf(" \"setup\": {\n"); \ + (setup); \ + serial_printf(" },\n"); \ + serial_printf(" \"repetitions\": ["); \ + for(size_t j = 0u; j < (M); ++j) { \ + rtcnt_t cycles = (rtcnt_t) -1; \ + _BENCHMARKS_MEASUREMENT(name, cycles, (func)); \ + if(cycles == (rtcnt_t) -1) { \ + serial_printf("null"); \ + } else { \ + serial_printf("%u", cycles); \ + } \ + if(j < (M)-1u) { \ + serial_printf(", "); \ + } else { \ + serial_printf("],\n"); \ + } \ + } \ + serial_printf(" \"teardown\": {\n"); \ + (teardown); \ + serial_printf(" },\n"); \ + serial_printf(" },\n"); \ + } \ + serial_printf(" ],\n"); \ + serial_printf("},\n"); \ + } + +#endif // BENCHMARKS_CAMPAIGN_H diff --git a/src/benchmarks/memory.c b/src/benchmarks/memory.c new file mode 100644 index 0000000000000000000000000000000000000000..7df4b0246f9d1222571011e95128e7e8144043ae --- /dev/null +++ b/src/benchmarks/memory.c @@ -0,0 +1,69 @@ +// This file is part of the execution-time evaluation for the qronos observer abstractions. +// Copyright (C) 2022-2023 Tim Rheinfels <tim.rheinfels@fau.de> +// See https://gitlab.cs.fau.de/qronos-state-abstractions/execution-time +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +/// +/// @file benchmarks/memory.c +/// +/// @brief Provides the implementation for accessing the statically allocated memory for all execution time measurements +/// +/// @author Tim Rheinfels <tim.rheinfels@fau.de> +/// + +#include "memory.h" + +#include <stdint.h> +#include <string.h> + +/// +/// @brief Size of the padding in bytes used for checking memory violations +/// +#define PADDING 16u + +/// +/// @brief Computes the maximum of the operands @p a and @p b +/// +/// @param[in] a First operand +/// @param[in] b Second operand +/// +/// @returns Maximum of @p a and @p b +/// +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +/// +/// @brief Actual benchmark memory +/// +static float work[2u * PADDING]; + +float * const benchmarks_memory = work + PADDING; + +void benchmarks_memory_init(void) +{ + memset(work, 0xA5, sizeof(work)); +} + +void benchmarks_memory_check(void) +{ + const uint8_t *p1 = (const uint8_t *) work; + const uint8_t *p2 = ((const uint8_t *) work) + sizeof(work) - PADDING * sizeof(float); + for(size_t i = 0u; i < PADDING; ++i) + { + if(*(p1++) != 0xA5u || *(p2++) != 0xA5u) + { + while(1); + } + } +} diff --git a/src/benchmarks/memory.h b/src/benchmarks/memory.h new file mode 100644 index 0000000000000000000000000000000000000000..945427a8c1a573d03177a6623fe27a433d4e00ee --- /dev/null +++ b/src/benchmarks/memory.h @@ -0,0 +1,49 @@ +// This file is part of the execution-time evaluation for the qronos observer abstractions. +// Copyright (C) 2022-2023 Tim Rheinfels <tim.rheinfels@fau.de> +// See https://gitlab.cs.fau.de/qronos-state-abstractions/execution-time +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +/// +/// @file benchmarks/memory.h +/// +/// @brief Provides the interface for accessing the statically allocated memory for all execution time measurements +/// +/// @author Tim Rheinfels <tim.rheinfels@fau.de> +/// + +#ifndef BENCHMARKS_MEMORY_H +#define BENCHMARKS_MEMORY_H + +/// +/// @brief Maximum number of measurement outputs for all experiments +/// +#define N_Y_MAX 60u + +/// +/// @brief Pointer to the benchmark working area +/// +extern float * const benchmarks_memory; + +/// +/// @brief Sets up sufficient memory space for running all execution time benchmarks individually +/// +void benchmarks_memory_init(void); + +/// +/// @brief Checks that the benchmarks did not use more space than allocated in @ref memory.c +/// +void benchmarks_memory_check(void); + +#endif // BENCHMARKS_MEMORY_H diff --git a/src/main.c b/src/main.c index aa02838256b61279fbe44743d44e17ea2fc94071..0781937dd647657d7d9ea7097b277e4ae4a8cafa 100644 --- a/src/main.c +++ b/src/main.c @@ -29,6 +29,8 @@ #include <ch.h> #include <hal.h> +#include <benchmarks.h> +#include <benchmarks/memory.h> #include <config.h> #include <crc32.h> #include <linalg.h> @@ -55,6 +57,9 @@ static THD_FUNCTION(benchmark_entry, arg) usb_init(); serial_init(); + // Colour memory region for tests + benchmarks_memory_init(); + // Wait for active USB connection while(!usb_active()) { @@ -95,6 +100,14 @@ static THD_FUNCTION(benchmark_entry, arg) goto end; } + // Perform execution time measurements + serial_printf(" \"execution_time_measurements\": [\n"); + benchmarks_run(); + serial_printf(" ],\n"); + + // Check if memory region was exceeded + benchmarks_memory_check(); + // We're done -> Clean up and wait forever end: serial_printf("}\n");