mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 05:12:57 +01:00
sys: add ztimer subsystem
Co-authored-by: Joakim Nohlgård <joakim.nohlgard@eistec.se>
This commit is contained in:
parent
698f313f6f
commit
6dd79366bb
37
Makefile.dep
37
Makefile.dep
@ -1013,6 +1013,43 @@ ifneq (,$(filter periph_uart_nonblocking,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_uart
|
||||
endif
|
||||
|
||||
ifneq (,$(filter xtimer_on_ztimer%,$(USEMODULE)))
|
||||
USEMODULE += ztimer_usec
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ztimer_%,$(USEMODULE)))
|
||||
USEMODULE += ztimer
|
||||
USEMODULE += ztimer_extend
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ztimer_periph,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_timer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ztimer_rtt,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_rtt
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ztimer_convert_frac,$(USEMODULE)))
|
||||
USEMODULE += frac
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ztimer,$(USEMODULE)))
|
||||
USEMODULE += ztimer_auto_init
|
||||
USEMODULE += ztimer_core
|
||||
USEMODULE += ztimer_convert_frac
|
||||
USEMODULE += ztimer_convert_shift
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ztimer_usec,$(USEMODULE)))
|
||||
USEMODULE += ztimer
|
||||
USEMODULE += ztimer_periph
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ztimer_msec,$(USEMODULE)))
|
||||
USEMODULE += ztimer
|
||||
endif
|
||||
|
||||
# Enable periph_gpio when periph_gpio_irq is enabled
|
||||
ifneq (,$(filter periph_gpio_irq,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
|
@ -94,6 +94,10 @@ PSEUDOMODULES += stdio_uart_rx
|
||||
PSEUDOMODULES += suit_%
|
||||
PSEUDOMODULES += wakaama_objects_%
|
||||
PSEUDOMODULES += zptr
|
||||
PSEUDOMODULES += ztimer%
|
||||
|
||||
# ztimer's main module is called "ztimer_core"
|
||||
NO_PSEUDOMODULES += ztimer_core
|
||||
|
||||
# handle suit_v4 being a distinct module
|
||||
NO_PSEUDOMODULES += suit_v4
|
||||
|
@ -392,6 +392,11 @@ void auto_init(void)
|
||||
extern void auto_init_random(void);
|
||||
auto_init_random();
|
||||
}
|
||||
if (IS_USED(MODULE_ZTIMER)) {
|
||||
LOG_DEBUG("Auto init ztimer.\n");
|
||||
void ztimer_init(void);
|
||||
ztimer_init();
|
||||
}
|
||||
if (IS_USED(MODULE_AUTO_INIT_XTIMER)) {
|
||||
LOG_DEBUG("Auto init xtimer.\n");
|
||||
extern void xtimer_init(void);
|
||||
|
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 */
|
||||
/** @} */
|
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
|
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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user