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

Merge pull request #20241 from benpicco/busy_wait

sys/busy_wait: add busy wait helper
This commit is contained in:
benpicco 2024-01-11 12:19:31 +00:00 committed by GitHub
commit 714958ad3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 189 additions and 19 deletions

View File

@ -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
* @{

View File

@ -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

View File

@ -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
* @{

View File

@ -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
* @{

View File

@ -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
* @{

View File

@ -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
* @{

View File

@ -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
* @{

View File

@ -25,6 +25,7 @@
#include <stdint.h>
#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

89
sys/include/busy_wait.h Normal file
View File

@ -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 <benjamin.valentin@ml-pa.com>
* @{
*/
#ifndef BUSY_WAIT_H
#define BUSY_WAIT_H
#include <stdint.h>
#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 */
/** @} */

View File

@ -0,0 +1,5 @@
include ../Makefile.sys_common
USEMODULE += ztimer_usec
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,3 @@
BOARD_INSUFFICIENT_MEMORY := \
atmega8 \
#

View File

@ -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 <benjamin.valentin@ml-pa.com>
* @}
*/
#include <stdio.h>
#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;
}