From 6dd79366bb2aec4125c37aab3ada494a3a7125c9 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 1 Feb 2018 09:53:04 +0100 Subject: [PATCH] sys: add ztimer subsystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Joakim Nohlgård --- Makefile.dep | 37 ++ makefiles/pseudomodules.inc.mk | 4 + sys/auto_init/auto_init.c | 5 + sys/include/ztimer.h | 506 ++++++++++++++++++++++++++ sys/include/ztimer/convert.h | 79 ++++ sys/include/ztimer/convert_frac.h | 91 +++++ sys/include/ztimer/convert_muldiv64.h | 85 +++++ sys/include/ztimer/convert_shift.h | 71 ++++ sys/include/ztimer/mock.h | 97 +++++ sys/include/ztimer/overhead.h | 47 +++ sys/include/ztimer/periph_rtc.h | 52 +++ sys/include/ztimer/periph_rtt.h | 53 +++ sys/include/ztimer/periph_timer.h | 64 ++++ sys/ztimer/Makefile | 19 + sys/ztimer/auto_init.c | 175 +++++++++ sys/ztimer/convert.c | 53 +++ sys/ztimer/convert_frac.c | 101 +++++ sys/ztimer/convert_muldiv64.c | 123 +++++++ sys/ztimer/convert_shift.c | 94 +++++ sys/ztimer/core.c | 342 +++++++++++++++++ sys/ztimer/mock.c | 124 +++++++ sys/ztimer/overhead.c | 50 +++ sys/ztimer/periph_rtc.c | 145 ++++++++ sys/ztimer/periph_rtt.c | 81 +++++ sys/ztimer/periph_timer.c | 82 +++++ sys/ztimer/util.c | 149 ++++++++ 26 files changed, 2729 insertions(+) create mode 100644 sys/include/ztimer.h create mode 100644 sys/include/ztimer/convert.h create mode 100644 sys/include/ztimer/convert_frac.h create mode 100644 sys/include/ztimer/convert_muldiv64.h create mode 100644 sys/include/ztimer/convert_shift.h create mode 100644 sys/include/ztimer/mock.h create mode 100644 sys/include/ztimer/overhead.h create mode 100644 sys/include/ztimer/periph_rtc.h create mode 100644 sys/include/ztimer/periph_rtt.h create mode 100644 sys/include/ztimer/periph_timer.h create mode 100644 sys/ztimer/Makefile create mode 100644 sys/ztimer/auto_init.c create mode 100644 sys/ztimer/convert.c create mode 100644 sys/ztimer/convert_frac.c create mode 100644 sys/ztimer/convert_muldiv64.c create mode 100644 sys/ztimer/convert_shift.c create mode 100644 sys/ztimer/core.c create mode 100644 sys/ztimer/mock.c create mode 100644 sys/ztimer/overhead.c create mode 100644 sys/ztimer/periph_rtc.c create mode 100644 sys/ztimer/periph_rtt.c create mode 100644 sys/ztimer/periph_timer.c create mode 100644 sys/ztimer/util.c diff --git a/Makefile.dep b/Makefile.dep index 5c468b343c..cea2fe6934 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -1013,6 +1013,43 @@ ifneq (,$(filter periph_uart_nonblocking,$(USEMODULE))) FEATURES_REQUIRED += periph_uart endif +ifneq (,$(filter xtimer_on_ztimer%,$(USEMODULE))) + USEMODULE += ztimer_usec +endif + +ifneq (,$(filter ztimer_%,$(USEMODULE))) + USEMODULE += ztimer + USEMODULE += ztimer_extend +endif + +ifneq (,$(filter ztimer_periph,$(USEMODULE))) + FEATURES_REQUIRED += periph_timer +endif + +ifneq (,$(filter ztimer_rtt,$(USEMODULE))) + FEATURES_REQUIRED += periph_rtt +endif + +ifneq (,$(filter ztimer_convert_frac,$(USEMODULE))) + USEMODULE += frac +endif + +ifneq (,$(filter ztimer,$(USEMODULE))) + USEMODULE += ztimer_auto_init + USEMODULE += ztimer_core + USEMODULE += ztimer_convert_frac + USEMODULE += ztimer_convert_shift +endif + +ifneq (,$(filter ztimer_usec,$(USEMODULE))) + USEMODULE += ztimer + USEMODULE += ztimer_periph +endif + +ifneq (,$(filter ztimer_msec,$(USEMODULE))) + USEMODULE += ztimer +endif + # Enable periph_gpio when periph_gpio_irq is enabled ifneq (,$(filter periph_gpio_irq,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 2dd7267fd0..e87cb7aba3 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -94,6 +94,10 @@ PSEUDOMODULES += stdio_uart_rx PSEUDOMODULES += suit_% PSEUDOMODULES += wakaama_objects_% PSEUDOMODULES += zptr +PSEUDOMODULES += ztimer% + +# ztimer's main module is called "ztimer_core" +NO_PSEUDOMODULES += ztimer_core # handle suit_v4 being a distinct module NO_PSEUDOMODULES += suit_v4 diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index f5f338296f..2e9e943ecc 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -392,6 +392,11 @@ void auto_init(void) extern void auto_init_random(void); auto_init_random(); } + if (IS_USED(MODULE_ZTIMER)) { + LOG_DEBUG("Auto init ztimer.\n"); + void ztimer_init(void); + ztimer_init(); + } if (IS_USED(MODULE_AUTO_INIT_XTIMER)) { LOG_DEBUG("Auto init xtimer.\n"); extern void xtimer_init(void); diff --git a/sys/include/ztimer.h b/sys/include/ztimer.h new file mode 100644 index 0000000000..da4609248a --- /dev/null +++ b/sys/include/ztimer.h @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * + * 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 sys_ztimer ztimer high level timer abstraction layer + * @ingroup sys + * @brief High level timer abstraction layer + * + * # Introduction + * + * ztimer provides a high level abstraction of hardware timers for application + * timing needs. + * + * The basic functions of the ztimer module are ztimer_now(), ztimer_sleep(), + * ztimer_set() and ztimer_remove(). + * + * They all take a pointer to a clock device (or virtual timer device) as first + * parameter. + * + * RIOT provides ZTIMER_USEC, ZTIMER_MSEC, ZTIMER_SEC by default, which can be + * used in an application by depending on the modules ztimer_usec, ztimer_msec + * or ztimer_sec. They will then automatically get configured. + * + * Every ztimer clock allows multiple timeouts to be scheduled. They all + * provide unsigned 32bit range. In this documentation, a timeout or its + * corresponding struct will be called `timer`, and when the time out has + * passed, it has `triggered`. + * + * As ztimer can use arbitrarily configurable backends, a ztimer clock instance + * can run at configurable frequencies. Throughout this documentation, one + * clock step is called `tick`. For the pre-defined clocks ZTIMER_USEC, + * ZTIMER_MSEC and ZTIMER_SEC, one clock tick corresponds to one microsecond, + * one millisecond or one second, respectively. + * + * ztimer_now() returns the current clock tick count as uint32_t. + * + * ztimer_sleep() pauses the current thread for the passed amount of clock + * ticks. E.g., ```ztimer_sleep(ZTIMER_SEC, 5);``` will suspend the currently + * running thread for five seconds. + * + * ztimer_set() takes a ztimer_t object (containing a function pointer and + * void * argument) and an interval as arguments. After at least the interval + * (in number of ticks for the corresponding clock) has passed, the callback + * will be called in interrupt context. + * A timer can be cancelled using ztimer_remove(). + * + * Example: + * + * ``` + * #include "ztimer.h" + * + * static void callback(void *arg) + * { + * puts(arg); + * } + * + * int main() + * { + * ztimer_t timeout = { .callback=callback, .arg="Hello ztimer!" }; + * ztimer_set(ZTIMER_SEC, &timeout, 2); + * + * ztimer_sleep(ZTIMER_SEC, 5); + * } + * ``` + * + * + * # Design + * + * ## clocks, virtual timers, chaining + * + * The system is composed of clocks (virtual ztimer devices) which can be + * chained to create an abstract view of a hardware timer/counter device. Each + * ztimer clock acts as a operation on the next clock in the chain. At the end of + * each ztimer chain there is always some kind of counter device object. + * + * Each clock device handles multiplexing (allowing multiple timers to be set) + * and extension to full 32bit. + * + * Hardware interface submodules: + * + * - @ref ztimer_periph_rtt_init "ztimer_periph_rtt" interface for periph_rtt + * - @ref ztimer_periph_rtc_init "ztimer_periph_rtc" interface for periph_rtc + * - @ref ztimer_periph_timer_init "ztimer_periphtimer" interface for periph_timer + * + * Filter submodules: + * + * - @ref ztimer_convert_frac_init "ztimer_convert_frac" for fast frequency + * conversion using the frac library + * - @ref ztimer_convert_muldiv64_init "ztimer_convert_muldiv64" for accurate + * but slow frequency conversion using 64bit division + * + * + * A common chain could be: + * + * 1. ztimer_periph_timer (e.g., on top of an 1024Hz 16bit hardware timer) + * 2. ztimer_convert_frac (to convert 1024 to 1000Hz) + * + * This is how e.g., the clock ZTIMER_MSEC might be configured on a specific + * system. + * + * Every clock in the chain can always be used on its own. E.g. in the example + * above, the ztimer_periph object can be used as ztimer clock with 1024Hz + * ticks in addition to the ztimer_convert_frac with 1000Hz. + * + * + * ## Timer handling + * + * Timers in ztimer are stored in a clock using a linked list for which each + * entry stores the difference to the previous entry in the timer (T[n]). The + * clock also stores the absolute time on which the relative offsets are based + * (B), effectively storing the absolute target time for each entry (as B + + * sum(T[0-n])). Storing the entries in this way allows all entries to use the + * full width of the used uint32_t, compared to storing the absolute time. + * + * In order to prevent timer processing offset to add up, whenever a timer + * triggers, the list's absolute base time is set to the *expected* trigger + * time (B + T[0]). The underlying clock is then set to alarm at (now() + + * (now() - B) + T[1]). Thus even though the list is keeping relative offsets, + * the time keeping is done by keeping track of the absolute times. + * + * + * ## Clock extension + * + * The API always allows setting full 32bit relative offsets for every clock. + * + * In some cases (e.g., a hardware timer only allowing getting/setting smaller + * values or a conversion which would overflow uint32_t for large intervals), + * ztimer takes care of extending timers. + * This is enabled automatically for every ztimer clock that has a "max_value" + * setting smaller than 2**32-1. If a ztimer_set() would overflow that value, + * intermediate intervals of length (max_value / 2) are set until the remaining + * interval fits into max_value. + * If extension is enabled for a clock, ztimer_now() uses interval + * checkpointing, storing the current time and corresponding clock tick value on + * each call and using that information to calculate the current time. + * This ensures correct ztimer_now() values if ztimer_now() is called at least + * once every "max_value" ticks. This is ensured by scheduling intermediate + * callbacks every (max_value / 2) ticks (even if no timeout is configured). + * + * + * ## Reliability + * + * Care has been taken to avoid any unexpected behaviour of ztimer. In + * particular, ztimer tries hard to avoid underflows (setting a backend timer + * to a value at or behind the current time, causing the timer interrupt to + * trigger one whole timer period too late). + * This is done by always setting relative timeouts to backend timers, with + * interrupts disabled and ensuring that very small values don't cause + * underflows. + * + * + * ## Configuration and convention + * + * As timer hardware and capabilities is diverse and ztimer allows configuring + * and using arbitrary clock backends and conversions, it is envisioned to + * provide default configurations that application developers can assume to be + * available. + * + * These are implemented by using pointers to ztimer clocks using default names. + * + * For now, there are: + * + * ZTIMER_USEC: clock providing microsecond ticks + * + * ZTIMER_MSEC: clock providing millisecond ticks, using a low power timer if + * available on the platform + * + * ZTIMER_SEC: clock providing second time, possibly using epoch semantics + * + * These pointers are defined in `ztimer.h` and can be used like this: + * + * ztimer_now(ZTIMER_USEC); + * + * They also need to be added to USEMODULE using the names `ztimer_usec`, + * `ztimer_msec` and `ztimer_sec`. + * + * + * ## Some notes on ztimer's accuracy + * + * 1. ztimer *should* wait "at least" the specified timeout + * + * 2. due to its implementation details, expect +-1 clock tick systemic + * inaccuracy for all clocks. + * + * 3. for the predefined clocks (ZTIMER_USEC, ZTIMER_MSEC, ZTIMER_SEC), tick + * conversion might be applied using ztimer_convert_*, causing errors due to + * integer conversion and rounding. In particular, most RTT's closest match + * for milliseconds are 1024Hz, which will be converted using convert_frac to + * provide the 1ms clock. + * + * 4. Some platforms don't have any timer that can be configured to 1us. E.g., + * the fe310 (hifive1/b) only supports a 32kHz timer, and most atmegas only + * support 250kHz. In order to not completely break all applications using + * ZTIMER_USEC, that clock will only provide ~30.5ms respectively 4us maximum + * accuracy on those boards. With DEVELHELP=1, a warning will be printed at + * boot time. + * + * 5. Due to +-1 systemic inaccuracies, it is advisable to use ZTIMER_MSEC for + * second timers up to 49 days (instead of ZTIMER_SEC). + * + * @{ + * + * @file + * @brief ztimer API + * + * @author Kaspar Schleiser + * @author Joakim Nohlgård + */ + +#ifndef ZTIMER_H +#define ZTIMER_H + +#include + +#include "kernel_types.h" +#include "msg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ztimer_base_t forward declaration + */ +typedef struct ztimer_base ztimer_base_t; + +/** + * @brief ztimer_clock_t forward declaration + */ +typedef struct ztimer_clock ztimer_clock_t; + +/** + * @brief Minimum information for each timer + */ +struct ztimer_base { + ztimer_base_t *next; /**< next timer in list */ + uint32_t offset; /**< offset from last timer in list */ +}; + +#if MODULE_ZTIMER_NOW64 +typedef uint64_t ztimer_now_t; /**< type for ztimer_now() result */ +#else +typedef uint32_t ztimer_now_t; /**< type for ztimer_now() result */ +#endif + +/** + * @brief ztimer structure + * + * This type represents an instance of a timer, which is set on an + * underlying clock object + */ +typedef struct { + ztimer_base_t base; /**< clock list entry */ + void (*callback)(void *arg); /**< timer callback function pointer */ + void *arg; /**< timer callback argument */ +} ztimer_t; + +/** + * @brief ztimer backend method structure + * + * This table contains pointers to the virtual methods for a ztimer clock. + * + * These functions used by ztimer core to interact with the underlying clock. + */ +typedef struct { + /** + * @brief Set a new timer target + */ + void (*set)(ztimer_clock_t *clock, uint32_t val); + + /** + * @brief Get the current count of the timer + */ + uint32_t (*now)(ztimer_clock_t *clock); + + /** + * @brief Cancel any set target + */ + void (*cancel)(ztimer_clock_t *clock); +} ztimer_ops_t; + +/** + * @brief ztimer device structure + */ +struct ztimer_clock { + ztimer_base_t list; /**< list of active timers */ + const ztimer_ops_t *ops; /**< pointer to methods structure */ + ztimer_base_t *last; /**< last timer in queue, for _is_set() */ + uint32_t adjust; /**< will be subtracted on every set() */ +#if MODULE_ZTIMER_EXTEND || MODULE_ZTIMER_NOW64 || DOXYGEN + /* values used for checkpointed intervals and 32bit extension */ + uint32_t max_value; /**< maximum relative timer value */ + uint32_t lower_last; /**< timer value at last now() call */ + ztimer_now_t checkpoint; /**< cumulated time at last now() call */ +#endif +}; + +/** + * @brief main ztimer callback handler + */ +void ztimer_handler(ztimer_clock_t *clock); + +/* User API */ +/** + * @brief Set a timer on a clock + * + * This will place @p timer in the timer targets queue of @p clock. + * + * @note The memory pointed to by @p timer is not copied and must + * remain in scope until the callback is fired or the timer + * is removed via @ref ztimer_remove + * + * @param[in] clock ztimer clock to operate on + * @param[in] timer timer entry to set + * @param[in] val timer target (relative ticks from now) + */ +void ztimer_set(ztimer_clock_t *clock, ztimer_t *timer, uint32_t val); + +/** + * @brief Remove a timer from a clock + * + * This will place @p timer in the timer targets queue for @p clock. + * + * This function does nothing if @p timer is not found in the timer queue of + * @p clock. + * + * @param[in] clock ztimer clock to operate on + * @param[in] timer timer entry to remove + */ +void ztimer_remove(ztimer_clock_t *clock, ztimer_t *timer); + +/** + * @brief Post a message after a delay + * + * This function sets a timer that will send a message @p offset ticks + * from now. + * + * @note The memory pointed to by @p timer and @p msg will not be copied, i.e. + * `*timer` and `*msg` needs to remain valid until the timer has triggered. + * + * @param[in] clock ztimer clock to operate on + * @param[in] timer ztimer timer struct to use + * @param[in] offset ticks from now + * @param[in] msg pointer to msg that will be sent + * @param[in] target_pid pid the message will be sent to + */ +void ztimer_set_msg(ztimer_clock_t *clock, ztimer_t *timer, uint32_t offset, + msg_t *msg, kernel_pid_t target_pid); + +/** + * @brief receive a message (blocking, with timeout) + * + * Similar to msg_receive(), but with a timeout parameter. + * The function will return after waiting at most @p timeout ticks. + * + * @note: This might function might leave a message with type MSG_ZTIMER in the + * thread's message queue, which must be handled (ignored). + * + * @param[in] clock ztimer clock to operate on + * @param[out] msg pointer to buffer which will be filled if a + * message is received + * @param[in] timeout relative timeout, in @p clock time units + * + * @return >=0 if a message was received + * @return -ETIME on timeout + */ +int ztimer_msg_receive_timeout(ztimer_clock_t *clock, msg_t *msg, + uint32_t timeout); + + /* created with dist/tools/define2u16.py */ +#define MSG_ZTIMER 0xc83e /**< msg type used by ztimer_msg_receive_timeout */ + +/* + * @brief ztimer_now() for extending timers + * + * @internal + * + * @param[in] clock ztimer clock to operate on + * @return Current count on the clock @p clock + */ +ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock); + +/** + * @brief ztimer_now() for extending timers + * + * @internal + * + * @param[in] clock ztimer clock to operate on + * @return Current count on the clock @p clock + */ +ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock); + +/** + * @brief Get the current time from a clock + * + * @param[in] clock ztimer clock to operate on + * + * @return Current count on @p clock + */ +static inline ztimer_now_t ztimer_now(ztimer_clock_t *clock) +{ +#if MODULE_ZTIMER_NOW64 + if (1) { +#elif MODULE_ZTIMER_EXTEND + if (clock->max_value < UINT32_MAX) { +#else + if (0) { +#endif + return _ztimer_now_extend(clock); + } + else { + return clock->ops->now(clock); + } +} + +/** + * @brief Suspend the calling thread until the time (@p last_wakeup + @p period) + * + * This function can be used to create periodic wakeups. + * + * When the function returns, @p last_wakeup is set to + * (@p last_wakeup + @p period). + * + * @c last_wakeup should be set to ztimer_now(@p clock) before first call of the + * function. + * + * If the time (@p last_wakeup + @p period) has already passed, the function + * sets @p last_wakeup to @p last_wakeup + @p period and returns immediately. + * + * @param[in] clock ztimer clock to operate on + * @param[in] last_wakeup base time stamp for the wakeup + * @param[in] period time in ticks that will be added to @p last_wakeup + */ +void ztimer_periodic_wakeup(ztimer_clock_t *clock, uint32_t *last_wakeup, + uint32_t period); + +/** + * @brief Put the calling thread to sleep for the specified number of ticks + * + * @param[in] clock ztimer clock to use + * @param[in] duration duration of sleep, in @p ztimer time units + */ +void ztimer_sleep(ztimer_clock_t *clock, uint32_t duration); + +/** + * @brief Set a timer that wakes up a thread + * + * This function sets a timer that will wake up a thread when the timer has + * expired. + * + * @param[in] clock ztimer clock to operate on + * @param[in] timer timer struct to work with. + * @param[in] offset clock ticks from now + * @param[in] pid pid of the thread that will be woken up + */ +void ztimer_set_wakeup(ztimer_clock_t *clock, ztimer_t *timer, uint32_t offset, + kernel_pid_t pid); + +/** + * @brief Set timeout thread flag after @p timeout + * + * This function will set THREAD_FLAG_TIMEOUT on the current thread after @p + * timeout usec have passed. + * + * @param[in] clock ztimer clock to operate on + * @param[in] timer timer struct to use + * @param[in] timeout timeout in ztimer_clock's ticks + */ +void ztimer_set_timeout_flag(ztimer_clock_t *clock, ztimer_t *timer, + uint32_t timeout); + +/** + * @brief Update ztimer clock head list offset + * + * @internal + * + * @param[in] clock ztimer clock to work on + */ +void ztimer_update_head_offset(ztimer_clock_t *clock); + +/** + * @brief Initialize the board-specific default ztimer configuration + */ +void ztimer_init(void); + +/* default ztimer virtual devices */ +/** + * @brief Default ztimer microsecond clock + */ +extern ztimer_clock_t *const ZTIMER_USEC; + +/** + * @brief Default ztimer millisecond clock + */ +extern ztimer_clock_t *const ZTIMER_MSEC; + +#ifdef __cplusplus +extern "C" { +#endif + +#endif /* ZTIMER_H */ +/** @} */ diff --git a/sys/include/ztimer/convert.h b/sys/include/ztimer/convert.h new file mode 100644 index 0000000000..098f092c43 --- /dev/null +++ b/sys/include/ztimer/convert.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Kaspar Schleiser + * + * 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 sys_ztimer_convert ztimer frequency conversion modules + * @ingroup sys_ztimer + * @brief ztimer frequency conversion modules + * + * ztimer provides multiple conversion modules: + * + * - ztimer_convert_shift: should be used if the fraction is a power of two. + * + * - ztimer_convert_frac: should be used if the fraction is not a power of two. + * rounding might be a bit off for some fractions. + * + * - ztimer_convert_muldiv64: can be used instead of ztimer_convert_frac, + * if 64bit division is cheap on the target board. + * + * @{ + * @file + * @brief ztimer frequency conversion base module + * @author Kaspar Schleiser + */ + +#ifndef ZTIMER_CONVERT_H +#define ZTIMER_CONVERT_H + +#include "ztimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief base type for ztimer convert modules + * + * This type is supposed to be extended. It provides common fields for a ztimer + * clock that has a parent clock. + * */ +typedef struct { + ztimer_clock_t super; /**< ztimer_clock super class */ + ztimer_clock_t *lower; /**< lower clock device */ + ztimer_t lower_entry; /**< timer entry in parent clock */ +} ztimer_convert_t; + +/** + * @brief Initialization function for ztimer_convert_t + * + * @p max_value needs to be set to the maximum value that can be converted + * without overflowing. E.g., if the conversion module slows down a lower + * clock by factor X, max_value needs to be set to UINT32_MAX / X. + * + * @param[in,out] ztimer_convert object to initialize + * @param[in] lower lower ztimer clock + * @param[in] max_value maximum value for this clock's set() + */ +void ztimer_convert_init(ztimer_convert_t *ztimer_convert, + ztimer_clock_t *lower, uint32_t max_value); + +/** + * @brief ztimer_convert common cancel() op + * + * Used by some conversion modules as ztimer_clock_t::ops.cancel(). + * + * @param[in] clock ztimer clock to operate on + */ +void ztimer_convert_cancel(ztimer_clock_t *clock); + +#ifdef __cplusplus +} +#endif + +#endif /* ZTIMER_CONVERT_H */ +/** @} */ diff --git a/sys/include/ztimer/convert_frac.h b/sys/include/ztimer/convert_frac.h new file mode 100644 index 0000000000..4e35d8fdd4 --- /dev/null +++ b/sys/include/ztimer/convert_frac.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * Copyright (C) 2018 Eistec AB + * + * 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 sys_ztimer_convert_frac ztimer_convert_frac frequency conversion layer + * @ingroup sys_ztimer_convert + * @brief Translates between clock tick rates + * + * Translates the ticks of an underlying clock into virtual ticks at a different + * frequency. + * This module makes use of frac for calculating fractions using multiplicative + * inversions, avoiding integer divisions. frac trades accuracy for speed. + * Please see the documentation of frac for more details. + * + * @{ + * @file + * @brief ztimer_convert_frac interface definitions + * + * @author Kaspar Schleiser + * @author Joakim Nohlgård + */ +#ifndef ZTIMER_CONVERT_FRAC_H +#define ZTIMER_CONVERT_FRAC_H + +#include +#include "ztimer.h" +#include "ztimer/convert.h" +#include "ztimer/convert_frac.h" +#include "frac.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ztimer_convert_frac frequency conversion layer class + */ +typedef struct { + /** + * @brief Superclass instance + */ + ztimer_convert_t super; + /** + * @brief Frequency conversion scaling constant from lower to self + */ + frac_t scale_now; + /** + * @brief Frequency conversion scaling constant from self to lower + */ + frac_t scale_set; + /** + * @brief Rounding value, will be added to all lower set(). + * + * E.g., 1000000/32768== ~30.5. `round` will be set to 30. + */ + uint32_t round; +} ztimer_convert_frac_t; + +/** + * @brief @ref ztimer_convert_frac_t constructor + * + * @param[in] self pointer to instance being initialized + * @param[in] lower pointer to underlying clock + * @param[in] freq_self desired frequency of this clock + * @param[in] freq_lower frequency of the underlying clock + */ +void ztimer_convert_frac_init(ztimer_convert_frac_t *self, ztimer_clock_t *lower, + uint32_t freq_self, uint32_t freq_lower); + +/** + * @brief Change the scaling without affecting the current count + * + * @param[in] self pointer to instance being initialized + * @param[in] freq_self desired frequency of this clock + * @param[in] freq_lower frequency of the underlying clock + */ +void ztimer_convert_frac_change_rate(ztimer_convert_frac_t *self, + uint32_t freq_self, uint32_t freq_lower); + +#ifdef __cplusplus +} +#endif + +#endif /* ZTIMER_CONVERT_FRAC_H */ +/** @} */ diff --git a/sys/include/ztimer/convert_muldiv64.h b/sys/include/ztimer/convert_muldiv64.h new file mode 100644 index 0000000000..5926daf6aa --- /dev/null +++ b/sys/include/ztimer/convert_muldiv64.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 Kaspar Schleiser + * + * 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 sys_ztimer_convert_muldiv64 plain 64bit carithmetic + * @ingroup sys_ztimer_convert + * @brief ztimer frequency conversion module (64bit arithmetic) + * + * This ztimer module allows converting a lower-level ztimer clockwith a given + * frequency to another frequency. + * + * It is configured by passing two parameters (div, mul). + * Given a lower clock frequency f_low and a desired upper frequency f_upper, + * div and mul must be chosen such that + * + * (f_upper * mul / div) == f_lower + * + * A div or mul value of 0 is treated as 1 (no multiplication or division by 0 is + * done). + * + * On every ztimer_set(), the target offset is first multiplied by mul and + * then divided by div, before passing it to the lower ztimer's ztimer_set(). + * + * On every ztimer_now(), the value from the lower ztimer is first multiplied + * by div and then divided by mul. + * + * Multiplication and division is done using 64bit multiplication / division, + * thus its use should be avoided in favour of more optimized conversion + * modules. + * + * Example: + * + * 1. if a ztimer_periph_timer with 250kHz is to be "sped up" to 1MHz, + * use div=4, mul=0 + * + * 2. if a ztimer with 1024Hz is to be converted to 1000Hz, use div=125, mul=128 + * + * @{ + * @file + * @brief ztimer frequency conversion module API + * @author Kaspar Schleiser + */ + +#ifndef ZTIMER_CONVERT_MULDIV64_H +#define ZTIMER_CONVERT_MULDIV64_H + +#include "ztimer.h" +#include "ztimer/convert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ztimer_convert_muldiv64 structure + */ +typedef struct { + ztimer_convert_t super; /**< super class */ + uint16_t mul; /**< please read */ + uint16_t div; /**< module docs */ +} ztimer_convert_muldiv64_t; + +/** + * @brief ztimer_convert_muldiv64 initialization function + * + * @param[in] ztimer_convert_muldiv64 instance to initialize + * @param[in] lower lower timer to convert + * @param[in] div see module doc + * @param[in] mul see module doc + */ +void ztimer_convert_muldiv64_init( + ztimer_convert_muldiv64_t *ztimer_convert_muldiv64, ztimer_clock_t *lower, + unsigned div, unsigned mul); + +#ifdef __cplusplus +} +#endif + +#endif /* ZTIMER_CONVERT_MULDIV64_H */ +/** @} */ diff --git a/sys/include/ztimer/convert_shift.h b/sys/include/ztimer/convert_shift.h new file mode 100644 index 0000000000..038122a648 --- /dev/null +++ b/sys/include/ztimer/convert_shift.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * + * 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 sys_ztimer_convert_shift conversion using shifts + * @ingroup sys_ztimer_convert + * @brief Translates between clock tick rates + * + * Translates the ticks of an underlying clock into virtual ticks at a different + * frequency, by using bit shifts. Thus it works only for fractions that are + * powers of 2. + * + * @{ + * @file + * @brief ztimer_convert_shift interface definitions + * + * @author Kaspar Schleiser + */ +#ifndef ZTIMER_CONVERT_SHIFT_H +#define ZTIMER_CONVERT_SHIFT_H + +#include + +#include "ztimer.h" +#include "ztimer/convert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ztimer_convert_shift frequency conversion layer class + */ +typedef struct { + /** + * @brief Superclass instance + */ + ztimer_convert_t super; + /** + * @brief Frequency conversion scaling constant from lower to self + * + * This value is saved as positive integer. The functions within the "ops" + * struct decides whether to left or right shift. + */ + unsigned shift; +} ztimer_convert_shift_t; + +/** + * @brief ztimer_convert_shift init() for (fake) increasing timer frequency + * + * Will cause every lower now() to be left-shifted and every set() to be + * right-shifted. + * + * @param[in] clock pointer to instance being initialized + * @param[in] lower pointer to underlying clock + * @param[in] shift shift value to use + */ +void ztimer_convert_shift_up_init(ztimer_convert_shift_t *clock, + ztimer_clock_t *lower, unsigned shift); + +#ifdef __cplusplus +} +#endif + +#endif /* ZTIMER_CONVERT_SHIFT_H */ +/** @} */ diff --git a/sys/include/ztimer/mock.h b/sys/include/ztimer/mock.h new file mode 100644 index 0000000000..a03bfbc487 --- /dev/null +++ b/sys/include/ztimer/mock.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2019 Kaspar Schleiser + * 2018 Eistec AB + * + * 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 sys_ztimer_mock ztimer mock clock backend + * @ingroup sys_ztimer + * @brief ztimer mock clock backend + * + * This ztimer module implements a virtual clock that can be used for unittests. + * It can be manually adjusted to different timestamps and manually fired to + * simulate different scenarios and test the ztimer implementation using this + * as a backing timer. + * + * @{ + * @file + * @brief ztimer mock clock backend API + * @author Joakim Nohlgård + * @author Kaspar Schleiser + */ + +#ifndef ZTIMER_MOCK_H +#define ZTIMER_MOCK_H + +#include +#include "ztimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ztimer mock clock class + */ +typedef struct { + ztimer_clock_t super; /**< superclass instance */ + uint32_t mask; /**< counter mask */ + uint32_t now; /**< current counter value */ + uint32_t target; /**< ticks left until alarm is hit */ + unsigned armed; /**< flag for checking if a target has been set */ + + struct { + unsigned now; /**< Number of calls to ztimer_ops_t::now */ + unsigned set; /**< Number of calls to ztimer_ops_t::set */ + unsigned cancel; /**< Number of calls to ztimer_ops_t::cancel */ + } calls; /**< Struct holding number of calls to each + operation */ +} ztimer_mock_t; + +/** + * @brief Advance the mock clock counter and update target + * + * This will call @ref ztimer_handler if the target was passed. + * + * @param[in] self instance to operate on + * @param[in] val counter increment value + */ +void ztimer_mock_advance(ztimer_mock_t *self, uint32_t val); + +/** + * @brief Set the mock clock counter value without updating timer target + * + * This will not touch the timer target. + * + * @param[in] self instance to operate on + * @param[in] target new absolute counter value + */ +void ztimer_mock_jump(ztimer_mock_t *self, uint32_t target); + +/** + * @brief Trigger the timer handlers + * + * This is equivalent to a hardware timer interrupt + * + * @param[in] self instance to operate on + */ +void ztimer_mock_fire(ztimer_mock_t *self); + +/** + * @brief Constructor + * + * @param[in] self instance to operate on + * @param[in] width counter width, 1 <= width <= 32 + */ +void ztimer_mock_init(ztimer_mock_t *self, unsigned width); + +#ifdef __cplusplus +} +#endif + +#endif /* ZTIMER_MOCK_H */ +/** @} */ diff --git a/sys/include/ztimer/overhead.h b/sys/include/ztimer/overhead.h new file mode 100644 index 0000000000..86637fc32e --- /dev/null +++ b/sys/include/ztimer/overhead.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer_overhead ztimer overhead utility + * @ingroup sys_ztimer + * @brief ztimer overhead measurement functionality + * + * @{ + * + * @file + * @brief ztimer_overhead API + * + * @author Kaspar Schleiser + */ + +#ifndef ZTIMER_OVERHEAD_H +#define ZTIMER_OVERHEAD_H + +#include "ztimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Measure ztimer overhead + * + * This function can be used to measure the overhead incurred by ztimer. + * It will configure a callback to trigger after @p base ticks, then return the + * number of ticks that have passed, minus @p base. + * + * @param[in] clock ztimer clock to operate on + * @param[in] base base interval to use + * @return (time from ztimer_set() until callback) - base + */ +uint32_t ztimer_overhead(ztimer_clock_t *clock, uint32_t base); + +#endif /* ZTIMER_OVERHEAD_H */ +/** @} */ diff --git a/sys/include/ztimer/periph_rtc.h b/sys/include/ztimer/periph_rtc.h new file mode 100644 index 0000000000..40b7bc3fb4 --- /dev/null +++ b/sys/include/ztimer/periph_rtc.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * + * 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 sys_ztimer_periph_rtc ztimer periph/rtc backend + * @ingroup sys_ztimer + * @brief ztimer periph/rtc backend + * + * This ztimer module implements a ztimer virtual clock on top of periph/rtc. + * + * @{ + * + * @file + * @brief ztimer rtc/timer backend API + * + * @author Kaspar Schleiser + */ + +#ifndef ZTIMER_PERIPH_RTC_H +#define ZTIMER_PERIPH_RTC_H + +#include "ztimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ztimer_periph_rtc structure definition + * + * rtc has no private fields, thus this is just a typedef to ztimer_clock_t. + */ +typedef ztimer_clock_t ztimer_periph_rtc_t; + +/** + * @brief ztimer rtc backend initialization function + * + * @param[in, out] clock ztimer_periph_rtc object to initialize + */ +void ztimer_periph_rtc_init(ztimer_periph_rtc_t *clock); + +#ifdef __cplusplus +} +#endif + +#endif /* ZTIMER_PERIPH_RTC_H */ +/** @} */ diff --git a/sys/include/ztimer/periph_rtt.h b/sys/include/ztimer/periph_rtt.h new file mode 100644 index 0000000000..2868285f8d --- /dev/null +++ b/sys/include/ztimer/periph_rtt.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * + * 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 sys_ztimer_periph_rtt ztimer periph/rtt backend + * @ingroup sys_ztimer + * @brief ztimer periph/rtt backend + * + * This ztimer module implements a ztimer virtual clock on top of periph/rtt. + * + * @{ + * + * @file + * @brief ztimer periph/rtt backend API + * + * @author Kaspar Schleiser + */ + +#ifndef ZTIMER_PERIPH_RTT_H +#define ZTIMER_PERIPH_RTT_H + +#include "ztimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ztimer_periph_rtt structure definition + * + * The periph/rtt backend has no private fields, thus this is just a typedef + * to ztimer_clock_t. + */ +typedef ztimer_clock_t ztimer_periph_rtt_t; + +/** + * @brief ztimer periph/rtt backend initialization function + * + * @param[in, out] clock ztimer_periph_rtt object to initialize + */ +void ztimer_periph_rtt_init(ztimer_periph_rtt_t *clock); + +#ifdef __cplusplus +} +#endif + +#endif /* ZTIMER_PERIPH_RTT_H */ +/** @} */ diff --git a/sys/include/ztimer/periph_timer.h b/sys/include/ztimer/periph_timer.h new file mode 100644 index 0000000000..d5abb768f1 --- /dev/null +++ b/sys/include/ztimer/periph_timer.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018 Kaspar Schleiser + * + * 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 sys_ztimer_periph_timer ztimer periph/timer backend + * @ingroup sys_ztimer + * @brief ztimer periph/timer backend + * + * This ztimer module implements a ztimer virtual clock on top of periph/timer. + * + * This module has two tuning values: + * "adjust": will be subtracted from every timer set. + * "min": Every timer will be set to max("min", value). + * @{ + * + * @file + * @brief ztimer periph/timer backend API + * + * @author Kaspar Schleiser + */ + +#ifndef ZTIMER_PERIPH_TIMER_H +#define ZTIMER_PERIPH_TIMER_H + +#include "ztimer.h" +#include "periph/timer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ztimer periph context structure + */ +typedef struct { + ztimer_clock_t super; /**< super class */ + tim_t dev; /**< periph timer device */ + uint16_t min; /**< optional minimum value */ +} ztimer_periph_timer_t; + +/** + * @brief ztimer periph initialization + * + * Initializes the given periph timer and sets up the ztimer device. + * + * @param[in] clock ztimer periph device to initialize + * @param[in] dev periph timer to use + * @param[in] freq frequency to configure + * @param[in] max_val maximum value this timer supports + */ +void ztimer_periph_timer_init(ztimer_periph_timer_t *clock, tim_t dev, + unsigned long freq, uint32_t max_val); + +#ifdef __cplusplus +} +#endif + +#endif /* ZTIMER_PERIPH_TIMER_H */ +/** @} */ diff --git a/sys/ztimer/Makefile b/sys/ztimer/Makefile new file mode 100644 index 0000000000..5cf7e3e1ef --- /dev/null +++ b/sys/ztimer/Makefile @@ -0,0 +1,19 @@ +# make all code end up in "ztimer_core.a" +MODULE := ztimer_core + +# ensure that "ztimer_foo" builds "ztimer_foo.c", not "ztimer_core_foo.c" +BASE_MODULE := ztimer + +# ztimer_core files +SRC := core.c util.c + +# enable submodules +SUBMODULES := 1 + +# "ztimer_extend" does not have corresponding .c +SUBMODULES_NOFORCE := 1 + +# disable obsolete warning +CFLAGS += -Wno-missing-field-initializers + +include $(RIOTBASE)/Makefile.base diff --git a/sys/ztimer/auto_init.c b/sys/ztimer/auto_init.c new file mode 100644 index 0000000000..acf33ad030 --- /dev/null +++ b/sys/ztimer/auto_init.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer + * @{ + * + * @file + * @brief ztimer initialization code + * + * + * This file could benefit a lot from code generation... + * + * Anyhow, this configures ztimer as follows: + * + * 1. if ztimer_msec in USEMODULE: + * 1.1. assume ztimer_msec uses periph_timer + * 1.2a. if no config given + * 1.2a.1a. use xtimer config if available + * 1.2a.1b. default to TIMER_DEV(0), 32bit + * 1.2b. else, use config + * + * 2. if ztimer_usec in USEMODULE: + * 2.1a. if periph_rtt in USEMODULE: use that + * 2.1b: else: convert from ZTIMER_MSEC + * + * @author Kaspar Schleiser + * + * @} + */ + +#include "board.h" +#include "ztimer.h" +#include "ztimer/convert_frac.h" +#include "ztimer/convert_shift.h" +#include "ztimer/convert_muldiv64.h" +#include "ztimer/periph_timer.h" +#include "ztimer/periph_rtt.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define WIDTH_TO_MAXVAL(width) (UINT32_MAX >> (32 - width)) + +#define CONFIG_ZTIMER_USEC_TYPE_PERIPH_TIMER (1) + +#define FREQ_1MHZ 1000000LU +#define FREQ_250KHZ 250000LU +#define FREQ_1KHZ 1000LU + +/* for ZTIMER_USEC, use xtimer configuration if available and no ztimer + * specific configuration is set. */ +#if CONFIG_ZTIMER_USEC_TYPE_PERIPH_TIMER +# ifndef CONFIG_ZTIMER_USEC_DEV +# ifdef XTIMER_DEV +# define CONFIG_ZTIMER_USEC_DEV XTIMER_DEV +# endif +# ifdef XTIMER_HZ +# define CONFIG_ZTIMER_USEC_FREQ XTIMER_HZ +# endif +# ifdef XTIMER_WIDTH +# define CONFIG_ZTIMER_USEC_WIDTH XTIMER_WIDTH +# endif +# endif +#endif + +#ifndef CONFIG_ZTIMER_USEC_DEV +#define CONFIG_ZTIMER_USEC_DEV (TIMER_DEV(0)) +#endif + +#ifndef CONFIG_ZTIMER_USEC_FREQ +#define CONFIG_ZTIMER_USEC_FREQ (FREQ_1MHZ) +#endif + +#ifndef CONFIG_ZTIMER_USEC_MIN +#define CONFIG_ZTIMER_USEC_MIN (10) +#endif + +#ifndef CONFIG_ZTIMER_USEC_WIDTH +#define CONFIG_ZTIMER_USEC_WIDTH (32) +#endif + +#if MODULE_ZTIMER_USEC +# if CONFIG_ZTIMER_USEC_TYPE_PERIPH_TIMER +static ztimer_periph_timer_t _ztimer_periph_timer_usec = { .min = CONFIG_ZTIMER_USEC_MIN }; +# if CONFIG_ZTIMER_USEC_FREQ == FREQ_1MHZ +ztimer_clock_t *const ZTIMER_USEC = &_ztimer_periph_timer_usec.super; +# elif CONFIG_ZTIMER_USEC_FREQ == 250000LU +static ztimer_convert_shift_t _ztimer_convert_shift_usec; +ztimer_clock_t *const ZTIMER_USEC = &_ztimer_convert_shift_usec.super.super; +# else +static ztimer_convert_frac_t _ztimer_convert_frac_usec; +ztimer_clock_t *const ZTIMER_USEC = &_ztimer_convert_frac_usec.super.super; +# define ZTIMER_USEC_CONVERT_LOWER (&_ztimer_periph_timer_usec.super) +# endif +# else +# error ztimer_usec selected, but no configuration available! +# endif +#endif + +#if MODULE_ZTIMER_MSEC +# if MODULE_PERIPH_TIMER_RTT +static ztimer_periph_timer_rtt_t _ztimer_periph_timer_rtt_msec; +# define ZTIMER_RTT_INIT (&_ztimer_periph_timer_rtt_msec) +# if RTT_FREQUENCY!=FREQ_1MHZ +static ztimer_convert_frac_t _ztimer_convert_frac_msec; +ztimer_clock_t *const ZTIMER_MSEC = &_ztimer_convert_frac_msec.super; +# define ZTIMER_MSEC_CONVERT_LOWER_FREQ RTT_FREQUENCY +# define ZTIMER_MSEC_CONVERT_LOWER (&_ztimer_periph_timer_rtt_msec) +# else +ztimer_clock_t *const ZTIMER_MSEC = &_ztimer_periph_timer_rtt_msec.super; +# endif +# elif MODULE_ZTIMER_USEC +static ztimer_convert_frac_t _ztimer_convert_frac_msec; +ztimer_clock_t *const ZTIMER_MSEC = &_ztimer_convert_frac_msec.super.super; +# if CONFIG_ZTIMER_USEC_FREQ < FREQ_1MHZ +# define ZTIMER_MSEC_CONVERT_LOWER ZTIMER_USEC_CONVERT_LOWER +# define ZTIMER_MSEC_CONVERT_LOWER_FREQ CONFIG_ZTIMER_USEC_FREQ +# else +# define ZTIMER_MSEC_CONVERT_LOWER (ZTIMER_USEC) +# define ZTIMER_MSEC_CONVERT_LOWER_FREQ FREQ_1MHZ +# endif +# else +# error No suitable ZTIMER_MSEC config. Maybe add USEMODULE += ztimer_usec? +# endif +#endif + +void ztimer_init(void) +{ +#if MODULE_ZTIMER_USEC +# if CONFIG_ZTIMER_USEC_TYPE_PERIPH_TIMER + DEBUG( + "ztimer_init(): ZTIMER_USEC using periph timer %u, freq %lu, width %u\n", + CONFIG_ZTIMER_USEC_DEV, CONFIG_ZTIMER_USEC_FREQ, CONFIG_ZTIMER_USEC_WIDTH); + + ztimer_periph_timer_init(&_ztimer_periph_timer_usec, CONFIG_ZTIMER_USEC_DEV, + CONFIG_ZTIMER_USEC_FREQ, + WIDTH_TO_MAXVAL(CONFIG_ZTIMER_USEC_WIDTH)); +# endif +# if CONFIG_ZTIMER_USEC_FREQ != FREQ_1MHZ +# if CONFIG_ZTIMER_USEC_FREQ == FREQ_250KHZ + DEBUG("ztimer_init(): ZTIMER_USEC convert_shift %lu to 1000000\n", + CONFIG_ZTIMER_USEC_FREQ); + ztimer_convert_shift_up_init(&_ztimer_convert_shift_usec, &_ztimer_periph_timer_usec.super, 2); +# else + DEBUG("ztimer_init(): ZTIMER_USEC convert_frac %lu to 1000000\n", + CONFIG_ZTIMER_USEC_FREQ); + ztimer_convert_frac_init(&_ztimer_convert_frac_usec, &_ztimer_periph_timer_usec.super, + FREQ_1MHZ, CONFIG_ZTIMER_USEC_FREQ); +# endif +# endif +#endif + +#ifdef ZTIMER_RTT_INIT + DEBUG("ztimer_init(): initializing rtt\n"); + ztimer_periph_timer_rtt_init(ZTIMER_RTT_INIT); +#endif + +#if MODULE_ZTIMER_MSEC +# if ZTIMER_MSEC_CONVERT_LOWER_FREQ + DEBUG("ztimer_init(): ZTIMER_MSEC convert_frac from %lu to 1000\n", + ZTIMER_MSEC_CONVERT_LOWER_FREQ); + ztimer_convert_frac_init(&_ztimer_convert_frac_msec, + ZTIMER_MSEC_CONVERT_LOWER, + FREQ_1KHZ, ZTIMER_MSEC_CONVERT_LOWER_FREQ); +# endif +#endif +} diff --git a/sys/ztimer/convert.c b/sys/ztimer/convert.c new file mode 100644 index 0000000000..1d6bc8cb89 --- /dev/null +++ b/sys/ztimer/convert.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer_convert + * @{ + * + * @file + * @brief ztimer frequency conversion module common code implementation + * + * @author Kaspar Schleiser + * + * @} + */ + +#include + +#include "ztimer/convert.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +void ztimer_convert_cancel(ztimer_clock_t *clock) +{ + ztimer_convert_t *ztimer_convert = (ztimer_convert_t *)clock; + + ztimer_remove(ztimer_convert->lower, &ztimer_convert->lower_entry); +} + +void ztimer_convert_init(ztimer_convert_t *ztimer_convert, ztimer_clock_t *lower, + uint32_t max_value) +{ + ztimer_convert_t tmp = { + .lower = lower, + .lower_entry = { + .callback = (void (*)(void *))ztimer_handler, + .arg = ztimer_convert, + }, + .super.max_value = max_value, + }; + + *ztimer_convert = tmp; + + DEBUG("ztimer_convert_init() max_value=%" PRIu32 "\n", + ztimer_convert->super.max_value); +} diff --git a/sys/ztimer/convert_frac.c b/sys/ztimer/convert_frac.c new file mode 100644 index 0000000000..f468997dd6 --- /dev/null +++ b/sys/ztimer/convert_frac.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * 2018 Joakim Nohlgård + * + * 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 sys_ztimer_convert_frac + * + * @{ + * + * @file + * @brief ztimer conversion using frac implementation + * + * @author Joakim Nohlgård + * @author Kaspar Schleiser + * @} + */ + +#include + +#include "frac.h" +#include "assert.h" +#include "irq.h" +#include "ztimer/convert.h" +#include "ztimer/convert_frac.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Compute the scaling parameters for the given two frequencies + * + * @param[in] self pointer to instance to operate on + * @param[in] freq_self desired frequency of this clock + * @param[in] freq_lower frequency of the underlying clock + */ +static void ztimer_convert_frac_compute_scale(ztimer_convert_frac_t *self, uint32_t freq_self, uint32_t freq_lower); + +static void ztimer_convert_frac_op_set(ztimer_clock_t *z, uint32_t val) +{ + ztimer_convert_frac_t *self = (ztimer_convert_frac_t *)z; + uint32_t target_lower = frac_scale(&self->scale_set, val + self->round); + DEBUG("ztimer_convert_frac_op_set(%"PRIu32")=%"PRIu32"\n", val, + target_lower); + ztimer_set(self->super.lower, &self->super.lower_entry, target_lower); +} + +static uint32_t ztimer_convert_frac_op_now(ztimer_clock_t *z) +{ + ztimer_convert_frac_t *self = (ztimer_convert_frac_t *)z; + uint32_t lower_now = ztimer_now(self->super.lower); + if (lower_now == 0) { + return 0; + } + uint32_t scaled = frac_scale(&self->scale_now, lower_now); + DEBUG("ztimer_convert_frac_op_now() %"PRIu32"->%"PRIu32"\n", lower_now, scaled); + return scaled; +} + +static const ztimer_ops_t ztimer_convert_frac_ops = { + .set = ztimer_convert_frac_op_set, + .now = ztimer_convert_frac_op_now, + .cancel = ztimer_convert_cancel, +}; + +static void ztimer_convert_frac_compute_scale(ztimer_convert_frac_t *self, uint32_t freq_self, uint32_t freq_lower) +{ + assert(freq_self); + assert(freq_lower); + frac_init(&self->scale_now, freq_self, freq_lower); + frac_init(&self->scale_set, freq_lower, freq_self); +} + +void ztimer_convert_frac_init(ztimer_convert_frac_t *self, ztimer_clock_t *lower, uint32_t freq_self, uint32_t freq_lower) +{ + DEBUG("ztimer_convert_frac_init: %p->%p fs=%" PRIu32 " fl=%" PRIu32 "\n", + (void *)self, (void *)lower, freq_self, freq_lower); + + *self = (ztimer_convert_frac_t) { + .super.super = { .ops = &ztimer_convert_frac_ops, }, + .super.lower = lower, + .super.lower_entry = { .callback = (void (*)(void *))ztimer_handler, .arg = &self->super, }, + }; + + ztimer_convert_frac_compute_scale(self, freq_self, freq_lower); + if (freq_self < freq_lower) { + self->super.super.max_value = frac_scale(&self->scale_set, UINT32_MAX); + } + else { + DEBUG("ztimer_convert_frac_init: rounding up val:%" PRIu32"\n", + (uint32_t)(freq_self / freq_lower)); + self->round = freq_self / freq_lower; + self->super.super.max_value = UINT32_MAX; + } +} diff --git a/sys/ztimer/convert_muldiv64.c b/sys/ztimer/convert_muldiv64.c new file mode 100644 index 0000000000..8732448076 --- /dev/null +++ b/sys/ztimer/convert_muldiv64.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer_convert_muldiv64 + * @{ + * + * @file + * @brief ztimer frequency conversion module using 64bit division + * + * @author Kaspar Schleiser + * + * @} + */ + +#include + +#include "ztimer/convert.h" +#include "ztimer/convert_muldiv64.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static void _ztimer_convert_muldiv64_set(ztimer_clock_t *ztimer, uint32_t val); + +/* returns ceil(x/y) */ +static uint64_t _integer_div_ceil(uint64_t x, uint64_t y) +{ + if (x == 0) { + return 0; + } + + return 1 + ((x - 1) / y); +} + +static uint32_t _convert_muldiv64_set( + const ztimer_convert_muldiv64_t *ztimer_convert_muldiv64, uint32_t val) +{ + uint64_t res = val; + + if (ztimer_convert_muldiv64->mul > 1) { + res *= ztimer_convert_muldiv64->mul; + } + if (ztimer_convert_muldiv64->div > 1) { + res = _integer_div_ceil(res, ztimer_convert_muldiv64->div); + } + + return res; +} + +static uint32_t _convert_muldiv64_now( + const ztimer_convert_muldiv64_t *ztimer_convert_muldiv64, uint32_t val) +{ + uint64_t res = val; + + if (ztimer_convert_muldiv64->div > 1) { + res *= ztimer_convert_muldiv64->div; + } + if (ztimer_convert_muldiv64->mul > 1) { + res /= ztimer_convert_muldiv64->mul; + } + DEBUG("_convert_muldiv64_now(%u * %u / %u == %u\n", (unsigned)val, + (unsigned)ztimer_convert_muldiv64->div ? ztimer_convert_muldiv64->div : 1, + (unsigned)ztimer_convert_muldiv64->mul ? ztimer_convert_muldiv64->mul : 1, + (unsigned)res); + return res; +} + +static void _ztimer_convert_muldiv64_set(ztimer_clock_t *ztimer, uint32_t val) +{ + ztimer_convert_muldiv64_t *ztimer_convert_muldiv64 = + (ztimer_convert_muldiv64_t *)ztimer; + + ztimer_set(ztimer_convert_muldiv64->super.lower, + &ztimer_convert_muldiv64->super.lower_entry, _convert_muldiv64_set( + ztimer_convert_muldiv64, + val)); +} + +static uint32_t _ztimer_convert_muldiv64_now(ztimer_clock_t *ztimer) +{ + const ztimer_convert_muldiv64_t *ztimer_convert_muldiv64 = + (ztimer_convert_muldiv64_t *)ztimer; + + return _convert_muldiv64_now(ztimer_convert_muldiv64, + ztimer_now(ztimer_convert_muldiv64->super.lower)); +} + +static const ztimer_ops_t _ztimer_convert_muldiv64_ops = { + .set = _ztimer_convert_muldiv64_set, + .now = _ztimer_convert_muldiv64_now, + .cancel = ztimer_convert_cancel, +}; + +void ztimer_convert_muldiv64_init( + ztimer_convert_muldiv64_t *ztimer_convert_muldiv64, ztimer_clock_t *lower, + unsigned div, unsigned mul) +{ + uint32_t max_value; + + if (mul > div) { + max_value = (uint64_t)UINT32_MAX * div / mul; + } + else { + max_value = UINT32_MAX; + } + + DEBUG( + "ztimer_convert_muldiv64_init() div=%u mul=%u lower_maxval=%" PRIu32 "\n", + div, mul, max_value); + + ztimer_convert_init(&ztimer_convert_muldiv64->super, lower, max_value); + ztimer_convert_muldiv64->super.super.ops = &_ztimer_convert_muldiv64_ops; + ztimer_convert_muldiv64->div = div; + ztimer_convert_muldiv64->mul = mul; +} diff --git a/sys/ztimer/convert_shift.c b/sys/ztimer/convert_shift.c new file mode 100644 index 0000000000..8cae834d08 --- /dev/null +++ b/sys/ztimer/convert_shift.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer_convert_shift + * @{ + * + * @file + * @brief ztimer frequency conversion module using shifts + * + * @author Kaspar Schleiser + * + * @} + */ + +#include + +#include "ztimer/convert.h" +#include "ztimer/convert_shift.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static uint32_t _convert_shift_up_set( + ztimer_convert_shift_t *ztimer_convert_shift, uint32_t val) +{ + uint32_t res = val; + + res >>= ztimer_convert_shift->shift; + + /* TODO: round up */ + + return res; +} + +static uint32_t _convert_shift_up_now( + ztimer_convert_shift_t *ztimer_convert_shift, uint32_t val) +{ + uint32_t res = val; + + res <<= ztimer_convert_shift->shift; + + DEBUG("_convert_shift_now(%u << %i == %u)\n", (unsigned)val, + (unsigned)ztimer_convert_shift->shift, (unsigned)res); + + return res; +} + +static void _ztimer_convert_shift_up_set(ztimer_clock_t *ztimer, uint32_t val) +{ + ztimer_convert_shift_t *ztimer_convert_shift = + (ztimer_convert_shift_t *)ztimer; + + ztimer_set(ztimer_convert_shift->super.lower, + &ztimer_convert_shift->super.lower_entry, _convert_shift_up_set( + ztimer_convert_shift, + val)); +} + +static uint32_t _ztimer_convert_shift_up_now(ztimer_clock_t *ztimer) +{ + ztimer_convert_shift_t *ztimer_convert_shift = + (ztimer_convert_shift_t *)ztimer; + + return _convert_shift_up_now(ztimer_convert_shift, + ztimer_now(ztimer_convert_shift->super.lower)); +} + +static const ztimer_ops_t _ztimer_convert_shift_ops_up = { + .set = _ztimer_convert_shift_up_set, + .now = _ztimer_convert_shift_up_now, + .cancel = ztimer_convert_cancel, +}; + +void ztimer_convert_shift_up_init(ztimer_convert_shift_t *clock, + ztimer_clock_t *lower, unsigned shift) +{ + uint32_t max_value = UINT32_MAX; + + DEBUG( + "ztimer_convert_shift_init() shift=%i lower_maxval=%" PRIu32 "\n", + shift, max_value); + + ztimer_convert_init(&clock->super, lower, max_value); + clock->super.super.ops = &_ztimer_convert_shift_ops_up; + clock->shift = shift; +} diff --git a/sys/ztimer/core.c b/sys/ztimer/core.c new file mode 100644 index 0000000000..736fbc31d5 --- /dev/null +++ b/sys/ztimer/core.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer + * @{ + * + * @file + * @brief ztimer core functinality + * + * This file contains ztimer's main API implementation and functionality + * present in all ztimer clocks (most notably multiplexing ant extension). + * + * @author Kaspar Schleiser + * + * @} + */ +#include +#include + +#include "kernel_defines.h" +#include "irq.h" +#include "ztimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static void _add_entry_to_list(ztimer_clock_t *clock, ztimer_base_t *entry); +static void _del_entry_from_list(ztimer_clock_t *clock, ztimer_base_t *entry); +static void _ztimer_update(ztimer_clock_t *clock); +static void _ztimer_print(const ztimer_clock_t *clock); + +#ifdef MODULE_ZTIMER_EXTEND +static inline uint32_t _min_u32(uint32_t a, uint32_t b) { + return a < b ? a : b; +} +#endif + +static unsigned _is_set(const ztimer_clock_t *clock, const ztimer_t *t) +{ + if (!clock->list.next) { + return 0; + } else { + return (t->base.next || &t->base == clock->last); + } +} + +void ztimer_remove(ztimer_clock_t *clock, ztimer_t *timer) +{ + unsigned state = irq_disable(); + + if (_is_set(clock, timer)) { + ztimer_update_head_offset(clock); + _del_entry_from_list(clock, &timer->base); + + _ztimer_update(clock); + } + + irq_restore(state); +} + +void ztimer_set(ztimer_clock_t *clock, ztimer_t *timer, uint32_t val) +{ + DEBUG("ztimer_set(): %p: set %p at %"PRIu32" offset %"PRIu32"\n", + (void *)clock, (void *)timer, clock->ops->now(clock), val); + + unsigned state = irq_disable(); + + ztimer_update_head_offset(clock); + if (_is_set(clock, timer)) { + _del_entry_from_list(clock, &timer->base); + } + + /* optionally subtract a configurable adjustment value */ + if (val > clock->adjust) { + val -= clock->adjust; + } else { + val = 0; + } + + timer->base.offset = val; + _add_entry_to_list(clock, &timer->base); + if (clock->list.next == &timer->base) { +#ifdef MODULE_ZTIMER_EXTEND + if (clock->max_value < UINT32_MAX) { + val = _min_u32(val, clock->max_value >> 1); + } + DEBUG("ztimer_set(): %p setting %"PRIu32"\n", (void *)clock, val); +#endif + clock->ops->set(clock, val); + } + + irq_restore(state); +} + +static void _add_entry_to_list(ztimer_clock_t *clock, ztimer_base_t *entry) +{ + uint32_t delta_sum = 0; + + ztimer_base_t *list = &clock->list; + + /* Jump past all entries which are set to an earlier target than the new entry */ + while (list->next) { + ztimer_base_t *list_entry = list->next; + if ((list_entry->offset + delta_sum) > entry->offset) { + break; + } + delta_sum += list_entry->offset; + list = list->next; + } + + /* Insert into list */ + entry->next = list->next; + entry->offset -= delta_sum; + if (entry->next) { + entry->next->offset -= entry->offset; + } + else { + clock->last = entry; + } + list->next = entry; + DEBUG("_add_entry_to_list() %p offset %"PRIu32"\n", (void *)entry, entry->offset); + +} + +static uint32_t _add_modulo(uint32_t a, uint32_t b, uint32_t mod) +{ + if (a < b) { + a += mod + 1; + } + return a-b; +} + +#ifdef MODULE_ZTIMER_EXTEND +ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock) +{ + assert(clock->max_value); + unsigned state = irq_disable(); + uint32_t lower_now = clock->ops->now(clock); + DEBUG("ztimer_now() checkpoint=%"PRIu32" lower_last=%"PRIu32" lower_now=%"PRIu32" diff=%"PRIu32"\n", + (uint32_t)clock->checkpoint, clock->lower_last, lower_now, + _add_modulo(lower_now, clock->lower_last, clock->max_value)); + clock->checkpoint += _add_modulo(lower_now, clock->lower_last, clock->max_value); + clock->lower_last = lower_now; + DEBUG("ztimer_now() returning %"PRIu32"\n", (uint32_t)clock->checkpoint); + ztimer_now_t now = clock->checkpoint; + irq_restore(state); + return now; +} +#endif /* MODULE_ZTIMER_EXTEND */ + +void ztimer_update_head_offset(ztimer_clock_t *clock) +{ + uint32_t old_base = clock->list.offset; + uint32_t now = ztimer_now(clock); + uint32_t diff = now - old_base; + + ztimer_base_t *entry = clock->list.next; + DEBUG("clock %p: ztimer_update_head_offset(): diff=%" PRIu32 " old head %p\n", + (void *)clock, diff, (void *)entry); + if (entry) { + do { + if (diff <= entry->offset) { + entry->offset -= diff; + break; + } + else { + diff -= entry->offset; + entry->offset = 0; + if (diff) { + /* skip timers with offset==0 */ + do { + entry = entry->next; + } while (entry && (entry->offset == 0)); + } + } + } while (diff && entry); + DEBUG("ztimer %p: ztimer_update_head_offset(): now=%" PRIu32 " new head %p", + (void *)clock, now, (void *)entry); + if (entry) { + DEBUG(" offset %" PRIu32 "\n", entry->offset); + } + else { + DEBUG("\n"); + } + } + + clock->list.offset = now; +} + +static void _del_entry_from_list(ztimer_clock_t *clock, ztimer_base_t *entry) +{ + DEBUG("_del_entry_from_list()\n"); + ztimer_base_t *list = &clock->list; + + assert(_is_set(clock, (ztimer_t *)entry)); + + while (list->next) { + ztimer_base_t *list_entry = list->next; + if (list_entry == entry) { + if (entry == clock->last) { + /* if entry was the last timer, set the clocks last to the + * previous entry, or NULL if that was the list ptr */ + clock->last = (list == &clock->list) ? NULL : list; + } + + list->next = entry->next; + if (list->next) { + list_entry = list->next; + list_entry->offset += entry->offset; + } + + /* reset the entry's next pointer so _is_set() considers it unset */ + entry->next = NULL; + break; + } + list = list->next; + } +} + +static ztimer_t *_now_next(ztimer_clock_t *clock) +{ + ztimer_base_t *entry = clock->list.next; + + if (entry && (entry->offset == 0)) { + clock->list.next = entry->next; + if (!entry->next) { + clock->last = NULL; + } + return (ztimer_t*)entry; + } + else { + return NULL; + } +} + +static void _ztimer_update(ztimer_clock_t *clock) +{ +#ifdef MODULE_ZTIMER_EXTEND + if (clock->max_value < UINT32_MAX) { + if (clock->list.next) { + clock->ops->set(clock, _min_u32(clock->list.next->offset, clock->max_value >> 1)); + } + else { + clock->ops->set(clock, clock->max_value >> 1); + } +#else + if (0) { +#endif + } + else { + if (clock->list.next) { + clock->ops->set(clock, clock->list.next->offset); + } + else { + clock->ops->cancel(clock); + } + } +} + +void ztimer_handler(ztimer_clock_t *clock) +{ + DEBUG("ztimer_handler(): %p now=%"PRIu32"\n", (void *)clock, clock->ops->now(clock)); + if (ENABLE_DEBUG) { + _ztimer_print(clock); + } + +#if MODULE_ZTIMER_EXTEND || MODULE_ZTIMER_NOW64 + if (IS_USED(MODULE_ZTIMER_NOW64) || clock->max_value < UINT32_MAX) { + /* calling now triggers checkpointing */ + uint32_t now = ztimer_now(clock); + + if (clock->list.next) { + uint32_t target = clock->list.offset + clock->list.next->offset; + int32_t diff = (int32_t)(target - now); + if (diff > 0) { + DEBUG("ztimer_handler(): %p postponing by %"PRIi32"\n", (void *)clock, diff); + clock->ops->set(clock, _min_u32(diff, clock->max_value >> 1)); + return; + } + else { + DEBUG("ztimer_handler(): %p diff=%"PRIi32"\n", (void *)clock, diff); + } + } + else { + DEBUG("ztimer_handler(): %p intermediate\n", (void *)clock); + clock->ops->set(clock, clock->max_value >> 1); + return; + } + } + else { + DEBUG("ztimer_handler(): no checkpointing\n"); + } +#endif + + clock->list.offset += clock->list.next->offset; + clock->list.next->offset = 0; + + ztimer_t *entry = _now_next(clock); + while (entry) { + DEBUG("ztimer_handler(): trigger %p->%p at %"PRIu32"\n", + (void *)entry, (void *)entry->base.next, clock->ops->now(clock)); + entry->callback(entry->arg); + entry = _now_next(clock); + if (!entry) { + /* See if any more alarms expired during callback processing */ + /* This reduces the number of implicit calls to clock->ops->now() */ + ztimer_update_head_offset(clock); + entry = _now_next(clock); + } + } + + _ztimer_update(clock); + + if (ENABLE_DEBUG) { + _ztimer_print(clock); + } + DEBUG("ztimer_handler(): %p done.\n", (void *)clock); + if (!irq_is_in()) { + thread_yield_higher(); + } +} + +static void _ztimer_print(const ztimer_clock_t *clock) +{ + const ztimer_base_t *entry = &clock->list; + uint32_t last_offset = 0; + do { + printf("0x%08x:%" PRIu32 "(%" PRIu32 ")%s", (unsigned)entry, entry->offset, entry->offset + + last_offset, entry->next ? "->" : (entry==clock->last ? "" : "!")); + last_offset += entry->offset; + + } while ((entry = entry->next)); + puts(""); +} diff --git a/sys/ztimer/mock.c b/sys/ztimer/mock.c new file mode 100644 index 0000000000..a79e8a7804 --- /dev/null +++ b/sys/ztimer/mock.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2018 Eistec AB + * 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer_mock + * + * @{ + * + * @file + * @brief ztimer mock implementation + * + * @author Joakim Nohlgård + * @} + */ + +#include +#include +#include "ztimer/mock.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* Functions for controlling the mock clock below */ + +void ztimer_mock_advance(ztimer_mock_t *self, uint32_t val) +{ + DEBUG("zmock_advance: start now=0x%08" PRIx32 " + 0x%08" PRIx32 ", target=0x%08" PRIx32 " (%u)\n", + self->now, val, self->target, self->armed); + while (val) { + uint32_t step = self->armed ? (self->target < val ? self->target : val) : val; + DEBUG("zmock_advance: step now=0x%08" PRIx32 " + 0x%08" PRIx32 ", target=0x%08" PRIx32 " (%u)\n", + self->now, step, self->target, self->armed); + + self->now = (self->now + step) & self->mask; + if (self->armed) { + /* Update target */ + if (step >= self->target) { + /* Target was hit */ + ztimer_mock_fire(self); + } + else { + self->target -= step; + } + } + val -= step; + } + DEBUG("zmock_advance: done now=0x%08" PRIx32 " + 0x%08" PRIx32 ", target=0x%08" PRIx32 " (%u)\n", + self->now, val, self->target, self->armed); +} + +void ztimer_mock_jump(ztimer_mock_t *self, uint32_t target) +{ + self->now = target & self->mask; + DEBUG("zmock_jump: now=0x%08" PRIx32 ", target=0x%08" PRIx32 " (%u)\n", + self->now, self->target, self->armed); + /* Do not touch target */ +} + +void ztimer_mock_fire(ztimer_mock_t *self) +{ + DEBUG("zmock_fire: now=0x%08" PRIx32 ", target=0x%08" PRIx32 " (%u)\n", + self->now, self->target, self->armed); + self->target = 0; + self->armed = 0; + /* Execute ztimer core interrupt handler */ + ztimer_handler(&self->super); +} + +/* Implementations for the standard ztimer operations below */ + +static void ztimer_mock_op_set(ztimer_clock_t *clock, uint32_t val) +{ + ztimer_mock_t *self = (ztimer_mock_t*)clock; + ++self->calls.set; + self->target = val & self->mask; + self->armed = 1; + DEBUG("zmock_set: %3u now=0x%08" PRIx32 ", target=0x%08" PRIx32 " (%u)\n", + self->calls.set, self->now, self->target, self->armed); +} + +static uint32_t ztimer_mock_op_now(ztimer_clock_t *clock) +{ + ztimer_mock_t *self = (ztimer_mock_t*)clock; + ++self->calls.now; + DEBUG("zmock_now: %3u now=0x%08" PRIx32 ", target=0x%08" PRIx32 " (%u)\n", + self->calls.now, self->now, self->target, self->armed); + return self->now; +} + +static void ztimer_mock_op_cancel(ztimer_clock_t *clock) +{ + ztimer_mock_t *self = (ztimer_mock_t*)clock; + ++self->calls.cancel; + DEBUG("zmock_cancel: %3u now=0x%08" PRIx32 ", target=0x%08" PRIx32 " (%u)\n", + self->calls.cancel, self->now, self->target, self->armed); + self->armed = 0; +} + +static const ztimer_ops_t ztimer_mock_ops = { + .set = ztimer_mock_op_set, + .now = ztimer_mock_op_now, + .cancel = ztimer_mock_op_cancel, +}; + +void ztimer_mock_init(ztimer_mock_t *self, unsigned width) +{ + uint32_t max_value = (~((uint32_t)0ul)) >> (32 - width); + *self = (ztimer_mock_t){ + .mask = max_value, + .super = { .ops = &ztimer_mock_ops, .max_value = max_value }, + }; + DEBUG("zmock_init: %p width=%u mask=0x%08" PRIx32 "\n", (void *)self, width, self->mask); + if (max_value < UINT32_MAX) { + self->super.ops->set(&self->super, self->super.max_value >> 1); + } +} diff --git a/sys/ztimer/overhead.c b/sys/ztimer/overhead.c new file mode 100644 index 0000000000..6502f49cc3 --- /dev/null +++ b/sys/ztimer/overhead.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer_overhead + * @{ + * + * @file + * @brief ztimer overhead measurement functions + * + * @author Kaspar Schleiser + * + * @} + */ + +#include "ztimer.h" +#include "ztimer/overhead.h" + +typedef struct { + ztimer_clock_t *clock; + volatile uint32_t *val; +} callback_arg_t; + +static void _callback(void *arg) +{ + callback_arg_t *callback_arg = (callback_arg_t *)arg; + + *callback_arg->val = ztimer_now(callback_arg->clock); +} + +uint32_t ztimer_overhead(ztimer_clock_t *clock, uint32_t base) +{ + volatile uint32_t after = 0; + uint32_t pre; + + callback_arg_t arg = { .clock = clock, .val = &after }; + ztimer_t t = { .callback = _callback, .arg = &arg }; + + pre = ztimer_now(clock); + ztimer_set(clock, &t, base); + while (!after) {} + return after - pre - base; +} diff --git a/sys/ztimer/periph_rtc.c b/sys/ztimer/periph_rtc.c new file mode 100644 index 0000000000..55053598f1 --- /dev/null +++ b/sys/ztimer/periph_rtc.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer_periph_rtc + * @{ + * + * @file + * @brief ztimer periph/rtc backend implementation + * + * This implementation simply converts an integer time to split RTC values and + * back, which is rather inefficient. If available, use ztimer_periph_rtt. + * + * @author Kaspar Schleiser + * + * @} + */ +#include "periph/rtc.h" +#include "ztimer/periph_rtc.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* this algorithm and the one from _timestamp_to_gmt_civil() have been adapted from + * http://ptspts.blogspot.com/2009/11/how-to-convert-unix-timestamp-to-civil.html. + * + * "The algorithmic solution above is part of the programming folklore." + */ +static uint32_t _gmt_civil_to_timestamp(unsigned y, unsigned m, unsigned d, + unsigned h, unsigned mi, unsigned s) +{ + if (m <= 2) { + y -= 1; + m += 12; + } + return (365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - + 719561) * 86400 + 3600 * h + 60 * mi + s; +} + +void _timestamp_to_gmt_civil(struct tm *_tm, uint32_t epoch) +{ + uint32_t s = epoch % 86400; + + epoch /= 86400; + uint32_t h = s / 3600; + uint32_t m = s / 60 % 60; + s = s % 60; + uint32_t x = (epoch * 4 + 102032) / 146097 + 15; + uint32_t b = epoch + 2442113 + x - (x / 4); + uint32_t c = (b * 20 - 2442) / 7305; + uint32_t d = b - 365 * c - c / 4; + uint32_t e = d * 1000 / 30601; + uint32_t f = d - e * 30 - e * 601 / 1000; + + if (e < 14) { + struct tm tmp = + { .tm_year = c - 4716 - 1900, .tm_mon = e - 1, .tm_mday = f, + .tm_hour = h, .tm_min = m, .tm_sec = s }; + *_tm = tmp; + } + else { + struct tm tmp = + { .tm_year = c - 4715 - 1900, .tm_mon = e - 13, .tm_mday = f, + .tm_hour = h, .tm_min = m, .tm_sec = s }; + *_tm = tmp; + } +} + +static void _ztimer_periph_rtc_callback(void *arg) +{ + ztimer_handler((ztimer_clock_t *)arg); +} + +static uint32_t _ztimer_periph_rtc_now(ztimer_clock_t *clock) +{ + (void)clock; + + struct tm time; + rtc_get_time(&time); + + return _gmt_civil_to_timestamp(time.tm_year + 1900, time.tm_mon, + time.tm_mday, time.tm_hour, time.tm_min, + time.tm_sec); +} + +static void _ztimer_periph_rtc_set(ztimer_clock_t *clock, uint32_t val) +{ + unsigned state = irq_disable(); + + uint32_t now = _ztimer_periph_rtc_now(NULL); + uint32_t target; + + do { + /* make sure there's no pending ISR */ + rtc_clear_alarm(); + + target = now + val; + + struct tm _tm; + _timestamp_to_gmt_civil(&_tm, target); + + /* TODO: ensure this doesn't underflow */ + rtc_set_alarm(&_tm, _ztimer_periph_rtc_callback, clock); + + if (val > 1) { + /* If val <= 1, it is possible that the RTC second flips somewhere + * between getting the current value and adding 1, resulting in + * setting the current time as target, which in turn would make the + * RTC never trigger. In that case, check the target that as been + * set is still in the future at the end of the loop body. + * + * Skip that if val was more than a second away. + */ + break; + } + } while (target <= (now = _ztimer_periph_rtc_now(NULL))); + + irq_restore(state); +} + +static void _ztimer_periph_rtc_cancel(ztimer_clock_t *clock) +{ + (void)clock; + rtc_clear_alarm(); +} + +static const ztimer_ops_t _ztimer_periph_rtc_ops = { + .set = _ztimer_periph_rtc_set, + .now = _ztimer_periph_rtc_now, + .cancel = _ztimer_periph_rtc_cancel, +}; + +void ztimer_periph_rtc_init(ztimer_periph_rtc_t *clock) +{ + clock->ops = &_ztimer_periph_rtc_ops; + clock->max_value = UINT32_MAX; + rtc_init(); + rtc_poweron(); +} diff --git a/sys/ztimer/periph_rtt.c b/sys/ztimer/periph_rtt.c new file mode 100644 index 0000000000..0d1c79914c --- /dev/null +++ b/sys/ztimer/periph_rtt.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer_periph_rtt + * @{ + * + * @file + * @brief ztimer periph/rtt implementation + * + * @author Kaspar Schleiser + * + * @} + */ +#include "periph/rtt.h" +#include "ztimer/periph_rtt.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#ifndef RTT_MIN_VALUE +#define RTT_MIN_VALUE (2U) +#endif + +static void _ztimer_periph_rtt_callback(void *arg) +{ + ztimer_handler((ztimer_clock_t *)arg); +} + +static void _ztimer_periph_rtt_set(ztimer_clock_t *clock, uint32_t val) +{ + if (val < RTT_MIN_VALUE) { + /* the rtt might advance right between the call to rtt_get_counter() + * and rtt_set_alarm(). If that happens with val==1, we'd set an alarm + * to the current time, which would then underflow. To avoid this, we + * set the alarm at least two ticks in the future. TODO: confirm this + * is sufficient, or conceive logic to lower this value. + * + * @note RTT_MIN_VALUE defaults to 2, but some platforms might have + * different values. + */ + val = RTT_MIN_VALUE; + } + + unsigned state = irq_disable(); + rtt_set_alarm(rtt_get_counter() + val, _ztimer_periph_rtt_callback, clock); + irq_restore(state); +} + +static uint32_t _ztimer_periph_rtt_now(ztimer_clock_t *clock) +{ + (void)clock; + return rtt_get_counter(); +} + +static void _ztimer_periph_rtt_cancel(ztimer_clock_t *clock) +{ + (void)clock; + rtt_clear_alarm(); +} + +static const ztimer_ops_t _ztimer_periph_rtt_ops = { + .set = _ztimer_periph_rtt_set, + .now = _ztimer_periph_rtt_now, + .cancel = _ztimer_periph_rtt_cancel, +}; + +void ztimer_periph_rtt_init(ztimer_periph_rtt_t *clock) +{ + clock->ops = &_ztimer_periph_rtt_ops; + clock->max_value = RTT_MAX_VALUE; + rtt_init(); + rtt_poweron(); +} diff --git a/sys/ztimer/periph_timer.c b/sys/ztimer/periph_timer.c new file mode 100644 index 0000000000..be789a12eb --- /dev/null +++ b/sys/ztimer/periph_timer.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Freie Universität Berlin + * 2020 Inria + * + * 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 sys_ztimer_periph_timer + * @{ + * + * @file + * @brief ztimer periph/timer backend implementation + * + * @author Kaspar Schleiser + * + * @} + */ + +#include "irq.h" +#include "ztimer/periph_timer.h" + +static void _ztimer_periph_timer_set(ztimer_clock_t *clock, uint32_t val) +{ + ztimer_periph_timer_t *ztimer_periph = (ztimer_periph_timer_t *)clock; + + uint16_t min = ztimer_periph->min; + + if (val < min) { + val = min; + } + +/* if this is undefined, timer_set() from drivers/periph_timer_common is used. + * That already dieables irq's. + * For the others, better ensure that happens. + */ +#ifdef PERIPH_TIMER_PROVIDES_SET + unsigned state = irq_disable(); +#endif + timer_set(ztimer_periph->dev, 0, val); +#ifdef PERIPH_TIMER_PROVIDES_SET + irq_restore(state); +#endif +} + +static uint32_t _ztimer_periph_timer_now(ztimer_clock_t *clock) +{ + ztimer_periph_timer_t *ztimer_periph = (ztimer_periph_timer_t *)clock; + + return timer_read(ztimer_periph->dev); +} + +static void _ztimer_periph_timer_cancel(ztimer_clock_t *clock) +{ + ztimer_periph_timer_t *ztimer_periph = (ztimer_periph_timer_t *)clock; + + timer_clear(ztimer_periph->dev, 0); +} + +static void _ztimer_periph_timer_callback(void *arg, int channel) +{ + (void)channel; + ztimer_handler((ztimer_clock_t *)arg); +} + +static const ztimer_ops_t _ztimer_periph_timer_ops = { + .set = _ztimer_periph_timer_set, + .now = _ztimer_periph_timer_now, + .cancel = _ztimer_periph_timer_cancel, +}; + +void ztimer_periph_timer_init(ztimer_periph_timer_t *clock, tim_t dev, unsigned long freq, + uint32_t max_val) +{ + clock->dev = dev; + clock->super.ops = &_ztimer_periph_timer_ops; + clock->super.max_value = max_val; + timer_init(dev, freq, _ztimer_periph_timer_callback, clock); +} diff --git a/sys/ztimer/util.c b/sys/ztimer/util.c new file mode 100644 index 0000000000..21a8f21320 --- /dev/null +++ b/sys/ztimer/util.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2020 Kaspar Schleiser + * 2020 Inria + * 2020 Freie Universität Berlin + * + * 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 sys_ztimer_util ztimer utility functions + * @ingroup sys_ztimer + * @{ + * + * @file + * @brief ztimer high-level utility function implementations + * + * @author Kaspar Schleiser + * + * @} + */ +#include +#include + +#include "irq.h" +#include "mutex.h" +#include "thread.h" +#include "ztimer.h" + +typedef struct { + mutex_t *mutex; + thread_t *thread; + int timeout; +} mutex_thread_t; + +static void _callback_unlock_mutex(void* arg) +{ + mutex_t *mutex = (mutex_t *) arg; + mutex_unlock(mutex); +} + +void ztimer_sleep(ztimer_clock_t *clock, uint32_t duration) +{ + assert(!irq_is_in()); + mutex_t mutex = MUTEX_INIT_LOCKED; + + ztimer_t timer = { + .callback = _callback_unlock_mutex, + .arg = (void*) &mutex, + }; + + ztimer_set(clock, &timer, duration); + mutex_lock(&mutex); +} + +void ztimer_periodic_wakeup(ztimer_clock_t *clock, ztimer_now_t *last_wakeup, uint32_t period) +{ + unsigned state = irq_disable(); + ztimer_now_t now = ztimer_now(clock); + ztimer_now_t target = *last_wakeup + period; + ztimer_now_t offset = target - now; + irq_restore(state); + + if (offset <= period) { + ztimer_sleep(clock, offset); + *last_wakeup = target; + } + else { + *last_wakeup = now; + } +} + +#ifdef MODULE_CORE_MSG +static void _callback_msg(void* arg) +{ + msg_t *msg = (msg_t*)arg; + msg_send_int(msg, msg->sender_pid); +} + +static inline void _setup_msg(ztimer_t *timer, msg_t *msg, kernel_pid_t target_pid) +{ + timer->callback = _callback_msg; + timer->arg = (void*) msg; + + /* use sender_pid field to get target_pid into callback function */ + msg->sender_pid = target_pid; +} + +void ztimer_set_msg(ztimer_clock_t *clock, ztimer_t *timer, uint32_t offset, msg_t *msg, kernel_pid_t target_pid) +{ + _setup_msg(timer, msg, target_pid); + ztimer_set(clock, timer, offset); +} + +int ztimer_msg_receive_timeout(ztimer_clock_t *clock, msg_t *msg, uint32_t timeout) +{ + if (msg_try_receive(msg) == 1) { + return 1; + } + + ztimer_t t; + msg_t m = { .type=MSG_ZTIMER, .content.ptr=&m }; + + ztimer_set_msg(clock, &t, timeout, &m, sched_active_pid); + + msg_receive(msg); + ztimer_remove(clock, &t); + if (msg->type == MSG_ZTIMER && msg->content.ptr == &m) { + /* we hit the timeout */ + return -ETIME; + } + else { + return 1; + } +} + +#endif /* MODULE_CORE_MSG */ + +#ifdef MODULE_CORE_THREAD_FLAGS +static void _set_timeout_flag_callback(void* arg) +{ + thread_flags_set(arg, THREAD_FLAG_TIMEOUT); +} + +void ztimer_set_timeout_flag(ztimer_clock_t *clock, ztimer_t *t, uint32_t timeout) +{ + t->callback = _set_timeout_flag_callback; + t->arg = (thread_t *)sched_active_thread; + thread_flags_clear(THREAD_FLAG_TIMEOUT); + ztimer_set(clock, t, timeout); +} +#endif + +static void _callback_wakeup(void *arg) +{ + thread_wakeup((kernel_pid_t)((intptr_t)arg)); +} + +void ztimer_set_wakeup(ztimer_clock_t *clock, ztimer_t *timer, uint32_t offset, + kernel_pid_t pid) +{ + ztimer_remove(clock, timer); + + timer->callback = _callback_wakeup; + timer->arg = (void *)((intptr_t)pid); + + ztimer_set(clock, timer, offset); +}