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:
commit
714958ad3c
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
89
sys/include/busy_wait.h
Normal 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 */
|
||||
/** @} */
|
5
tests/sys/busy_wait/Makefile
Normal file
5
tests/sys/busy_wait/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
include ../Makefile.sys_common
|
||||
|
||||
USEMODULE += ztimer_usec
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
3
tests/sys/busy_wait/Makefile.ci
Normal file
3
tests/sys/busy_wait/Makefile.ci
Normal file
@ -0,0 +1,3 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
atmega8 \
|
||||
#
|
50
tests/sys/busy_wait/main.c
Normal file
50
tests/sys/busy_wait/main.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user