sys: clock: additional sys_clock api calls
Additional entries for the sys_clock API, comprised of: * sys_clock_gettime() * sys_clock_settime() * sys_clock_nanosleep() along with the constants * SYS_CLOCK_REALTIME * SYS_CLOCK_MONOTONIC * SYS_TIMER_ABSTIME The primary motivation for this API is so that libc and other libraries have a familiar-enough API to reach to when POSIX is not available, since POSIX is optional in Zephyr. By adding this API to lib/os, we also eliminate dependency cycles between libc and posix, as lib/os is a mutual dependency. Signed-off-by: Chris Friedt <cfriedt@tenstorrent.com>
This commit is contained in:
parent
282b47cf37
commit
a3e934f12c
134
include/zephyr/sys/clock.h
Normal file
134
include/zephyr/sys/clock.h
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Tenstorrent AI ULC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief System clock APIs
|
||||
*
|
||||
* APIs for getting, setting, and sleeping with respect to system clocks.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYSCLOCK_H_
|
||||
#define ZEPHYR_INCLUDE_SYSCLOCK_H_
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <zephyr/toolchain.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @addtogroup clock_apis
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief The real-time clock (i.e. "wall clock")
|
||||
*
|
||||
* This clock is used to measure time since the epoch (1970-01-01 00:00:00 UTC).
|
||||
*
|
||||
* It is not a steady clock; i.e. it may be adjusted for a number of reasons from initialization
|
||||
* of a hardware real-time-clock, to network-time synchronization, to manual adjustment from the
|
||||
* application.
|
||||
*/
|
||||
#define SYS_CLOCK_REALTIME 1
|
||||
|
||||
/**
|
||||
* @brief The monotonic clock
|
||||
*
|
||||
* This steady clock is used to measure time since the system booted. Time from this clock is
|
||||
* always monotonically increasing.
|
||||
*/
|
||||
#define SYS_CLOCK_MONOTONIC 4
|
||||
|
||||
/**
|
||||
* @brief The flag used for specifying absolute timeouts
|
||||
*
|
||||
* This flag may be passed to @ref sys_clock_nanosleep to indicate the requested timeout is an
|
||||
* absolute time with respect to the specified clock.
|
||||
*/
|
||||
#define SYS_TIMER_ABSTIME 4
|
||||
|
||||
/**
|
||||
* @brief Get the offset @ref SYS_CLOCK_REALTIME with respect to @ref SYS_CLOCK_MONOTONIC
|
||||
*
|
||||
* The "wall clock" (i.e. @ref SYS_CLOCK_REALTIME) depends on a base time that is set by the
|
||||
* system. The base time may be updated for a number of reasons, such as initialization of a
|
||||
* hardware real-time-clock (RTC), network time protocol (NTP) synchronization, or manual
|
||||
* adjustment by the application.
|
||||
*
|
||||
* This function retrieves the current time offset, as a `timespec` object, for
|
||||
* @ref SYS_CLOCK_REALTIME, with respect to @ref SYS_CLOCK_MONOTONIC, and writes it to the
|
||||
* provided memory location pointed-to by @a tp.
|
||||
*
|
||||
* @note This function may assert if @a tp is NULL.
|
||||
*
|
||||
* @param tp Pointer to memory where time will be written.
|
||||
*/
|
||||
__syscall void sys_clock_getrtoffset(struct timespec *tp);
|
||||
|
||||
/**
|
||||
* @brief Get the current time from the specified clock
|
||||
*
|
||||
* @param clock_id The clock from which to query time.
|
||||
* @param tp Pointer to memory where time will be written.
|
||||
* @retval 0 on success.
|
||||
* @retval -EINVAL when an invalid @a clock_id is specified.
|
||||
*/
|
||||
int sys_clock_gettime(int clock_id, struct timespec *tp);
|
||||
|
||||
/**
|
||||
* @brief Set the current time for the specified clock
|
||||
*
|
||||
* @param clock_id The clock for which the time should be set.
|
||||
* @param tp Pointer to memory specifying the desired time.
|
||||
* @retval 0 on success.
|
||||
* @retval -EINVAL when an invalid @a clock_id is specified or when @a tp contains nanoseconds
|
||||
* outside of the range `[0, 999999999]`.
|
||||
*/
|
||||
__syscall int sys_clock_settime(int clock_id, const struct timespec *tp);
|
||||
|
||||
/**
|
||||
* @brief Sleep for the specified amount of time with respect to the specified clock.
|
||||
*
|
||||
* This function will cause the calling thread to sleep either
|
||||
* - until the absolute time specified by @a rqtp (if @a flags includes @ref SYS_TIMER_ABSTIME), or
|
||||
* - until the relative time specified by @a rqtp (if @a flags does not include
|
||||
* @ref SYS_TIMER_ABSTIME).
|
||||
*
|
||||
* The accepted values for @a clock_id include
|
||||
* - @ref SYS_CLOCK_REALTIME
|
||||
* - @ref SYS_CLOCK_MONOTONIC
|
||||
*
|
||||
* If @a rmtp is not NULL, and the thread is awoken prior to the time specified by @a rqtp, then
|
||||
* any remaining time will be written to @a rmtp. If the thread has slept for at least the time
|
||||
* specified by @a rqtp, then @a rmtp will be set to zero.
|
||||
*
|
||||
* @param clock_id The clock to by which to sleep.
|
||||
* @param flags Flags to modify the behavior of the sleep operation.
|
||||
* @param rqtp Pointer to the requested time to sleep.
|
||||
* @param rmtp Pointer to memory into which to copy the remaining time, if any.
|
||||
*
|
||||
* @retval 0 on success.
|
||||
* @retval -EINVAL when an invalid @a clock_id, when @a rqtp contains nanoseconds outside of the
|
||||
* range `[0, 999999999]`, or when @a rqtp contains a negative value.
|
||||
*/
|
||||
__syscall int sys_clock_nanosleep(int clock_id, int flags, const struct timespec *rqtp,
|
||||
struct timespec *rmtp);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <zephyr/syscalls/clock.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -1,11 +1,13 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_syscall_header(
|
||||
${ZEPHYR_BASE}/include/zephyr/sys/clock.h
|
||||
${ZEPHYR_BASE}/include/zephyr/sys/mutex.h
|
||||
)
|
||||
|
||||
zephyr_sources(
|
||||
cbprintf_packaged.c
|
||||
clock.c
|
||||
printk.c
|
||||
sem.c
|
||||
thread_entry.c
|
||||
|
||||
226
lib/os/clock.c
Normal file
226
lib/os/clock.c
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
* Copyright (c) 2018 Friedt Professional Engineering Services, Inc
|
||||
* Copyright (c) 2025 Tenstorrent AI ULC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/internal/syscall_handler.h>
|
||||
#include <zephyr/sys/clock.h>
|
||||
#include <zephyr/sys/timeutil.h>
|
||||
#include <zephyr/toolchain.h>
|
||||
|
||||
/*
|
||||
* `k_uptime_get` returns a timestamp offset on an always increasing
|
||||
* value from the system start. To support the `SYS_CLOCK_REALTIME`
|
||||
* clock, this `rt_clock_offset` records the time that the system was
|
||||
* started. This can either be set via 'sys_clock_settime', or could be
|
||||
* set from a real time clock, if such hardware is present.
|
||||
*/
|
||||
static struct timespec rt_clock_offset;
|
||||
static struct k_spinlock rt_clock_offset_lock;
|
||||
|
||||
static bool is_valid_clock_id(int clock_id)
|
||||
{
|
||||
switch (clock_id) {
|
||||
case SYS_CLOCK_MONOTONIC:
|
||||
case SYS_CLOCK_REALTIME:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void timespec_from_ticks(uint64_t ticks, struct timespec *ts)
|
||||
{
|
||||
uint64_t elapsed_secs = ticks / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
|
||||
uint64_t nremainder = ticks % CONFIG_SYS_CLOCK_TICKS_PER_SEC;
|
||||
|
||||
*ts = (struct timespec){
|
||||
.tv_sec = (time_t)elapsed_secs,
|
||||
/* For ns 32 bit conversion can be used since its smaller than 1sec. */
|
||||
.tv_nsec = (int32_t)k_ticks_to_ns_floor32(nremainder),
|
||||
};
|
||||
}
|
||||
|
||||
int sys_clock_gettime(int clock_id, struct timespec *ts)
|
||||
{
|
||||
if (!is_valid_clock_id(clock_id)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (clock_id) {
|
||||
case SYS_CLOCK_REALTIME: {
|
||||
struct timespec offset;
|
||||
|
||||
timespec_from_ticks(k_uptime_ticks(), ts);
|
||||
sys_clock_getrtoffset(&offset);
|
||||
if (unlikely(!timespec_add(ts, &offset))) {
|
||||
/* Saturate rather than reporting an overflow in 292 billion years */
|
||||
*ts = (struct timespec){
|
||||
.tv_sec = (time_t)INT64_MAX,
|
||||
.tv_nsec = NSEC_PER_SEC - 1,
|
||||
};
|
||||
}
|
||||
} break;
|
||||
|
||||
case SYS_CLOCK_MONOTONIC:
|
||||
timespec_from_ticks(k_uptime_ticks(), ts);
|
||||
break;
|
||||
|
||||
default:
|
||||
CODE_UNREACHABLE;
|
||||
return -EINVAL; /* Should never reach here */
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(timespec_is_valid(ts));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void z_impl_sys_clock_getrtoffset(struct timespec *tp)
|
||||
{
|
||||
__ASSERT_NO_MSG(tp != NULL);
|
||||
|
||||
K_SPINLOCK(&rt_clock_offset_lock) {
|
||||
*tp = rt_clock_offset;
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(timespec_is_valid(tp));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
void z_vrfy_sys_clock_getrtoffset(struct timespec *tp)
|
||||
{
|
||||
K_OOPS(K_SYSCALL_MEMORY_WRITE(tp, sizeof(*tp)));
|
||||
return z_impl_sys_clock_getrtoffset(tp);
|
||||
}
|
||||
#include <zephyr/syscalls/sys_clock_getrtoffset_mrsh.c>
|
||||
#endif /* CONFIG_USERSPACE */
|
||||
|
||||
int z_impl_sys_clock_settime(int clock_id, const struct timespec *tp)
|
||||
{
|
||||
struct timespec offset;
|
||||
|
||||
if (clock_id != SYS_CLOCK_REALTIME) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!timespec_is_valid(tp)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timespec_from_ticks(k_uptime_ticks(), &offset);
|
||||
(void)timespec_negate(&offset);
|
||||
(void)timespec_add(&offset, tp);
|
||||
|
||||
K_SPINLOCK(&rt_clock_offset_lock) {
|
||||
rt_clock_offset = offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
int z_vrfy_sys_clock_settime(int clock_id, const struct timespec *ts)
|
||||
{
|
||||
K_OOPS(K_SYSCALL_MEMORY_READ(ts, sizeof(*ts)));
|
||||
return z_impl_sys_clock_settime(clock_id, ts);
|
||||
}
|
||||
#include <zephyr/syscalls/sys_clock_settime_mrsh.c>
|
||||
#endif /* CONFIG_USERSPACE */
|
||||
|
||||
int z_impl_sys_clock_nanosleep(int clock_id, int flags, const struct timespec *rqtp,
|
||||
struct timespec *rmtp)
|
||||
{
|
||||
k_timepoint_t end;
|
||||
k_timeout_t timeout;
|
||||
struct timespec duration;
|
||||
const bool update_rmtp = rmtp != NULL;
|
||||
const bool abstime = (flags & SYS_TIMER_ABSTIME) != 0;
|
||||
|
||||
if (!is_valid_clock_id(clock_id)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((rqtp->tv_sec < 0) || !timespec_is_valid(rqtp)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (abstime) {
|
||||
/* convert absolute time to relative time duration */
|
||||
(void)sys_clock_gettime(clock_id, &duration);
|
||||
(void)timespec_negate(&duration);
|
||||
(void)timespec_add(&duration, rqtp);
|
||||
} else {
|
||||
duration = *rqtp;
|
||||
}
|
||||
|
||||
/* sleep for relative time duration */
|
||||
if (unlikely(rqtp->tv_sec >= UINT64_MAX / NSEC_PER_SEC)) {
|
||||
uint64_t ns = (uint64_t)k_sleep(K_SECONDS(duration.tv_sec - 1)) * NSEC_PER_MSEC;
|
||||
struct timespec rem = {
|
||||
.tv_sec = (time_t)(ns / NSEC_PER_SEC),
|
||||
.tv_nsec = ns % NSEC_PER_MSEC,
|
||||
};
|
||||
|
||||
duration.tv_sec = 1;
|
||||
(void)timespec_add(&duration, &rem);
|
||||
}
|
||||
|
||||
timeout = timespec_to_timeout(&duration);
|
||||
end = sys_timepoint_calc(timeout);
|
||||
do {
|
||||
(void)k_sleep(timeout);
|
||||
timeout = sys_timepoint_timeout(end);
|
||||
} while (!K_TIMEOUT_EQ(timeout, K_NO_WAIT));
|
||||
|
||||
if (update_rmtp) {
|
||||
*rmtp = (struct timespec){
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 0,
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
int z_vrfy_sys_clock_nanosleep(int clock_id, int flags, const struct timespec *rqtp,
|
||||
struct timespec *rmtp)
|
||||
{
|
||||
K_OOPS(K_SYSCALL_MEMORY_READ(rqtp, sizeof(*rqtp)));
|
||||
if (rmtp != NULL) {
|
||||
K_OOPS(K_SYSCALL_MEMORY_WRITE(rmtp, sizeof(*rmtp)));
|
||||
}
|
||||
return z_impl_sys_clock_nanosleep(clock_id, flags, rqtp, rmtp);
|
||||
}
|
||||
#include <zephyr/syscalls/sys_clock_nanosleep_mrsh.c>
|
||||
#endif /* CONFIG_USERSPACE */
|
||||
|
||||
#ifdef CONFIG_ZTEST
|
||||
#include <zephyr/ztest.h>
|
||||
static void reset_clock_offset(void)
|
||||
{
|
||||
K_SPINLOCK(&rt_clock_offset_lock) {
|
||||
rt_clock_offset = (struct timespec){0};
|
||||
}
|
||||
}
|
||||
|
||||
static void clock_offset_reset_rule_after(const struct ztest_unit_test *test, void *data)
|
||||
{
|
||||
ARG_UNUSED(test);
|
||||
ARG_UNUSED(data);
|
||||
|
||||
reset_clock_offset();
|
||||
}
|
||||
|
||||
ZTEST_RULE(clock_offset_reset_rule, NULL, clock_offset_reset_rule_after);
|
||||
#endif /* CONFIG_ZTEST */
|
||||
Loading…
Reference in New Issue
Block a user