From bfd29f0fa760491d53292810fe0287c1b934d5d8 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 9 Jan 2024 18:19:12 +0100 Subject: [PATCH 1/6] sys/busy_wait: add busy wait helper --- cpu/msp430/clock.c | 17 +------- sys/include/busy_wait.h | 89 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 sys/include/busy_wait.h diff --git a/cpu/msp430/clock.c b/cpu/msp430/clock.c index e9d7f55070..9f65000ba4 100644 --- a/cpu/msp430/clock.c +++ b/cpu/msp430/clock.c @@ -25,6 +25,7 @@ #include #include "debug.h" +#include "busy_wait.h" #include "macros/math.h" #include "macros/units.h" #include "periph_conf.h" @@ -99,22 +100,6 @@ static void check_config(void) } } -static void busy_wait(uint16_t loops) -{ - while (loops) { - /* This empty inline assembly should be enough to convince the - * compiler that the loop cannot be optimized out. Tested with - * GCC 12.2 and clang 16.0.0 successfully. */ - __asm__ __volatile__ ( - "" - : /* no outputs */ - : /* no inputs */ - : /* no clobbers */ - ); - loops--; - } -} - static void wait_for_crystals_to_stabilize(void) { /* The MSP430x1xx MCU family have an oscillator fault detector that sets diff --git a/sys/include/busy_wait.h b/sys/include/busy_wait.h new file mode 100644 index 0000000000..243f5c4258 --- /dev/null +++ b/sys/include/busy_wait.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 ML!PA Consulting GmbH + * + * 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 busy_wait Busy Waiting low-level helpers + * @ingroup sys + * + * @brief This modules provides helper functions for busy waiting + * on short intervals before timers are initialized, e.g. + * in `board_init()`. + * + * @author Benjamin Valentin + * @{ + */ + +#ifndef BUSY_WAIT_H +#define BUSY_WAIT_H + +#include +#include "periph_conf.h" +#include "time_units.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CPU cycles per busy wait loop iteration + * + * This can be used to roughly estimate the number of cycles + * for a given wait time. + */ +#ifndef CPU_CYCLES_PER_LOOP +#define CPU_CYCLES_PER_LOOP 3 +#endif + +/** + * @brief Spin for a number of cycles. + * + * This will not take into account cycles spent on interrupts + * if they are enabled and occur. + * + * @param loops Number of loop iterations to take + */ +static inline void busy_wait(unsigned loops) +{ + while (loops) { + /* This empty inline assembly should be enough to convince the + * compiler that the loop cannot be optimized out. Tested with + * GCC 12.2 and clang 16.0.0 successfully. */ + __asm__ __volatile__ ( + "" + : /* no outputs */ + : /* no inputs */ + : /* no clobbers */ + ); + loops--; + } +} + +/** + * @brief Spin for a number of microseconds. + * + * This will roughly try to match the requested delay time, but don't + * expect high accuracy. + * + * This will not take into account cycles spent on interrupts + * if they are enabled and occur. + * + * @param usec Number of µs to spin for. + */ +static inline void busy_wait_us(unsigned usec) +{ + unsigned loops = ((uint64_t)usec * CLOCK_CORECLOCK) + / (US_PER_SEC * CPU_CYCLES_PER_LOOP); + busy_wait(loops); +} + +#ifdef __cplusplus +} +#endif + +#endif /* BUSY_WAIT_H */ +/** @} */ From 79b51efc15fd59b6087acb5132154c32d82ee95b Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 9 Jan 2024 18:19:36 +0100 Subject: [PATCH 2/6] cpu/cortexm_common: Cortex M0/M23 needs 4 cycles/busy loop --- cpu/cortexm_common/include/cpu_conf_common.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cpu/cortexm_common/include/cpu_conf_common.h b/cpu/cortexm_common/include/cpu_conf_common.h index 18661942db..4e862fb089 100644 --- a/cpu/cortexm_common/include/cpu_conf_common.h +++ b/cpu/cortexm_common/include/cpu_conf_common.h @@ -170,6 +170,14 @@ extern "C" { */ #define IRQ_API_INLINED (1) +#if defined(CPU_CORE_CORTEX_M0) || defined(CPU_CORE_CORTEX_M0PLUS) \ + || defined(CPU_CORE_CORTEX_M23) +/** + * @brief CPU cycles per busy wait loop + */ +#define CPU_CYCLES_PER_LOOP (4) +#endif + #ifdef __cplusplus } #endif From 5dc43bd62d92ff3bae829ac66fc9cc2b4763345c Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 9 Jan 2024 23:07:20 +0100 Subject: [PATCH 3/6] cpu/avr8_common: AVR-8 needs 7 cycles per busy loop --- cpu/avr8_common/include/cpu.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpu/avr8_common/include/cpu.h b/cpu/avr8_common/include/cpu.h index 6e183661e7..c9f5c63a3d 100644 --- a/cpu/avr8_common/include/cpu.h +++ b/cpu/avr8_common/include/cpu.h @@ -57,6 +57,11 @@ extern "C" #endif /** @} */ +/** + * @brief CPU cycles per busy wait loop + */ +#define CPU_CYCLES_PER_LOOP (7) + /** * @name Use shared I2C functions * @{ From 8bcfe7b7b65e88e10dd18852af29d1e1fe29a4b7 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 9 Jan 2024 23:20:35 +0100 Subject: [PATCH 4/6] cpu/esp8266: ESP8266 needs 5 cycles per busy loop --- cpu/esp8266/include/periph_cpu.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpu/esp8266/include/periph_cpu.h b/cpu/esp8266/include/periph_cpu.h index 63905996e1..cb84b376cf 100644 --- a/cpu/esp8266/include/periph_cpu.h +++ b/cpu/esp8266/include/periph_cpu.h @@ -34,6 +34,11 @@ extern "C" { */ #define CPUID_LEN (4U) +/** + * @brief CPU cycles per busy wait loop + */ +#define CPU_CYCLES_PER_LOOP (5) + /** * @name GPIO configuration * @{ From fff9ff14da57063430b53c384c89db5197b370ea Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 9 Jan 2024 23:33:55 +0100 Subject: [PATCH 5/6] cpu/esp32: add busy loop cycles for all sub-arches --- cpu/esp32/include/periph_cpu_esp32.h | 5 +++++ cpu/esp32/include/periph_cpu_esp32c3.h | 5 +++++ cpu/esp32/include/periph_cpu_esp32s2.h | 5 +++++ cpu/esp32/include/periph_cpu_esp32s3.h | 11 ++++++++--- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/cpu/esp32/include/periph_cpu_esp32.h b/cpu/esp32/include/periph_cpu_esp32.h index 98fe0a3a91..e31b629836 100644 --- a/cpu/esp32/include/periph_cpu_esp32.h +++ b/cpu/esp32/include/periph_cpu_esp32.h @@ -28,6 +28,11 @@ extern "C" { /** Mapping configured ESP32 default clock to CLOCK_CORECLOCK define */ #define CLOCK_CORECLOCK (1000000UL * CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) +/** + * @brief CPU cycles per busy wait loop + */ +#define CPU_CYCLES_PER_LOOP (6) + /** * @name Predefined GPIO names * @{ diff --git a/cpu/esp32/include/periph_cpu_esp32c3.h b/cpu/esp32/include/periph_cpu_esp32c3.h index e2b243280a..874ca9ce6d 100644 --- a/cpu/esp32/include/periph_cpu_esp32c3.h +++ b/cpu/esp32/include/periph_cpu_esp32c3.h @@ -28,6 +28,11 @@ extern "C" { /** Mapping configured ESP32-C3 default clock to CLOCK_CORECLOCK define */ #define CLOCK_CORECLOCK (1000000UL * CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ) +/** + * @brief CPU cycles per busy wait loop + */ +#define CPU_CYCLES_PER_LOOP (4) + /** * @name Predefined GPIO names * @{ diff --git a/cpu/esp32/include/periph_cpu_esp32s2.h b/cpu/esp32/include/periph_cpu_esp32s2.h index 941940bfcf..1e1cdddda2 100644 --- a/cpu/esp32/include/periph_cpu_esp32s2.h +++ b/cpu/esp32/include/periph_cpu_esp32s2.h @@ -28,6 +28,11 @@ extern "C" { /** Mapping configured ESP32-S2 default clock to CLOCK_CORECLOCK define */ #define CLOCK_CORECLOCK (1000000UL * CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ) +/** + * @brief CPU cycles per busy wait loop + */ +#define CPU_CYCLES_PER_LOOP (6) + /** * @name Predefined GPIO names * @{ diff --git a/cpu/esp32/include/periph_cpu_esp32s3.h b/cpu/esp32/include/periph_cpu_esp32s3.h index 85ab49965c..c9aaccb8a9 100644 --- a/cpu/esp32/include/periph_cpu_esp32s3.h +++ b/cpu/esp32/include/periph_cpu_esp32s3.h @@ -19,13 +19,18 @@ #ifndef PERIPH_CPU_ESP32S3_H #define PERIPH_CPU_ESP32S3_H -/** Mapping configured ESP32-S3 default clock to CLOCK_CORECLOCK define */ -#define CLOCK_CORECLOCK (1000000UL * CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ) - #ifdef __cplusplus extern "C" { #endif +/** Mapping configured ESP32-S3 default clock to CLOCK_CORECLOCK define */ +#define CLOCK_CORECLOCK (1000000UL * CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ) + +/** + * @brief CPU cycles per busy wait loop + */ +#define CPU_CYCLES_PER_LOOP (5) + /** * @name Predefined GPIO names * @{ From 9cf1c47e86d1361de854078983fcd67effd2a2bf Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 9 Jan 2024 18:48:22 +0100 Subject: [PATCH 6/6] tests/sys/busy_wait: add test for busy wait --- tests/sys/busy_wait/Makefile | 5 ++++ tests/sys/busy_wait/Makefile.ci | 3 ++ tests/sys/busy_wait/main.c | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 tests/sys/busy_wait/Makefile create mode 100644 tests/sys/busy_wait/Makefile.ci create mode 100644 tests/sys/busy_wait/main.c diff --git a/tests/sys/busy_wait/Makefile b/tests/sys/busy_wait/Makefile new file mode 100644 index 0000000000..72da3ee294 --- /dev/null +++ b/tests/sys/busy_wait/Makefile @@ -0,0 +1,5 @@ +include ../Makefile.sys_common + +USEMODULE += ztimer_usec + +include $(RIOTBASE)/Makefile.include diff --git a/tests/sys/busy_wait/Makefile.ci b/tests/sys/busy_wait/Makefile.ci new file mode 100644 index 0000000000..72db76ccb5 --- /dev/null +++ b/tests/sys/busy_wait/Makefile.ci @@ -0,0 +1,3 @@ +BOARD_INSUFFICIENT_MEMORY := \ + atmega8 \ + # diff --git a/tests/sys/busy_wait/main.c b/tests/sys/busy_wait/main.c new file mode 100644 index 0000000000..6c1c3df4ef --- /dev/null +++ b/tests/sys/busy_wait/main.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 ML!PA Consulting GmbH + * + * 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 Busy Wait loop Test Application + * + * This can be used to determine `CPU_CYCLES_PER_LOOP` by + * comparing the time the busy wait loop took with the + * actual µsec timer. + * + * @author Benjamin Valentin + * @} + */ + +#include +#include "busy_wait.h" +#include "ztimer/stopwatch.h" + +static inline void _measure_interval(ztimer_stopwatch_t *clock, unsigned usec) +{ + unsigned usec_real; + + printf("waiting for %u µs…\n", usec); + ztimer_stopwatch_start(clock); + busy_wait_us(usec); + usec_real = ztimer_stopwatch_measure(clock); + printf("took %u µs (diff: %d µs)\n", usec_real, (int)usec_real - usec); +} + +int main(void) +{ + ztimer_stopwatch_t clock; + ztimer_stopwatch_init(ZTIMER_USEC, &clock); + + _measure_interval(&clock, 10); + _measure_interval(&clock, 100); + _measure_interval(&clock, 1000); + _measure_interval(&clock, 10000); + + return 0; +}