1
0
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:
Juergen Fitschen 2022-12-06 18:30:16 +01:00 committed by GitHub
commit 71a606a1db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 841 additions and 28 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
USEMODULE += ztimer_core
USEMODULE += ztimer_mock
USEMODULE += ztimer_convert_muldiv64
USEMODULE += ztimer_convert_frac
USEMODULE += ztimer_ondemand

View File

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

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

View File

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

View 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

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

View 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;
}