1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #16928 from kaspar030/ztimer64

sys/ztimer64: initial PR
This commit is contained in:
Koen Zandberg 2021-12-09 10:17:15 +01:00 committed by GitHub
commit d959ce7eea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1673 additions and 4 deletions

View File

@ -49,7 +49,7 @@ extern "C" {
* to avoid not printing of debug in interrupts
*/
#ifndef THREAD_STACKSIZE_IDLE
#ifdef MODULE_XTIMER
#if MODULE_XTIMER || MODULE_ZTIMER64
/* xtimer's 64 bit arithmetic doesn't perform well on 8 bit archs. In order to
* prevent a stack overflow when an timer triggers while the idle thread is
* running, we have to increase the stack size then

View File

@ -165,7 +165,9 @@ PSEUDOMODULES += wakaama_objects_%
PSEUDOMODULES += wifi_enterprise
PSEUDOMODULES += xtimer_on_ztimer
PSEUDOMODULES += zptr
PSEUDOMODULES += ztimer%
PSEUDOMODULES += ztimer
PSEUDOMODULES += ztimer_%
PSEUDOMODULES += ztimer64_%
# ztimer's main module is called "ztimer_core"
NO_PSEUDOMODULES += ztimer_core

View File

@ -84,6 +84,7 @@ rsource "usb_board_reset/Kconfig"
rsource "vfs/Kconfig"
rsource "xtimer/Kconfig"
rsource "ztimer/Kconfig"
rsource "ztimer64/Kconfig"
config MODULE_CPP
bool "Use CPP compiler"

View File

@ -755,10 +755,15 @@ ifneq (,$(filter suit_%,$(USEMODULE)))
endif
# include ztimer dependencies
ifneq (,$(filter ztimer% %ztimer,$(USEMODULE)))
ifneq (,$(filter ztimer ztimer_% %ztimer,$(USEMODULE)))
include $(RIOTBASE)/sys/ztimer/Makefile.dep
endif
# include ztimer64 dependencies
ifneq (,$(filter ztimer64%,$(USEMODULE)))
include $(RIOTBASE)/sys/ztimer64/Makefile.dep
endif
ifneq (,$(filter evtimer,$(USEMODULE)))
ifneq (,$(filter evtimer_on_ztimer,$(USEMODULE)))
USEMODULE += ztimer_msec

View File

@ -33,6 +33,11 @@ void auto_init(void)
void ztimer_init(void);
ztimer_init();
}
if (IS_USED(MODULE_AUTO_INIT_ZTIMER64)) {
LOG_DEBUG("Auto init ztimer64.\n");
void ztimer64_init(void);
ztimer64_init();
}
if (IS_USED(MODULE_AUTO_INIT_XTIMER) &&
!IS_USED(MODULE_ZTIMER_XTIMER_COMPAT)) {
LOG_DEBUG("Auto init xtimer.\n");

534
sys/include/ztimer64.h Normal file
View File

@ -0,0 +1,534 @@
/*
* Copyright (C) 2021 Inria
* 2021 Freie Universität Berlin
* 2021 Kaspar Schleiser <kaspar@schleiser.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_ztimer
* @defgroup sys_ztimer64 ztimer 64bit version
* @brief ztimer 64bit version
*
* # Introduction
*
* This module implements a 64bit version of the ztimer API.
* All ztimer_* functions have a ztimer64_* equivalent that behaves the same,
* but allowing offsets >= 2^32.
*
* In addition to that, ztimer64 offers functions taking absolute target times
* as parameter.
* If the absolute target time is in the past, a timer would trigger right away.
*
* Here's a table for absolute variants:
*
* | relative version | absolute version |
* |--------------------------------|--------------------------------|
* | ztimer64_set() | ztimer64_set_at() |
* | ztimer64_sleep() | ztimer64_sleep_until() |
* | ztimer64_set_msg() | ztimer64_set_msg_at() |
* | ztimer64_set_wakeup() | ztimer64_set_wakeup_at() |
* | ztimer64_set_timeout_flag() | ztimer64_set_timeout_flag_at() |
* | ztimer64_msg_receive_timeout() | ztimer64_msg_receive_until() |
* | ztimer64_mutex_lock_timeout() | ztimer64_mutex_lock_until() |
* | ztimer64_rmutex_lock_timeout() | ztimer64_rmutex_lock_until() |
*
* ## ztimer64 clocks
*
* ztimer64 provides automatic setup of ZTIMER64_SEC, ZTIMER64_MSEC and
* ZTIMER64_USEC, using the ztimer(32) clocks as base clocks.
* Enable them by depending on ztimer64_sec, ztimer64_msec and / or ztimer64_usec.
*
* @warning ztimer64 always keeps a timer running on the base clock. Depending
* on the base clock, this blocks low-power sleep modes. If the hardware
* supports it, the msec and sec timers should be using a low-power capable
* timer (e.g., periph_rtt). ztimer64_usec will almost certainly block
* low-power sleep.
*
* TODO:
* - some explicit power management
* - implement adjust_set and adjust_sleep API
* - implement setting a 64bit reference time (epoch)
*
* @{
*
* @file
* @brief ztimer 64bit API
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef ZTIMER64_H
#define ZTIMER64_H
#include <stdint.h>
#include "mutex.h"
#include "msg.h"
#include "ztimer.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ztimer64_t forward declaration
*/
typedef struct ztimer64_base ztimer64_base_t;
/**
* @brief ztimer64_clock_t forward declaration
*/
typedef struct ztimer64_clock ztimer64_clock_t;
/**
* @brief Minimum information for each timer
*/
struct ztimer64_base {
ztimer64_base_t *next; /**< next timer in list */
uint64_t target; /**< absolute target time */
};
/**
* @brief ztimer64 structure
*
* This type represents an instance of a timer, which is set on an
* underlying clock object
*/
typedef struct {
ztimer64_base_t base; /**< clock list entry */
void (*callback)(void *arg); /**< timer callback function pointer */
void *arg; /**< timer callback argument */
} ztimer64_t;
/**
* @brief ztimer64 clock structure
*/
struct ztimer64_clock {
ztimer64_base_t *first; /**< list of active timers */
ztimer_clock_t *base_clock; /**< 32bit clock backend */
ztimer_t base_timer; /**< 32bit backend timer */
uint64_t checkpoint; /**< lower timer checkpoint offset */
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_PM_LAYERED || DOXYGEN
uint8_t block_pm_mode; /**< min. pm mode to block for the clock to run */
#endif
};
/**
* @brief Get the current time from a clock
*
* @param[in] clock ztimer clock to operate on
*
* @return Current count on @p clock
*/
uint64_t ztimer64_now(ztimer64_clock_t *clock);
/**
* @brief Get absolute target time for a clock given offset
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] offset relative target time
*
* @returns absolute target time
*/
static inline uint64_t ztimer64_offset2absolute(ztimer64_clock_t *clock,
uint64_t offset)
{
unsigned state = irq_disable();
uint64_t result = ztimer64_now(clock) + offset;
irq_restore(state);
return result;
}
/**
* @brief Set a timer on a clock (absolute version)
*
* This will place @p timer in the timer targets queue of @p clock.
*
* @note The memory pointed to by @p timer is not copied and must
* remain in scope until the callback is fired or the timer
* is removed via @ref ztimer64_remove
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] timer timer entry to set
* @param[in] target absolute target time
*/
void ztimer64_set_at(ztimer64_clock_t *clock, ztimer64_t *timer,
uint64_t target);
/**
* @brief Set a timer on a clock (relative version)
*
* This will place @p timer in the timer targets queue of @p clock.
*
* @note The memory pointed to by @p timer is not copied and must
* remain in scope until the callback is fired or the timer
* is removed via @ref ztimer64_remove
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] timer timer entry to set
* @param[in] offset relative target time
*/
static inline void ztimer64_set(ztimer64_clock_t *clock, ztimer64_t *timer,
uint64_t offset)
{
ztimer64_set_at(clock, timer,
ztimer64_offset2absolute(clock, offset));
}
/**
* @brief Check if a timer is currently active
*
* @param[in] timer timer to check
*
* @return 1 if timer is active
* @return 0 if timer is not active
*/
unsigned ztimer64_is_set(const ztimer64_t *timer);
/**
* @brief Remove a timer from a clock
*
* This will place @p timer in the timer targets queue for @p clock.
*
* This function does nothing if @p timer is not found in the timer queue of
* @p clock.
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] timer timer entry to remove
*/
void ztimer64_remove(ztimer64_clock_t *clock, ztimer64_t *timer);
/**
* @brief Post a message at a specified time
*
* This function sets a timer that will send a message at time @p target.
*
* @note The memory pointed to by @p timer and @p msg will not be copied, i.e.
* `*timer` and `*msg` needs to remain valid until the timer has triggered.
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] timer ztimer64 timer struct to use
* @param[in] target absolute target time
* @param[in] msg pointer to msg that will be sent
* @param[in] target_pid pid the message will be sent to
*/
void ztimer64_set_msg_at(ztimer64_clock_t *clock, ztimer64_t *timer,
uint64_t target,
msg_t *msg, kernel_pid_t target_pid);
/**
* @brief Post a message after a delay (relative version)
*
* This function sets a timer that will send a message @p offset ticks
* from now.
*
* @note The memory pointed to by @p timer and @p msg will not be copied, i.e.
* `*timer` and `*msg` needs to remain valid until the timer has triggered.
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] timer ztimer64 timer struct to use
* @param[in] offset relative target time
* @param[in] msg pointer to msg that will be sent
* @param[in] target_pid pid the message will be sent to
*/
static inline void ztimer64_set_msg(ztimer64_clock_t *clock, ztimer64_t *timer,
uint64_t offset,
msg_t *msg, kernel_pid_t target_pid)
{
ztimer64_set_msg_at(clock, timer,
ztimer64_offset2absolute(clock, offset),
msg, target_pid);
}
/**
* @brief receive a message (blocking, with absolute timeout)
*
* Similar to msg_receive(), but with a timeout parameter.
* The function will return after waiting at most until @p target.
*
* @note: This might function might leave a message with type MSG_ZTIMER64 in the
* thread's message queue, which must be handled (ignored).
*
* @param[in] clock ztimer64 clock to operate on
* @param[out] msg pointer to buffer which will be filled if a
* message is received
* @param[in] target absolute target, in @p clock time
*
* @return >=0 if a message was received
* @return -ETIME on timeout
*/
int ztimer64_msg_receive_until(ztimer64_clock_t *clock, msg_t *msg,
uint64_t target);
/**
* @brief receive a message (blocking, with relative timeout)
*
* Similar to msg_receive(), but with a timeout parameter.
* The function will return after waiting at most @p timeout ticks.
*
* @note: This might function might leave a message with type MSG_ZTIMER64 in the
* thread's message queue, which must be handled (ignored).
*
* @param[in] clock ztimer64 clock to operate on
* @param[out] msg pointer to buffer which will be filled if a
* message is received
* @param[in] timeout relative timeout, in @p clock time units
*
* @return >=0 if a message was received
* @return -ETIME on timeout
*/
static inline int ztimer64_msg_receive_timeout(ztimer64_clock_t *clock,
msg_t *msg,
uint64_t timeout)
{
return ztimer64_msg_receive_until(clock, msg,
ztimer64_offset2absolute(clock, timeout));
}
#define MSG_ZTIMER64 0xc83f /**< msg type used by ztimer64_msg_receive_timeout */
/**
* @brief Suspend the calling thread until the time (@p last_wakeup + @p period)
*
* This function can be used to create periodic wakeups.
*
* When the function returns, @p last_wakeup is set to
* (@p last_wakeup + @p period).
*
* @c last_wakeup should be set to ztimer64_now(@p clock) before first call of the
* function.
*
* If the time (@p last_wakeup + @p period) has already passed, the function
* sets @p last_wakeup to @p last_wakeup + @p period and returns immediately.
*
* @note if you period is smaller than 2^32 ticks, consider using the 32bit version.
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] last_wakeup base time stamp for the wakeup
* @param[in] period time in ticks that will be added to @p last_wakeup
*/
void ztimer64_periodic_wakeup(ztimer64_clock_t *clock, uint64_t *last_wakeup,
uint64_t period);
/**
* @brief Put the calling thread to sleep until the specified time
*
* @param[in] clock ztimer64 clock to use
* @param[in] target wakeup time, in @p clock time
*/
void ztimer64_sleep_until(ztimer64_clock_t *clock, uint64_t target);
/**
* @brief Put the calling thread to sleep for the specified number of ticks
*
* @param[in] clock ztimer64 clock to use
* @param[in] duration duration of sleep, in @p ztimer time units
*/
static inline void ztimer64_sleep(ztimer64_clock_t *clock, uint64_t duration)
{
ztimer64_sleep_until(clock, ztimer64_offset2absolute(clock, duration));
}
/**
* @brief Busy-wait until specified target time
*
* @note: This blocks lower priority threads. Use only for *very* short delays.
*
* @param[in] clock ztimer64 clock to use
* @param[in] target time when spinning should end, in @p clock time
*/
static inline void ztimer64_spin_until(ztimer64_clock_t *clock, uint64_t target)
{
while (ztimer64_now(clock) <= target) {}
}
/**
* @brief Set a timer that wakes up a thread (absolute version)
*
* This function sets a timer that will wake up a thread when the timer has
* expired.
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] timer timer struct to work with.
* @param[in] target wakeup time
* @param[in] pid pid of the thread that will be woken up
*/
void ztimer64_set_wakeup_at(ztimer64_clock_t *clock, ztimer64_t *timer,
uint64_t target,
kernel_pid_t pid);
/**
* @brief Set a timer that wakes up a thread (relative version)
*
* This function sets a timer that will wake up a thread when the timer has
* expired.
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] timer timer struct to work with.
* @param[in] offset clock ticks from now
* @param[in] pid pid of the thread that will be woken up
*/
static inline void ztimer64_set_wakeup(ztimer64_clock_t *clock,
ztimer64_t *timer, uint64_t offset,
kernel_pid_t pid)
{
ztimer64_set_wakeup_at(clock, timer,
ztimer64_offset2absolute(clock, offset), pid);
}
/**
* @brief Set timeout thread flag at @p target time
*
* This function will set THREAD_FLAG_TIMEOUT on the current thread at time
* @p target.
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] timer timer struct to use
* @param[in] target target in @p clock time
*/
void ztimer64_set_timeout_flag_at(ztimer64_clock_t *clock, ztimer64_t *timer,
uint64_t target);
/**
* @brief Set timeout thread flag after @p timeout
*
* This function will set THREAD_FLAG_TIMEOUT on the current thread after @p
* timeout usec have passed.
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] timer timer struct to use
* @param[in] timeout timeout in ztimer64_clock's ticks
*/
static inline void ztimer64_set_timeout_flag(ztimer64_clock_t *clock,
ztimer64_t *timer,
uint64_t timeout)
{
ztimer64_set_timeout_flag_at(clock, timer,
ztimer64_offset2absolute(clock, timeout));
}
/**
* @brief Try to lock the given mutex, but give up at @p target time
*
* @param[in] clock ztimer64 clock to operate on
* @param[in,out] mutex Mutex object to lock
* @param[in] target time after which to give up
*
* @retval 0 Success, caller has the mutex
* @retval -ECANCELED Failed to obtain mutex within @p timeout
*/
int ztimer64_mutex_lock_until(ztimer64_clock_t *clock, mutex_t *mutex,
uint64_t target);
/**
* @brief Try to lock the given mutex, but give up after @p timeout
*
* @param[in] clock ztimer64 clock to operate on
* @param[in,out] mutex Mutex object to lock
* @param[in] timeout timeout after which to give up
*
* @retval 0 Success, caller has the mutex
* @retval -ECANCELED Failed to obtain mutex within @p timeout
*/
static inline int ztimer64_mutex_lock_timeout(ztimer64_clock_t *clock,
mutex_t *mutex,
uint64_t timeout)
{
return ztimer64_mutex_lock_until(clock, mutex,
ztimer64_offset2absolute(clock, timeout));
}
/**
* @brief Try to lock the given rmutex, but give up at @p time
*
* @param[in] clock ztimer64 clock to operate on
* @param[in,out] rmutex rmutex object to lock
* @param[in] target time after which to give up
*
* @retval 0 Success, caller has the rmutex
* @retval -ECANCELED Failed to obtain rmutex before @p time
*/
int ztimer64_rmutex_lock_until(ztimer64_clock_t *clock, rmutex_t *rmutex,
uint64_t target);
/**
* @brief Try to lock the given rmutex, but give up after @p timeout
*
* @param[in] clock ztimer64 clock to operate on
* @param[in,out] rmutex rmutex object to lock
* @param[in] timeout timeout after which to give up
*
* @retval 0 Success, caller has the rmutex
* @retval -ECANCELED Failed to obtain rmutex within @p timeout
*/
static inline int ztimer64_rmutex_lock_timeout(ztimer64_clock_t *clock,
rmutex_t *rmutex,
uint64_t timeout)
{
return ztimer64_rmutex_lock_until(clock, rmutex,
ztimer64_offset2absolute(clock, timeout));
}
/**
* @brief Update ztimer clock head list offset
*
* @internal
*
* @param[in] clock ztimer clock to work on
*/
void ztimer64_update_head_offset(ztimer64_clock_t *clock);
/**
* @brief Initialize the board-specific default ztimer configuration
*/
void ztimer64_init(void);
/**
* @brief Initialize @p clock to be run from @p base_clock
* @param[in,out] clock Clock to initialize
* @param[in] base_clock Base clock to use
*/
void ztimer64_clock_init(ztimer64_clock_t *clock, ztimer_clock_t *base_clock);
/* default ztimer virtual devices */
/**
* @brief Default ztimer microsecond clock
*/
extern ztimer64_clock_t *const ZTIMER64_USEC;
/**
* @brief Default ztimer millisecond clock
*/
extern ztimer64_clock_t *const ZTIMER64_MSEC;
/**
* @brief Default ztimer second clock
*/
extern ztimer64_clock_t *const ZTIMER64_SEC;
/**
* @brief Measure ztimer64 overhead
*
* This function can be used to measure the overhead incurred by ztimer64.
* It will configure a callback to trigger after @p base ticks, then return the
* number of ticks that have passed, minus @p base.
*
* @param[in] clock ztimer64 clock to operate on
* @param[in] base base interval to use
* @return (time from ztimer64_set() until callback) - base
*/
int64_t ztimer64_overhead(ztimer64_clock_t *clock, uint64_t base);
#ifdef __cplusplus
}
#endif
#endif /* ZTIMER64_H */
/** @} */

