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

Merge pull request #11874 from kaspar030/ztimer

sys/ztimer: initial import
This commit is contained in:
Koen Zandberg 2020-03-06 09:46:27 +01:00 committed by GitHub
commit ed316309c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 4109 additions and 10 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();

View File

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

View File

@ -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
}
/**

506
sys/include/ztimer.h Normal file
View File

@ -0,0 +1,506 @@
/*
* Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef ZTIMER_H
#define ZTIMER_H
#include <stdint.h>
#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 */
/** @} */

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
*/
#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 */
/** @} */

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef ZTIMER_CONVERT_FRAC_H
#define ZTIMER_CONVERT_FRAC_H
#include <stdint.h>
#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 */
/** @} */

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
*/
#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 */
/** @} */

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
*/
#ifndef ZTIMER_CONVERT_SHIFT_H
#define ZTIMER_CONVERT_SHIFT_H
#include <stdint.h>
#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 */
/** @} */

97
sys/include/ztimer/mock.h Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <joakim.nohlgard@eistec.se>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef ZTIMER_MOCK_H
#define ZTIMER_MOCK_H
#include <stdint.h>
#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 */
/** @} */

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*/
#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 */
/** @} */

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
*/
#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 */
/** @} */

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
*/
#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 */
/** @} */

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
*/
#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 */
/** @} */

View File

@ -0,0 +1,178 @@
/*
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
*/
#ifndef ZTIMER_XTIMER_COMPAT_H
#define ZTIMER_XTIMER_COMPAT_H
#include <stdbool.h>
#include <stdint.h>
#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 */

View File

@ -24,9 +24,12 @@
#include <stdint.h>
#include <string.h>
#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;
}

19
sys/ztimer/Makefile Normal file
View File

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

77
sys/ztimer/Makefile.dep Normal file
View File

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

175
sys/ztimer/auto_init.c Normal file
View File

@ -0,0 +1,175 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#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
}

53
sys/ztimer/convert.c Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#include <stdio.h>
#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);
}

101
sys/ztimer/convert_frac.c Normal file
View File

@ -0,0 +1,101 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 2020 Freie Universität Berlin
* 2020 Inria
* 2018 Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
* 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 <joakim.nohlgard@eistec.se>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include <stdint.h>
#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;
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#include <stdio.h>
#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;
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#include <stdio.h>
#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;
}

342
sys/ztimer/core.c Normal file
View File

@ -0,0 +1,342 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#include <assert.h>
#include <stdint.h>
#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("");
}

124
sys/ztimer/mock.c Normal file
View File

@ -0,0 +1,124 @@
/*
* Copyright (C) 2018 Eistec AB
* 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <joakim.nohlgard@eistec.se>
* @}
*/
#include <stdint.h>
#include <inttypes.h>
#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);
}
}

50
sys/ztimer/overhead.c Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#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;
}

145
sys/ztimer/periph_rtc.c Normal file
View File

@ -0,0 +1,145 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#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();
}

81
sys/ztimer/periph_rtt.c Normal file
View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#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();
}

82
sys/ztimer/periph_timer.c Normal file
View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#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);
}

149
sys/ztimer/util.c Normal file
View File

@ -0,0 +1,149 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
*
* @}
*/
#include <assert.h>
#include <errno.h>
#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);
}

View File

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

View File

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

View File

@ -0,0 +1,3 @@
USEMODULE += ztimer_core
USEMODULE += ztimer_mock
USEMODULE += ztimer_convert_muldiv64

View File

@ -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 <joakim.nohlgard@eistec.se>
*/
#include "ztimer.h"
#include "ztimer/mock.h"
#include "ztimer/convert_muldiv64.h"
#include "embUnit/embUnit.h"
#include "tests-ztimer.h"
#include <stdio.h>
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;
}
/** @} */

View File

@ -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 <joakim.nohlgard@eistec.se>
*/
#include "ztimer.h"
#include "ztimer/mock.h"
#include "embUnit/embUnit.h"
#include "tests-ztimer.h"
#include <stdio.h>
/**
* @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;
}
/** @} */

View File

@ -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 <joakim.nohlgard@eistec.se>
*/
#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());
}
/** @} */

View File

@ -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 <joakim.nohlgard@eistec.se>
*/
#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 */
/** @} */

View File

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

View File

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

View File

@ -0,0 +1,8 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-nano \
arduino-uno \
atmega328p \
nucleo-f031k6 \
stm32f030f4-demo \
#

View File

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

136
tests/ztimer_msg/main.c Normal file
View File

@ -0,0 +1,136 @@
/*
* Copyright (C) 2015-19 Kaspar Schleiser <kaspar@schleiser.de>
* 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 <kaspar@schleiser.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
* @author Sebastian Meiling <s@mlng.net>
* @}
*/
#include <stdio.h>
#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);
}
}

View File

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

View File

@ -0,0 +1,6 @@
DEVELHELP ?= 0
include ../Makefile.tests_common
USEMODULE += ztimer_overhead ztimer_usec
include $(RIOTBASE)/Makefile.include

View File

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

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 <kaspar@schleiser.de>
*
* @}
*/
#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
#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;
}

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
#
# 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))