mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #17607 from jue89/feature/ztimer-ondemand
ztimer: add ztimer_ondemand module for implicit power management
This commit is contained in:
commit
71a606a1db
@ -361,6 +361,20 @@ typedef struct {
|
||||
* @param clock ztimer clock to cancel a pending alarm, if any
|
||||
*/
|
||||
void (*cancel)(ztimer_clock_t *clock);
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND || DOXYGEN
|
||||
/**
|
||||
* @brief Starts the underlying timer
|
||||
* @param clock ztimer clock to start
|
||||
*/
|
||||
void (*start)(ztimer_clock_t *clock);
|
||||
|
||||
/**
|
||||
* @brief Stops the underlying timer
|
||||
* @param clock ztimer clock to stop
|
||||
*/
|
||||
void (*stop)(ztimer_clock_t *clock);
|
||||
#endif
|
||||
} ztimer_ops_t;
|
||||
|
||||
/**
|
||||
@ -373,14 +387,21 @@ struct ztimer_clock {
|
||||
uint16_t adjust_set; /**< will be subtracted on every set() */
|
||||
uint16_t adjust_sleep; /**< will be subtracted on every sleep(),
|
||||
in addition to adjust_set */
|
||||
#if MODULE_ZTIMER_ONDEMAND || DOXYGEN
|
||||
uint16_t adjust_clock_start; /**< will be subtracted on every set(),
|
||||
if the underlying periph is in
|
||||
stopped state */
|
||||
uint16_t users; /**< user count of this clock */
|
||||
#endif
|
||||
#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
|
||||
#if MODULE_PM_LAYERED || DOXYGEN
|
||||
uint8_t block_pm_mode; /**< min. pm mode to block for the clock to run */
|
||||
#if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND || DOXYGEN
|
||||
uint8_t block_pm_mode; /**< min. pm mode to block for the clock to run
|
||||
don't use in combination with ztimer_ondemand! */
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -393,6 +414,47 @@ struct ztimer_clock {
|
||||
void ztimer_handler(ztimer_clock_t *clock);
|
||||
|
||||
/* User API */
|
||||
/**
|
||||
* @brief Acquire a clock
|
||||
*
|
||||
* This will indicate the the underlying clock is required to be running.
|
||||
* If time differences are measured using @ref ztimer_now this will make
|
||||
* sure ztimer won't turn of the clock source.
|
||||
*
|
||||
* @param[in] clock ztimer clock to operate on
|
||||
*
|
||||
* @return true if this was the first acquisition on this this clock
|
||||
*/
|
||||
#if MODULE_ZTIMER_ONDEMAND || DOXYGEN
|
||||
bool ztimer_acquire(ztimer_clock_t *clock);
|
||||
#else
|
||||
static inline bool ztimer_acquire(ztimer_clock_t *clock)
|
||||
{
|
||||
(void) clock;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Release a clock
|
||||
*
|
||||
* This will indicate the the underlying clock isn't required to be running
|
||||
* anymore and may be turned off.
|
||||
*
|
||||
* @param[in] clock ztimer clock to operate on
|
||||
*
|
||||
* @return true if this was the last clock user
|
||||
*/
|
||||
#if MODULE_ZTIMER_ONDEMAND || DOXYGEN
|
||||
bool ztimer_release(ztimer_clock_t *clock);
|
||||
#else
|
||||
static inline bool ztimer_release(ztimer_clock_t *clock)
|
||||
{
|
||||
(void) clock;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set a timer on a clock
|
||||
*
|
||||
@ -492,6 +554,17 @@ int ztimer_msg_receive_timeout(ztimer_clock_t *clock, msg_t *msg,
|
||||
*/
|
||||
ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock);
|
||||
|
||||
/**
|
||||
* @brief asserts the given clock to be active
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @note This function is internal
|
||||
*
|
||||
* @param[in] clock ztimer clock to operate on
|
||||
*/
|
||||
void _ztimer_assert_clock_active(ztimer_clock_t *clock);
|
||||
|
||||
/**
|
||||
* @brief Get the current time from a clock
|
||||
*
|
||||
@ -511,14 +584,18 @@ ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock);
|
||||
* * Two values can only be compared when the clock has been continuously
|
||||
* active between the first and the second reading.
|
||||
*
|
||||
* A clock is guaranteed to be active from the time any timer is set (the
|
||||
* first opportunity to get a "now" value from it is the return value of @ref
|
||||
* ztimer_set) until the time the timer's callback returns. The clock also
|
||||
* stays active when timers are set back-to-back (which is the case when the
|
||||
* first timer's callback sets the second timer), or when they overlap (which
|
||||
* can be known by starting the second timer and afterwards observing that
|
||||
* @ref ztimer_is_set or @ref ztimer_remove returns true in a low-priority
|
||||
* context).
|
||||
* Calling @ref ztimer_acquire before using `ztimer_now()` is the preferred
|
||||
* way to guarantee that a clock is continuously active. Make sure to call
|
||||
* the corresponding @ref ztimer_release after the last `ztimer_now()` call.
|
||||
*
|
||||
* A clock is also guaranteed to be active from the time any timer is set
|
||||
* (the first opportunity to get a "now" value from it is the return value of
|
||||
* @ref ztimer_set) until the time the timer's callback returns. The clock
|
||||
* also stays active when timers are set back-to-back (which is the case when
|
||||
* the first timer's callback sets the second timer), or when they overlap
|
||||
* (which can be known by starting the second timer and afterwards observing
|
||||
* that @ref ztimer_is_set or @ref ztimer_remove returns true in a
|
||||
* low-priority context).
|
||||
*
|
||||
* In contrast, the clock is not guaranteed to be active if a timer is
|
||||
* removed and then a second one is started (even if the thread does not
|
||||
@ -593,12 +670,19 @@ ztimer_now_t _ztimer_now_extend(ztimer_clock_t *clock);
|
||||
* unrelated components are altered that change the systems's power
|
||||
* management behavior.
|
||||
*
|
||||
* @warning Make sure to call @ref ztimer_acquire(@p clock) before fetching
|
||||
* the clock's current time.
|
||||
*
|
||||
* @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_ONDEMAND && DEVELHELP
|
||||
_ztimer_assert_clock_active(clock);
|
||||
#endif
|
||||
|
||||
#if MODULE_ZTIMER_NOW64
|
||||
if (1) {
|
||||
#elif MODULE_ZTIMER_EXTEND
|
||||
@ -627,6 +711,10 @@ static inline ztimer_now_t ztimer_now(ztimer_clock_t *clock)
|
||||
* 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.
|
||||
*
|
||||
* @warning Make sure to call @ref ztimer_acquire(@p clock) before making use
|
||||
* of @ref ztimer_periodic_wakeup. After usage
|
||||
* @ref ztimer_release(@p clock) should be called.
|
||||
*
|
||||
* @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
|
||||
@ -652,11 +740,13 @@ void ztimer_sleep(ztimer_clock_t *clock, uint32_t duration);
|
||||
*/
|
||||
static inline void ztimer_spin(ztimer_clock_t *clock, uint32_t duration)
|
||||
{
|
||||
ztimer_acquire(clock);
|
||||
uint32_t end = ztimer_now(clock) + duration;
|
||||
|
||||
/* Rely on integer overflow. `end - now` will be smaller than `duration`,
|
||||
* counting down, until it underflows to UINT32_MAX. Loop ends then. */
|
||||
while ((end - ztimer_now(clock)) <= duration) {}
|
||||
ztimer_release(clock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,6 +150,19 @@ extern "C" {
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief An offset for ZTIMER_USEC allowing to compensate for the offset
|
||||
* introduced by turning on the underlying peripheral.
|
||||
*
|
||||
* @note This value can be measured with the
|
||||
* `tests/ztimer_ondemand_benchmark` tool.
|
||||
*
|
||||
* This value should be configured in the board.h.
|
||||
*/
|
||||
#ifndef CONFIG_ZTIMER_USEC_ADJUST_CLOCK_START
|
||||
#define CONFIG_ZTIMER_USEC_ADJUST_CLOCK_START 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief An offset for ZTIMER_USEC allowing to compensate for the offset
|
||||
* of @ref ztimer_set(). It can be measured with @ref ztimer_overhead_set()
|
||||
|
@ -71,6 +71,24 @@ void ztimer_convert_init(ztimer_convert_t *ztimer_convert,
|
||||
*/
|
||||
void ztimer_convert_cancel(ztimer_clock_t *clock);
|
||||
|
||||
/**
|
||||
* @brief ztimer_convert common start() op
|
||||
*
|
||||
* Used by some conversion modules as ztimer_clock_t::ops.start().
|
||||
*
|
||||
* @param[in] clock ztimer clock to operate on
|
||||
*/
|
||||
void ztimer_convert_start(ztimer_clock_t *clock);
|
||||
|
||||
/**
|
||||
* @brief ztimer_convert common stop() op
|
||||
*
|
||||
* Used by some conversion modules as ztimer_clock_t::ops.stop().
|
||||
*
|
||||
* @param[in] clock ztimer clock to operate on
|
||||
*/
|
||||
void ztimer_convert_stop(ztimer_clock_t *clock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -43,11 +43,14 @@ typedef struct {
|
||||
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 */
|
||||
unsigned running; /**< flag for checking if the timer is running */
|
||||
|
||||
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 */
|
||||
unsigned start; /**< Number of calls to ztimer_ops_t::start */
|
||||
unsigned stop; /**< Number of calls to ztimer_ops_t::stop */
|
||||
} calls; /**< Struct holding number of calls to each
|
||||
operation */
|
||||
} ztimer_mock_t;
|
||||
|
@ -228,6 +228,42 @@ config MODULE_ZTIMER_MOCK
|
||||
manually fired to simulate different scenarios and test the ztimer
|
||||
implementation using this as a backing timer.
|
||||
|
||||
menuconfig MODULE_ZTIMER_ONDEMAND
|
||||
bool "Run ztimer clocks only on demand"
|
||||
help
|
||||
This ztimer extensions keeps track of ztimer users and may disable
|
||||
underlying peripherals if not users are requiring a ztimer clock.
|
||||
|
||||
if MODULE_ZTIMER_ONDEMAND
|
||||
|
||||
config MODULE_ZTIMER_ONDEMAND_STRICT
|
||||
bool "Strict ztimer on demand"
|
||||
help
|
||||
Enforce ztimer clocks to be running before calling ztimer_now().
|
||||
|
||||
config MODULE_ZTIMER_ONDEMAND_TIMER
|
||||
bool "Run Timer only on demand"
|
||||
depends on MODULE_ZTIMER_PERIPH_TIMER
|
||||
help
|
||||
Turn off the underlying Timer peripheral if related ztimer clocks
|
||||
have no users.
|
||||
|
||||
config MODULE_ZTIMER_ONDEMAND_RTT
|
||||
bool "Run RTT only on demand"
|
||||
depends on MODULE_ZTIMER_PERIPH_RTT
|
||||
help
|
||||
Turn off the underlying RTT peripheral if related ztimer clocks
|
||||
have no users.
|
||||
|
||||
config MODULE_ZTIMER_ONDEMAND_RTC
|
||||
bool "Run RTC only on demand"
|
||||
depends on MODULE_ZTIMER_PERIPH_RTC
|
||||
help
|
||||
Turn off the underlying RTC peripheral if related ztimer clocks
|
||||
have no users.
|
||||
|
||||
endif # MODULE_ZTIMER_ONDEMAND
|
||||
|
||||
config MODULE_ZTIMER_INIT
|
||||
bool
|
||||
|
||||
|
@ -37,6 +37,10 @@ ifneq (,$(filter ztimer_%,$(USEMODULE)))
|
||||
USEMODULE += ztimer_extend
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ztimer_ondemand_%,$(USEMODULE)))
|
||||
USEMODULE += ztimer_ondemand
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ztimer_convert_%,$(USEMODULE)))
|
||||
USEMODULE += ztimer_convert
|
||||
endif
|
||||
|
@ -35,6 +35,20 @@ void ztimer_convert_cancel(ztimer_clock_t *clock)
|
||||
ztimer_remove(ztimer_convert->lower, &ztimer_convert->lower_entry);
|
||||
}
|
||||
|
||||
void ztimer_convert_start(ztimer_clock_t *clock)
|
||||
{
|
||||
ztimer_convert_t *ztimer_convert = (ztimer_convert_t *)clock;
|
||||
|
||||
ztimer_acquire(ztimer_convert->lower);
|
||||
}
|
||||
|
||||
void ztimer_convert_stop(ztimer_clock_t *clock)
|
||||
{
|
||||
ztimer_convert_t *ztimer_convert = (ztimer_convert_t *)clock;
|
||||
|
||||
ztimer_release(ztimer_convert->lower);
|
||||
}
|
||||
|
||||
void ztimer_convert_init(ztimer_convert_t *ztimer_convert,
|
||||
ztimer_clock_t *lower,
|
||||
uint32_t max_value)
|
||||
@ -46,7 +60,7 @@ void ztimer_convert_init(ztimer_convert_t *ztimer_convert,
|
||||
.arg = ztimer_convert,
|
||||
},
|
||||
.super.max_value = max_value,
|
||||
# ifdef MODULE_PM_LAYERED
|
||||
# if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
.super.block_pm_mode = ZTIMER_CLOCK_NO_REQUIRED_PM_MODE,
|
||||
# endif
|
||||
};
|
||||
|
@ -74,6 +74,10 @@ 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,
|
||||
#if MODULE_ZTIMER_ONDEMAND
|
||||
.start = ztimer_convert_start,
|
||||
.stop = ztimer_convert_stop,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void ztimer_convert_frac_compute_scale(ztimer_convert_frac_t *self,
|
||||
@ -103,7 +107,12 @@ void ztimer_convert_frac_init(ztimer_convert_frac_t *self,
|
||||
ztimer_convert_frac_compute_scale(self, freq_self, freq_lower);
|
||||
if (freq_self < freq_lower) {
|
||||
self->super.super.max_value = frac_scale(&self->scale_now, UINT32_MAX);
|
||||
#if !MODULE_ZTIMER_ONDEMAND
|
||||
/* extend lower clock only if the ondemand driver isn't selected
|
||||
* otherwise, the clock extension will be called with the first
|
||||
* ztimer_acquire() call */
|
||||
ztimer_init_extend(&self->super.super);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
DEBUG("ztimer_convert_frac_init: rounding up val:%" PRIu32 "\n",
|
||||
@ -111,7 +120,7 @@ void ztimer_convert_frac_init(ztimer_convert_frac_t *self,
|
||||
self->round = freq_self / freq_lower;
|
||||
self->super.super.max_value = UINT32_MAX;
|
||||
}
|
||||
#ifdef MODULE_PM_LAYERED
|
||||
#if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
self->super.super.block_pm_mode = ZTIMER_CLOCK_NO_REQUIRED_PM_MODE;
|
||||
#endif
|
||||
}
|
||||
|
@ -98,6 +98,10 @@ static const ztimer_ops_t _ztimer_convert_muldiv64_ops = {
|
||||
.set = _ztimer_convert_muldiv64_set,
|
||||
.now = _ztimer_convert_muldiv64_now,
|
||||
.cancel = ztimer_convert_cancel,
|
||||
#if MODULE_ZTIMER_ONDEMAND
|
||||
.start = ztimer_convert_start,
|
||||
.stop = ztimer_convert_stop,
|
||||
#endif
|
||||
};
|
||||
|
||||
void ztimer_convert_muldiv64_init(
|
||||
@ -121,5 +125,10 @@ void ztimer_convert_muldiv64_init(
|
||||
ztimer_convert_muldiv64->super.super.ops = &_ztimer_convert_muldiv64_ops;
|
||||
ztimer_convert_muldiv64->div = div;
|
||||
ztimer_convert_muldiv64->mul = mul;
|
||||
#if !MODULE_ZTIMER_ONDEMAND
|
||||
/* extend lower clock only if the ondemand driver isn't selected
|
||||
* otherwise, the clock extension will be called with the first
|
||||
* ztimer_acquire() call */
|
||||
ztimer_init_extend(&ztimer_convert_muldiv64->super.super);
|
||||
#endif
|
||||
}
|
||||
|
@ -78,6 +78,10 @@ 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,
|
||||
#if MODULE_ZTIMER_ONDEMAND
|
||||
.start = ztimer_convert_start,
|
||||
.stop = ztimer_convert_stop,
|
||||
#endif
|
||||
};
|
||||
|
||||
void ztimer_convert_shift_up_init(ztimer_convert_shift_t *clock,
|
||||
|
@ -28,10 +28,11 @@
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "irq.h"
|
||||
#ifdef MODULE_PM_LAYERED
|
||||
#if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
#include "pm_layered.h"
|
||||
#endif
|
||||
#include "ztimer.h"
|
||||
#include "log.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
@ -49,6 +50,68 @@ static inline uint32_t _min_u32(uint32_t a, uint32_t b)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND
|
||||
static bool _ztimer_acquire(ztimer_clock_t *clock)
|
||||
{
|
||||
bool first_clock_user = false;
|
||||
unsigned state = irq_disable();
|
||||
|
||||
DEBUG("ztimer_acquire(): %p: %" PRIu16 " user(s)\n",
|
||||
(void *)clock, clock->users + 1);
|
||||
|
||||
if (clock->users++ == 0) {
|
||||
if (clock->ops->start) {
|
||||
clock->ops->start(clock);
|
||||
}
|
||||
|
||||
first_clock_user = true;
|
||||
}
|
||||
|
||||
irq_restore(state);
|
||||
|
||||
return first_clock_user;
|
||||
}
|
||||
|
||||
bool ztimer_acquire(ztimer_clock_t *clock)
|
||||
{
|
||||
bool first_clock_user = _ztimer_acquire(clock);
|
||||
|
||||
if (first_clock_user) {
|
||||
/* if the clock just has been enabled, make sure to set possibly
|
||||
* required checkpoints for clock extension */
|
||||
_ztimer_update(clock);
|
||||
}
|
||||
|
||||
return first_clock_user;
|
||||
}
|
||||
|
||||
bool ztimer_release(ztimer_clock_t *clock)
|
||||
{
|
||||
bool no_clock_user_left = false;
|
||||
unsigned state = irq_disable();
|
||||
|
||||
assert(clock->users > 0);
|
||||
|
||||
DEBUG("ztimer_release(): %p: %" PRIu16 " user(s)\n",
|
||||
(void *)clock, clock->users - 1);
|
||||
|
||||
if (--clock->users == 0) {
|
||||
/* make sure the timer isn't armed before turning off */
|
||||
clock->ops->cancel(clock);
|
||||
|
||||
if (clock->ops->stop) {
|
||||
clock->ops->stop(clock);
|
||||
}
|
||||
|
||||
no_clock_user_left = true;
|
||||
}
|
||||
|
||||
irq_restore(state);
|
||||
|
||||
return no_clock_user_left;
|
||||
}
|
||||
#endif /* MODULE_ZTIMER_ONDEMAND */
|
||||
|
||||
static unsigned _is_set(const ztimer_clock_t *clock, const ztimer_t *t)
|
||||
{
|
||||
if (!clock->list.next) {
|
||||
@ -71,13 +134,22 @@ unsigned ztimer_is_set(const ztimer_clock_t *clock, const ztimer_t *timer)
|
||||
bool ztimer_remove(ztimer_clock_t *clock, ztimer_t *timer)
|
||||
{
|
||||
bool was_removed = false;
|
||||
bool no_clock_user_left = false;
|
||||
unsigned state = irq_disable();
|
||||
|
||||
if (_is_set(clock, timer)) {
|
||||
_ztimer_update_head_offset(clock);
|
||||
was_removed = _del_entry_from_list(clock, &timer->base);
|
||||
|
||||
_ztimer_update(clock);
|
||||
#if MODULE_ZTIMER_ONDEMAND
|
||||
if (was_removed) {
|
||||
no_clock_user_left = ztimer_release(clock);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!no_clock_user_left) {
|
||||
_ztimer_update(clock);
|
||||
}
|
||||
}
|
||||
|
||||
irq_restore(state);
|
||||
@ -86,15 +158,29 @@ bool ztimer_remove(ztimer_clock_t *clock, ztimer_t *timer)
|
||||
|
||||
uint32_t ztimer_set(ztimer_clock_t *clock, ztimer_t *timer, uint32_t val)
|
||||
{
|
||||
unsigned state = irq_disable();
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND
|
||||
/* warm up our clock ... */
|
||||
if (_ztimer_acquire(clock) == true) {
|
||||
/* compensate delay that turning on the clock has introduced */
|
||||
if (val > clock->adjust_clock_start) {
|
||||
val -= clock->adjust_clock_start;
|
||||
}
|
||||
else {
|
||||
val = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG("ztimer_set(): %p: set %p at %" PRIu32 " offset %" PRIu32 "\n",
|
||||
(void *)clock, (void *)timer, clock->ops->now(clock), val);
|
||||
|
||||
unsigned state = irq_disable();
|
||||
|
||||
uint32_t now = _ztimer_update_head_offset(clock);
|
||||
|
||||
bool was_set = false;
|
||||
if (_is_set(clock, timer)) {
|
||||
_del_entry_from_list(clock, &timer->base);
|
||||
was_set = _del_entry_from_list(clock, &timer->base);
|
||||
}
|
||||
|
||||
/* optionally subtract a configurable adjustment value */
|
||||
@ -118,6 +204,20 @@ uint32_t ztimer_set(ztimer_clock_t *clock, ztimer_t *timer, uint32_t val)
|
||||
}
|
||||
|
||||
irq_restore(state);
|
||||
|
||||
/* the clock is armed now
|
||||
* everything down below doesn't impact timing */
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND
|
||||
if (was_set) {
|
||||
/* the given ztimer_t was set in the past
|
||||
* remove the previously set instance */
|
||||
ztimer_release(clock);
|
||||
}
|
||||
#else
|
||||
(void)was_set;
|
||||
#endif
|
||||
|
||||
return now;
|
||||
}
|
||||
|
||||
@ -127,7 +227,7 @@ static void _add_entry_to_list(ztimer_clock_t *clock, ztimer_base_t *entry)
|
||||
|
||||
ztimer_base_t *list = &clock->list;
|
||||
|
||||
#ifdef MODULE_PM_LAYERED
|
||||
#if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
/* First timer on the clock's linked list */
|
||||
if (list->next == NULL &&
|
||||
clock->block_pm_mode != ZTIMER_CLOCK_NO_REQUIRED_PM_MODE) {
|
||||
@ -266,7 +366,7 @@ static bool _del_entry_from_list(ztimer_clock_t *clock, ztimer_base_t *entry)
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
#ifdef MODULE_PM_LAYERED
|
||||
#if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
/* The last timer just got removed from the clock's linked list */
|
||||
if (clock->list.next == NULL &&
|
||||
clock->block_pm_mode != ZTIMER_CLOCK_NO_REQUIRED_PM_MODE) {
|
||||
@ -286,7 +386,7 @@ static ztimer_t *_now_next(ztimer_clock_t *clock)
|
||||
if (!entry->next) {
|
||||
/* The last timer just got removed from the clock's linked list */
|
||||
clock->last = NULL;
|
||||
#ifdef MODULE_PM_LAYERED
|
||||
#if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
if (clock->block_pm_mode != ZTIMER_CLOCK_NO_REQUIRED_PM_MODE) {
|
||||
pm_unblock(clock->block_pm_mode);
|
||||
}
|
||||
@ -337,6 +437,8 @@ static void _ztimer_update(ztimer_clock_t *clock)
|
||||
|
||||
void ztimer_handler(ztimer_clock_t *clock)
|
||||
{
|
||||
bool no_clock_user_left = false;
|
||||
|
||||
DEBUG("ztimer_handler(): %p now=%" PRIu32 "\n", (void *)clock, clock->ops->now(
|
||||
clock));
|
||||
if (IS_ACTIVE(ENABLE_DEBUG)) {
|
||||
@ -383,6 +485,12 @@ void ztimer_handler(ztimer_clock_t *clock)
|
||||
(void *)entry, (void *)entry->base.next, clock->ops->now(
|
||||
clock));
|
||||
entry->callback(entry->arg);
|
||||
#if MODULE_ZTIMER_ONDEMAND
|
||||
no_clock_user_left = ztimer_release(clock);
|
||||
if (no_clock_user_left) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
entry = _now_next(clock);
|
||||
if (!entry) {
|
||||
/* See if any more alarms expired during callback processing */
|
||||
@ -393,7 +501,10 @@ void ztimer_handler(ztimer_clock_t *clock)
|
||||
}
|
||||
}
|
||||
|
||||
_ztimer_update(clock);
|
||||
/* only arm the clock if there are users left requiring the clock */
|
||||
if (!no_clock_user_left) {
|
||||
_ztimer_update(clock);
|
||||
}
|
||||
|
||||
if (IS_ACTIVE(ENABLE_DEBUG)) {
|
||||
_ztimer_print(clock);
|
||||
@ -419,3 +530,17 @@ static void _ztimer_print(const ztimer_clock_t *clock)
|
||||
} while ((entry = entry->next));
|
||||
puts("");
|
||||
}
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND && DEVELHELP
|
||||
void _ztimer_assert_clock_active(ztimer_clock_t *clock)
|
||||
{
|
||||
if (clock->users == 0) {
|
||||
LOG_WARNING("WARNING! You are accessing ztimer_now() on a non-active clock!\n"
|
||||
" Make sure to call ztimer_acquire() before accessing ztimer_now().\n"
|
||||
" Once you've finished don't forget to call ztimer_release().\n");
|
||||
}
|
||||
#if MODULE_ZTIMER_ONDEMAND_STRICT
|
||||
assert(clock->users > 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
@ -292,7 +292,7 @@ void ztimer_init(void)
|
||||
CONFIG_ZTIMER_USEC_WIDTH);
|
||||
ztimer_periph_timer_init(&ZTIMER_TIMER, CONFIG_ZTIMER_USEC_DEV,
|
||||
ZTIMER_TIMER_FREQ, WIDTH_TO_MAXVAL(CONFIG_ZTIMER_USEC_WIDTH));
|
||||
# ifdef MODULE_PM_LAYERED
|
||||
# if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
LOG_DEBUG("ztimer_init(): ZTIMER_TIMER setting block_pm_mode to %i\n",
|
||||
CONFIG_ZTIMER_TIMER_BLOCK_PM_MODE);
|
||||
ZTIMER_TIMER_CLK.block_pm_mode = CONFIG_ZTIMER_TIMER_BLOCK_PM_MODE;
|
||||
@ -306,7 +306,7 @@ void ztimer_init(void)
|
||||
CONFIG_ZTIMER_LPTIMER_WIDTH);
|
||||
ztimer_periph_timer_init(&ZTIMER_LPTIMER, CONFIG_ZTIMER_LPTIMER_DEV,
|
||||
ZTIMER_LPTIMER_FREQ, WIDTH_TO_MAXVAL(CONFIG_ZTIMER_LPTIMER_WIDTH));
|
||||
# ifdef MODULE_PM_LAYERED
|
||||
# if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
LOG_DEBUG("ztimer_init(): ZTIMER_LPTIMER setting block_pm_mode to %i\n",
|
||||
CONFIG_ZTIMER_LPTIMER_BLOCK_PM_MODE);
|
||||
ZTIMER_LPTIMER_CLK.block_pm_mode = CONFIG_ZTIMER_LPTIMER_BLOCK_PM_MODE;
|
||||
@ -316,7 +316,7 @@ void ztimer_init(void)
|
||||
#if INIT_ZTIMER_RTT
|
||||
LOG_DEBUG("ztimer_init(): initializing rtt\n");
|
||||
ztimer_periph_rtt_init(&ZTIMER_RTT);
|
||||
# ifdef MODULE_PM_LAYERED
|
||||
# if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
LOG_DEBUG("ztimer_init(): ZTIMER_RTT setting block_pm_mode to %i\n",
|
||||
CONFIG_ZTIMER_RTT_BLOCK_PM_MODE);
|
||||
ZTIMER_RTT_CLK.block_pm_mode = CONFIG_ZTIMER_RTT_BLOCK_PM_MODE;
|
||||
@ -326,7 +326,7 @@ void ztimer_init(void)
|
||||
#if INIT_ZTIMER_RTC
|
||||
LOG_DEBUG("ztimer_init(): initializing rtc\n");
|
||||
ztimer_periph_rtc_init(&ZTIMER_RTC);
|
||||
# ifdef MODULE_PM_LAYERED
|
||||
# if MODULE_PM_LAYERED && !MODULE_ZTIMER_ONDEMAND
|
||||
LOG_DEBUG("ztimer_init(): ZTIMER_RTC setting block_pm_mode to %i\n",
|
||||
CONFIG_ZTIMER_RTC_BLOCK_PM_MODE);
|
||||
ZTIMER_RTC_CLK.block_pm_mode = CONFIG_ZTIMER_RTC_BLOCK_PM_MODE;
|
||||
@ -359,6 +359,13 @@ void ztimer_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
# if MODULE_ZTIMER_ONDEMAND
|
||||
/* configure 'adjust_clock_start' */
|
||||
if (CONFIG_ZTIMER_USEC_ADJUST_CLOCK_START) {
|
||||
ZTIMER_USEC->adjust_clock_start = CONFIG_ZTIMER_USEC_ADJUST_CLOCK_START;
|
||||
}
|
||||
# endif
|
||||
|
||||
/* calculate or set 'adjust_set' */
|
||||
if (CONFIG_ZTIMER_USEC_ADJUST_SET) {
|
||||
ZTIMER_USEC->adjust_set = CONFIG_ZTIMER_USEC_ADJUST_SET;
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include "assert.h"
|
||||
#include "ztimer/mock.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
@ -84,6 +85,8 @@ static void ztimer_mock_op_set(ztimer_clock_t *clock, uint32_t val)
|
||||
{
|
||||
ztimer_mock_t *self = (ztimer_mock_t *)clock;
|
||||
|
||||
assert(self->running == 1);
|
||||
|
||||
++self->calls.set;
|
||||
self->target = val & self->mask;
|
||||
self->armed = 1;
|
||||
@ -107,6 +110,8 @@ static void ztimer_mock_op_cancel(ztimer_clock_t *clock)
|
||||
{
|
||||
ztimer_mock_t *self = (ztimer_mock_t *)clock;
|
||||
|
||||
assert(self->running == 1);
|
||||
|
||||
++self->calls.cancel;
|
||||
DEBUG(
|
||||
"zmock_cancel: %3u now=0x%08" PRIx32 ", target=0x%08" PRIx32 " (%u)\n",
|
||||
@ -114,10 +119,34 @@ static void ztimer_mock_op_cancel(ztimer_clock_t *clock)
|
||||
self->armed = 0;
|
||||
}
|
||||
|
||||
static void ztimer_mock_op_start(ztimer_clock_t *clock)
|
||||
{
|
||||
ztimer_mock_t *self = (ztimer_mock_t *)clock;
|
||||
|
||||
++self->calls.start;
|
||||
DEBUG("zmock_start: %3u\n", self->calls.start);
|
||||
self->running = 1;
|
||||
}
|
||||
|
||||
static void ztimer_mock_op_stop(ztimer_clock_t *clock)
|
||||
{
|
||||
ztimer_mock_t *self = (ztimer_mock_t *)clock;
|
||||
|
||||
assert(self->armed == 0);
|
||||
|
||||
++self->calls.stop;
|
||||
DEBUG("zmock_stop: %3u\n", self->calls.stop);
|
||||
self->running = 0;
|
||||
}
|
||||
|
||||
static const ztimer_ops_t ztimer_mock_ops = {
|
||||
.set = ztimer_mock_op_set,
|
||||
.now = ztimer_mock_op_now,
|
||||
.cancel = ztimer_mock_op_cancel,
|
||||
#if MODULE_ZTIMER_ONDEMAND
|
||||
.start = ztimer_mock_op_start,
|
||||
.stop = ztimer_mock_op_stop,
|
||||
#endif
|
||||
};
|
||||
|
||||
void ztimer_mock_init(ztimer_mock_t *self, unsigned width)
|
||||
@ -128,8 +157,14 @@ void ztimer_mock_init(ztimer_mock_t *self, unsigned width)
|
||||
.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 !MODULE_ZTIMER_ONDEMAND
|
||||
/* turn the timer on by default if ondemand feature is not used */
|
||||
self->running = 1;
|
||||
#endif
|
||||
|
||||
DEBUG("zmock_init: %p width=%u mask=0x%08" PRIx32 " runnnig=%u\n", (void *)self, width,
|
||||
self->mask, self->running);
|
||||
if (max_value < UINT32_MAX) {
|
||||
self->super.ops->set(&self->super, self->super.max_value >> 1);
|
||||
}
|
||||
|
@ -43,18 +43,21 @@ int32_t ztimer_overhead_set(ztimer_clock_t *clock, uint32_t base)
|
||||
callback_arg_t arg = { .clock = clock, .val = &after };
|
||||
ztimer_t t = { .callback = _callback, .arg = &arg };
|
||||
|
||||
ztimer_acquire(clock);
|
||||
pre = ztimer_now(clock);
|
||||
ztimer_set(clock, &t, base);
|
||||
while (!after) {}
|
||||
ztimer_release(clock);
|
||||
return after - pre - base;
|
||||
}
|
||||
|
||||
int32_t ztimer_overhead_sleep(ztimer_clock_t *clock, uint32_t base)
|
||||
{
|
||||
ztimer_acquire(clock);
|
||||
uint32_t pre = ztimer_now(clock);
|
||||
|
||||
ztimer_sleep(clock, base);
|
||||
uint32_t after = ztimer_now(clock);
|
||||
ztimer_release(clock);
|
||||
|
||||
return after - pre - base;
|
||||
}
|
||||
|
@ -68,6 +68,8 @@ void ztimer_periodic_init(ztimer_clock_t *clock, ztimer_periodic_t *timer,
|
||||
|
||||
void ztimer_periodic_start(ztimer_periodic_t *timer)
|
||||
{
|
||||
ztimer_acquire(timer->clock);
|
||||
|
||||
uint32_t now = ztimer_now(timer->clock);
|
||||
|
||||
timer->last = now;
|
||||
@ -77,4 +79,6 @@ void ztimer_periodic_start(ztimer_periodic_t *timer)
|
||||
void ztimer_periodic_stop(ztimer_periodic_t *timer)
|
||||
{
|
||||
ztimer_remove(timer->clock, &timer->timer);
|
||||
|
||||
ztimer_release(timer->clock);
|
||||
}
|
||||
|
@ -138,10 +138,28 @@ static void _ztimer_periph_rtc_cancel(ztimer_clock_t *clock)
|
||||
rtc_clear_alarm();
|
||||
}
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND_RTC
|
||||
static void _ztimer_periph_rtc_start(ztimer_clock_t *clock)
|
||||
{
|
||||
(void)clock;
|
||||
rtc_poweron();
|
||||
}
|
||||
|
||||
static void _ztimer_periph_rtc_stop(ztimer_clock_t *clock)
|
||||
{
|
||||
(void)clock;
|
||||
rtc_poweroff();
|
||||
}
|
||||
#endif /* MODULE_ZTIMER_ONDEMAND_RTC */
|
||||
|
||||
static const ztimer_ops_t _ztimer_periph_rtc_ops = {
|
||||
.set = _ztimer_periph_rtc_set,
|
||||
.now = _ztimer_periph_rtc_now,
|
||||
.cancel = _ztimer_periph_rtc_cancel,
|
||||
#if MODULE_ZTIMER_ONDEMAND_RTC
|
||||
.start = _ztimer_periph_rtc_start,
|
||||
.stop = _ztimer_periph_rtc_stop,
|
||||
#endif
|
||||
};
|
||||
|
||||
void ztimer_periph_rtc_init(ztimer_periph_rtc_t *clock)
|
||||
@ -149,5 +167,10 @@ void ztimer_periph_rtc_init(ztimer_periph_rtc_t *clock)
|
||||
clock->ops = &_ztimer_periph_rtc_ops;
|
||||
clock->max_value = UINT32_MAX;
|
||||
rtc_init();
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND_RTC
|
||||
rtc_poweroff();
|
||||
#else
|
||||
rtc_poweron();
|
||||
#endif
|
||||
}
|
||||
|
@ -74,10 +74,28 @@ static void _ztimer_periph_rtt_cancel(ztimer_clock_t *clock)
|
||||
rtt_clear_alarm();
|
||||
}
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND_RTT
|
||||
static void _ztimer_periph_rtt_start(ztimer_clock_t *clock)
|
||||
{
|
||||
(void)clock;
|
||||
rtt_poweron();
|
||||
}
|
||||
|
||||
static void _ztimer_periph_rtt_stop(ztimer_clock_t *clock)
|
||||
{
|
||||
(void)clock;
|
||||
rtt_poweroff();
|
||||
}
|
||||
#endif /* MODULE_ZTIMER_ONDEMAND_RTT */
|
||||
|
||||
static const ztimer_ops_t _ztimer_periph_rtt_ops = {
|
||||
.set = _ztimer_periph_rtt_set,
|
||||
.now = _ztimer_periph_rtt_now,
|
||||
.cancel = _ztimer_periph_rtt_cancel,
|
||||
#if MODULE_ZTIMER_ONDEMAND_RTT
|
||||
.start = _ztimer_periph_rtt_start,
|
||||
.stop = _ztimer_periph_rtt_stop,
|
||||
#endif
|
||||
};
|
||||
|
||||
void ztimer_periph_rtt_init(ztimer_periph_rtt_t *clock)
|
||||
@ -85,6 +103,13 @@ void ztimer_periph_rtt_init(ztimer_periph_rtt_t *clock)
|
||||
clock->ops = &_ztimer_periph_rtt_ops;
|
||||
clock->max_value = RTT_MAX_VALUE;
|
||||
rtt_init();
|
||||
#if MODULE_ZTIMER_ONDEMAND_RTT
|
||||
rtt_poweroff();
|
||||
#else
|
||||
rtt_poweron();
|
||||
#endif
|
||||
|
||||
#if !MODULE_ZTIMER_ONDEMAND
|
||||
ztimer_init_extend(clock);
|
||||
#endif
|
||||
}
|
||||
|
@ -78,10 +78,30 @@ static void _ztimer_periph_timer_callback(void *arg, int channel)
|
||||
ztimer_handler((ztimer_clock_t *)arg);
|
||||
}
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND_TIMER
|
||||
static void _ztimer_periph_timer_start(ztimer_clock_t *clock)
|
||||
{
|
||||
ztimer_periph_timer_t *ztimer_periph = (ztimer_periph_timer_t *)clock;
|
||||
|
||||
timer_start(ztimer_periph->dev);
|
||||
}
|
||||
|
||||
static void _ztimer_periph_timer_stop(ztimer_clock_t *clock)
|
||||
{
|
||||
ztimer_periph_timer_t *ztimer_periph = (ztimer_periph_timer_t *)clock;
|
||||
|
||||
timer_stop(ztimer_periph->dev);
|
||||
}
|
||||
#endif /* MODULE_ZTIMER_ONDEMAND_TIMER */
|
||||
|
||||
static const ztimer_ops_t _ztimer_periph_timer_ops = {
|
||||
.set = _ztimer_periph_timer_set,
|
||||
.now = _ztimer_periph_timer_now,
|
||||
.cancel = _ztimer_periph_timer_cancel,
|
||||
#if MODULE_ZTIMER_ONDEMAND_TIMER
|
||||
.start = _ztimer_periph_timer_start,
|
||||
.stop = _ztimer_periph_timer_stop,
|
||||
#endif
|
||||
};
|
||||
|
||||
void ztimer_periph_timer_init(ztimer_periph_timer_t *clock, tim_t dev,
|
||||
@ -94,5 +114,17 @@ void ztimer_periph_timer_init(ztimer_periph_timer_t *clock, tim_t dev,
|
||||
|
||||
(void)ret;
|
||||
assert(ret == 0);
|
||||
|
||||
#if !MODULE_ZTIMER_ONDEMAND
|
||||
/* extend lower clock only if the ondemand driver isn't selected
|
||||
* otherwise, the clock extension will be called with the first
|
||||
* ztimer_acquire() call */
|
||||
ztimer_init_extend(&clock->super);
|
||||
#endif
|
||||
|
||||
#if MODULE_ZTIMER_ONDEMAND_TIMER
|
||||
/* turn off the timer peripheral after init
|
||||
* the first ztimer_acquire() call starts the peripheral */
|
||||
timer_stop(dev);
|
||||
#endif
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
USEMODULE += ztimer_core
|
||||
USEMODULE += ztimer_mock
|
||||
USEMODULE += ztimer_convert_muldiv64
|
||||
USEMODULE += ztimer_convert_frac
|
||||
USEMODULE += ztimer_ondemand
|
||||
|
@ -199,6 +199,10 @@ static void test_ztimer_mock_set16(void)
|
||||
ztimer_clock_t *z = &zmock.super;
|
||||
|
||||
ztimer_mock_init(&zmock, 16);
|
||||
|
||||
/* make sure ztimer stays turned on */
|
||||
ztimer_acquire(z);
|
||||
|
||||
uint32_t now = ztimer_now(z);
|
||||
TEST_ASSERT_EQUAL_INT(0, now);
|
||||
|
||||
|
193
tests/unittests/tests-ztimer/tests-ztimer-ondemand.c
Normal file
193
tests/unittests/tests-ztimer/tests-ztimer-ondemand.c
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2022 SSV Software Systems GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Unit tests for ztimer_ondemand
|
||||
*
|
||||
* @author Juergen Fitschen <me@jue.yt>
|
||||
*/
|
||||
|
||||
#include "ztimer.h"
|
||||
#include "ztimer/mock.h"
|
||||
#include "ztimer/convert_frac.h"
|
||||
|
||||
#include "embUnit/embUnit.h"
|
||||
|
||||
#include "tests-ztimer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void test_ztimer_ondemand_acquire_release(void)
|
||||
{
|
||||
ztimer_mock_t zmock;
|
||||
ztimer_clock_t *z = &zmock.super;
|
||||
|
||||
/* initialize 32bit mock timer */
|
||||
ztimer_mock_init(&zmock, 32);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.running);
|
||||
|
||||
ztimer_acquire(z);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.armed);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.calls.start);
|
||||
|
||||
ztimer_acquire(z);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.calls.start);
|
||||
|
||||
ztimer_release(z);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.running);
|
||||
|
||||
ztimer_release(z);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.armed);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.calls.stop);
|
||||
}
|
||||
|
||||
static void test_ztimer_ondemand_acquire_ops_unsupported(void)
|
||||
{
|
||||
ztimer_mock_t zmock;
|
||||
ztimer_ops_t zmock_ops;
|
||||
ztimer_clock_t *z = &zmock.super;
|
||||
|
||||
/* initialize mock timer without start and stop ops */
|
||||
ztimer_mock_init(&zmock, 24);
|
||||
zmock_ops = *zmock.super.ops;
|
||||
zmock_ops.start = NULL;
|
||||
zmock_ops.stop = NULL;
|
||||
zmock.super.ops = &zmock_ops;
|
||||
|
||||
ztimer_acquire(z);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.calls.start);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.armed);
|
||||
|
||||
ztimer_release(z);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.calls.stop);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.armed);
|
||||
}
|
||||
|
||||
static void test_ztimer_ondemand_acquire_release_extend(void)
|
||||
{
|
||||
ztimer_mock_t zmock;
|
||||
ztimer_clock_t *z = &zmock.super;
|
||||
|
||||
/* initialize 24bit mock timer that must be extended */
|
||||
ztimer_mock_init(&zmock, 24);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.running);
|
||||
|
||||
ztimer_acquire(z);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.armed);
|
||||
|
||||
ztimer_release(z);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.armed);
|
||||
}
|
||||
|
||||
#define HAS_BEEN_EXEC (1)
|
||||
|
||||
static void _set_cb(void *arg)
|
||||
{
|
||||
int *val = arg;
|
||||
*val = HAS_BEEN_EXEC;
|
||||
}
|
||||
|
||||
static void test_ztimer_ondemand_timers(void)
|
||||
{
|
||||
ztimer_mock_t zmock;
|
||||
ztimer_clock_t *z = &zmock.super;
|
||||
|
||||
ztimer_mock_init(&zmock, 32);
|
||||
z->adjust_clock_start = 10;
|
||||
|
||||
/* set first timer */
|
||||
int a_arg = 0;
|
||||
ztimer_t a = {.callback = _set_cb, .arg = &a_arg};
|
||||
const uint32_t a_val = 100;
|
||||
ztimer_set(z, &a, a_val);
|
||||
TEST_ASSERT_EQUAL_INT(a_val - z->adjust_clock_start, zmock.target);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.armed);
|
||||
|
||||
/* set second timer */
|
||||
int b_arg = 0;
|
||||
ztimer_t b = {.callback = _set_cb, .arg = &b_arg};
|
||||
const uint32_t b_val = 200;
|
||||
uint32_t diff = zmock.target;
|
||||
ztimer_set(z, &b, b_val);
|
||||
|
||||
/* let the first timer fire */
|
||||
ztimer_mock_advance(&zmock, zmock.target);
|
||||
TEST_ASSERT_EQUAL_INT(HAS_BEEN_EXEC, a_arg);
|
||||
TEST_ASSERT_EQUAL_INT(b_val - diff, zmock.target);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.armed);
|
||||
|
||||
/* let the second timer fire -> no users left! */
|
||||
ztimer_mock_advance(&zmock, zmock.target);
|
||||
TEST_ASSERT_EQUAL_INT(HAS_BEEN_EXEC, b_arg);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.armed);
|
||||
|
||||
/* set and remove timer */
|
||||
ztimer_set(z, &a, a_val);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.armed);
|
||||
ztimer_remove(z, &a);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.armed);
|
||||
}
|
||||
|
||||
static void test_ztimer_ondemand_timers_converted(void)
|
||||
{
|
||||
ztimer_mock_t zmock;
|
||||
ztimer_convert_frac_t zc;
|
||||
ztimer_clock_t *z = &zc.super.super;
|
||||
|
||||
/* the base timer must be extended */
|
||||
ztimer_mock_init(&zmock, 24);
|
||||
const uint32_t factor = 10;
|
||||
ztimer_convert_frac_init(&zc, &zmock.super, 1, factor);
|
||||
|
||||
z->adjust_clock_start = 10;
|
||||
|
||||
int a_arg = 0;
|
||||
ztimer_t a = {.callback = _set_cb, .arg = &a_arg};
|
||||
const uint32_t a_val = 100;
|
||||
ztimer_set(z, &a, a_val);
|
||||
TEST_ASSERT_EQUAL_INT((a_val - z->adjust_clock_start) * factor, zmock.target);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(1, zmock.armed);
|
||||
|
||||
ztimer_mock_advance(&zmock, zmock.target);
|
||||
TEST_ASSERT_EQUAL_INT(HAS_BEEN_EXEC, a_arg);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.running);
|
||||
TEST_ASSERT_EQUAL_INT(0, zmock.armed);
|
||||
}
|
||||
|
||||
Test *tests_ztimer_ondemand_tests(void)
|
||||
{
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
new_TestFixture(test_ztimer_ondemand_acquire_release),
|
||||
new_TestFixture(test_ztimer_ondemand_acquire_ops_unsupported),
|
||||
new_TestFixture(test_ztimer_ondemand_acquire_release_extend),
|
||||
new_TestFixture(test_ztimer_ondemand_timers),
|
||||
new_TestFixture(test_ztimer_ondemand_timers_converted),
|
||||
};
|
||||
|
||||
EMB_UNIT_TESTCALLER(ztimer_tests, NULL, NULL, fixtures);
|
||||
|
||||
return (Test *)&ztimer_tests;
|
||||
}
|
||||
|
||||
/** @} */
|
@ -21,10 +21,12 @@
|
||||
|
||||
Test *tests_ztimer_mock_tests(void);
|
||||
Test *tests_ztimer_convert_muldiv64_tests(void);
|
||||
Test *tests_ztimer_ondemand_tests(void);
|
||||
|
||||
void tests_ztimer(void)
|
||||
{
|
||||
TESTS_RUN(tests_ztimer_mock_tests());
|
||||
TESTS_RUN(tests_ztimer_convert_muldiv64_tests());
|
||||
TESTS_RUN(tests_ztimer_ondemand_tests());
|
||||
}
|
||||
/** @} */
|
||||
|
26
tests/ztimer_ondemand_benchmark/Makefile
Normal file
26
tests/ztimer_ondemand_benchmark/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
# required modules
|
||||
USEMODULE += ztimer
|
||||
USEMODULE += ztimer_ondemand
|
||||
USEMODULE += ztimer_ondemand_timer
|
||||
USEMODULE += ztimer_ondemand_rtt
|
||||
USEMODULE += ztimer_ondemand_rtc
|
||||
FEATURES_REQUIRED = periph_timer
|
||||
DEFAULT_MODULE += test_utils_interactive_sync
|
||||
|
||||
# Select clock to benchmark:
|
||||
# - ztimer_usec
|
||||
USEMODULE += ztimer_usec
|
||||
# - ztimer_msec
|
||||
#USEMODULE += ztimer_msec
|
||||
# - ztimer_sec
|
||||
#USEMODULE += ztimer_sec
|
||||
|
||||
# You may sepcify the compare timer device
|
||||
#CFLAGS += '-DCOMPARE_TIMER_DEV=TIMER_DEV(2)'
|
||||
|
||||
# You may sepcify the compare timer frequency
|
||||
#CFLAGS += '-DCOMPARE_TIMER_FREQ=9750000'
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
10
tests/ztimer_ondemand_benchmark/README.md
Normal file
10
tests/ztimer_ondemand_benchmark/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
ztimer_ondemand_benchmark
|
||||
=========================
|
||||
|
||||
This application measures latencies introduced by `ztimer_acquire()` and
|
||||
`ztimer_release()`. Since it is directly interacting with the underlying
|
||||
peripheral, you need at least two `periph_timer` instances if you're
|
||||
benchmarking a ztimer clock based on `ztimer_periph_timer`.
|
||||
|
||||
The application tries to select a unused timer automatically. If this selection
|
||||
process fails, users may specify device and frequency in this test's Makefile.
|
122
tests/ztimer_ondemand_benchmark/main.c
Normal file
122
tests/ztimer_ondemand_benchmark/main.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2022 SSV Software Systems GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Benchmarks ztimer_acquire() and ztimer_release()
|
||||
*
|
||||
* @author Juergen Fitschen <me@jue.yt>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "ztimer.h"
|
||||
#include "ztimer/config.h"
|
||||
#include "periph/timer.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#if IS_ACTIVE(MODULE_ZTIMER_SEC)
|
||||
# define CLOCK ZTIMER_SEC
|
||||
#elif IS_ACTIVE(MODULE_ZTIMER_MSEC)
|
||||
# define CLOCK ZTIMER_MSEC
|
||||
#elif IS_ACTIVE(MODULE_ZTIMER_USEC)
|
||||
# define CLOCK ZTIMER_USEC
|
||||
# define CLOCK_IS_ZTIMER_USEC
|
||||
#else
|
||||
# error "No ztimer clock selected!"
|
||||
#endif
|
||||
|
||||
#ifndef COMPARE_TIMER_DEV
|
||||
# if IS_ACTIVE(MODULE_ZTIMER_PERIPH_TIMER)
|
||||
# if CONFIG_ZTIMER_USEC_DEV == TIMER_DEV(0)
|
||||
# define COMPARE_TIMER_DEV TIMER_DEV(1)
|
||||
# else
|
||||
# define COMPARE_TIMER_DEV TIMER_DEV(0)
|
||||
# endif
|
||||
# else
|
||||
# define COMPARE_TIMER_DEV TIMER_DEV(0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef COMPARE_TIMER_FREQ
|
||||
# define COMPARE_TIMER_FREQ 1000000U
|
||||
#endif
|
||||
|
||||
static inline uint32_t bench_start(uint32_t adjust)
|
||||
{
|
||||
return timer_read(COMPARE_TIMER_DEV) + adjust;
|
||||
}
|
||||
|
||||
static inline uint32_t bench_finish(const char *name, uint32_t start)
|
||||
{
|
||||
uint32_t stop = timer_read(COMPARE_TIMER_DEV);
|
||||
uint32_t diff = stop - start;
|
||||
if (name) {
|
||||
printf(" - %s took %"PRIu32" ticks\n", name, diff);
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
static inline uint32_t bench_overhead(void) {
|
||||
uint32_t start = bench_start(0);
|
||||
uint32_t diff = bench_finish(NULL, start);
|
||||
return diff;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint32_t start, adjust;
|
||||
uint32_t first_acquire, second_acquire;
|
||||
uint32_t poweron_diff;
|
||||
|
||||
timer_init(COMPARE_TIMER_DEV, COMPARE_TIMER_FREQ, NULL, NULL);
|
||||
|
||||
adjust = bench_overhead();
|
||||
|
||||
printf("Compare timer frequency: %"PRIu32"Hz\n", (uint32_t) COMPARE_TIMER_FREQ);
|
||||
printf("Compare timer overhead: %"PRIu32" ticks (will be taken into account)\n", adjust);
|
||||
|
||||
puts("---");
|
||||
|
||||
puts("Benchmarking:");
|
||||
|
||||
start = bench_start(adjust);
|
||||
ztimer_acquire(CLOCK);
|
||||
first_acquire = bench_finish("ztimer_acquire() on inactive clock", start);
|
||||
|
||||
start = bench_start(adjust);
|
||||
ztimer_acquire(CLOCK);
|
||||
second_acquire = bench_finish("ztimer_acquire() on active clock", start);
|
||||
|
||||
start = bench_start(adjust);
|
||||
ztimer_release(CLOCK);
|
||||
bench_finish("ztimer_release() with users left", start);
|
||||
|
||||
start = bench_start(adjust);
|
||||
ztimer_release(CLOCK);
|
||||
bench_finish("ztimer_release() with no users left", start);
|
||||
|
||||
puts("---");
|
||||
poweron_diff = first_acquire - second_acquire;
|
||||
|
||||
#if defined(CLOCK_IS_ZTIMER_USEC) && COMPARE_TIMER_FREQ == 1000000U
|
||||
printf("Add this to you board.h:\n");
|
||||
printf(" #define CONFIG_ZTIMER_USEC_ADJUST_CLOCK_START %"PRIu32"\n", poweron_diff);
|
||||
#else
|
||||
printf("If the ztimer clock runs with with a frequency of %"PRIu32"Hz, set\n",
|
||||
(uint32_t) COMPARE_TIMER_FREQ);
|
||||
printf(" clock->adjust_clock_start = %"PRIu32"\n", poweron_diff);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user