View File

@ -24,6 +24,17 @@
#include "irq.h"
#include "ztimer/periph_timer.h"
#ifndef ZTIMER_PERIPH_TIMER_OFFSET
/* This can be used for testing. E.g.,
*
* CFLAGS="-DZTIMER_PERIPH_TIMER_OFFSET=4000000000LU" make ...
*
* The value will be added to every lower level timer read.
* @note this breaks if the low-level timer doesn't have 32bit width!
*/
#define ZTIMER_PERIPH_TIMER_OFFSET 0LU
#endif
static void _ztimer_periph_timer_set(ztimer_clock_t *clock, uint32_t val)
{
ztimer_periph_timer_t *ztimer_periph = (ztimer_periph_timer_t *)clock;
@ -51,7 +62,7 @@ static uint32_t _ztimer_periph_timer_now(ztimer_clock_t *clock)
{
ztimer_periph_timer_t *ztimer_periph = (ztimer_periph_timer_t *)clock;
return timer_read(ztimer_periph->dev);
return timer_read(ztimer_periph->dev) + ZTIMER_PERIPH_TIMER_OFFSET;
}
static void _ztimer_periph_timer_cancel(ztimer_clock_t *clock)

47
sys/ztimer64/Kconfig Normal file
View File

@ -0,0 +1,47 @@
# Copyright (c) 2021 Inria
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
menu "ztimer64 - High level timer abstraction layer"
# TODO: this extra indirection in the entry point for ztimer_usec is required
# to allow xtimer compatibility modules to depend on ztimer being there while
# still selecting ztimer_usec.
config ZTIMER64_USEC
bool "Microseconds 64bit Timer"
select ZTIMER_USEC
select MODULE_ZTIMER64
select MODULE_ZTIMER64_USEC
config MODULE_ZTIMER64_USEC
bool
select MODULE_ZTIMER_USEC
config MODULE_ZTIMER64_MSEC
bool "Milliseconds 64bit Timer"
select MODULE_ZTIMER_MSEC
config MODULE_ZTIMER64_SEC
bool "Milliseconds 64bit Timer"
select MODULE_ZTIMER_SEC
config MODULE_ZTIMER64_INIT
bool
config MODULE_ZTIMER64
bool
depends on TEST_KCONFIG
if MODULE_ZTIMER64
config MODULE_AUTO_INIT_ZTIMER64
bool "Auto initialize ztimer64"
depends on MODULE_AUTO_INIT
select MODULE_ZTIMER_INIT
select MODULE_ZTIMER64_INIT
default y
endif # MODULE_ZTIMER64
endmenu

1
sys/ztimer64/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

20
sys/ztimer64/Makefile.dep Normal file
View File

@ -0,0 +1,20 @@
#
# ztimer64 dependencies
#
USEMODULE += ztimer
USEMODULE += ztimer64
ifneq (,$(filter ztimer64_usec,$(USEMODULE)))
DEFAULT_MODULE += auto_init_ztimer64
USEMODULE += ztimer_usec
endif
ifneq (,$(filter ztimer64_msec,$(USEMODULE)))
DEFAULT_MODULE += auto_init_ztimer64
USEMODULE += ztimer_msec
endif
ifneq (,$(filter ztimer64_sec,$(USEMODULE)))
DEFAULT_MODULE += auto_init_ztimer64
USEMODULE += ztimer_sec
endif

50
sys/ztimer64/overhead.c Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2020 Kaspar Schleiser <kaspar@schleiser.de>
* 2020 Freie Universität Berlin
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @ingroup sys_ztimer64
* @{
*
* @file
* @brief ztimer64 overhead measurement functions
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include "atomic_utils.h"
#include "ztimer64.h"
typedef struct {
ztimer64_clock_t *clock;
volatile uint64_t *val;
} callback_arg_t;
static void _callback(void *arg)
{
callback_arg_t *callback_arg = (callback_arg_t *)arg;
atomic_store_u64(callback_arg->val, ztimer64_now(callback_arg->clock));
}
int64_t ztimer64_overhead(ztimer64_clock_t *clock, uint64_t base)
{
volatile uint64_t after = 0;
uint64_t pre;
callback_arg_t arg = { .clock = clock, .val = &after };
ztimer64_t t = { .callback = _callback, .arg = &arg };
pre = ztimer64_now(clock);
ztimer64_set(clock, &t, base);
while (!atomic_load_u64(&after)) {}
return after - pre - base;
}

192
sys/ztimer64/util.c Normal file
View File

@ -0,0 +1,192 @@
/*
* Copyright (C) 2021 Kaspar Schleiser <kaspar@schleiser.de>
* 2021 Inria
* 2021 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @defgroup sys_ztimer64_util ztimer64 utility functions
* @ingroup sys_ztimer64
* @{
*
* @file
* @brief ztimer64 high-level utility function implementations
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <assert.h>
#include <errno.h>
#include "irq.h"
#include "mutex.h"
#include "rmutex.h"
#include "thread.h"
#include "ztimer64.h"
static void _callback_unlock_mutex(void *arg)
{
mutex_t *mutex = (mutex_t *)arg;
mutex_unlock(mutex);
}
void ztimer64_sleep_until(ztimer64_clock_t *clock, uint64_t target)
{
assert(!irq_is_in());
mutex_t mutex = MUTEX_INIT_LOCKED;
ztimer64_t timer = {
.callback = _callback_unlock_mutex,
.arg = (void *)&mutex,
};
ztimer64_set_at(clock, &timer, target);
mutex_lock(&mutex);
}
void ztimer64_periodic_wakeup(ztimer64_clock_t *clock, uint64_t *last_wakeup,
uint64_t period)
{
unsigned state = irq_disable();
uint64_t now = ztimer64_now(clock);
uint64_t target = *last_wakeup + period;
irq_restore(state);
if (target > now) {
ztimer64_sleep_until(clock, target);
*last_wakeup = target;
}
else {
*last_wakeup = now;
}
}
#ifdef MODULE_CORE_MSG
static void _callback_msg(void *arg)
{
msg_t *msg = (msg_t *)arg;
msg_send_int(msg, msg->sender_pid);
}
static inline void _setup_msg(ztimer64_t *timer, msg_t *msg,
kernel_pid_t target_pid)
{
timer->callback = _callback_msg;
timer->arg = (void *)msg;
/* use sender_pid field to get target_pid into callback function */
msg->sender_pid = target_pid;
}
void ztimer64_set_msg_at(ztimer64_clock_t *clock, ztimer64_t *timer,
uint64_t target,
msg_t *msg, kernel_pid_t target_pid)
{
_setup_msg(timer, msg, target_pid);
ztimer64_set_at(clock, timer, target);
}
int ztimer64_msg_receive_until(ztimer64_clock_t *clock, msg_t *msg,
uint64_t target)
{
if (msg_try_receive(msg) == 1) {
return 1;
}
ztimer64_t t;
msg_t m = { .type = MSG_ZTIMER, .content.ptr = &m };
ztimer64_set_msg_at(clock, &t, target, &m, thread_getpid());
msg_receive(msg);
ztimer64_remove(clock, &t);
if (msg->type == MSG_ZTIMER64 && msg->content.ptr == &m) {
/* we hit the timeout */
return -ETIME;
}
else {
return 1;
}
}
#endif /* MODULE_CORE_MSG */
#ifdef MODULE_CORE_THREAD_FLAGS
static void _set_timeout_flag_callback(void *arg)
{
thread_flags_set(arg, THREAD_FLAG_TIMEOUT);
}
void ztimer64_set_timeout_flag_at(ztimer64_clock_t *clock, ztimer64_t *t,
uint64_t target)
{
t->callback = _set_timeout_flag_callback;
t->arg = thread_get_active();
thread_flags_clear(THREAD_FLAG_TIMEOUT);
ztimer64_set(clock, t, target);
}
#endif
static void _callback_wakeup(void *arg)
{
thread_wakeup((kernel_pid_t)((intptr_t)arg));
}
void ztimer64_set_wakeup_at(ztimer64_clock_t *clock, ztimer64_t *timer,
uint64_t target,
kernel_pid_t pid)
{
ztimer64_remove(clock, timer);
timer->callback = _callback_wakeup;
timer->arg = (void *)((intptr_t)pid);
ztimer64_set_at(clock, timer, target);
}
static void timeout_cb(void *arg)
{
mutex_cancel(arg);
}
int ztimer64_mutex_lock_until(ztimer64_clock_t *clock, mutex_t *mutex,
uint64_t target)
{
if (mutex_trylock(mutex)) {
return 0;
}
mutex_cancel_t mc = mutex_cancel_init(mutex);
ztimer64_t t = { .callback = timeout_cb, .arg = &mc };
ztimer64_set_at(clock, &t, target);
if (mutex_lock_cancelable(&mc)) {
return -ECANCELED;
}
ztimer64_remove(clock, &t);
return 0;
}
int ztimer64_rmutex_lock_until(ztimer64_clock_t *clock, rmutex_t *rmutex,
uint64_t target)
{
if (rmutex_trylock(rmutex)) {
return 0;
}
if (ztimer64_mutex_lock_until(clock, &rmutex->mutex, target) == 0) {
atomic_store_explicit(&rmutex->owner,
thread_getpid(), memory_order_relaxed);
rmutex->refcount++;
return 0;
}
return -ECANCELED;
}

300
sys/ztimer64/ztimer64.c Normal file
View File

@ -0,0 +1,300 @@
/*
* Copyright (C) 2021 Kaspar Schleiser <kaspar@schleiser.de>
* 2021 Freie Universität Berlin
* 2021 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @ingroup sys_ztimer64
* @{
*
* @file
* @brief ztimer64 core functionality
*
* This file contains ztimer64's main API implementation and functionality
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <assert.h>
#include <stdint.h>
#include <inttypes.h>
#include "ztimer64.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/* Checkpointing interval, don't change. See `ztimer64_now()` for an explanation
* of the checkpointing.
*/
#define ZTIMER64_CHECKPOINT_INTERVAL (1LU << 31)
static int _add_entry_to_list(ztimer64_clock_t *clock, ztimer64_base_t *entry);
static int _del_entry_from_list(ztimer64_clock_t *clock,
ztimer64_base_t *entry);
static void _ztimer64_update(ztimer64_clock_t *clock);
/* debug aid */
void ztimer64_clock_print(const ztimer64_clock_t *clock);
static unsigned _is_set(const ztimer64_t *t)
{
/* no timer can ever trigger at `target==0`, so we use that
* as marker */
return !!(t->base.target);
}
unsigned ztimer64_is_set(const ztimer64_t *timer)
{
return _is_set(timer);
}
void ztimer64_remove(ztimer64_clock_t *clock, ztimer64_t *timer)
{
unsigned state = irq_disable();
if (_is_set(timer)) {
if (_del_entry_from_list(clock, &timer->base) == 0) {
_ztimer64_update(clock);
}
}
irq_restore(state);
}
void ztimer64_set_at(ztimer64_clock_t *clock, ztimer64_t *timer, uint64_t val)
{
unsigned state = irq_disable();
if (_is_set(timer)) {
_del_entry_from_list(clock, &timer->base);
}
/* optionally subtract a configurable adjustment value */
/* there is no API to set this ATM */
if (val > clock->adjust_set) {
val -= clock->adjust_set;
}
else {
val = 0;
}
timer->base.target = val;
/* NOTE: if val is 0 at this point, this will be fixed to "now()" in _ztimer64_update() */
if (_add_entry_to_list(clock, &timer->base)) {
_ztimer64_update(clock);
}
irq_restore(state);
}
static int _add_entry_to_list(ztimer64_clock_t *clock, ztimer64_base_t *entry)
{
ztimer64_base_t *pos = clock->first;
if (pos) {
if (pos->target > entry->target) {
/* special case: new entry's target is earlier than old list head */
entry->next = pos;
clock->first = entry;
return 1;
}
else {
/* Jump past all entries which are set to an earlier target than the new entry */
while (pos->next && pos->next->target <= entry->target) {
pos = pos->next;
}
entry->next = pos->next;
pos->next = entry;
return 0;
}
}
else {
/* adding first entry */
entry->next = 0;
clock->first = entry;
return 1;
}
}
static int _del_entry_from_list(ztimer64_clock_t *clock, ztimer64_base_t *entry)
{
DEBUG("_del_entry_from_list()\n");
assert(_is_set((ztimer64_t *)entry));
if (clock->first == entry) {
/* special case: removing first entry */
clock->first = entry->next;
entry->next = 0;
return 1;
}
else {
ztimer64_base_t *pos = clock->first;
while (pos->next) {
if (pos->next == entry) {
pos->next = entry->next;
break;
}
pos = pos->next;
}
entry->next = 0;
return 0;
}
}
uint64_t ztimer64_now(ztimer64_clock_t *clock)
{
uint64_t now;
unsigned state = irq_disable();
uint32_t base_now = ztimer_now(clock->base_clock);
/* ztimer64 checkpointing works by storing
* the upper 33 bits in clock->checkpoint.
* The final time is the lower 32bit time ORed
* with that checkpoint.
* The highest bit of the lower 32bit is used
* as control bit. If the checkpoint and 32bit
* time both have that bit set (or not), no correction is necessary.
* If checkpoint and 32bit time disagree on that bit,
* the checkpoint lags behind, and needs to be updated.
* This works as long as the checkpoint is at most (2**32-1) behind,
* which is ensured by setting the base clock timer at most
* ZTIMER64_CHECKPOINT_INTERVAL into the future.
*/
if ((clock->checkpoint & ZTIMER64_CHECKPOINT_INTERVAL)
^ (base_now & ZTIMER64_CHECKPOINT_INTERVAL)) {
clock->checkpoint += ZTIMER64_CHECKPOINT_INTERVAL;
}
now = clock->checkpoint | base_now;
irq_restore(state);
return now;
}
static void _ztimer64_update(ztimer64_clock_t *clock)
{
uint64_t now = ztimer64_now(clock);
uint64_t next_checkpoint = clock->checkpoint + ZTIMER64_CHECKPOINT_INTERVAL;
uint64_t target;
if (next_checkpoint < now) {
next_checkpoint = now;
}
if (!clock->first) {
target = next_checkpoint;
}
else {
uint64_t next = clock->first->target;
if (next > now) {
if (next < next_checkpoint) {
/* timer within range */
target = next;
}
else {
/* timer out of range */
target = next_checkpoint;
}
}
else {
/* timer has passed already, trigger ASAP */
target = now;
/* if we arrive here due to a timer with "target==0" being set,
* fix this here to consider it "set".
*/
if (next == 0) {
clock->first->target = now;
}
}
}
ztimer_set(clock->base_clock, &clock->base_timer, target - now);
}
void ztimer64_handler(void *arg)
{
ztimer64_clock_t *clock = arg;
while (clock->first) {
uint64_t now = ztimer64_now(clock);
if (clock->first->target <= now) {
ztimer64_t *timer = (ztimer64_t *)clock->first;
/* remove timer from list */
clock->first = clock->first->next;
/* clear timer struct so it can be re-set */
timer->base.next = NULL;
timer->base.target = 0;
/* shoot callback */
timer->callback(timer->arg);
}
else {
/* timer target is in the future */
break;
}
}
_ztimer64_update(clock);
}
void ztimer64_clock_init(ztimer64_clock_t *clock, ztimer_clock_t *base_clock)
{
*clock =
(ztimer64_clock_t){ .base_clock = base_clock,
.base_timer =
{ .callback = ztimer64_handler, .arg = clock } };
/* initialize checkpointing */
_ztimer64_update(clock);
}
#if MODULE_ZTIMER64_USEC
static ztimer64_clock_t _ztimer64_usec;
ztimer64_clock_t *const ZTIMER64_USEC = &_ztimer64_usec;
#endif
#if MODULE_ZTIMER64_MSEC
static ztimer64_clock_t _ztimer64_msec;
ztimer64_clock_t *const ZTIMER64_MSEC = &_ztimer64_msec;
#endif
#if MODULE_ZTIMER64_SEC
static ztimer64_clock_t _ztimer64_sec;
ztimer64_clock_t *const ZTIMER64_SEC = &_ztimer64_sec;
#endif
void ztimer64_init(void)
{
#if MODULE_ZTIMER64_USEC
ztimer64_clock_init(ZTIMER64_USEC, ZTIMER_USEC);
#endif
#if MODULE_ZTIMER64_MSEC
ztimer64_clock_init(ZTIMER64_MSEC, ZTIMER_MSEC);
#endif
#if MODULE_ZTIMER64_SEC
ztimer64_clock_init(ZTIMER64_SEC, ZTIMER_SEC);
#endif
}
void ztimer64_clock_print(const ztimer64_clock_t *clock)
{
const ztimer64_base_t *entry = clock->first;
while (entry) {
printf("0x%08x:%" PRIu64 "\n", (unsigned)entry,
entry->target);
entry = entry->next;
}
puts("");
}

View File

@ -0,0 +1,6 @@
# avoid clang warning in tests-ztimer/tests-ztimer-extend.c:141
ifeq (llvm,$(TOOLCHAIN))
CFLAGS += -Wno-gnu-folding-constant
endif
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
USEMODULE += ztimer_core
USEMODULE += ztimer_mock
USEMODULE += ztimer64

View File

@ -0,0 +1,216 @@
/*
* 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 Unittests for ztimer64
*
*/
#include <stdio.h>
#include <string.h>
#include "ztimer.h"
#include "ztimer64.h"
#include "ztimer/mock.h"
#include "embUnit/embUnit.h"
#include "tests-ztimer64.h"
#define ZTIMER64_CHECKPOINT_INTERVAL (1LLU << 31)
/**
* @brief Simple callback for counting alarms
*/
static void cb_incr(void *arg)
{
uint32_t *ptr = arg;
*ptr += 1;
}
static ztimer_mock_t zmock;
static ztimer_clock_t *z = &zmock.super;
static ztimer64_clock_t z64mock;
static ztimer64_clock_t *z64 = &z64mock;
static void setup(void)
{
memset(&zmock, '\0', sizeof(ztimer_mock_t));
memset(&z64mock, '\0', sizeof(ztimer64_clock_t));
/* ztimer base clock is already extended to 32bit */
ztimer_mock_init(&zmock, 32);
ztimer64_clock_init(z64, z);
}
/**
* @brief
*/
static void test_ztimer64_now(void)
{
uint64_t now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(0, now64);
ztimer_mock_advance(&zmock, 123);
now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(123, now64);
ztimer_mock_jump(&zmock, 0x10000000ul);
now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(0x10000000ul, now64);
ztimer_mock_advance(&zmock, 0x98765432ul);
now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(0xa8765432ul, now64);
/* 32bit overflow */
ztimer_mock_advance(&zmock, 0x41234567ul);
ztimer_mock_advance(&zmock, 0x40000000ul);
now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(0x129999999ul, now64);
}
/**
* @brief Testing 32 bit wide mock clock set functionality
*/
static void test_ztimer64_set(void)
{
uint64_t now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(0, now64);
uint32_t count = 0;
ztimer64_t alarm = { .callback = cb_incr, .arg = &count, };
ztimer64_set(z64, &alarm, 1000);
ztimer_mock_advance(&zmock, 1); /* now = 1*/
TEST_ASSERT_EQUAL_INT(0, count);
ztimer_mock_advance(&zmock, 100); /* now = 101 */
TEST_ASSERT_EQUAL_INT(0, count);
ztimer_mock_advance(&zmock, 898); /* now = 999 */
now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(999, now64);
TEST_ASSERT_EQUAL_INT(0, count);
ztimer_mock_advance(&zmock, 1); /* now = 1000*/
TEST_ASSERT_EQUAL_INT(1, count);
ztimer_mock_advance(&zmock, 1); /* now = 1001*/
TEST_ASSERT_EQUAL_INT(1, count);
ztimer_mock_advance(&zmock, 1000); /* now = 2001*/
TEST_ASSERT_EQUAL_INT(1, count);
ztimer64_set(z64, &alarm, 3);
ztimer_mock_advance(&zmock, 999); /* now = 3000*/
TEST_ASSERT_EQUAL_INT(2, count);
ztimer64_set(z64, &alarm, 4000001000ul);
ztimer_mock_advance(&zmock, 1000); /* now = 4000*/
TEST_ASSERT_EQUAL_INT(2, count);
ztimer_mock_advance(&zmock, 4000000000ul); /* now = 4000004000*/
now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(4000004000ul, now64);
TEST_ASSERT_EQUAL_INT(3, count);
/* 32bit overflow */
ztimer64_set(z64, &alarm, 8000000000ul);
ztimer_mock_advance(&zmock, 4000000000ul);
ztimer_mock_advance(&zmock, 3999999999ul); /* now = 12000003999*/
TEST_ASSERT_EQUAL_INT(3, count);
ztimer_mock_advance(&zmock, 1ul); /* now = 12000004000*/
TEST_ASSERT_EQUAL_INT(4, count);
ztimer64_set(z64, &alarm, 15);
ztimer_mock_advance(&zmock, 14);
ztimer64_remove(z64, &alarm);
ztimer_mock_advance(&zmock, 1000);
TEST_ASSERT_EQUAL_INT(4, count);
}
/**
* @brief Testing 32 bit wide mock clock set functionality
*/
static void test_ztimer64_set_0(void)
{
uint64_t now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(0, now64);
uint32_t count = 0;
ztimer64_t alarm = { .callback = cb_incr, .arg = &count, };
/* should trigger asap, note that for a real timer this will be
to minimal value that guarantees the ISR not to be missed,
e.g: CONFIG_ZTIMER_USEC_MIN or RTT_MIN_OFFSET */
ztimer64_set(z64, &alarm, 0);
ztimer_mock_advance(&zmock, 1); /* now = 1*/
TEST_ASSERT_EQUAL_INT(1, count);
}
/**
* @brief Testing timers set in the past
*/
static void test_ztimer64_set_at(void)
{
uint64_t now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(0, now64);
uint32_t count = 0;
ztimer64_t alarm = { .callback = cb_incr, .arg = &count, };
/* test setting an offset that is now in the past, should trigger asap */
ztimer_mock_advance(&zmock, 1010); /* now = 1010 */
ztimer64_set_at(z64, &alarm, 1000);
/* should trigger on next tick */
ztimer_mock_advance(&zmock, 1); /* now = 1011 */
TEST_ASSERT_EQUAL_INT(1, count);
}
static void test_ztimer64_checkpoint(void)
{
uint64_t now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(0, now64);
/* base_now is 0x7fff_ffff */
ztimer_mock_jump(&zmock, ZTIMER64_CHECKPOINT_INTERVAL - 1);
now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(ZTIMER64_CHECKPOINT_INTERVAL - 1, now64);
/* base_now is 0x8000_0000 */
ztimer_mock_jump(&zmock, ZTIMER64_CHECKPOINT_INTERVAL);
now64 = ztimer64_now(z64);
TEST_ASSERT_EQUAL_INT(ZTIMER64_CHECKPOINT_INTERVAL, now64);
/* base_now is 0x0000_0000 */
ztimer_mock_jump(&zmock, 0);
now64 = ztimer64_now(z64);
/* overflow is caught in checkpoint */
TEST_ASSERT_EQUAL_INT(2 * ZTIMER64_CHECKPOINT_INTERVAL, now64);
/* base_now is 0xffff_ffff, max jump that can be caught */
ztimer_mock_jump(&zmock, UINT32_MAX);
now64 = ztimer64_now(z64);
/* overflow is caught in checkpoint */
TEST_ASSERT_EQUAL_INT(2 * ZTIMER64_CHECKPOINT_INTERVAL + UINT32_MAX, now64);
/* overflow is missed, 2**32 ticks elapsed
- microseconds: 4293 seconds
- nanoseconds: 4.29 seconds (e.g.: ptp) -> should not use ztimer as base.
*/
ztimer_mock_jump(&zmock, UINT32_MAX);
TEST_ASSERT_EQUAL_INT(2 * ZTIMER64_CHECKPOINT_INTERVAL + UINT32_MAX, now64);
}
Test *tests_ztimer64_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_ztimer64_now),
new_TestFixture(test_ztimer64_set),
new_TestFixture(test_ztimer64_set_0),
new_TestFixture(test_ztimer64_set_at),
new_TestFixture(test_ztimer64_checkpoint),
};
EMB_UNIT_TESTCALLER(ztimer64_tests, setup, NULL, fixtures);
return (Test *)&ztimer64_tests;
}
/** @} */

