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:
commit
ed316309c8
@ -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
|
||||
|
28
Makefile.dep
28
Makefile.dep
@ -683,12 +683,6 @@ ifneq (,$(filter arduino_pwm,$(FEATURES_USED)))
|
||||
FEATURES_REQUIRED += periph_pwm
|
||||
endif
|
||||
|
||||
ifneq (,$(filter xtimer,$(USEMODULE)))
|
||||
DEFAULT_MODULE += auto_init_xtimer
|
||||
FEATURES_REQUIRED += periph_timer
|
||||
USEMODULE += div
|
||||
endif
|
||||
|
||||
ifneq (,$(filter saul,$(USEMODULE)))
|
||||
USEMODULE += phydat
|
||||
endif
|
||||
@ -1021,6 +1015,28 @@ ifneq (,$(filter periph_uart_nonblocking,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_uart
|
||||
endif
|
||||
|
||||
# include ztimer dependencies
|
||||
ifneq (,$(filter ztimer%,$(USEMODULE)))
|
||||
include $(RIOTBASE)/sys/ztimer/Makefile.dep
|
||||
endif
|
||||
|
||||
# handle xtimer's deps. Needs to be done *after* ztimer
|
||||
ifneq (,$(filter xtimer,$(USEMODULE)))
|
||||
ifeq (,$(filter ztimer_xtimer_compat,$(USEMODULE)))
|
||||
# xtimer is used, ztimer xtimer wrapper is not
|
||||
DEFAULT_MODULE += auto_init_xtimer
|
||||
USEMODULE += div
|
||||
ifeq (,$(filter xtimer_on_ztimer,$(USEMODULE)))
|
||||
# ztimer is not used, so use *periph_timer as low-level timer*.
|
||||
FEATURES_REQUIRED += periph_timer
|
||||
else
|
||||
# will use *ztimer_usec as low-level timer*
|
||||
endif
|
||||
else
|
||||
# ztimer_xtimer_compat is used, all of *xtimer's API will be mapped on ztimer.*
|
||||
endif
|
||||
endif
|
||||
|
||||
# Enable periph_gpio when periph_gpio_irq is enabled
|
||||
ifneq (,$(filter periph_gpio_irq,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
||||
|
@ -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
506
sys/include/ztimer.h
Normal 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 */
|
||||
/** @} */
|
79
sys/include/ztimer/convert.h
Normal file
79
sys/include/ztimer/convert.h
Normal 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 */
|
||||
/** @} */
|
91
sys/include/ztimer/convert_frac.h
Normal file
91
sys/include/ztimer/convert_frac.h
Normal 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 */
|
||||
/** @} */
|
85
sys/include/ztimer/convert_muldiv64.h
Normal file
85
sys/include/ztimer/convert_muldiv64.h
Normal 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 */
|
||||
/** @} */
|
71
sys/include/ztimer/convert_shift.h
Normal file
71
sys/include/ztimer/convert_shift.h
Normal 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
97
sys/include/ztimer/mock.h
Normal 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 */
|
||||
/** @} */
|
47
sys/include/ztimer/overhead.h
Normal file
47
sys/include/ztimer/overhead.h
Normal 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 */
|
||||
/** @} */
|
52
sys/include/ztimer/periph_rtc.h
Normal file
52
sys/include/ztimer/periph_rtc.h
Normal 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 */
|
||||
/** @} */
|
53
sys/include/ztimer/periph_rtt.h
Normal file
53
sys/include/ztimer/periph_rtt.h
Normal 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 */
|
||||
/** @} */
|
64
sys/include/ztimer/periph_timer.h
Normal file
64
sys/include/ztimer/periph_timer.h
Normal 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 */
|
||||
/** @} */
|
178
sys/include/ztimer/xtimer_compat.h
Normal file
178
sys/include/ztimer/xtimer_compat.h
Normal 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 */
|
@ -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
19
sys/ztimer/Makefile
Normal 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
77
sys/ztimer/Makefile.dep
Normal 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
175
sys/ztimer/auto_init.c
Normal 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
53
sys/ztimer/convert.c
Normal 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
101
sys/ztimer/convert_frac.c
Normal 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;
|
||||
}
|
||||
}
|
123
sys/ztimer/convert_muldiv64.c
Normal file
123
sys/ztimer/convert_muldiv64.c
Normal 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;
|
||||
}
|
94
sys/ztimer/convert_shift.c
Normal file
94
sys/ztimer/convert_shift.c
Normal 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
342
sys/ztimer/core.c
Normal 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
124
sys/ztimer/mock.c
Normal 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
50
sys/ztimer/overhead.c
Normal 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
145
sys/ztimer/periph_rtc.c
Normal 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
81
sys/ztimer/periph_rtt.c
Normal 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
82
sys/ztimer/periph_timer.c
Normal 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
149
sys/ztimer/util.c
Normal 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);
|
||||
}
|
176
sys/ztimer/xtimer2ztimer.cocci
Normal file
176
sys/ztimer/xtimer2ztimer.cocci
Normal 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)
|
6
tests/unittests/tests-ztimer/Makefile
Normal file
6
tests/unittests/tests-ztimer/Makefile
Normal 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
|
3
tests/unittests/tests-ztimer/Makefile.include
Normal file
3
tests/unittests/tests-ztimer/Makefile.include
Normal file
@ -0,0 +1,3 @@
|
||||
USEMODULE += ztimer_core
|
||||
USEMODULE += ztimer_mock
|
||||
USEMODULE += ztimer_convert_muldiv64
|
216
tests/unittests/tests-ztimer/tests-ztimer-convert-muldiv64.c
Normal file
216
tests/unittests/tests-ztimer/tests-ztimer-convert-muldiv64.c
Normal 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;
|
||||
}
|
||||
|
||||
/** @} */
|
272
tests/unittests/tests-ztimer/tests-ztimer-mock.c
Normal file
272
tests/unittests/tests-ztimer/tests-ztimer-mock.c
Normal 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;
|
||||
}
|
||||
|
||||
/** @} */
|
30
tests/unittests/tests-ztimer/tests-ztimer.c
Normal file
30
tests/unittests/tests-ztimer/tests-ztimer.c
Normal 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());
|
||||
}
|
||||
/** @} */
|
37
tests/unittests/tests-ztimer/tests-ztimer.h
Normal file
37
tests/unittests/tests-ztimer/tests-ztimer.h
Normal 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 */
|
||||
/** @} */
|
@ -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;
|
||||
|
8
tests/ztimer_msg/Makefile
Normal file
8
tests/ztimer_msg/Makefile
Normal 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
|
8
tests/ztimer_msg/Makefile.ci
Normal file
8
tests/ztimer_msg/Makefile.ci
Normal file
@ -0,0 +1,8 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
nucleo-f031k6 \
|
||||
stm32f030f4-demo \
|
||||
#
|
9
tests/ztimer_msg/README.md
Normal file
9
tests/ztimer_msg/README.md
Normal 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
136
tests/ztimer_msg/main.c
Normal 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);
|
||||
}
|
||||
}
|
30
tests/ztimer_msg/tests/01-run.py
Executable file
30
tests/ztimer_msg/tests/01-run.py
Executable 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))
|
6
tests/ztimer_overhead/Makefile
Normal file
6
tests/ztimer_overhead/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
DEVELHELP ?= 0
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += ztimer_overhead ztimer_usec
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
7
tests/ztimer_overhead/README.md
Normal file
7
tests/ztimer_overhead/README.md
Normal 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.
|
56
tests/ztimer_overhead/main.c
Normal file
56
tests/ztimer_overhead/main.c
Normal 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;
|
||||
}
|
18
tests/ztimer_overhead/tests/01-run.py
Executable file
18
tests/ztimer_overhead/tests/01-run.py
Executable 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))
|
Loading…
Reference in New Issue
Block a user