mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
ztimer: introduce ztimer_acquire() and ztimer_release()
This commit is contained in:
parent
904dc0131f
commit
a228ca7548
@ -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,6 +387,9 @@ 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 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 */
|
||||
@ -394,6 +411,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
|
||||
*
|
||||
@ -512,14 +570,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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -49,6 +49,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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
USEMODULE += ztimer_core
|
||||
USEMODULE += ztimer_mock
|
||||
USEMODULE += ztimer_convert_muldiv64
|
||||
USEMODULE += ztimer_ondemand
|
||||
|
109
tests/unittests/tests-ztimer/tests-ztimer-ondemand.c
Normal file
109
tests/unittests/tests-ztimer/tests-ztimer-ondemand.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 "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);
|
||||
}
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
/** @} */
|
||||
|
Loading…
Reference in New Issue
Block a user