diff --git a/CODEOWNERS b/CODEOWNERS index 7f5a8a4ed0..dbb1f6457d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -117,6 +117,10 @@ /sys/usb/ @bergzand @dylad @aabadie /sys/xtimer/ @kaspar030 @MichelRottleuthner +/sys/include/ztimer.h @kaspar030 @bergzand +/sys/include/ztimer/ @kaspar030 @bergzand +/sys/ztimer/ @kaspar030 @bergzand + /tests/ @smlng @leandrolanzieri @aabadie @MichelRottleuthner @fjmolinas /tests/emb6* @miri64 /tests/gnrc* @miri64 diff --git a/Makefile.dep b/Makefile.dep index 0a15db0046..ddf96394bd 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -683,12 +683,6 @@ ifneq (,$(filter arduino_pwm,$(FEATURES_USED))) FEATURES_REQUIRED += periph_pwm endif -ifneq (,$(filter xtimer,$(USEMODULE))) - DEFAULT_MODULE += auto_init_xtimer - FEATURES_REQUIRED += periph_timer - USEMODULE += div -endif - ifneq (,$(filter saul,$(USEMODULE))) USEMODULE += phydat endif @@ -1021,6 +1015,28 @@ ifneq (,$(filter periph_uart_nonblocking,$(USEMODULE))) FEATURES_REQUIRED += periph_uart endif +# include ztimer dependencies +ifneq (,$(filter ztimer%,$(USEMODULE))) + include $(RIOTBASE)/sys/ztimer/Makefile.dep +endif + +# handle xtimer's deps. Needs to be done *after* ztimer +ifneq (,$(filter xtimer,$(USEMODULE))) + ifeq (,$(filter ztimer_xtimer_compat,$(USEMODULE))) + # xtimer is used, ztimer xtimer wrapper is not + DEFAULT_MODULE += auto_init_xtimer + USEMODULE += div + ifeq (,$(filter xtimer_on_ztimer,$(USEMODULE))) + # ztimer is not used, so use *periph_timer as low-level timer*. + FEATURES_REQUIRED += periph_timer + else + # will use *ztimer_usec as low-level timer* + endif + else + # ztimer_xtimer_compat is used, all of *xtimer's API will be mapped on ztimer.* + endif +endif + # Enable periph_gpio when periph_gpio_irq is enabled ifneq (,$(filter periph_gpio_irq,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio diff --git a/boards/common/arduino-atmega/include/board_common.h b/boards/common/arduino-atmega/include/board_common.h index b9b2c87f00..458145e893 100644 --- a/boards/common/arduino-atmega/include/board_common.h +++ b/boards/common/arduino-atmega/include/board_common.h @@ -105,6 +105,16 @@ extern "C" { #define XTIMER_BACKOFF (40) /** @} */ +/** + * @name ztimer configuration values + * @{ + */ +#define CONFIG_ZTIMER_USEC_TYPE ZTIMER_TYPE_PERIPH_TIMER +#define CONFIG_ZTIMER_USEC_DEV (TIMER_DEV(0)) +#define CONFIG_ZTIMER_USEC_FREQ (250000LU) +#define CONFIG_ZTIMER_USEC_WIDTH (16) +/** @} */ + /** * @name Configuration parameters for the W5100 driver * @{ diff --git a/boards/ek-lm4f120xl/include/board.h b/boards/ek-lm4f120xl/include/board.h index adad110abe..7b7578f3ec 100644 --- a/boards/ek-lm4f120xl/include/board.h +++ b/boards/ek-lm4f120xl/include/board.h @@ -64,6 +64,15 @@ extern "C" { #define LED2_TOGGLE (LED_PORT ^= LED2_MASK) /* @} */ +/** + * @name ztimer configuration + * @{ + */ +#define CONFIG_ZTIMER_USEC_TYPE ZTIMER_TYPE_PERIPH_TIMER +#define CONFIG_ZTIMER_USEC_DEV TIMER_DEV(0) +#define CONFIG_ZTIMER_USEC_MIN (8) +/** @} */ + /** * @brief Initialize board specific hardware, including clock, LEDs and std-IO */ diff --git a/boards/frdm-kw41z/include/board.h b/boards/frdm-kw41z/include/board.h index b905078f88..b844b4d82c 100644 --- a/boards/frdm-kw41z/include/board.h +++ b/boards/frdm-kw41z/include/board.h @@ -89,6 +89,14 @@ extern "C" #endif /** @} */ +/** + * @name ztimer configuration + * @{ + */ +#define CONFIG_ZTIMER_USEC_TYPE ZTIMER_TYPE_PERIPH_TIMER +#define CONFIG_ZTIMER_USEC_DEV (TIMER_PIT_DEV(0)) +/** @} */ + /** * @name NOR flash hardware configuration * @{ diff --git a/boards/native/include/board.h b/boards/native/include/board.h index d4cf336c5a..9a36e3b4c9 100644 --- a/boards/native/include/board.h +++ b/boards/native/include/board.h @@ -183,6 +183,16 @@ static const motor_driver_config_t motor_driver_config[] = { #define MOTOR_DRIVER_NUMOF ARRAY_SIZE(motor_driver_config) /** @} */ +/** + * @name ztimer configuration + * @{ + */ +#define CONFIG_ZTIMER_USEC_TYPE ZTIMER_TYPE_PERIPH_TIMER +#define CONFIG_ZTIMER_USEC_DEV TIMER_DEV(0) +/* on native, anything can happen... */ +#define CONFIG_ZTIMER_USEC_MIN (64) +/** @} */ + #endif /* __cplusplus */ #ifdef __cplusplus diff --git a/boards/phynode-kw41z/include/board.h b/boards/phynode-kw41z/include/board.h index 80beb71fc6..ae9932a784 100644 --- a/boards/phynode-kw41z/include/board.h +++ b/boards/phynode-kw41z/include/board.h @@ -104,6 +104,14 @@ extern "C" #endif /** @} */ +/** + * @name ztimer configuration + * @{ + */ +#define CONFIG_ZTIMER_USEC_TYPE ZTIMER_TYPE_PERIPH_TIMER +#define CONFIG_ZTIMER_USEC_DEV (TIMER_PIT_DEV(0)) +/** @} */ + /** * @name Define the interface for the CCS811 gas sensors * @{ diff --git a/boards/samr21-xpro/include/board.h b/boards/samr21-xpro/include/board.h index 7708c65332..fdc7271497 100644 --- a/boards/samr21-xpro/include/board.h +++ b/boards/samr21-xpro/include/board.h @@ -37,6 +37,15 @@ extern "C" { #define XTIMER_CHAN (0) /** @} */ +/** + * @name ztimer configuration + * @{ + */ +#define CONFIG_ZTIMER_USEC_TYPE ZTIMER_TYPE_PERIPH_TIMER +#define CONFIG_ZTIMER_USEC_DEV TIMER_DEV(1) +#define CONFIG_ZTIMER_USEC_MIN (8) +/** @} */ + /** * @name AT86RF233 configuration * diff --git a/boards/usb-kw41z/include/board.h b/boards/usb-kw41z/include/board.h index d3e2170f2f..e08553dd92 100644 --- a/boards/usb-kw41z/include/board.h +++ b/boards/usb-kw41z/include/board.h @@ -100,6 +100,16 @@ extern "C" #endif /** @} */ +/** + * @name ztimer configuration + * @{ + */ +#define CONFIG_ZTIMER_USEC_TYPE ZTIMER_TYPE_PERIPH_TIMER +#define CONFIG_ZTIMER_USEC_DEV (TIMER_PIT_DEV(0)) +#define CONFIG_ZTIMER_USEC_FREQ (1000000LU) +#define CONFIG_ZTIMER_USEC_WIDTH (32) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 362b54a1a2..2964ac030a 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -96,6 +96,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/Makefile b/sys/Makefile index 984ec1aa83..0321b81dec 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -76,8 +76,8 @@ endif ifneq (,$(filter log_%,$(USEMODULE))) DIRS += log endif -ifneq (,$(filter xtimer,$(USEMODULE))) - DIRS += xtimer +ifneq (,$(filter ztimer_xtimer_compat,$(USEMODULE))) + FILTER += xtimer endif ifneq (,$(filter cpp11-compat,$(USEMODULE))) DIRS += cpp11-compat @@ -163,7 +163,10 @@ endif ifneq (,$(filter netif,$(USEMODULE))) DIRS += net/netif endif +ifneq (,$(filter ztimer_core,$(USEMODULE))) + DIRS += ztimer +endif -DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(USEMODULE)))) +DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(filter-out $(FILTER), $(USEMODULE))))) include $(RIOTBASE)/Makefile.base diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 92c865b063..315ffac856 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -33,7 +33,13 @@ void auto_init(void) extern void auto_init_random(void); auto_init_random(); } - if (IS_USED(MODULE_AUTO_INIT_XTIMER)) { + if (IS_USED(MODULE_AUTO_INIT_ZTIMER)) { + LOG_DEBUG("Auto init ztimer.\n"); + void ztimer_init(void); + ztimer_init(); + } + if (IS_USED(MODULE_AUTO_INIT_XTIMER) && + !IS_USED(MODULE_ZTIMER_XTIMER_COMPAT)) { LOG_DEBUG("Auto init xtimer.\n"); extern void xtimer_init(void); xtimer_init(); diff --git a/sys/include/xtimer.h b/sys/include/xtimer.h index 38c1040e0d..bd1e1c50a3 100644 --- a/sys/include/xtimer.h +++ b/sys/include/xtimer.h @@ -38,8 +38,14 @@ #include "mutex.h" #include "kernel_types.h" +#ifdef MODULE_ZTIMER_XTIMER_COMPAT +#include "ztimer/xtimer_compat.h" +#else + +#ifndef MODULE_XTIMER_ON_ZTIMER #include "board.h" #include "periph_conf.h" +#endif #ifdef __cplusplus extern "C" { @@ -617,5 +623,7 @@ static inline int xtimer_msg_receive_timeout64(msg_t *msg, uint64_t timeout); } #endif +#endif /* MODULE_XTIMER_ON_ZTIMER */ + /** @} */ #endif /* XTIMER_H */ diff --git a/sys/include/xtimer/implementation.h b/sys/include/xtimer/implementation.h index cc61ada24a..78ac653adb 100644 --- a/sys/include/xtimer/implementation.h +++ b/sys/include/xtimer/implementation.h @@ -27,9 +27,15 @@ #error "Do not include this file directly! Use xtimer.h instead" #endif +#ifdef MODULE_XTIMER_ON_ZTIMER +#include "ztimer.h" +#else #include "periph/timer.h" +#endif + #include "irq.h" + #ifdef __cplusplus extern "C" { #endif @@ -46,7 +52,12 @@ extern volatile uint64_t _xtimer_current_time; */ static inline uint32_t _xtimer_lltimer_now(void) { +#ifndef MODULE_XTIMER_ON_ZTIMER return timer_read(XTIMER_DEV); +#else + return ztimer_now(ZTIMER_USEC); +#endif + } /** 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/include/ztimer/xtimer_compat.h b/sys/include/ztimer/xtimer_compat.h new file mode 100644 index 0000000000..8751fac4da --- /dev/null +++ b/sys/include/ztimer/xtimer_compat.h @@ -0,0 +1,178 @@ +/* + * 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. + */ + +/** + * @ingroup sys_ztimer_util + * @{ + * @file + * @brief ztimer xtimer wrapper interface + * + * Please check out xtimer's documentation for usage. + * + * @author Kaspar Schleiser + */ +#ifndef ZTIMER_XTIMER_COMPAT_H +#define ZTIMER_XTIMER_COMPAT_H + +#include +#include + +#include "div.h" +#include "timex.h" +#ifdef MODULE_CORE_MSG +#include "msg.h" +#endif /* MODULE_CORE_MSG */ +#include "mutex.h" +#include "kernel_types.h" + +#include "ztimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* the xtimer API is documented elsewhere. This is just an (incomplete) wrapper, + * so skip doxygen. + */ +#ifndef DOXYGEN + +typedef ztimer_t xtimer_t; +typedef uint32_t xtimer_ticks32_t; +typedef uint64_t xtimer_ticks64_t; + +static inline xtimer_ticks32_t xtimer_now(void) +{ + return ztimer_now(ZTIMER_USEC); +} + +static inline xtimer_ticks64_t xtimer_now64(void) +{ + return ztimer_now(ZTIMER_USEC); +} + +/*static void xtimer_now_timex(timex_t *out) { + }*/ + +static inline uint32_t xtimer_now_usec(void) +{ + return ztimer_now(ZTIMER_USEC); +} + +static inline uint64_t xtimer_now_usec64(void) +{ + return ztimer_now(ZTIMER_USEC); +} + +static inline void xtimer_sleep(uint32_t seconds) +{ + /* TODO: use ZTIMER_SEC */ + ztimer_sleep(ZTIMER_USEC, seconds * 1000000LU); +} + +static inline void xtimer_usleep(uint32_t microseconds) +{ + ztimer_sleep(ZTIMER_USEC, microseconds); +} + +static inline void xtimer_nanosleep(uint32_t nanoseconds) +{ + ztimer_sleep(ZTIMER_USEC, nanoseconds / NS_PER_US); +} + +static inline void xtimer_set(xtimer_t *timer, uint32_t offset) +{ + ztimer_set(ZTIMER_USEC, timer, offset); +} + +static inline void xtimer_remove(xtimer_t *timer) +{ + ztimer_remove(ZTIMER_USEC, timer); +} + +static inline void xtimer_set_msg(xtimer_t *timer, uint32_t offset, msg_t *msg, + kernel_pid_t target_pid) +{ + ztimer_set_msg(ZTIMER_USEC, timer, offset, msg, target_pid); +} + +static inline void xtimer_periodic_wakeup(xtimer_ticks32_t *last_wakeup, + uint32_t period) +{ + ztimer_periodic_wakeup(ZTIMER_USEC, last_wakeup, period); +} + +static inline uint32_t xtimer_usec_from_ticks(xtimer_ticks32_t ticks) +{ + return ticks; +} + +static inline xtimer_ticks32_t xtimer_ticks_from_usec(uint32_t usec) +{ + return usec; +} + +static inline void xtimer_now_timex(timex_t *out) +{ + uint64_t now = xtimer_now_usec64(); + + out->seconds = div_u64_by_1000000(now); + out->microseconds = now - (out->seconds * US_PER_SEC); +} + +static inline int xtimer_msg_receive_timeout(msg_t *msg, uint32_t timeout) +{ + return ztimer_msg_receive_timeout(ZTIMER_USEC, msg, timeout); +} + +static inline void xtimer_set_wakeup(xtimer_t *timer, uint32_t offset, + kernel_pid_t pid) +{ + ztimer_set_wakeup(ZTIMER_USEC, timer, offset, pid); +} + +/* + static inline void xtimer_set64(xtimer_t *timer, uint64_t offset_us); + static inline void xtimer_tsleep32(xtimer_ticks32_t ticks); + static inline void xtimer_tsleep64(xtimer_ticks64_t ticks); + static inline void xtimer_spin(xtimer_ticks32_t ticks); + static inline void xtimer_periodic_wakeup(xtimer_ticks32_t *last_wakeup, + uint32_t period); + static inline void xtimer_set_wakeup64(xtimer_t *timer, uint64_t offset, + kernel_pid_t pid); + static inline xtimer_ticks32_t xtimer_ticks_from_usec(uint32_t usec); + static inline xtimer_ticks64_t xtimer_ticks_from_usec64(uint64_t usec); + static inline uint32_t xtimer_usec_from_ticks(xtimer_ticks32_t ticks); + static inline uint64_t xtimer_usec_from_ticks64(xtimer_ticks64_t ticks); + static inline xtimer_ticks32_t xtimer_ticks(uint32_t ticks); + static inline xtimer_ticks64_t xtimer_ticks64(uint64_t ticks); + static inline xtimer_ticks32_t xtimer_diff(xtimer_ticks32_t a, + xtimer_ticks32_t b); + static inline xtimer_ticks64_t xtimer_diff64(xtimer_ticks64_t a, + xtimer_ticks64_t b); + static inline xtimer_ticks32_t xtimer_diff32_64(xtimer_ticks64_t a, + xtimer_ticks64_t b); + static inline bool xtimer_less(xtimer_ticks32_t a, xtimer_ticks32_t b); + static inline bool xtimer_less64(xtimer_ticks64_t a, xtimer_ticks64_t b); + int xtimer_mutex_lock_timeout(mutex_t *mutex, uint64_t us); + void xtimer_set_timeout_flag(xtimer_t *t, uint32_t timeout); + + #if defined(MODULE_CORE_MSG) || defined(DOXYGEN) + static inline void xtimer_set_msg64(xtimer_t *timer, uint64_t offset, + msg_t *msg, kernel_pid_t target_pid); + static inline int xtimer_msg_receive_timeout64(msg_t *msg, uint64_t timeout); + #endif + */ + +#endif /* DOXYGEN */ + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* ZTIMER_XTIMER_COMPAT_H */ diff --git a/sys/xtimer/xtimer_core.c b/sys/xtimer/xtimer_core.c index 58e8acc1c3..cdc34ffcac 100644 --- a/sys/xtimer/xtimer_core.c +++ b/sys/xtimer/xtimer_core.c @@ -24,9 +24,12 @@ #include #include + +#ifndef MODULE_XTIMER_ON_ZTIMER #include "board.h" #include "periph/timer.h" #include "periph_conf.h" +#endif #include "xtimer.h" #include "irq.h" @@ -50,12 +53,20 @@ static inline void _update_long_timers(uint64_t *now); static inline void _schedule_earliest_lltimer(uint32_t now); static void _timer_callback(void); + +#ifndef MODULE_XTIMER_ON_ZTIMER static void _periph_timer_callback(void *arg, int chan); +#else +static void _ztimer_callback(void *arg); +static ztimer_t _ztimer = { .callback=_ztimer_callback }; +#endif void xtimer_init(void) { +#ifndef MODULE_XTIMER_ON_ZTIMER /* initialize low-level timer */ timer_init(XTIMER_DEV, XTIMER_HZ, _periph_timer_callback, NULL); +#endif /* register initial overflow tick */ _schedule_earliest_lltimer(_xtimer_now()); @@ -107,12 +118,20 @@ void _xtimer_set64(xtimer_t *timer, uint32_t offset, uint32_t long_offset) irq_restore(state); } +#ifndef MODULE_XTIMER_ON_ZTIMER static void _periph_timer_callback(void *arg, int chan) { (void)arg; (void)chan; _timer_callback(); } +#else +static void _ztimer_callback(void *arg) +{ + (void)arg; + _timer_callback(); +} +#endif static void _shoot(xtimer_t *timer) { @@ -141,7 +160,11 @@ static inline void _schedule_earliest_lltimer(uint32_t now) } DEBUG("_schedule_earliest_lltimer(): setting %" PRIu32 "\n", _xtimer_lltimer_mask(target)); +#ifndef MODULE_XTIMER_ON_ZTIMER timer_set_absolute(XTIMER_DEV, XTIMER_CHAN, _xtimer_lltimer_mask(target)); +#else + ztimer_set(ZTIMER_USEC, &_ztimer, target - ztimer_now(ZTIMER_USEC)); +#endif _lltimer_ongoing = true; } 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/Makefile.dep b/sys/ztimer/Makefile.dep new file mode 100644 index 0000000000..1ccc74b298 --- /dev/null +++ b/sys/ztimer/Makefile.dep @@ -0,0 +1,77 @@ +# +# ztimer dependencies +# + +# "ztimer" is the default meta-module of ztimer +ifneq (,$(filter ztimer,$(USEMODULE))) + USEMODULE += ztimer_core + USEMODULE += ztimer_convert_frac + USEMODULE += ztimer_convert_shift + + # ztimer's auto_init code resides in it's submodule "ztimer_auto_init", + # but RIOT's auto_init scheme expects "auto_init_ztimer" in DEFAULT_MODULES so + # it can be disabled (by adding to DISABLE_MODULES). + # + # "auto_init_%" modules cannot have further dependencies, so we cannot do + # "if auto_init_ztimer: use ztimer_auto_init". + # + # So, if neither "auto_init" nor "auto_init_ztimer" are disabled, pull in + # "ztimer_auto_init". + DEFAULT_MODULE += auto_init_ztimer + ifeq (,$(filter auto_init auto_init_ztimer,$(DISABLE_MODULE))) + USEMODULE += ztimer_auto_init + endif +endif + +# unless ztimer_xtimer_compat is used, make xtimer use ztimer as backend. +ifneq (,$(filter ztimer,$(USEMODULE))) + ifneq (,$(filter xtimer,$(USEMODULE))) + ifeq (,$(filter ztimer_xtimer_compat,$(USEMODULE))) + USEMODULE += xtimer_on_ztimer + endif + endif +endif + +# make xtimer use ztimer_usec as low level timer +ifneq (,$(filter xtimer_on_ztimer,$(USEMODULE))) + USEMODULE += ztimer_usec + PSEUDOMODULES += xtimer_on_ztimer +endif + +# "ztimer_xtimer_compat" is a wrapper of the xtimer API on ztimer_used +# (it is currently incomplete). Unless doing testing, use "xtimer_on_ztimer". +ifneq (,$(filter ztimer_xtimer_compat,$(USEMODULE))) + USEMODULE += div + USEMODULE += ztimer_usec + PSEUDOMODULES += xtimer +endif + +ifneq (,$(filter ztimer_%,$(USEMODULE))) + USEMODULE += ztimer_core + USEMODULE += ztimer_extend +endif + +ifneq (,$(filter ztimer_convert_%,$(USEMODULE))) + USEMODULE += ztimer_convert +endif + +ifneq (,$(filter ztimer_periph_timer,$(USEMODULE))) + FEATURES_REQUIRED += periph_timer +endif + +ifneq (,$(filter ztimer_periph_rtt,$(USEMODULE))) + FEATURES_REQUIRED += periph_rtt +endif + +ifneq (,$(filter ztimer_convert_frac,$(USEMODULE))) + USEMODULE += frac +endif + +ifneq (,$(filter ztimer_usec,$(USEMODULE))) + USEMODULE += ztimer + USEMODULE += ztimer_periph_timer +endif + +ifneq (,$(filter ztimer_msec,$(USEMODULE))) + USEMODULE += ztimer +endif 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); +} diff --git a/sys/ztimer/xtimer2ztimer.cocci b/sys/ztimer/xtimer2ztimer.cocci new file mode 100644 index 0000000000..e408238860 --- /dev/null +++ b/sys/ztimer/xtimer2ztimer.cocci @@ -0,0 +1,176 @@ +// coccinelle script for converting xtimer usage to ztimer API +// this adds the ZTIMER_USEC as clock parameter, unless a multiplication of +// known constants (US_PER_MS, ...) makes it feasable to us a slower clock. + +@@ +@@ + +- #include "xtimer.h" ++ #include "ztimer.h" + +@@ +expression e; +@@ + +- xtimer_usleep(e) ++ ztimer_sleep(ZTIMER_USEC, e) + +@@ +expression e; +@@ + +- xtimer_set(e) ++ ztimer_set(ZTIMER_USEC, e) + +@@ +expression e; +@@ + +- xtimer_remove(e) ++ ztimer_remove(ZTIMER_USEC, e) + +@@ +expression e, e2; +@@ + +- xtimer_set(e, e2) ++ ztimer_set(ZTIMER_USEC, e, e2) + +@@ +identifier i; +@@ + +- xtimer_t i ++ ztimer_t i + = ...; + +@@ +identifier i; +@@ + +- xtimer_t i; ++ ztimer_t i; + +@@ +@@ + +- xtimer_now_usec() ++ ztimer_get(ZTIMER_USEC) + +@@ +@@ + +- _xtimer_now() ++ ztimer_get(ZTIMER_USEC) + +@@ +@@ + +- xtimer_now_usec64() ++ ztimer_now64() + +@@ +identifier i; +@@ + +struct { +... +- xtimer_t i; ++ ztimer_t i; +... +} + +@@ +identifier i, i2; +@@ + +struct i2 { +... +- xtimer_t i; ++ ztimer_t i; +... +} + + +@@ +identifier fn; +identifier i; +@@ + + fn(..., +- xtimer_t i ++ ztimer_t i + ,...) {...} + +@@ +identifier fn; +identifier i; +@@ + + fn(..., +- xtimer_t *i ++ ztimer_t *i + ,...) {...} + +@@ +expression e1, e2, e3, e4; +@@ + +- xtimer_set_msg(e1,e2,e3,e4) ++ ztimer_set_msg(ZTIMER_USEC, e1, e2, e3, e4) + +@@ +expression e; +@@ + +- xtimer_usleep64(e * US_PER_MS) ++ ztimer_sleep(ZTIMER_MSEC, e) + +@@ +expression e, e2; +@@ + +- xtimer_msg_receive_timeout(e, e2 * US_PER_MS) ++ ztimer_msg_receive_timeout(ZTIMER_MSEC, e, e2) + +@@ +expression e, e2; +@@ + +- xtimer_msg_receive_timeout(e, e2) ++ ztimer_msg_receive_timeout(ZTIMER_USEC, e, e2) + +@@ +identifier i; +expression e; +@@ + +- xtimer_ticks32_t i = e; ++ uint32_t i = e; + +@@ +identifier i; +@@ + +- xtimer_ticks32_t i; ++ uint32_t i; + +@@ +expression e; +@@ + +- xtimer_usec_from_ticks(e) ++ e + +@@ +@@ + +- xtimer_now() ++ ztimer_get(ZTIMER_USEC) + +@@ +expression e, e2; +@@ + +- xtimer_periodic_wakeup(e, e2) ++ ztimer_periodic_wakeup(ZTIMER_USEC, e, e2) diff --git a/tests/unittests/tests-ztimer/Makefile b/tests/unittests/tests-ztimer/Makefile new file mode 100644 index 0000000000..6c572ed15c --- /dev/null +++ b/tests/unittests/tests-ztimer/Makefile @@ -0,0 +1,6 @@ +# avoid clang warning in tests-ztimer/tests-ztimer-extend.c:141 +ifeq (llvm,$(TOOLCHAIN)) + CFLAGS += -Wno-gnu-folding-constant +endif + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-ztimer/Makefile.include b/tests/unittests/tests-ztimer/Makefile.include new file mode 100644 index 0000000000..2b29b6928c --- /dev/null +++ b/tests/unittests/tests-ztimer/Makefile.include @@ -0,0 +1,3 @@ +USEMODULE += ztimer_core +USEMODULE += ztimer_mock +USEMODULE += ztimer_convert_muldiv64 diff --git a/tests/unittests/tests-ztimer/tests-ztimer-convert-muldiv64.c b/tests/unittests/tests-ztimer/tests-ztimer-convert-muldiv64.c new file mode 100644 index 0000000000..6ae9174c38 --- /dev/null +++ b/tests/unittests/tests-ztimer/tests-ztimer-convert-muldiv64.c @@ -0,0 +1,216 @@ +/* + * 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. + */ + +/** + * @{ + * + * @file + * @brief Unittests for ztimer_convert_muldiv64 + * + * @author Joakim Nohlgård + */ + +#include "ztimer.h" +#include "ztimer/mock.h" +#include "ztimer/convert_muldiv64.h" + +#include "embUnit/embUnit.h" + +#include "tests-ztimer.h" + +#include + +static void test_ztimer_convert_muldiv64_now_helper(uint32_t div, uint32_t mul) +{ + ztimer_mock_t zmock; + ztimer_convert_muldiv64_t zc; + ztimer_clock_t *z = &zc.super.super; + + ztimer_mock_init(&zmock, 16); + ztimer_convert_muldiv64_init(&zc, &zmock.super, div, mul); + + uint32_t last = 0; + for (uint32_t i = 0; i <= 0xfffff; i++) { + uint32_t now = ztimer_now(z); + uint64_t should = (uint64_t)i * div; + should /= mul; + + TEST_ASSERT(now >= last); + + TEST_ASSERT_EQUAL_INT(should, now); + ztimer_mock_advance(&zmock, 1); + last = now; + } +} + +/** + * @brief Basic checks for ztimer_convert_muldiv64 + */ +static void test_ztimer_convert_muldiv64_now(void) +{ + test_ztimer_convert_muldiv64_now_helper(15625, 512); +} + +static void _set_cb(void *arg) +{ + int *val = arg; + *val = 1; +} + +static void test_ztimer_convert_muldiv64_set_speedup(void) +{ + ztimer_mock_t zmock; + ztimer_convert_muldiv64_t zc; + ztimer_clock_t *z = &zc.super.super; + unsigned val = 0; + + /* initialize 32bit mock timer */ + ztimer_mock_init(&zmock, 32); + + /* initialize convert to do "speedup" of the mock timer + * (all now() values multiplied by 1000, all set() will be divided by + * the same) */ + ztimer_convert_muldiv64_init(&zc, &zmock.super, 1000, 1); + + ztimer_t t = { .callback=_set_cb, .arg=&val }; + + /* mock now() starts at 0, convert at 0 * 1000 = 0 */ + TEST_ASSERT_EQUAL_INT(0, ztimer_now(&zmock.super)); + TEST_ASSERT_EQUAL_INT(0, ztimer_now(z)); + + /* set convert to 1 (integer divide would set this to 0 on lower timer, but + * convert is supposed to do divide with ceiling) */ + ztimer_set(z, &t, 1); + + /* advance mock to 1 (convert should be at 1000) */ + ztimer_mock_advance(&zmock, 1); + TEST_ASSERT_EQUAL_INT(1, ztimer_now(&zmock.super)); + TEST_ASSERT_EQUAL_INT(1000, ztimer_now(z)); + + /* convert must have triggered t, as 1000 is > 1 */ + TEST_ASSERT_EQUAL_INT(1, val); + + /* reset helper variable */ + val = 0; + + /* set t to +500 (absolute: 1500) */ + ztimer_set(z, &t, 500); + + /* advance mock to 2 (convert: 2000) */ + ztimer_mock_advance(&zmock, 1); + TEST_ASSERT_EQUAL_INT(2, ztimer_now(&zmock.super)); + TEST_ASSERT_EQUAL_INT(2000, ztimer_now(z)); + + /* assert that timer triggered */ + TEST_ASSERT_EQUAL_INT(1, val); + val = 0; + + /* set t to 4294967000 (absolute: 4294969000) */ + /* 4294967000 == (UINT32_MAX // 1000 * 1000) */ + ztimer_set(z, &t, 4294967000); + + /* advance mock to just before t's trigger time */ + /* this has overflowed convert */ + ztimer_mock_advance(&zmock, 4294966LU); + TEST_ASSERT_EQUAL_INT(4294968LU, ztimer_now(&zmock.super)); + + /* convert has overflowed ((2000 + 4294966000) % 2**32 = 704U)*/ + TEST_ASSERT_EQUAL_INT(704LU, ztimer_now(z)); + + /* assert t hasn't triggered yet */ + TEST_ASSERT_EQUAL_INT(0, val); + + /* advance mock to 4294969 (convert to 4294969000) */ + ztimer_mock_advance(&zmock, 0x1); + + /* assert t has triggered */ + TEST_ASSERT_EQUAL_INT(1, val); +} + +static void test_ztimer_convert_muldiv64_set_slowdown(void) +{ + ztimer_mock_t zmock; + ztimer_convert_muldiv64_t zc; + ztimer_clock_t *z = &zc.super.super; + unsigned val = 0; + + /* initialize 32bit mock timer */ + ztimer_mock_init(&zmock, 32); + + /* initialize convert to do "slowdown" of the mock timer + * (all now() values divided by 1000, all set() will be multiplied by + * the same) */ + ztimer_convert_muldiv64_init(&zc, &zmock.super, 1, 1000); + + ztimer_t t = { .callback=_set_cb, .arg=&val }; + + /* mock now() starts at 0, convert at 0 / 1000 = 0 */ + TEST_ASSERT_EQUAL_INT(0, ztimer_now(&zmock.super)); + TEST_ASSERT_EQUAL_INT(0, ztimer_now(z)); + + /* set t on convert to 1 (should be 1000 on mock) */ + ztimer_set(z, &t, 1); + + /* advance mock to 999 (convert should be at (999/1000)==1 */ + ztimer_mock_advance(&zmock, 999); + TEST_ASSERT_EQUAL_INT(999, ztimer_now(&zmock.super)); + TEST_ASSERT_EQUAL_INT(0, ztimer_now(z)); + + /* convert must not have triggered */ + TEST_ASSERT_EQUAL_INT(0, val); + + /* advance mock to 1000 (convert: 1) */ + ztimer_mock_advance(&zmock, 1); + TEST_ASSERT_EQUAL_INT(1000, ztimer_now(&zmock.super)); + TEST_ASSERT_EQUAL_INT(1, ztimer_now(z)); + /* assert that timer triggered */ + TEST_ASSERT_EQUAL_INT(1, val); + val = 0; + + /* testing intermediate timer and (lack of) quantization */ + /* max value mock overflows at 4294967.xxx * 1000, so there will + * be some intermediate timers. + * when setting z at (mock=1000, z=1) to +4294968, + * it should trigger at (mock=1704) */ + ztimer_set(z, &t, 4294968); + + ztimer_mock_advance(&zmock, UINT32_MAX); + TEST_ASSERT_EQUAL_INT(999, ztimer_now(&zmock.super)); + + /* ztimer_now(z) is now at (UINT32_MAX + 1000)/1000) == 4294968 */ + TEST_ASSERT_EQUAL_INT(4294968, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(0, val); + + ztimer_mock_advance(&zmock, 704); + TEST_ASSERT_EQUAL_INT(1703, ztimer_now(&zmock.super)); + TEST_ASSERT_EQUAL_INT(4294969, ztimer_now(z)); + + /* assert that this has not triggered yet. */ + TEST_ASSERT_EQUAL_INT(0, val); + + ztimer_mock_advance(&zmock, 1); + TEST_ASSERT_EQUAL_INT(1704, ztimer_now(&zmock.super)); + TEST_ASSERT_EQUAL_INT(4294969, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(1, val); + val = 0; +} + +Test *tests_ztimer_convert_muldiv64_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_ztimer_convert_muldiv64_set_speedup), + new_TestFixture(test_ztimer_convert_muldiv64_set_slowdown), + new_TestFixture(test_ztimer_convert_muldiv64_now), + }; + + EMB_UNIT_TESTCALLER(ztimer_tests, NULL, NULL, fixtures); + + return (Test *)&ztimer_tests; +} + +/** @} */ diff --git a/tests/unittests/tests-ztimer/tests-ztimer-mock.c b/tests/unittests/tests-ztimer/tests-ztimer-mock.c new file mode 100644 index 0000000000..02701fb47e --- /dev/null +++ b/tests/unittests/tests-ztimer/tests-ztimer-mock.c @@ -0,0 +1,272 @@ +/* + * 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. + */ + +/** + * @{ + * + * @file + * @brief Unittests for ztimer + * + * @author Joakim Nohlgård + */ + +#include "ztimer.h" +#include "ztimer/mock.h" + +#include "embUnit/embUnit.h" + +#include "tests-ztimer.h" + +#include +/** + * @brief Simple callback for counting alarms + */ +static void cb_incr(void *arg) +{ + uint32_t *ptr = arg; + *ptr += 1; +} + +/** + * @brief Testing 32 bit wide mock clock now functionality + */ +static void test_ztimer_mock_now32(void) +{ + ztimer_mock_t zmock; + ztimer_clock_t *z = &zmock.super; + + /* Basic sanity test of the mock implementation */ + ztimer_mock_init(&zmock, 32); + uint32_t now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0, now); + + ztimer_mock_advance(&zmock, 123); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(123, now); + + ztimer_mock_jump(&zmock, 0x10000000ul); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0x10000000ul, now); + + ztimer_mock_advance(&zmock, 0x98765432ul); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0xa8765432ul, now); + ztimer_mock_advance(&zmock, 0x41234567ul); + ztimer_mock_advance(&zmock, 0x40000000ul); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0x29999999ul, now); +} + +/** + * @brief Testing 16 bit wide mock clock now functionality + */ +static void test_ztimer_mock_now16(void) +{ + ztimer_mock_t zmock; + ztimer_clock_t *z = &zmock.super; + + /* testing a 16 bit counter */ + ztimer_mock_init(&zmock, 16); + uint32_t now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0, now); + + ztimer_mock_advance(&zmock, 123); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(123, now); + + ztimer_mock_advance(&zmock, 30000ul); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(30123ul, now); + + ztimer_mock_advance(&zmock, 0x10000ul); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(30123ul + 0x10000, now); + ztimer_mock_advance(&zmock, 0x8000ul); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(30123ul + 0x10000 + 0x8000, now); +} + +/** + * @brief Testing 8 bit wide mock clock now functionality + */ +static void test_ztimer_mock_now8(void) +{ + ztimer_mock_t zmock; + ztimer_clock_t *z = &zmock.super; + + /* testing a small counter */ + ztimer_mock_init(&zmock, 8); + uint32_t now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0, now); + + ztimer_mock_advance(&zmock, 123); + puts("advanced 123"); + now = ztimer_now(z); + puts("advanced 123 now"); + TEST_ASSERT_EQUAL_INT(123, now); + + ztimer_mock_advance(&zmock, 0x100); + puts("advanced"); + now = ztimer_now(z); + puts("now"); + TEST_ASSERT_EQUAL_INT(0x100 + 123, now); + + ztimer_mock_advance(&zmock, 180); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0x100 + 123 + 180, now); +} + +/** + * @brief Testing 3 bit wide mock clock now functionality + */ +static void test_ztimer_mock_now3(void) +{ + ztimer_mock_t zmock; + ztimer_clock_t *z = &zmock.super; + + /* testing a tiny counter */ + ztimer_mock_init(&zmock, 3); + uint32_t now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0, now); + + ztimer_mock_advance(&zmock, 7); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(7, now); + + ztimer_mock_advance(&zmock, 8); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(7 + 8, now); + + ztimer_mock_advance(&zmock, 10); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(7 + 8 + 10, now); +} + +/** + * @brief Testing 32 bit wide mock clock set functionality + */ +static void test_ztimer_mock_set32(void) +{ + ztimer_mock_t zmock; + ztimer_clock_t *z = &zmock.super; + + ztimer_mock_init(&zmock, 32); + uint32_t now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0, now); + + uint32_t count = 0; + ztimer_t alarm = { .callback = cb_incr, .arg = &count, }; + ztimer_set(z, &alarm, 1000); + + ztimer_mock_advance(&zmock, 1); /* now = 1*/ + TEST_ASSERT_EQUAL_INT(0, count); + ztimer_mock_advance(&zmock, 100); /* now = 101 */ + TEST_ASSERT_EQUAL_INT(0, count); + ztimer_mock_advance(&zmock, 898); /* now = 999 */ + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(999, now); + TEST_ASSERT_EQUAL_INT(0, count); + ztimer_mock_advance(&zmock, 1); /* now = 1000*/ + TEST_ASSERT_EQUAL_INT(1, count); + ztimer_mock_advance(&zmock, 1); /* now = 1001*/ + TEST_ASSERT_EQUAL_INT(1, count); + ztimer_mock_advance(&zmock, 1000); /* now = 2001*/ + TEST_ASSERT_EQUAL_INT(1, count); + ztimer_set(z, &alarm, 3); + ztimer_mock_advance(&zmock, 999); /* now = 3000*/ + TEST_ASSERT_EQUAL_INT(2, count); + ztimer_set(z, &alarm, 4000001000ul); + ztimer_mock_advance(&zmock, 1000); /* now = 4000*/ + TEST_ASSERT_EQUAL_INT(2, count); + ztimer_mock_advance(&zmock, 4000000000ul); /* now = 4000004000*/ + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(4000004000ul, now); + TEST_ASSERT_EQUAL_INT(3, count); + ztimer_set(z, &alarm, 15); + ztimer_mock_advance(&zmock, 14); + ztimer_remove(z, &alarm); + ztimer_mock_advance(&zmock, 1000); + TEST_ASSERT_EQUAL_INT(3, count); +} + +/** + * @brief Testing 16 bit wide mock clock set functionality + */ +static void test_ztimer_mock_set16(void) +{ + ztimer_mock_t zmock; + ztimer_clock_t *z = &zmock.super; + + ztimer_mock_init(&zmock, 16); + uint32_t now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(0, now); + + uint32_t count = 0; + ztimer_t alarm = { .callback = cb_incr, .arg = &count, }; + ztimer_set(z, &alarm, 1000); + + ztimer_mock_advance(&zmock, 1); + TEST_ASSERT_EQUAL_INT(1, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(0, count); + ztimer_mock_advance(&zmock, 100); + TEST_ASSERT_EQUAL_INT(1 + 100, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(0, count); + ztimer_mock_advance(&zmock, 898); + now = ztimer_now(z); + TEST_ASSERT_EQUAL_INT(1 + 100 + 898, now); + TEST_ASSERT_EQUAL_INT(0, count); + ztimer_mock_advance(&zmock, 1); + TEST_ASSERT_EQUAL_INT(1 + 100 + 898 + 1, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(1, count); + ztimer_mock_advance(&zmock, 1); + TEST_ASSERT_EQUAL_INT(1 + 100 + 898 + 1 + 1, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(1, count); + ztimer_mock_advance(&zmock, 1000); + TEST_ASSERT_EQUAL_INT(1 + 100 + 898 + 1 + 1 + 1000, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(1, count); + ztimer_set(z, &alarm, UINT16_MAX); + ztimer_mock_advance(&zmock, 0x10000ul); + /* 1 + 100 + 898 + 1 + 1 + 1000 + 0x10000 = 67537 */ + TEST_ASSERT_EQUAL_INT(67537ul, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(2, count); + ztimer_set(z, &alarm, UINT16_MAX); + ztimer_mock_advance(&zmock, 0x10000000ul); + TEST_ASSERT_EQUAL_INT(67537ul + 0x10000000ul, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(3, count); + ztimer_set(z, &alarm, 0x10001ul); + ztimer_mock_advance(&zmock, 1); + TEST_ASSERT_EQUAL_INT(67537ul + 0x10000000ul + 1, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(3, count); + ztimer_mock_advance(&zmock, UINT16_MAX); + TEST_ASSERT_EQUAL_INT(67537ul + 0x10000000ul + 1 + UINT16_MAX, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(3, count); + ztimer_mock_advance(&zmock, 0x1); + TEST_ASSERT_EQUAL_INT(67537ul + 0x10000000ul + 1 + UINT16_MAX + 1, ztimer_now(z)); + TEST_ASSERT_EQUAL_INT(4, count); + now = ztimer_now(z); + /* 67537ul + 0x10000000ul + 1 + UINT16_MAX + 1 = 0x100207d2 */ + TEST_ASSERT_EQUAL_INT(0x100207d2, now); +} + +Test *tests_ztimer_mock_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_ztimer_mock_now32), + new_TestFixture(test_ztimer_mock_now16), + new_TestFixture(test_ztimer_mock_now8), + new_TestFixture(test_ztimer_mock_now3), + new_TestFixture(test_ztimer_mock_set32), + new_TestFixture(test_ztimer_mock_set16), + }; + + EMB_UNIT_TESTCALLER(ztimer_tests, NULL, NULL, fixtures); + + return (Test *)&ztimer_tests; +} + +/** @} */ diff --git a/tests/unittests/tests-ztimer/tests-ztimer.c b/tests/unittests/tests-ztimer/tests-ztimer.c new file mode 100644 index 0000000000..2d785badf7 --- /dev/null +++ b/tests/unittests/tests-ztimer/tests-ztimer.c @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/** + * @{ + * + * @file + * @brief Unittest entry point for the ztimer test group + * + * @author Joakim Nohlgård + */ + +#include "embUnit/embUnit.h" + +#include "tests-ztimer.h" + +Test *tests_ztimer_mock_tests(void); +Test *tests_ztimer_convert_muldiv64_tests(void); + +void tests_ztimer(void) +{ + TESTS_RUN(tests_ztimer_mock_tests()); + TESTS_RUN(tests_ztimer_convert_muldiv64_tests()); +} +/** @} */ diff --git a/tests/unittests/tests-ztimer/tests-ztimer.h b/tests/unittests/tests-ztimer/tests-ztimer.h new file mode 100644 index 0000000000..6740d319bd --- /dev/null +++ b/tests/unittests/tests-ztimer/tests-ztimer.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for ztimer + * + * @author Joakim Nohlgård + */ +#ifndef TESTS_ZTIMER_H +#define TESTS_ZTIMER_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_ztimer(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_ZTIMER_H */ +/** @} */ diff --git a/tests/xtimer_drift/main.c b/tests/xtimer_drift/main.c index c442d77988..810862e3d9 100644 --- a/tests/xtimer_drift/main.c +++ b/tests/xtimer_drift/main.c @@ -120,7 +120,11 @@ void *worker_thread(void *arg) expected = last + TEST_HZ * test_interval; int32_t jitter = now - expected; printf("now=%" PRIu32 ".%06" PRIu32 " (0x%08" PRIx32 " ticks), ", +#ifdef MODULE_ZTIMER_XTIMER_COMPAT + sec, us, ticks); +#else sec, us, ticks.ticks32); +#endif printf("drift=%" PRId32 " us, jitter=%" PRId32 " us\n", drift, jitter); last = now; diff --git a/tests/ztimer_msg/Makefile b/tests/ztimer_msg/Makefile new file mode 100644 index 0000000000..e198e2c38e --- /dev/null +++ b/tests/ztimer_msg/Makefile @@ -0,0 +1,8 @@ +include ../Makefile.tests_common + +USEMODULE += ztimer_usec + +# uncomment this to test using ztimer msec on rtt +#USEMODULE += ztimer_msec ztimer_periph_rtt + +include $(RIOTBASE)/Makefile.include diff --git a/tests/ztimer_msg/Makefile.ci b/tests/ztimer_msg/Makefile.ci new file mode 100644 index 0000000000..8a0aef2b9e --- /dev/null +++ b/tests/ztimer_msg/Makefile.ci @@ -0,0 +1,8 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-nano \ + arduino-uno \ + atmega328p \ + nucleo-f031k6 \ + stm32f030f4-demo \ + # diff --git a/tests/ztimer_msg/README.md b/tests/ztimer_msg/README.md new file mode 100644 index 0000000000..b9d1ab991a --- /dev/null +++ b/tests/ztimer_msg/README.md @@ -0,0 +1,9 @@ +# Overview + +This test application is a direct translation of xtimer_msg to the ztimer API. +It is meant mostly as a means to do size comparisons, thus tries to be as close +as possible to the original. + +One notable change is the option to choose a different ztimer clock. +By default, the test will use ZTIMER_USEC, unless ZTIMER_MSEC is compiled in, +which will be used in that case. diff --git a/tests/ztimer_msg/main.c b/tests/ztimer_msg/main.c new file mode 100644 index 0000000000..b40422e9ed --- /dev/null +++ b/tests/ztimer_msg/main.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015-19 Kaspar Schleiser + * 2013 INRIA + * 2017 HAW Hamburg + * + * 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 xtimer_msg test application + * + * @author Kaspar Schleiser + * @author Oliver Hahm + * @author Christian Mehlis + * @author Sebastian Meiling + * @} + */ + +#include + +#include "ztimer.h" +#include "thread.h" +#include "msg.h" +#include "timex.h" + +#ifdef MODULE_ZTIMER_MSEC +#define ZTIMER ZTIMER_MSEC +#define TICKS_PER_SEC MS_PER_SEC +#else +#define ZTIMER ZTIMER_USEC +#define TICKS_PER_SEC US_PER_SEC +#endif + +char timer_stack[THREAD_STACKSIZE_DEFAULT]; +char timer_stack_local[THREAD_STACKSIZE_DEFAULT]; + +struct timer_msg { + ztimer_t timer; + uint32_t interval; + char *text; + msg_t msg; +}; + +struct timer_msg msg_a = { .interval = (2 * TICKS_PER_SEC), + .text = "Hello World" }; +struct timer_msg msg_b = { .interval = (5 * TICKS_PER_SEC), + .text = "This is a Test" }; + +void *timer_thread(void *arg) +{ + (void) arg; + + printf("This is thread %" PRIkernel_pid "\n", thread_getpid()); + + /* The queue is required to avoid loss of a 2nd message, when the 1st is + * still processed. The timing ensures that at most 1 message is queued. + */ + msg_t msgq[1]; + msg_init_queue(msgq, 1); + + while (1) { + msg_t m; + msg_receive(&m); + struct timer_msg *tmsg = m.content.ptr; + uint32_t now = ztimer_now(ZTIMER); + printf("now=%" PRIu32 ":%" PRIu32 " -> every %" PRIu32 ".%" PRIu32 "s: %s\n", + (now / TICKS_PER_SEC), + (now % TICKS_PER_SEC), + tmsg->interval / TICKS_PER_SEC, + tmsg->interval % TICKS_PER_SEC, + tmsg->text); + + tmsg->msg.type = 12345; + tmsg->msg.content.ptr = tmsg; + ztimer_set_msg(ZTIMER, &tmsg->timer, tmsg->interval, &tmsg->msg, thread_getpid()); + } +} + +void *timer_thread_local(void *arg) +{ + (void) arg; + + printf("This is thread %" PRIkernel_pid "\n", thread_getpid()); + + while (1) { + msg_t m; + msg_receive(&m); + + uint32_t now = ztimer_now(ZTIMER); + int sec = now / TICKS_PER_SEC; + int min = sec / 60; + int hr = sec / 3600; + printf("sec=%d min=%d hour=%d\n", sec, min, hr); + } +} + +int main(void) +{ + msg_t m; + kernel_pid_t pid = thread_create( + timer_stack, + sizeof(timer_stack), + THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, + timer_thread, + NULL, + "timer"); + + puts("sending 1st msg"); + m.content.ptr = &msg_a; + msg_try_send(&m, pid); + + puts("sending 2nd msg"); + m.content.ptr = &msg_b; + msg_try_send(&m, pid); + + kernel_pid_t pid2 = thread_create( + timer_stack_local, + sizeof(timer_stack_local), + THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, + timer_thread_local, + NULL, + "timer local"); + + while (1) { + ztimer_sleep(ZTIMER, 1 * TICKS_PER_SEC); + msg_try_send(&m, pid2); + } +} diff --git a/tests/ztimer_msg/tests/01-run.py b/tests/ztimer_msg/tests/01-run.py new file mode 100755 index 0000000000..136b6443a4 --- /dev/null +++ b/tests/ztimer_msg/tests/01-run.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2017 HAW Hamburg +# +# 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. + +import sys +from testrunner import run + + +def testfunc(child): + # 1st check for periodic 2s Hello World message, i.e., 2 output + 1 msg + for _ in range(7): + child.expect(r"sec=\d+ min=\d+ hour=\d+") + child.expect(r"sec=\d+ min=\d+ hour=\d+") + child.expect(r"now=\d+:\d+ -> every 2.0s: Hello World") + # 2nd check for periodic 5s test message, i.e., 5 output + 1 msg + for _ in range(3): + child.expect(r"sec=\d+ min=\d+ hour=\d+") + child.expect(r"sec=\d+ min=\d+ hour=\d+") + child.expect(r"sec=\d+ min=\d+ hour=\d+") + child.expect(r"sec=\d+ min=\d+ hour=\d+") + child.expect(r"sec=\d+ min=\d+ hour=\d+") + child.expect(r"now=\d+:\d+ -> every 5.0s: This is a Test") + + +if __name__ == "__main__": + sys.exit(run(testfunc)) diff --git a/tests/ztimer_overhead/Makefile b/tests/ztimer_overhead/Makefile new file mode 100644 index 0000000000..9c105c8470 --- /dev/null +++ b/tests/ztimer_overhead/Makefile @@ -0,0 +1,6 @@ +DEVELHELP ?= 0 +include ../Makefile.tests_common + +USEMODULE += ztimer_overhead ztimer_usec + +include $(RIOTBASE)/Makefile.include diff --git a/tests/ztimer_overhead/README.md b/tests/ztimer_overhead/README.md new file mode 100644 index 0000000000..7724edf39d --- /dev/null +++ b/tests/ztimer_overhead/README.md @@ -0,0 +1,7 @@ +# Introduction + +This test application sets up a ztimer_periph at 1MHz, then measures 1024 +times how much overhead ztimer adds. + +It uses the "ztimer_overhead()" function. See it's documentation for more +information. diff --git a/tests/ztimer_overhead/main.c b/tests/ztimer_overhead/main.c new file mode 100644 index 0000000000..9e2723e2a8 --- /dev/null +++ b/tests/ztimer_overhead/main.c @@ -0,0 +1,56 @@ +/* + * 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. + */ + +/** + * @ingroup test + * @{ + * + * @file + * @brief ztimer overhead test application + * + * @author Kaspar Schleiser + * + * @} + */ + +#include +#include +#include + +#include "ztimer.h" +#include "ztimer/overhead.h" + +#define BASE 1000 +#define SAMPLES 1024 + +int main(void) +{ + uint32_t total = 0; + + uint16_t min = 0xFFFF; + uint16_t max = 0; + + /* unset configured adjustment */ + /* ZTIMER_USEC->adjust = 0; */ + + unsigned n = SAMPLES; + while (n--) { + unsigned overhead = ztimer_overhead(ZTIMER_USEC, BASE); + total += overhead; + if (overhead < min) { + min = overhead; + } + else if (overhead > max) { + max = overhead; + } + } + + printf("min=%u max=%u avg=%" PRIu32 "\n", min, max, (total / SAMPLES)); + + return 0; +} diff --git a/tests/ztimer_overhead/tests/01-run.py b/tests/ztimer_overhead/tests/01-run.py new file mode 100755 index 0000000000..8430a94fea --- /dev/null +++ b/tests/ztimer_overhead/tests/01-run.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# 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. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect(r"min=\d+ max=\d+ avg=\d+\r\n") + + +if __name__ == "__main__": + sys.exit(run(testfunc))