1
0
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:
Jue 2022-02-02 18:56:49 +01:00
parent 904dc0131f
commit a228ca7548
8 changed files with 288 additions and 10 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,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

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

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

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

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

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

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

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