View File

@ -0,0 +1,25 @@
/*
* 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 Unittest entry point for the ztimer64 test group
*
*/
#include "embUnit/embUnit.h"
#include "tests-ztimer64.h"
Test *tests_ztimer64_tests(void);
void tests_ztimer64(void)
{
TESTS_RUN(tests_ztimer64_tests());
}
/** @} */

View File

@ -0,0 +1,34 @@
/*
* 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.
*/
/**
* @addtogroup unittests
* @{
*
* @file
* @brief Unittests for ztimer64
*
*/
#ifndef TESTS_ZTIMER64_H
#define TESTS_ZTIMER64_H
#include "embUnit.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The entry point of this test suite.
*/
void tests_ztimer(void);
#ifdef __cplusplus
}
#endif
#endif /* TESTS_ZTIMER64_H */
/** @} */

View File

@ -0,0 +1,11 @@
include ../Makefile.tests_common
USEMODULE += ztimer64_usec
# uncomment this to test using ztimer64 msec on rtt
#USEMODULE += ztimer64_msec ztimer_periph_rtt
# uncomment this to test using ztimer64 sec on rtc
#USEMODULE += ztimer64_sec ztimer_periph_rtc
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,12 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-nano \
arduino-uno \
atmega328p \
atmega328p-xplained-mini \
nucleo-f031k6 \
nucleo-l011k4 \
samd10-xmini \
stm32f030f4-demo \
#

