diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index e4559ac4ad994..8720dddc1c7b7 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -2582,7 +2582,6 @@ Utilities: - tests/unit/list/ - tests/unit/intmath/ - tests/unit/pot/ - - tests/lib/time/ - tests/lib/onoff/ - tests/lib/sys_util/ - tests/lib/sprintf/ diff --git a/doc/releases/release-notes-4.2.rst b/doc/releases/release-notes-4.2.rst index b2dcd604aab45..336a816875f5e 100644 --- a/doc/releases/release-notes-4.2.rst +++ b/doc/releases/release-notes-4.2.rst @@ -240,6 +240,9 @@ New APIs and options * :c:func:`util_eq` * :c:func:`util_memeq` + * :c:func:`sys_clock_gettime` + * :c:func:`sys_clock_settime` + * :c:func:`sys_clock_nanosleep` * LoRaWAN * :c:func:`lorawan_request_link_check` diff --git a/include/zephyr/posix/time.h b/include/zephyr/posix/time.h index 7f944b7db97ae..df2980601722e 100644 --- a/include/zephyr/posix/time.h +++ b/include/zephyr/posix/time.h @@ -60,13 +60,14 @@ struct itimerspec { #include #include #include +#include #ifdef __cplusplus extern "C" { #endif #ifndef CLOCK_REALTIME -#define CLOCK_REALTIME 1 +#define CLOCK_REALTIME SYS_CLOCK_REALTIME #endif #ifndef CLOCK_PROCESS_CPUTIME_ID @@ -78,11 +79,11 @@ extern "C" { #endif #ifndef CLOCK_MONOTONIC -#define CLOCK_MONOTONIC 4 +#define CLOCK_MONOTONIC SYS_CLOCK_MONOTONIC #endif #ifndef TIMER_ABSTIME -#define TIMER_ABSTIME 4 +#define TIMER_ABSTIME SYS_TIMER_ABSTIME #endif int clock_gettime(clockid_t clock_id, struct timespec *ts); diff --git a/include/zephyr/sys/clock.h b/include/zephyr/sys/clock.h new file mode 100644 index 0000000000000..3452bd964d1b6 --- /dev/null +++ b/include/zephyr/sys/clock.h @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2014-2015 Wind River Systems, Inc. + * Copyright (c) 2025 Tenstorrent AI ULC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief System clock APIs + * + * Declare variables used by both system timer device driver and kernel + * components that use timer functionality. + * + * APIs for getting, setting, and sleeping with respect to system clocks. + */ + +#ifndef ZEPHYR_INCLUDE_SYS_CLOCK_H_ +#define ZEPHYR_INCLUDE_SYS_CLOCK_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup clock_apis + * @{ + */ + +/** + * @brief Tick precision used in timeout APIs + * + * This type defines the word size of the timeout values used in + * k_timeout_t objects, and thus defines an upper bound on maximum + * timeout length (or equivalently minimum tick duration). Note that + * this does not affect the size of the system uptime counter, which + * is always a 64 bit count of ticks. + */ +#ifdef CONFIG_TIMEOUT_64BIT +typedef int64_t k_ticks_t; +#else +typedef uint32_t k_ticks_t; +#endif + +#define K_TICKS_FOREVER ((k_ticks_t)(-1)) + +/** + * @brief Kernel timeout type + * + * Timeout arguments presented to kernel APIs are stored in this + * opaque type, which is capable of representing times in various + * formats and units. It should be constructed from application data + * using one of the macros defined for this purpose (e.g. `K_MSEC()`, + * `K_TIMEOUT_ABS_TICKS()`, etc...), or be one of the two constants + * K_NO_WAIT or K_FOREVER. Applications should not inspect the + * internal data once constructed. Timeout values may be compared for + * equality with the `K_TIMEOUT_EQ()` macro. + */ +typedef struct { + k_ticks_t ticks; +} k_timeout_t; + +/** + * @brief Compare timeouts for equality + * + * The k_timeout_t object is an opaque struct that should not be + * inspected by application code. This macro exists so that users can + * test timeout objects for equality with known constants + * (e.g. K_NO_WAIT and K_FOREVER) when implementing their own APIs in + * terms of Zephyr timeout constants. + * + * @return True if the timeout objects are identical + */ +#define K_TIMEOUT_EQ(a, b) ((a).ticks == (b).ticks) + +/** number of nanoseconds per microsecond */ +#define NSEC_PER_USEC 1000U + +/** number of nanoseconds per millisecond */ +#define NSEC_PER_MSEC 1000000U + +/** number of microseconds per millisecond */ +#define USEC_PER_MSEC 1000U + +/** number of milliseconds per second */ +#define MSEC_PER_SEC 1000U + +/** number of seconds per minute */ +#define SEC_PER_MIN 60U + +/** number of seconds per hour */ +#define SEC_PER_HOUR 3600U + +/** number of seconds per day */ +#define SEC_PER_DAY 86400U + +/** number of minutes per hour */ +#define MIN_PER_HOUR 60U + +/** number of hours per day */ +#define HOUR_PER_DAY 24U + +/** number of microseconds per second */ +#define USEC_PER_SEC ((USEC_PER_MSEC) * (MSEC_PER_SEC)) + +/** number of nanoseconds per second */ +#define NSEC_PER_SEC ((NSEC_PER_USEC) * (USEC_PER_MSEC) * (MSEC_PER_SEC)) + +/** @} */ + +/** @cond INTERNAL_HIDDEN */ +#define Z_TIMEOUT_NO_WAIT_INIT {0} +#define Z_TIMEOUT_NO_WAIT ((k_timeout_t)Z_TIMEOUT_NO_WAIT_INIT) +#if defined(__cplusplus) && ((__cplusplus - 0) < 202002L) +#define Z_TIMEOUT_TICKS_INIT(t) {(t)} +#else +#define Z_TIMEOUT_TICKS_INIT(t) {.ticks = (t)} +#endif +#define Z_TIMEOUT_TICKS(t) ((k_timeout_t)Z_TIMEOUT_TICKS_INIT(t)) +#define Z_FOREVER Z_TIMEOUT_TICKS(K_TICKS_FOREVER) + +#ifdef CONFIG_TIMEOUT_64BIT +#define Z_TIMEOUT_MS(t) Z_TIMEOUT_TICKS((k_ticks_t)k_ms_to_ticks_ceil64(MAX(t, 0))) +#define Z_TIMEOUT_US(t) Z_TIMEOUT_TICKS((k_ticks_t)k_us_to_ticks_ceil64(MAX(t, 0))) +#define Z_TIMEOUT_NS(t) Z_TIMEOUT_TICKS((k_ticks_t)k_ns_to_ticks_ceil64(MAX(t, 0))) +#define Z_TIMEOUT_CYC(t) Z_TIMEOUT_TICKS((k_ticks_t)k_cyc_to_ticks_ceil64(MAX(t, 0))) +#define Z_TIMEOUT_MS_TICKS(t) ((k_ticks_t)k_ms_to_ticks_ceil64(MAX(t, 0))) +#else +#define Z_TIMEOUT_MS(t) Z_TIMEOUT_TICKS((k_ticks_t)k_ms_to_ticks_ceil32(MAX(t, 0))) +#define Z_TIMEOUT_US(t) Z_TIMEOUT_TICKS((k_ticks_t)k_us_to_ticks_ceil32(MAX(t, 0))) +#define Z_TIMEOUT_NS(t) Z_TIMEOUT_TICKS((k_ticks_t)k_ns_to_ticks_ceil32(MAX(t, 0))) +#define Z_TIMEOUT_CYC(t) Z_TIMEOUT_TICKS((k_ticks_t)k_cyc_to_ticks_ceil32(MAX(t, 0))) +#define Z_TIMEOUT_MS_TICKS(t) ((k_ticks_t)k_ms_to_ticks_ceil32(MAX(t, 0))) +#endif + +/* Converts between absolute timeout expiration values (packed into + * the negative space below K_TICKS_FOREVER) and (non-negative) delta + * timeout values. If the result of Z_TICK_ABS(t) is >= 0, then the + * value was an absolute timeout with the returned expiration time. + * Note that this macro is bidirectional: Z_TICK_ABS(Z_TICK_ABS(t)) == + * t for all inputs, and that the representation of K_TICKS_FOREVER is + * the same value in both spaces! Clever, huh? + */ +#define Z_TICK_ABS(t) (K_TICKS_FOREVER - 1 - (t)) + +/* Test for relative timeout */ +#if CONFIG_TIMEOUT_64BIT +#define Z_IS_TIMEOUT_RELATIVE(timeout) (Z_TICK_ABS((timeout).ticks) < 0) +#else +#define Z_IS_TIMEOUT_RELATIVE(timeout) true +#endif + +/* added tick needed to account for tick in progress */ +#define _TICK_ALIGN 1 + +/** @endcond */ + +#ifndef CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME +#if defined(CONFIG_SYS_CLOCK_EXISTS) +#if CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == 0 +#error "SYS_CLOCK_HW_CYCLES_PER_SEC must be non-zero!" +#endif /* CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == 0 */ +#endif /* CONFIG_SYS_CLOCK_EXISTS */ +#endif /* CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME */ + +/* kernel clocks */ + +/* + * We default to using 64-bit intermediates in timescale conversions, + * but if the HW timer cycles/sec, ticks/sec and ms/sec are all known + * to be nicely related, then we can cheat with 32 bits instead. + */ +/** + * @addtogroup clock_apis + * @{ + */ + +#ifdef CONFIG_SYS_CLOCK_EXISTS + +#if defined(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME) || \ + (MSEC_PER_SEC % CONFIG_SYS_CLOCK_TICKS_PER_SEC) || \ + (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC % CONFIG_SYS_CLOCK_TICKS_PER_SEC) +#define _NEED_PRECISE_TICK_MS_CONVERSION +#endif + +#endif + +/** + * SYS_CLOCK_HW_CYCLES_TO_NS_AVG converts CPU clock cycles to nanoseconds + * and calculates the average cycle time + */ +#define SYS_CLOCK_HW_CYCLES_TO_NS_AVG(X, NCYCLES) (uint32_t)(k_cyc_to_ns_floor64(X) / NCYCLES) + +/** + * + * @brief Return the lower part of the current system tick count + * + * @return the current system tick count + * + */ +uint32_t sys_clock_tick_get_32(void); + +/** + * + * @brief Return the current system tick count + * + * @return the current system tick count + * + */ +int64_t sys_clock_tick_get(void); + +#ifndef CONFIG_SYS_CLOCK_EXISTS +#define sys_clock_tick_get() (0) +#define sys_clock_tick_get_32() (0) +#endif + +#ifdef CONFIG_SYS_CLOCK_EXISTS + +/** + * @brief Kernel timepoint type + * + * Absolute timepoints are stored in this opaque type. + * It is best not to inspect its content directly. + * + * @see sys_timepoint_calc() + * @see sys_timepoint_timeout() + * @see sys_timepoint_expired() + */ +typedef struct { + uint64_t tick; +} k_timepoint_t; + +/** + * @brief Calculate a timepoint value + * + * Returns a timepoint corresponding to the expiration (relative to an + * unlocked "now"!) of a timeout object. When used correctly, this should + * be called once, synchronously with the user passing a new timeout value. + * It should not be used iteratively to adjust a timeout (see + * `sys_timepoint_timeout()` for that purpose). + * + * @param timeout Timeout value relative to current time (may also be + * `K_FOREVER` or `K_NO_WAIT`). + * @retval Timepoint value corresponding to given timeout + * + * @see sys_timepoint_timeout() + * @see sys_timepoint_expired() + */ +k_timepoint_t sys_timepoint_calc(k_timeout_t timeout); + +/** + * @brief Remaining time to given timepoint + * + * Returns the timeout interval between current time and provided timepoint. + * If the timepoint is now in the past or if it was created with `K_NO_WAIT` + * then `K_NO_WAIT` is returned. If it was created with `K_FOREVER` then + * `K_FOREVER` is returned. + * + * @param timepoint Timepoint for which a timeout value is wanted. + * @retval Corresponding timeout value. + * + * @see sys_timepoint_calc() + */ +k_timeout_t sys_timepoint_timeout(k_timepoint_t timepoint); + +/** + * @brief Compare two timepoint values. + * + * This function is used to compare two timepoint values. + * + * @param a Timepoint to compare + * @param b Timepoint to compare against. + * @return zero if both timepoints are the same. Negative value if timepoint @a a is before + * timepoint @a b, positive otherwise. + */ +static inline int sys_timepoint_cmp(k_timepoint_t a, k_timepoint_t b) +{ + if (a.tick == b.tick) { + return 0; + } + return a.tick < b.tick ? -1 : 1; +} + +#else + +/* + * When timers are configured out, timepoints can't relate to anything. + * The best we can do is to preserve whether or not they are derived from + * K_NO_WAIT. Anything else will translate back to K_FOREVER. + */ +typedef struct { + bool wait; +} k_timepoint_t; + +static inline k_timepoint_t sys_timepoint_calc(k_timeout_t timeout) +{ + k_timepoint_t timepoint; + + timepoint.wait = !K_TIMEOUT_EQ(timeout, Z_TIMEOUT_NO_WAIT); + return timepoint; +} + +static inline k_timeout_t sys_timepoint_timeout(k_timepoint_t timepoint) +{ + return timepoint.wait ? Z_FOREVER : Z_TIMEOUT_NO_WAIT; +} + +static inline int sys_timepoint_cmp(k_timepoint_t a, k_timepoint_t b) +{ + if (a.wait == b.wait) { + return 0; + } + return b.wait ? -1 : 1; +} + +#endif + +/** + * @brief Indicates if timepoint is expired + * + * @param timepoint Timepoint to evaluate + * @retval true if the timepoint is in the past, false otherwise + * + * @see sys_timepoint_calc() + */ +static inline bool sys_timepoint_expired(k_timepoint_t timepoint) +{ + return K_TIMEOUT_EQ(sys_timepoint_timeout(timepoint), Z_TIMEOUT_NO_WAIT); +} + +/** @} */ + +/** + * @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 + +/** @cond INTERNAL_HIDDEN */ +/* forward declaration as workaround for time.h */ +struct timespec; +/** @endcond INTERNAL_HIDDEN */ + +/** + * @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); + +/** + * @} + */ + +#ifndef CONFIG_BOARD_UNIT_TESTING +#include +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_SYS_CLOCK_H_ */ diff --git a/include/zephyr/sys_clock.h b/include/zephyr/sys_clock.h index 31bbd9a3c2b04..60b0ac702f095 100644 --- a/include/zephyr/sys_clock.h +++ b/include/zephyr/sys_clock.h @@ -4,344 +4,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -/** - * @file - * @brief Variables needed for system clock - * - * - * Declare variables used by both system timer device driver and kernel - * components that use timer functionality. - */ - -#ifndef ZEPHYR_INCLUDE_SYS_CLOCK_H_ -#define ZEPHYR_INCLUDE_SYS_CLOCK_H_ - -#include -#include - -#include -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @addtogroup clock_apis - * @{ - */ - -/** - * @brief Tick precision used in timeout APIs - * - * This type defines the word size of the timeout values used in - * k_timeout_t objects, and thus defines an upper bound on maximum - * timeout length (or equivalently minimum tick duration). Note that - * this does not affect the size of the system uptime counter, which - * is always a 64 bit count of ticks. - */ -#ifdef CONFIG_TIMEOUT_64BIT -typedef int64_t k_ticks_t; -#else -typedef uint32_t k_ticks_t; -#endif - -#define K_TICKS_FOREVER ((k_ticks_t) -1) - -/** - * @brief Kernel timeout type - * - * Timeout arguments presented to kernel APIs are stored in this - * opaque type, which is capable of representing times in various - * formats and units. It should be constructed from application data - * using one of the macros defined for this purpose (e.g. `K_MSEC()`, - * `K_TIMEOUT_ABS_TICKS()`, etc...), or be one of the two constants - * K_NO_WAIT or K_FOREVER. Applications should not inspect the - * internal data once constructed. Timeout values may be compared for - * equality with the `K_TIMEOUT_EQ()` macro. - */ -typedef struct { - k_ticks_t ticks; -} k_timeout_t; - -/** - * @brief Compare timeouts for equality - * - * The k_timeout_t object is an opaque struct that should not be - * inspected by application code. This macro exists so that users can - * test timeout objects for equality with known constants - * (e.g. K_NO_WAIT and K_FOREVER) when implementing their own APIs in - * terms of Zephyr timeout constants. - * - * @return True if the timeout objects are identical - */ -#define K_TIMEOUT_EQ(a, b) ((a).ticks == (b).ticks) - -/** number of nanoseconds per microsecond */ -#define NSEC_PER_USEC 1000U - -/** number of nanoseconds per millisecond */ -#define NSEC_PER_MSEC 1000000U - -/** number of microseconds per millisecond */ -#define USEC_PER_MSEC 1000U - -/** number of milliseconds per second */ -#define MSEC_PER_SEC 1000U - -/** number of seconds per minute */ -#define SEC_PER_MIN 60U - -/** number of seconds per hour */ -#define SEC_PER_HOUR 3600U - -/** number of seconds per day */ -#define SEC_PER_DAY 86400U - -/** number of minutes per hour */ -#define MIN_PER_HOUR 60U - -/** number of hours per day */ -#define HOUR_PER_DAY 24U - -/** number of microseconds per second */ -#define USEC_PER_SEC ((USEC_PER_MSEC) * (MSEC_PER_SEC)) - -/** number of nanoseconds per second */ -#define NSEC_PER_SEC ((NSEC_PER_USEC) * (USEC_PER_MSEC) * (MSEC_PER_SEC)) - -/** @} */ - -/** @cond INTERNAL_HIDDEN */ -#define Z_TIMEOUT_NO_WAIT_INIT {0} -#define Z_TIMEOUT_NO_WAIT ((k_timeout_t) Z_TIMEOUT_NO_WAIT_INIT) -#if defined(__cplusplus) && ((__cplusplus - 0) < 202002L) -#define Z_TIMEOUT_TICKS_INIT(t) { (t) } -#else -#define Z_TIMEOUT_TICKS_INIT(t) { .ticks = (t) } -#endif -#define Z_TIMEOUT_TICKS(t) ((k_timeout_t) Z_TIMEOUT_TICKS_INIT(t)) -#define Z_FOREVER Z_TIMEOUT_TICKS(K_TICKS_FOREVER) - -#ifdef CONFIG_TIMEOUT_64BIT -# define Z_TIMEOUT_MS(t) Z_TIMEOUT_TICKS((k_ticks_t)k_ms_to_ticks_ceil64(MAX(t, 0))) -# define Z_TIMEOUT_US(t) Z_TIMEOUT_TICKS((k_ticks_t)k_us_to_ticks_ceil64(MAX(t, 0))) -# define Z_TIMEOUT_NS(t) Z_TIMEOUT_TICKS((k_ticks_t)k_ns_to_ticks_ceil64(MAX(t, 0))) -# define Z_TIMEOUT_CYC(t) Z_TIMEOUT_TICKS((k_ticks_t)k_cyc_to_ticks_ceil64(MAX(t, 0))) -# define Z_TIMEOUT_MS_TICKS(t) ((k_ticks_t)k_ms_to_ticks_ceil64(MAX(t, 0))) -#else -# define Z_TIMEOUT_MS(t) Z_TIMEOUT_TICKS((k_ticks_t)k_ms_to_ticks_ceil32(MAX(t, 0))) -# define Z_TIMEOUT_US(t) Z_TIMEOUT_TICKS((k_ticks_t)k_us_to_ticks_ceil32(MAX(t, 0))) -# define Z_TIMEOUT_NS(t) Z_TIMEOUT_TICKS((k_ticks_t)k_ns_to_ticks_ceil32(MAX(t, 0))) -# define Z_TIMEOUT_CYC(t) Z_TIMEOUT_TICKS((k_ticks_t)k_cyc_to_ticks_ceil32(MAX(t, 0))) -# define Z_TIMEOUT_MS_TICKS(t) ((k_ticks_t)k_ms_to_ticks_ceil32(MAX(t, 0))) -#endif - -/* Converts between absolute timeout expiration values (packed into - * the negative space below K_TICKS_FOREVER) and (non-negative) delta - * timeout values. If the result of Z_TICK_ABS(t) is >= 0, then the - * value was an absolute timeout with the returned expiration time. - * Note that this macro is bidirectional: Z_TICK_ABS(Z_TICK_ABS(t)) == - * t for all inputs, and that the representation of K_TICKS_FOREVER is - * the same value in both spaces! Clever, huh? - */ -#define Z_TICK_ABS(t) (K_TICKS_FOREVER - 1 - (t)) - -/* Test for relative timeout */ -#if CONFIG_TIMEOUT_64BIT -/* Positive values are relative/delta timeouts and negative values are absolute - * timeouts, except -1 which is reserved for K_TIMEOUT_FOREVER. 0 is K_NO_WAIT, - * which is historically considered a relative timeout. - * K_TIMEOUT_FOREVER is not considered a relative timeout and neither is it - * considerd an absolute timeouts (so !Z_IS_TIMEOUT_RELATIVE() does not - * necessarily mean it is an absolute timeout if ticks == -1); - */ -#define Z_IS_TIMEOUT_RELATIVE(timeout) (((timeout).ticks) >= 0) -#else -#define Z_IS_TIMEOUT_RELATIVE(timeout) true -#endif - -/* added tick needed to account for tick in progress */ -#define _TICK_ALIGN 1 - -/** @endcond */ - -#ifndef CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME -#if defined(CONFIG_SYS_CLOCK_EXISTS) -#if CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == 0 -#error "SYS_CLOCK_HW_CYCLES_PER_SEC must be non-zero!" -#endif /* CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == 0 */ -#endif /* CONFIG_SYS_CLOCK_EXISTS */ -#endif /* CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME */ - -/* kernel clocks */ - -/* - * We default to using 64-bit intermediates in timescale conversions, - * but if the HW timer cycles/sec, ticks/sec and ms/sec are all known - * to be nicely related, then we can cheat with 32 bits instead. - */ -/** - * @addtogroup clock_apis - * @{ - */ - -#ifdef CONFIG_SYS_CLOCK_EXISTS - -#if defined(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME) || \ - (MSEC_PER_SEC % CONFIG_SYS_CLOCK_TICKS_PER_SEC) || \ - (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC % CONFIG_SYS_CLOCK_TICKS_PER_SEC) -#define _NEED_PRECISE_TICK_MS_CONVERSION -#endif - -#endif - -/** - * SYS_CLOCK_HW_CYCLES_TO_NS_AVG converts CPU clock cycles to nanoseconds - * and calculates the average cycle time - */ -#define SYS_CLOCK_HW_CYCLES_TO_NS_AVG(X, NCYCLES) \ - (uint32_t)(k_cyc_to_ns_floor64(X) / NCYCLES) - -/** - * - * @brief Return the lower part of the current system tick count - * - * @return the current system tick count - * - */ -uint32_t sys_clock_tick_get_32(void); - -/** - * - * @brief Return the current system tick count - * - * @return the current system tick count - * - */ -int64_t sys_clock_tick_get(void); - -#ifndef CONFIG_SYS_CLOCK_EXISTS -#define sys_clock_tick_get() (0) -#define sys_clock_tick_get_32() (0) -#endif - -#ifdef CONFIG_SYS_CLOCK_EXISTS - -/** - * @brief Kernel timepoint type - * - * Absolute timepoints are stored in this opaque type. - * It is best not to inspect its content directly. - * - * @see sys_timepoint_calc() - * @see sys_timepoint_timeout() - * @see sys_timepoint_expired() - */ -typedef struct { uint64_t tick; } k_timepoint_t; - -/** - * @brief Calculate a timepoint value - * - * Returns a timepoint corresponding to the expiration (relative to an - * unlocked "now"!) of a timeout object. When used correctly, this should - * be called once, synchronously with the user passing a new timeout value. - * It should not be used iteratively to adjust a timeout (see - * `sys_timepoint_timeout()` for that purpose). - * - * @param timeout Timeout value relative to current time (may also be - * `K_FOREVER` or `K_NO_WAIT`). - * @retval Timepoint value corresponding to given timeout - * - * @see sys_timepoint_timeout() - * @see sys_timepoint_expired() - */ -k_timepoint_t sys_timepoint_calc(k_timeout_t timeout); - -/** - * @brief Remaining time to given timepoint - * - * Returns the timeout interval between current time and provided timepoint. - * If the timepoint is now in the past or if it was created with `K_NO_WAIT` - * then `K_NO_WAIT` is returned. If it was created with `K_FOREVER` then - * `K_FOREVER` is returned. - * - * @param timepoint Timepoint for which a timeout value is wanted. - * @retval Corresponding timeout value. - * - * @see sys_timepoint_calc() - */ -k_timeout_t sys_timepoint_timeout(k_timepoint_t timepoint); - -/** - * @brief Compare two timepoint values. - * - * This function is used to compare two timepoint values. - * - * @param a Timepoint to compare - * @param b Timepoint to compare against. - * @return zero if both timepoints are the same. Negative value if timepoint @a a is before - * timepoint @a b, positive otherwise. - */ -static inline int sys_timepoint_cmp(k_timepoint_t a, k_timepoint_t b) -{ - if (a.tick == b.tick) { - return 0; - } - return a.tick < b.tick ? -1 : 1; -} - -#else - -/* - * When timers are configured out, timepoints can't relate to anything. - * The best we can do is to preserve whether or not they are derived from - * K_NO_WAIT. Anything else will translate back to K_FOREVER. - */ -typedef struct { bool wait; } k_timepoint_t; - -static inline k_timepoint_t sys_timepoint_calc(k_timeout_t timeout) -{ - k_timepoint_t timepoint; - - timepoint.wait = !K_TIMEOUT_EQ(timeout, Z_TIMEOUT_NO_WAIT); - return timepoint; -} - -static inline k_timeout_t sys_timepoint_timeout(k_timepoint_t timepoint) -{ - return timepoint.wait ? Z_FOREVER : Z_TIMEOUT_NO_WAIT; -} - -static inline int sys_timepoint_cmp(k_timepoint_t a, k_timepoint_t b) -{ - if (a.wait == b.wait) { - return 0; - } - return b.wait ? -1 : 1; -} - -#endif - -/** - * @brief Indicates if timepoint is expired - * - * @param timepoint Timepoint to evaluate - * @retval true if the timepoint is in the past, false otherwise - * - * @see sys_timepoint_calc() - */ -static inline bool sys_timepoint_expired(k_timepoint_t timepoint) -{ - return K_TIMEOUT_EQ(sys_timepoint_timeout(timepoint), Z_TIMEOUT_NO_WAIT); -} - -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* ZEPHYR_INCLUDE_SYS_CLOCK_H_ */ +#include diff --git a/lib/libc/Kconfig b/lib/libc/Kconfig index 5d981e4719b0b..3157fbd99c337 100644 --- a/lib/libc/Kconfig +++ b/lib/libc/Kconfig @@ -84,6 +84,7 @@ config MINIMAL_LIBC imply COMMON_LIBC_MALLOC imply COMMON_LIBC_CALLOC imply COMMON_LIBC_REALLOCARRAY + imply COMMON_LIBC_TIME help Build with minimal C library. @@ -96,6 +97,7 @@ config PICOLIBC select TC_PROVIDES_POSIX_C_LANG_SUPPORT_R imply COMMON_LIBC_MALLOC imply COMMON_LIBC_ABORT + imply COMMON_LIBC_TIME depends on PICOLIBC_SUPPORTED help Build with picolibc library. The picolibc library is built as @@ -116,6 +118,7 @@ config NEWLIB_LIBC imply POSIX_FILE_SYSTEM_ALIAS_FSTAT imply POSIX_MULTI_PROCESS_ALIAS_GETPID imply POSIX_SIGNALS_ALIAS_KILL + imply COMMON_LIBC_TIME help Build with newlib library. The newlib library is expected to be part of the SDK in this case. @@ -137,7 +140,7 @@ config IAR_LIBC depends on IAR_LIBC_SUPPORTED depends on "$(ZEPHYR_TOOLCHAIN_VARIANT)" = "iar" select COMMON_LIBC_STRNLEN - select COMMON_LIBC_TIME if POSIX_TIMERS + select COMMON_LIBC_TIME help Use the full IAR Compiler runtime libraries. A reduced Zephyr minimal libc will be used for library functionality diff --git a/lib/libc/common/source/thrd/thrd.c b/lib/libc/common/source/thrd/thrd.c index d811fe31ce6b7..ad2cb72b602fb 100644 --- a/lib/libc/common/source/thrd/thrd.c +++ b/lib/libc/common/source/thrd/thrd.c @@ -10,6 +10,7 @@ #include #include #include +#include struct thrd_trampoline_arg { thrd_start_t func; @@ -44,7 +45,11 @@ thrd_t thrd_current(void) int thrd_sleep(const struct timespec *duration, struct timespec *remaining) { - return nanosleep(duration, remaining); + if (sys_clock_nanosleep(SYS_CLOCK_REALTIME, 0, duration, remaining) != 0) { + return thrd_error; + } + + return thrd_success; } void thrd_yield(void) diff --git a/lib/libc/common/source/time/time.c b/lib/libc/common/source/time/time.c index 85840fa8cd949..5eccd733d97b8 100644 --- a/lib/libc/common/source/time/time.c +++ b/lib/libc/common/source/time/time.c @@ -1,22 +1,23 @@ /* * Copyright (c) 2021 Golioth, Inc. + * Copyright (c) 2025 Tenstorrent AI ULC * * SPDX-License-Identifier: Apache-2.0 */ +#include #include -/* clock_gettime() prototype */ -#include +#include time_t time(time_t *tloc) { struct timespec ts; int ret; - ret = clock_gettime(CLOCK_REALTIME, &ts); + ret = sys_clock_gettime(SYS_CLOCK_REALTIME, &ts); if (ret < 0) { - /* errno is already set by clock_gettime */ + errno = -ret; return (time_t) -1; } diff --git a/lib/os/CMakeLists.txt b/lib/os/CMakeLists.txt index 80f5f16f5d791..c8c56d7e07281 100644 --- a/lib/os/CMakeLists.txt +++ b/lib/os/CMakeLists.txt @@ -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 diff --git a/lib/os/clock.c b/lib/os/clock.c new file mode 100644 index 0000000000000..494c00c541f56 --- /dev/null +++ b/lib/os/clock.c @@ -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 +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * `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 +#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 +#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 +#endif /* CONFIG_USERSPACE */ + +#ifdef CONFIG_ZTEST +#include +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 */ diff --git a/lib/posix/options/CMakeLists.txt b/lib/posix/options/CMakeLists.txt index 3df52af792ff9..5ef2032e5a91c 100644 --- a/lib/posix/options/CMakeLists.txt +++ b/lib/posix/options/CMakeLists.txt @@ -47,10 +47,7 @@ if (NOT CONFIG_TC_PROVIDES_POSIX_BARRIERS) endif() if (NOT CONFIG_TC_PROVIDES_POSIX_CLOCK_SELECTION) - zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK_SELECTION - clock_common.c - clock_selection.c - ) + zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK_SELECTION clock_selection.c) endif() if (NOT CONFIG_TC_PROVIDES_POSIX_C_LIB_EXT) @@ -124,7 +121,6 @@ endif() if (NOT CONFIG_TC_PROVIDES_POSIX_TIMERS) zephyr_library_sources_ifdef(CONFIG_POSIX_TIMERS clock.c - clock_common.c timer.c timespec_to_timeout.c ) @@ -166,7 +162,6 @@ zephyr_library_sources_ifdef(CONFIG_XOPEN_STREAMS stropts.c) if (NOT CONFIG_TC_PROVIDES_XSI_SINGLE_PROCESS) zephyr_library_sources_ifdef(CONFIG_XSI_SINGLE_PROCESS - clock_common.c env_common.c xsi_single_process.c ) diff --git a/lib/posix/options/clock.c b/lib/posix/options/clock.c index 1f45334673128..239dadb692663 100644 --- a/lib/posix/options/clock.c +++ b/lib/posix/options/clock.c @@ -8,18 +8,23 @@ #include #include -#include +#include #include +#include #include - -extern int z_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, - struct timespec *rmtp); -extern int z_clock_gettime(clockid_t clock_id, struct timespec *ts); -extern int z_clock_settime(clockid_t clock_id, const struct timespec *tp); +#include int clock_gettime(clockid_t clock_id, struct timespec *ts) { - return z_clock_gettime(clock_id, ts); + int ret; + + ret = sys_clock_gettime((int)clock_id, ts); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; } int clock_getres(clockid_t clock_id, struct timespec *res) @@ -54,7 +59,15 @@ int clock_getres(clockid_t clock_id, struct timespec *res) */ int clock_settime(clockid_t clock_id, const struct timespec *tp) { - return z_clock_settime(clock_id, tp); + int ret; + + ret = sys_clock_settime((int)clock_id, tp); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; } /* @@ -88,7 +101,20 @@ int usleep(useconds_t useconds) int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { - return z_clock_nanosleep(CLOCK_MONOTONIC, 0, rqtp, rmtp); + int ret; + + if (rqtp == NULL) { + errno = EFAULT; + return -1; + } + + ret = sys_clock_nanosleep(SYS_CLOCK_REALTIME, 0, rqtp, rmtp); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; } int clock_getcpuclockid(pid_t pid, clockid_t *clock_id) diff --git a/lib/posix/options/clock_common.c b/lib/posix/options/clock_common.c deleted file mode 100644 index c8271cf185dde..0000000000000 --- a/lib/posix/options/clock_common.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * 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 "posix_clock.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * `k_uptime_get` returns a timestamp based on an always increasing - * value from the system start. To support the `CLOCK_REALTIME` - * clock, this `rt_clock_base` records the time that the system was - * started. This can either be set via 'clock_settime', or could be - * set from a real time clock, if such hardware is present. - */ -static struct timespec rt_clock_base; -static SYS_SEM_DEFINE(rt_clock_base_lock, 1, 1); - -int z_impl___posix_clock_get_base(clockid_t clock_id, struct timespec *base) -{ - switch (clock_id) { - case CLOCK_MONOTONIC: - base->tv_sec = 0; - base->tv_nsec = 0; - break; - - case CLOCK_REALTIME: - SYS_SEM_LOCK(&rt_clock_base_lock) { - *base = rt_clock_base; - } - break; - - default: - errno = EINVAL; - return -1; - } - - return 0; -} - -#ifdef CONFIG_USERSPACE -int z_vrfy___posix_clock_get_base(clockid_t clock_id, struct timespec *ts) -{ - K_OOPS(K_SYSCALL_MEMORY_WRITE(ts, sizeof(*ts))); - return z_impl___posix_clock_get_base(clock_id, ts); -} -#include -#endif - -int z_clock_gettime(clockid_t clock_id, struct timespec *ts) -{ - struct timespec base = {.tv_sec = 0, .tv_nsec = 0}; - - switch (clock_id) { - case CLOCK_MONOTONIC: - break; - - case CLOCK_REALTIME: - (void)__posix_clock_get_base(clock_id, &base); - break; - - default: - errno = EINVAL; - return -1; - } - - uint64_t ticks = k_uptime_ticks(); - uint64_t elapsed_secs = ticks / CONFIG_SYS_CLOCK_TICKS_PER_SEC; - uint64_t nremainder = ticks - elapsed_secs * CONFIG_SYS_CLOCK_TICKS_PER_SEC; - - ts->tv_sec = (time_t)elapsed_secs; - /* For ns 32 bit conversion can be used since its smaller than 1sec. */ - ts->tv_nsec = (int32_t)k_ticks_to_ns_floor32(nremainder); - - if (unlikely(!timespec_normalize(ts)) || unlikely(!timespec_add(ts, &base))) { - errno = EOVERFLOW; - return -1; - } - - return 0; -} - -int z_clock_settime(clockid_t clock_id, const struct timespec *tp) -{ - struct timespec base; - - if (clock_id != CLOCK_REALTIME) { - errno = EINVAL; - return -1; - } - - if (!timespec_is_valid(tp)) { - errno = EINVAL; - return -1; - } - - uint64_t elapsed_nsecs = k_ticks_to_ns_floor64(k_uptime_ticks()); - int64_t delta = (int64_t)NSEC_PER_SEC * tp->tv_sec + tp->tv_nsec - elapsed_nsecs; - - base.tv_sec = delta / NSEC_PER_SEC; - base.tv_nsec = delta % NSEC_PER_SEC; - - if (unlikely(!timespec_normalize(&base))) { - errno = EOVERFLOW; - return -1; - } - - SYS_SEM_LOCK(&rt_clock_base_lock) { - rt_clock_base = base; - } - - return 0; -} - -int z_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, - struct timespec *rmtp) -{ - uint64_t ns; - uint64_t us; - uint64_t uptime_ns; - const bool update_rmtp = rmtp != NULL; - - if (!((clock_id == CLOCK_REALTIME) || (clock_id == CLOCK_MONOTONIC))) { - errno = EINVAL; - return -1; - } - - if (rqtp == NULL) { - errno = EFAULT; - return -1; - } - - if ((rqtp->tv_sec < 0) || !timespec_is_valid(rqtp)) { - errno = EINVAL; - return -1; - } - - if ((flags & TIMER_ABSTIME) == 0 && unlikely(rqtp->tv_sec >= ULLONG_MAX / NSEC_PER_SEC)) { - ns = rqtp->tv_nsec + NSEC_PER_SEC + - (uint64_t)k_sleep(K_SECONDS(rqtp->tv_sec - 1)) * NSEC_PER_MSEC; - } else { - ns = (uint64_t)rqtp->tv_sec * NSEC_PER_SEC + rqtp->tv_nsec; - } - - uptime_ns = k_ticks_to_ns_ceil64(sys_clock_tick_get()); - - if (flags & TIMER_ABSTIME && clock_id == CLOCK_REALTIME) { - SYS_SEM_LOCK(&rt_clock_base_lock) { - ns -= rt_clock_base.tv_sec * NSEC_PER_SEC + rt_clock_base.tv_nsec; - } - } - - if ((flags & TIMER_ABSTIME) == 0) { - ns += uptime_ns; - } - - if (ns <= uptime_ns) { - goto do_rmtp_update; - } - - us = DIV_ROUND_UP(ns, NSEC_PER_USEC); - do { - us = k_sleep(K_TIMEOUT_ABS_US(us)) * 1000; - } while (us != 0); - -do_rmtp_update: - if (update_rmtp) { - rmtp->tv_sec = 0; - rmtp->tv_nsec = 0; - } - - return 0; -} - -#ifdef CONFIG_ZTEST -#include -static void reset_clock_base(void) -{ - SYS_SEM_LOCK(&rt_clock_base_lock) { - rt_clock_base = (struct timespec){0}; - } -} - -static void clock_base_reset_rule_after(const struct ztest_unit_test *test, void *data) -{ - ARG_UNUSED(test); - ARG_UNUSED(data); - - reset_clock_base(); -} - -ZTEST_RULE(clock_base_reset_rule, NULL, clock_base_reset_rule_after); -#endif /* CONFIG_ZTEST */ diff --git a/lib/posix/options/clock_selection.c b/lib/posix/options/clock_selection.c index 6bddc3212080c..c67d48ff0253f 100644 --- a/lib/posix/options/clock_selection.c +++ b/lib/posix/options/clock_selection.c @@ -13,15 +13,26 @@ #include #include +#include #include -extern int z_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, - struct timespec *rmtp); - -extern int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, - struct timespec *rmtp) +int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, + struct timespec *rmtp) { - return z_clock_nanosleep(clock_id, flags, rqtp, rmtp); + int ret; + + if (rqtp == NULL) { + errno = EFAULT; + return -1; + } + + ret = sys_clock_nanosleep((int)clock_id, flags, rqtp, rmtp); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; } int pthread_condattr_getclock(const pthread_condattr_t *ZRESTRICT att, diff --git a/lib/posix/options/cond.c b/lib/posix/options/cond.c index f2b5af94ad725..84ad6ff571c40 100644 --- a/lib/posix/options/cond.c +++ b/lib/posix/options/cond.c @@ -108,7 +108,7 @@ static int cond_wait(pthread_cond_t *cond, pthread_mutex_t *mu, const struct tim timeout = K_MSEC(timespec_to_timeoutms(cv->attr.clock, abstime)); } - LOG_DBG("Waiting on cond %p with timeout %llx", cv, timeout.ticks); + LOG_DBG("Waiting on cond %p with timeout %" PRIx64, cv, (int64_t)timeout.ticks); ret = k_condvar_wait(&cv->condvar, m, timeout); if (ret == -EAGAIN) { LOG_DBG("Timeout waiting on cond %p", cv); diff --git a/lib/posix/options/mutex.c b/lib/posix/options/mutex.c index 52dec35d24911..219c6dbaf8946 100644 --- a/lib/posix/options/mutex.c +++ b/lib/posix/options/mutex.c @@ -122,7 +122,7 @@ static int acquire_mutex(pthread_mutex_t *mu, k_timeout_t timeout) SYS_SEM_LOCK_BREAK; } - LOG_DBG("Locking mutex %p with timeout %llx", m, timeout.ticks); + LOG_DBG("Locking mutex %p with timeout %" PRIx64, m, (int64_t)timeout.ticks); ret = 0; bit = posix_mutex_to_offset(m); diff --git a/lib/posix/options/posix_clock.h b/lib/posix/options/posix_clock.h index 86b4e2e7eb221..c4c03a572a451 100644 --- a/lib/posix/options/posix_clock.h +++ b/lib/posix/options/posix_clock.h @@ -61,12 +61,8 @@ static inline bool tp_diff_in_range_ns(const struct timespec *a, const struct ti uint32_t timespec_to_timeoutms(clockid_t clock_id, const struct timespec *abstime); -__syscall int __posix_clock_get_base(clockid_t clock_id, struct timespec *ts); - /** INTERNAL_HIDDEN @endcond */ -#include - #ifdef __cplusplus } #endif diff --git a/lib/posix/options/xsi_single_process.c b/lib/posix/options/xsi_single_process.c index cc5e82cb99a75..006577fedbeeb 100644 --- a/lib/posix/options/xsi_single_process.c +++ b/lib/posix/options/xsi_single_process.c @@ -12,14 +12,15 @@ #include #include +#include #include #include +#include #include LOG_MODULE_REGISTER(xsi_single_process, CONFIG_XSI_SINGLE_PROCESS_LOG_LEVEL); extern int z_setenv(const char *name, const char *val, int overwrite); -extern int z_clock_gettime(clockid_t clockid, struct timespec *tp); long gethostid(void) { @@ -46,10 +47,11 @@ int gettimeofday(struct timeval *tv, void *tz) */ ARG_UNUSED(tz); - res = z_clock_gettime(CLOCK_REALTIME, &ts); + res = sys_clock_gettime(SYS_CLOCK_REALTIME, &ts); if (res < 0) { - LOG_DBG("%s() failed: %d", "clock_gettime", res); - return res; + LOG_DBG("%s() failed: %d", "sys_clock_gettime", res); + errno = -res; + return -1; } tv->tv_sec = ts.tv_sec; diff --git a/samples/net/cloud/aws_iot_mqtt/prj.conf b/samples/net/cloud/aws_iot_mqtt/prj.conf index ef5f6574ef752..825bd8514b952 100644 --- a/samples/net/cloud/aws_iot_mqtt/prj.conf +++ b/samples/net/cloud/aws_iot_mqtt/prj.conf @@ -39,7 +39,6 @@ CONFIG_NET_DHCPV4=y # SNTP CONFIG_SNTP=y -CONFIG_POSIX_TIMERS=y CONFIG_NET_CONFIG_CLOCK_SNTP_INIT=y CONFIG_NET_CONFIG_SNTP_INIT_SERVER="0.pool.ntp.org" diff --git a/samples/net/lwm2m_client/overlay-data-cache.conf b/samples/net/lwm2m_client/overlay-data-cache.conf index 523b23519addb..f8356b12d95a0 100644 --- a/samples/net/lwm2m_client/overlay-data-cache.conf +++ b/samples/net/lwm2m_client/overlay-data-cache.conf @@ -1,5 +1,4 @@ CONFIG_ZCBOR=y CONFIG_ZCBOR_CANONICAL=y CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT=y -CONFIG_XSI_SINGLE_PROCESS=y CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT=y diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index b622ea3ef076d..0b4f765963ada 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -1026,6 +1026,7 @@ def check_no_undef_outside_kconfig(self, kconf): "BOARD_MPS2_AN521_CPUTEST", # Used for board and SoC extension feature tests "BOARD_NATIVE_SIM_NATIVE_64_TWO", # Used for board and SoC extension feature tests "BOARD_NATIVE_SIM_NATIVE_ONE", # Used for board and SoC extension feature tests + "BOARD_UNIT_TESTING", # Used for tests/unit "BOOT_DIRECT_XIP", # Used in sysbuild for MCUboot configuration "BOOT_DIRECT_XIP_REVERT", # Used in sysbuild for MCUboot configuration "BOOT_ENCRYPTION_KEY_FILE", # Used in sysbuild diff --git a/subsys/logging/Kconfig.processing b/subsys/logging/Kconfig.processing index f354e8b5df616..2f7516c3df36e 100644 --- a/subsys/logging/Kconfig.processing +++ b/subsys/logging/Kconfig.processing @@ -160,7 +160,6 @@ config LOG_TIMESTAMP_64BIT config LOG_TIMESTAMP_USE_REALTIME bool "Use real time clock for timestamp" select LOG_TIMESTAMP_64BIT - depends on POSIX_TIMERS help When enabled, real time clock is used for timestamping. diff --git a/subsys/logging/log_core.c b/subsys/logging/log_core.c index 25345f75fb0f1..bef3ccb6cdeca 100644 --- a/subsys/logging/log_core.c +++ b/subsys/logging/log_core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -23,10 +24,6 @@ #include #include -#ifdef CONFIG_LOG_TIMESTAMP_USE_REALTIME -#include -#endif - #if CONFIG_USERSPACE && CONFIG_LOG_ALWAYS_RUNTIME #include K_APPMEM_PARTITION_DEFINE(k_log_partition); @@ -247,7 +244,7 @@ static log_timestamp_t default_rt_get_timestamp(void) { struct timespec tspec; - clock_gettime(CLOCK_REALTIME, &tspec); + sys_clock_gettime(SYS_CLOCK_REALTIME, &tspec); return ((uint64_t)tspec.tv_sec * MSEC_PER_SEC) + (tspec.tv_nsec / NSEC_PER_MSEC); } diff --git a/subsys/net/lib/config/Kconfig b/subsys/net/lib/config/Kconfig index d0a98d207ed62..82b534888d438 100644 --- a/subsys/net/lib/config/Kconfig +++ b/subsys/net/lib/config/Kconfig @@ -206,7 +206,7 @@ endif # NET_DHCPV6 config NET_CONFIG_CLOCK_SNTP_INIT bool "Initialize system clock using SNTP on application startup" - depends on SNTP && POSIX_TIMERS + depends on SNTP help Perform an SNTP request over networking to get and absolute wall clock time, and initialize system time from it, so diff --git a/subsys/net/lib/config/init_clock_sntp.c b/subsys/net/lib/config/init_clock_sntp.c index dd63564f9c02f..7f0b9d7979ff3 100644 --- a/subsys/net/lib/config/init_clock_sntp.c +++ b/subsys/net/lib/config/init_clock_sntp.c @@ -12,7 +12,6 @@ LOG_MODULE_DECLARE(net_config, CONFIG_NET_CONFIG_LOG_LEVEL); #include #include #include -#include #ifdef CONFIG_NET_CONFIG_SNTP_INIT_RESYNC static void sntp_resync_handler(struct k_work *work); @@ -60,7 +59,7 @@ int net_init_clock_via_sntp(void) tspec.tv_sec = ts.seconds; tspec.tv_nsec = ((uint64_t)ts.fraction * (1000 * 1000 * 1000)) >> 32; - res = clock_settime(CLOCK_REALTIME, &tspec); + res = sys_clock_settime(SYS_CLOCK_REALTIME, &tspec); end: #ifdef CONFIG_NET_CONFIG_SNTP_INIT_RESYNC diff --git a/subsys/net/lib/lwm2m/Kconfig b/subsys/net/lib/lwm2m/Kconfig index 9872e2acd915c..8bd0cdef300f6 100644 --- a/subsys/net/lib/lwm2m/Kconfig +++ b/subsys/net/lib/lwm2m/Kconfig @@ -227,7 +227,7 @@ config LWM2M_RD_CLIENT_MAX_RETRIES config LWM2M_RESOURCE_DATA_CACHE_SUPPORT bool "Resource Time series data cache support" depends on (LWM2M_RW_SENML_JSON_SUPPORT || LWM2M_RW_SENML_CBOR_SUPPORT) - depends on (XSI_SINGLE_PROCESS && RING_BUFFER) + depends on RING_BUFFER help Enable time series data storage. Requires time() to provide current Unix timestamp. diff --git a/tests/lib/c_lib/thrd/src/cnd.c b/tests/lib/c_lib/thrd/src/cnd.c index 82741e352bbcf..01d9f822f5de5 100644 --- a/tests/lib/c_lib/thrd/src/cnd.c +++ b/tests/lib/c_lib/thrd/src/cnd.c @@ -71,7 +71,7 @@ static int test_cnd_thread_fn(void *arg) struct libc_cnd_fixture *const fixture = arg; if (fixture->do_timedwait) { - zassume_ok(clock_gettime(CLOCK_REALTIME, &time_point)); + zassume_ok(sys_clock_gettime(SYS_CLOCK_REALTIME, &time_point)); timespec_add_ms(&time_point, WAIT_TIME_MS); res = cnd_timedwait(&fixture->cond, &fixture->mutex, &time_point); } else { diff --git a/tests/lib/c_lib/thrd/src/mtx.c b/tests/lib/c_lib/thrd/src/mtx.c index 21dc3d0c1c764..8041396983eff 100644 --- a/tests/lib/c_lib/thrd/src/mtx.c +++ b/tests/lib/c_lib/thrd/src/mtx.c @@ -94,7 +94,7 @@ static int mtx_timedlock_fn(void *arg) struct timespec time_point; mtx_t *mtx = (mtx_t *)arg; - zassume_ok(clock_gettime(CLOCK_MONOTONIC, &time_point)); + zassume_ok(sys_clock_gettime(SYS_CLOCK_MONOTONIC, &time_point)); timespec_add_ms(&time_point, TIMEDLOCK_TIMEOUT_MS); return mtx_timedlock(mtx, &time_point); diff --git a/tests/lib/c_lib/thrd/src/thrd.c b/tests/lib/c_lib/thrd/src/thrd.c index 70dca94b4a9a5..6a9f0b5de171d 100644 --- a/tests/lib/c_lib/thrd/src/thrd.c +++ b/tests/lib/c_lib/thrd/src/thrd.c @@ -10,6 +10,7 @@ #include #include +#include #include static thrd_t thr; @@ -23,17 +24,21 @@ ZTEST(libc_thrd, test_thrd_sleep) struct timespec remaining; const uint16_t delay_ms[] = {0, 100, 200, 400}; - zassert_not_equal(0, thrd_sleep(NULL, NULL)); - zassert_ok(thrd_sleep(&duration, NULL)); - zassert_ok(thrd_sleep(&duration, &duration)); + if (false) { + /* duration may not be NULL */ + zassert_not_equal(thrd_success, thrd_sleep(NULL, NULL)); + } + + zassert_equal(thrd_success, thrd_sleep(&duration, NULL)); + zassert_equal(thrd_success, thrd_sleep(&duration, &duration)); for (int i = 0; i < ARRAY_SIZE(delay_ms); ++i) { - duration = (struct timespec){.tv_nsec = delay_ms[i] * NSEC_PER_MSEC}; + timespec_from_timeout(K_MSEC(delay_ms[i]), &duration); remaining = (struct timespec){.tv_sec = 4242, .tv_nsec = 4242}; printk("sleeping %d ms\n", delay_ms[i]); start = k_uptime_get(); - zassert_ok(thrd_sleep(&duration, &remaining)); + zassert_equal(thrd_success, thrd_sleep(&duration, &remaining)); end = k_uptime_get(); zassert_equal(remaining.tv_sec, 0); zassert_equal(remaining.tv_nsec, 0); diff --git a/tests/lib/c_lib/thrd/src/thrd.h b/tests/lib/c_lib/thrd/src/thrd.h index 02861b2ac6ea9..103b30a4f5f63 100644 --- a/tests/lib/c_lib/thrd/src/thrd.h +++ b/tests/lib/c_lib/thrd/src/thrd.h @@ -11,6 +11,7 @@ #include #include +#include /* arbitrary magic numbers used for testing */ #define BIOS_FOOD 0xb105f00d @@ -20,12 +21,12 @@ static inline void timespec_add_ms(struct timespec *ts, uint32_t ms) { - bool oflow; + struct timespec ms_as_ts = { + .tv_sec = ms / MSEC_PER_SEC, + .tv_nsec = (ms % MSEC_PER_SEC) * NSEC_PER_MSEC, + }; - ts->tv_nsec += ms * NSEC_PER_MSEC; - oflow = ts->tv_nsec >= NSEC_PER_SEC; - ts->tv_sec += oflow; - ts->tv_nsec -= oflow * NSEC_PER_SEC; + (void)timespec_add(ts, &ms_as_ts); } #endif diff --git a/tests/lib/time/CMakeLists.txt b/tests/lib/c_lib/time/CMakeLists.txt similarity index 100% rename from tests/lib/time/CMakeLists.txt rename to tests/lib/c_lib/time/CMakeLists.txt diff --git a/tests/lib/c_lib/time/prj.conf b/tests/lib/c_lib/time/prj.conf new file mode 100644 index 0000000000000..97b1a4e7da24f --- /dev/null +++ b/tests/lib/c_lib/time/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ZTEST=y +CONFIG_PICOLIBC=y diff --git a/tests/lib/time/src/main.c b/tests/lib/c_lib/time/src/main.c similarity index 83% rename from tests/lib/time/src/main.c rename to tests/lib/c_lib/time/src/main.c index 227ed25131f52..e7346738a16f3 100644 --- a/tests/lib/time/src/main.c +++ b/tests/lib/c_lib/time/src/main.c @@ -37,7 +37,7 @@ ZTEST(libc_time, test_time_passing) time_current = time(NULL); zassert_equal(time_current, time_initial + i, "Current time (%d) does not match expected time (%d)", - (int) time_current, (int) (time_initial + i)); + (int)time_current, (int)(time_initial + i)); } } @@ -49,14 +49,12 @@ ZTEST(libc_time, test_time_param) time_result = time(&time_param); - zassert_equal(time_result, time_param, - "time() result does not match param value"); + zassert_equal(time_result, time_param, "time() result does not match param value"); for (i = 0; i < 10; i++) { k_sleep(K_SECONDS(1)); - zassert_equal(time_result, time_param, - "time() result does not match param value"); + zassert_equal(time_result, time_param, "time() result does not match param value"); } } diff --git a/tests/lib/time/testcase.yaml b/tests/lib/c_lib/time/testcase.yaml similarity index 100% rename from tests/lib/time/testcase.yaml rename to tests/lib/c_lib/time/testcase.yaml diff --git a/tests/lib/time/prj.conf b/tests/lib/time/prj.conf deleted file mode 100644 index dd87f5a1338c0..0000000000000 --- a/tests/lib/time/prj.conf +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_ZTEST=y -CONFIG_POSIX_TIMERS=y -CONFIG_XSI_SINGLE_PROCESS=y -CONFIG_PICOLIBC=y