View File

@ -0,0 +1,9 @@
# Overview
This test application is a direct translation of ztimer_msg to the ztimer64API.
It is meant mostly as a means to do size comparisons, thus tries to be as close
as possible to the original.
One notable change is the option to choose a different ztimer clock.
By default, the test will use ZTIMER64_USEC, unless ZTIMER64_MSEC is compiled in,
which will be used in that case.

View File

@ -0,0 +1,3 @@
# this file enables modules defined in Kconfig. Do not use this file for
# application configuration. This is only needed during migration.
CONFIG_ZTIMER64_USEC=y

152
tests/ztimer64_msg/main.c Normal file
View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2015-19 Kaspar Schleiser <kaspar@schleiser.de>
* 2013 INRIA
* 2017 HAW Hamburg
*
* 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 ztimer64_msg test application
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
* @author Sebastian Meiling <s@mlng.net>
* @}
*/
#include <stdio.h>
#include "ztimer64.h"
#include "thread.h"
#include "msg.h"
#include "timex.h"
#include "test_utils/expect.h"
#ifdef MODULE_ZTIMER64_SEC
#define ZTIMER64 ZTIMER64_SEC
#define TICKS_PER_SEC 1
#elif MODULE_ZTIMER64_MSEC
#define ZTIMER64 ZTIMER64_MSEC
#define TICKS_PER_SEC MS_PER_SEC
#else
#define ZTIMER64 ZTIMER64_USEC
#define TICKS_PER_SEC US_PER_SEC
#endif
char timer_stack[THREAD_STACKSIZE_DEFAULT];
char timer_stack_local[THREAD_STACKSIZE_DEFAULT];
struct timer_msg {
ztimer64_t timer;
uint64_t interval;
char *text;
msg_t msg;
};
struct timer_msg msg_a = { .interval = (2 * TICKS_PER_SEC),
.text = "Hello World" };
struct timer_msg msg_b = { .interval = (5 * TICKS_PER_SEC),
.text = "This is a Test" };
void *timer_thread(void *arg)
{
(void)arg;
printf("This is thread %" PRIkernel_pid "\n", thread_getpid());
/* The queue is required to avoid loss of a 2nd message, when the 1st is
* still processed. The timing ensures that at most 1 message is queued.
*/
msg_t msgq[1];
msg_init_queue(msgq, 1);
while (1) {
msg_t m;
msg_receive(&m);
struct timer_msg *tmsg = m.content.ptr;
uint64_t now = ztimer64_now(ZTIMER64);
/* casts are needed to solve for sometimes TICKS_PER_SEC being UL
* result of / and % of uint32_t will always fit into uint32_t
*/
printf(
"now=%" PRIu32 ":%" PRIu32 " -> every %" PRIu32 ".%" PRIu32 "s: %s\n",
(uint32_t)(now / TICKS_PER_SEC),
(uint32_t)(now % TICKS_PER_SEC),
(uint32_t)(tmsg->interval / TICKS_PER_SEC),
(uint32_t)(tmsg->interval % TICKS_PER_SEC),
tmsg->text);
tmsg->msg.type = 12345;
tmsg->msg.content.ptr = tmsg;
ztimer64_set_msg(ZTIMER64, &tmsg->timer, tmsg->interval, &tmsg->msg,
thread_getpid());
}
}
void *timer_thread_local(void *arg)
{
(void)arg;
printf("This is thread %" PRIkernel_pid "\n", thread_getpid());
while (1) {
msg_t m;
msg_receive(&m);
uint64_t now = ztimer64_now(ZTIMER64);
uint32_t sec = now / TICKS_PER_SEC;
uint32_t min = sec / 60;
uint32_t hr = sec / 3600;
printf("sec=%" PRIu32 " min=%" PRIu32 " hour=%" PRIu32 "\n", sec, min,
hr);
}
}
int main(void)
{
msg_t m;
kernel_pid_t pid = thread_create(
timer_stack,
sizeof(timer_stack),
THREAD_PRIORITY_MAIN - 1,
THREAD_CREATE_STACKTEST,
timer_thread,
NULL,
"timer");
expect(pid_is_valid(pid));
puts("sending 1st msg");
m.content.ptr = &msg_a;
msg_try_send(&m, pid);
puts("sending 2nd msg");
m.content.ptr = &msg_b;
msg_try_send(&m, pid);
kernel_pid_t pid2 = thread_create(
timer_stack_local,
sizeof(timer_stack_local),
THREAD_PRIORITY_MAIN - 1,
THREAD_CREATE_STACKTEST,
timer_thread_local,
NULL,
"timer local");
expect(pid_is_valid(pid2));
while (1) {
ztimer64_sleep(ZTIMER64, 1LLU * TICKS_PER_SEC);
msg_try_send(&m, pid2);
}
}

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
# Copyright (C) 2017 HAW Hamburg
#
# 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.
import sys
from testrunner import run
def testfunc(child):
# 1st check for periodic 2s Hello World message, i.e., 2 output + 1 msg
for _ in range(7):
child.expect(r"sec=\d+ min=\d+ hour=\d+")
child.expect(r"sec=\d+ min=\d+ hour=\d+")
child.expect(r"now=\d+:\d+ -> every 2.0s: Hello World")
# 2nd check for periodic 5s test message, i.e., 5 output + 1 msg
for _ in range(3):
child.expect(r"sec=\d+ min=\d+ hour=\d+")
child.expect(r"sec=\d+ min=\d+ hour=\d+")
child.expect(r"sec=\d+ min=\d+ hour=\d+")
child.expect(r"sec=\d+ min=\d+ hour=\d+")
child.expect(r"sec=\d+ min=\d+ hour=\d+")
child.expect(r"now=\d+:\d+ -> every 5.0s: This is a Test")
if __name__ == "__main__":
sys.exit(run(testfunc))