diff --git a/arch/tricore/src/common/tricore_internal.h b/arch/tricore/src/common/tricore_internal.h index e90dbe2551097..1b22948abeb85 100644 --- a/arch/tricore/src/common/tricore_internal.h +++ b/arch/tricore/src/common/tricore_internal.h @@ -241,8 +241,9 @@ void tricore_earlyserialinit(void); /* System Timer *************************************************************/ -struct oneshot_lowerhalf_s * -tricore_systimer_initialize(volatile void *tbase, int irq, uint64_t freq); +void tricore_systimer_initialize(volatile void *tbase, + int irq, + uint64_t freq); /* Debug ********************************************************************/ diff --git a/arch/tricore/src/common/tricore_systimer.c b/arch/tricore/src/common/tricore_systimer.c index 70ad40629cffb..8e5a60a55e6e5 100644 --- a/arch/tricore/src/common/tricore_systimer.c +++ b/arch/tricore/src/common/tricore_systimer.c @@ -28,6 +28,7 @@ #include #include +#include #include #include "tricore_internal.h" @@ -47,6 +48,7 @@ struct tricore_systimer_lowerhalf_s { struct oneshot_lowerhalf_s lower; volatile void *tbase; + int irq; uint64_t freq; uint64_t alarm; oneshot_callback_t callback; @@ -332,6 +334,13 @@ tricore_systimer_tick_start(struct oneshot_lowerhalf_s *lower, static int tricore_systimer_interrupt(int irq, void *context, void *arg) { +#ifdef CONFIG_HRTIMER + (void)irq; + (void)context; + (void)arg; + + hrtimer_upper_process(&g_default_hrtimer_upperhalf); +#else struct tricore_systimer_lowerhalf_s *priv = arg; tricore_systimer_set_timecmp(priv, UINT64_MAX); @@ -339,6 +348,7 @@ static int tricore_systimer_interrupt(int irq, void *context, void *arg) { priv->callback(&priv->lower, priv->arg); } +#endif return 0; } @@ -356,13 +366,15 @@ static int tricore_systimer_interrupt(int irq, void *context, void *arg) * ****************************************************************************/ -struct oneshot_lowerhalf_s * -tricore_systimer_initialize(volatile void *tbase, int irq, uint64_t freq) +void tricore_systimer_initialize(volatile void *tbase, + int irq, + uint64_t freq) { struct tricore_systimer_lowerhalf_s *priv = &g_systimer_lower; priv->tbase = tbase; priv->freq = freq; + priv->irq = irq; spin_lock_init(&priv->lock); IfxStm_setCompareControl(tbase, @@ -376,7 +388,39 @@ tricore_systimer_initialize(volatile void *tbase, int irq, uint64_t freq) IfxStm_enableComparatorInterrupt(tbase, IfxStm_Comparator_0); irq_attach(irq, tricore_systimer_interrupt, priv); + + up_alarm_set_lowerhalf((struct oneshot_lowerhalf_s *)priv); + +#ifdef CONFIG_HRTIMER + hrtimer_upper_start(&g_default_hrtimer_upperhalf, + HRTIMER_CLOCK_ALARM); +#endif up_enable_irq(irq); +} + +int up_alarm_start(const struct timespec *ts) +{ + struct tricore_systimer_lowerhalf_s *priv = &g_systimer_lower; + uint64_t mtime = ts->tv_sec * priv->freq + + ts->tv_nsec * priv->freq / NSEC_PER_SEC; + + IfxStm_updateCompare(priv->tbase, IfxStm_Comparator_0, mtime); + up_enable_irq(priv->irq); +} + +int up_alarm_gettime(FAR struct timespec *ts) +{ + struct tricore_systimer_lowerhalf_s *priv = &g_systimer_lower; + uint64_t mtime = IfxStm_get(priv->tbase); + uint64_t nsecs = mtime * NSEC_PER_SEC / priv->freq; + + ts->tv_sec = nsecs / NSEC_PER_SEC; + ts->tv_nsec = nsecs % NSEC_PER_SEC; +} + +int up_alarm_trigger(void) +{ + struct tricore_systimer_lowerhalf_s *priv = &g_systimer_lower; - return (struct oneshot_lowerhalf_s *)priv; + up_trigger_irq(priv->irq, 0); } diff --git a/arch/tricore/src/tc3xx/tc3xx_timerisr.c b/arch/tricore/src/tc3xx/tc3xx_timerisr.c index c52f77adf1a40..c6c76eea5b9c2 100644 --- a/arch/tricore/src/tc3xx/tc3xx_timerisr.c +++ b/arch/tricore/src/tc3xx/tc3xx_timerisr.c @@ -55,11 +55,5 @@ void up_timer_initialize(void) { - struct oneshot_lowerhalf_s *lower; - - lower = tricore_systimer_initialize(&MODULE_STM0, 192, SCU_FREQUENCY); - - DEBUGASSERT(lower != NULL); - - up_alarm_set_lowerhalf(lower); + tricore_systimer_initialize(&MODULE_STM0, 192, SCU_FREQUENCY); } diff --git a/boards/tricore/tc397/a2g-tc397-5v-tft/configs/nsh/defconfig b/boards/tricore/tc397/a2g-tc397-5v-tft/configs/nsh/defconfig index 151937b177c66..c17469c35adfb 100644 --- a/boards/tricore/tc397/a2g-tc397-5v-tft/configs/nsh/defconfig +++ b/boards/tricore/tc397/a2g-tc397-5v-tft/configs/nsh/defconfig @@ -53,3 +53,4 @@ CONFIG_TESTING_GETPRIME=y CONFIG_TESTING_OSTEST=y CONFIG_UART0_SERIAL_CONSOLE=y CONFIG_SCHED_EVENTS=y +CONFIG_HRTIMER=y diff --git a/drivers/timers/arch_alarm.c b/drivers/timers/arch_alarm.c index 8133ec0ff85de..fae4c489e9594 100644 --- a/drivers/timers/arch_alarm.c +++ b/drivers/timers/arch_alarm.c @@ -139,19 +139,22 @@ static void oneshot_callback(FAR struct oneshot_lowerhalf_s *lower, void up_alarm_set_lowerhalf(FAR struct oneshot_lowerhalf_s *lower) { -#ifdef CONFIG_SCHED_TICKLESS +#ifdef CONFIG_HRTIMER + g_oneshot_lower = lower; +#else +# ifdef CONFIG_SCHED_TICKLESS clock_t ticks = 0; -#endif - +# endif g_oneshot_lower = lower; -#ifdef CONFIG_SCHED_TICKLESS +# ifdef CONFIG_SCHED_TICKLESS ONESHOT_TICK_MAX_DELAY(g_oneshot_lower, &ticks); g_oneshot_maxticks = ticks < UINT32_MAX ? ticks : UINT32_MAX; -#else +# else ONESHOT_TICK_CURRENT(g_oneshot_lower, &g_current_tick); ONESHOT_TICK_START(g_oneshot_lower, oneshot_callback, NULL, 1); -#endif +# endif +#endif /* CONFIG_HRTIMER */ } /**************************************************************************** diff --git a/include/nuttx/arch.h b/include/nuttx/arch.h index 13fd3571e092a..f5340312da080 100644 --- a/include/nuttx/arch.h +++ b/include/nuttx/arch.h @@ -1932,6 +1932,88 @@ int up_timer_gettime(FAR struct timespec *ts); int up_timer_gettick(FAR clock_t *ticks); void up_timer_getmask(FAR clock_t *mask); +/**************************************************************************** + * Name: up_alarm_gettime + * + * Description: + * Return the current time according to the underlying high-resolution + * alarm timer. This is the time base used by the alarm hardware. + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Location to return the current time. Must not be NULL. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. + * + ****************************************************************************/ + +#if (defined(CONFIG_HRTIMER) && defined(CONFIG_ALARM_ARCH)) +int up_alarm_gettime(FAR struct timespec *ts); +int up_alarm_gettick(FAR clock_t *ticks); +#endif + +/**************************************************************************** + * Name: up_alarm_trigger + * + * Description: + * Force the expiration of the alarm immediately, causing the alarm + * handler nxsched_alarm_expiration() to be invoked as soon as possible. + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +#if (defined(CONFIG_HRTIMER) && defined(CONFIG_ALARM_ARCH)) +int up_alarm_trigger(void); +#endif + +/**************************************************************************** + * Name: up_timer_trigger + * + * Description: + * Force the expiration of the interval timer immediately, causing the + * system tick or equivalent timer handler to be invoked as soon as + * possible. This is typically used for testing or debugging purposes. + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +#if (defined(CONFIG_HRTIMER) && defined(CONFIG_TIMER_ARCH)) +int up_timer_trigger(void); +#endif + /**************************************************************************** * Name: up_alarm_cancel * @@ -1966,12 +2048,12 @@ void up_timer_getmask(FAR clock_t *mask); * ****************************************************************************/ -#if defined(CONFIG_SCHED_TICKLESS) && defined(CONFIG_SCHED_TICKLESS_ALARM) -# ifndef CONFIG_SCHED_TICKLESS_TICK_ARGUMENT +#if (defined(CONFIG_HRTIMER) && defined(CONFIG_ALARM_ARCH)) || \ + (defined(CONFIG_SCHED_TICKLESS) && defined(CONFIG_SCHED_TICKLESS_ALARM)) int up_alarm_cancel(FAR struct timespec *ts); -# else +#ifdef CONFIG_SCHED_TICKLESS_TICK_ARGUMENT int up_alarm_tick_cancel(FAR clock_t *ticks); -# endif +# endif #endif /**************************************************************************** @@ -1999,12 +2081,12 @@ int up_alarm_tick_cancel(FAR clock_t *ticks); * ****************************************************************************/ -#if defined(CONFIG_SCHED_TICKLESS) && defined(CONFIG_SCHED_TICKLESS_ALARM) -# ifndef CONFIG_SCHED_TICKLESS_TICK_ARGUMENT +#if (defined(CONFIG_HRTIMER) && defined(CONFIG_ALARM_ARCH)) || \ + (defined(CONFIG_SCHED_TICKLESS) && defined(CONFIG_SCHED_TICKLESS_ALARM)) int up_alarm_start(FAR const struct timespec *ts); -# else +#ifdef CONFIG_SCHED_TICKLESS_TICK_ARGUMENT int up_alarm_tick_start(clock_t ticks); -# endif +# endif #endif /**************************************************************************** @@ -2043,12 +2125,12 @@ int up_alarm_tick_start(clock_t ticks); * ****************************************************************************/ -#if defined(CONFIG_SCHED_TICKLESS) && !defined(CONFIG_SCHED_TICKLESS_ALARM) -# ifndef CONFIG_SCHED_TICKLESS_TICK_ARGUMENT +#if (defined(CONFIG_HRTIMER) && defined(CONFIG_TIMER_ARCH)) || \ + (defined(CONFIG_SCHED_TICKLESS) && !defined(CONFIG_SCHED_TICKLESS_ALARM)) int up_timer_cancel(FAR struct timespec *ts); -# else +#ifdef CONFIG_SCHED_TICKLESS_TICK_ARGUMENT int up_timer_tick_cancel(FAR clock_t *ticks); -# endif +#endif #endif /**************************************************************************** @@ -2075,15 +2157,13 @@ int up_timer_tick_cancel(FAR clock_t *ticks); * non-reentrancy. * ****************************************************************************/ - -#if defined(CONFIG_SCHED_TICKLESS) && !defined(CONFIG_SCHED_TICKLESS_ALARM) -# ifndef CONFIG_SCHED_TICKLESS_TICK_ARGUMENT +#if (defined(CONFIG_HRTIMER) && defined(CONFIG_TIMER_ARCH)) || \ + (defined(CONFIG_SCHED_TICKLESS) && !defined(CONFIG_SCHED_TICKLESS_ALARM)) int up_timer_start(FAR const struct timespec *ts); -# else -int up_timer_tick_start(clock_t ticks); -# endif +#ifdef CONFIG_SCHED_TICKLESS_TICK_ARGUMENT +int up_timer_tick_start(FAR clock_t *ticks); +#endif #endif - /**************************************************************************** * Name: up_getsp * diff --git a/include/nuttx/hrtimer.h b/include/nuttx/hrtimer.h new file mode 100644 index 0000000000000..3ec3b60e68a89 --- /dev/null +++ b/include/nuttx/hrtimer.h @@ -0,0 +1,302 @@ +/**************************************************************************** + * include/nuttx/hrtimer.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_HRTIMER_H +#define __INCLUDE_NUTTX_HRTIMER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Maximum representable timer value */ + +#define HRTIMER_MAX_VALUE UINT64_MAX + +/* The maximum increment that can be safely distinguished without overflow */ + +#define HRTIMER_MAX_INCREMENT (HRTIMER_MAX_VALUE >> 1) + +/* Default expiration increment if no timer is active */ + +#define HRTIMER_DEFAULT_INCREMENT ((uint64_t)UINT32_MAX >> 2) + +/* Maximum number of hrtimer process handlers invoked in one batch */ + +#define MAX_HRTIMER_PROCESS_CNT 4u + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Timer mode: absolute or relative */ + +enum hrtimer_mode +{ + HRTIMER_MODE_ABS = 0x0, /* Absolute expiration time */ + HRTIMER_MODE_REL = 0x1 /* Relative delay from now */ +}; + +/* Clock source for hrtimers */ + +typedef enum +{ + HRTIMER_CLOCK_ALARM = 0x0, /* Alarm-based hardware clock */ + HRTIMER_CLOCK_TIMER = 0x1 /* Timer-based hardware clock */ +} htimer_clockid_t; + +/* Forward declarations */ + +struct hrtimer_s; +struct hrtimer_node; +struct hrtimer_upperhalf_s; + +/* Callback type for high-resolution timer expiration */ + +typedef void (*hrtimer_callback_t)(FAR struct hrtimer_s *); + +/* High-resolution timer tree node */ + +struct hrtimer_node +{ + uint64_t expiration_time; /* Expiration time in nanoseconds */ + RB_ENTRY(hrtimer_node) entry; /* Red-black tree linkage */ +}; + +/* Red-black tree root for hrtimers */ + +RB_HEAD(hrtimer_tree, hrtimer_node); + +/* High-resolution timer instance */ + +struct hrtimer_s +{ + struct hrtimer_node node; /* Expiration time node */ + FAR struct hrtimer_upperhalf_s *upper; /* Associated timer queue */ + hrtimer_callback_t callback; /* Expiration callback */ + FAR void *args; /* Optional callback argument */ +}; + +/* High-resolution timer upper-half instance */ + +struct hrtimer_upperhalf_s +{ + FAR struct hrtimer_tree tree; /* Timer tree (RB tree of active timers) */ + htimer_clockid_t clockid; /* Clock source type */ + bool started; /* True if timer queue has started */ + spinlock_t lock; /* Spinlock protecting the queue */ +}; + +/* Global default hrtimer queue and systick timer */ + +extern struct hrtimer_upperhalf_s g_default_hrtimer_upperhalf; +extern struct hrtimer_s g_hrtimer_systick_timer; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +# define EXTERN extern "C" +extern "C" +{ +#else +# define EXTERN extern +#endif + +/**************************************************************************** + * Name: hrtimer_reload + * + * Description: + * Reload (reschedule) a high-resolution timer by advancing its expiration + * time with the specified interval in nanoseconds. + * + * Input Parameters: + * hrtimer - Timer instance to reload + * ns - Interval to add in nanoseconds + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +int hrtimer_reload(FAR struct hrtimer_s *hrtimer, uint64_t ns); + +/**************************************************************************** + * Name: hrtimer_upper_start + * + * Description: + * Start the high-resolution timer queue. + * + * Input Parameters: + * upper - Timer upper-half instance + * clockid - Clock source identifier + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +int hrtimer_upper_start(FAR struct hrtimer_upperhalf_s *upper, + htimer_clockid_t clockid); + +/**************************************************************************** + * Name: hrtimer_upper_process + * + * Description: + * Process and handle expired timers in the queue. + * + * Input Parameters: + * upper - Timer upper-half instance + * + ****************************************************************************/ + +void hrtimer_upper_process(FAR struct hrtimer_upperhalf_s *upper); + +/**************************************************************************** + * Name: hrtimer_upper_add_abs + * + * Description: + * Add a timer with an absolute expiration time. + * + * Input Parameters: + * upper - Timer upper-half instance + * hrtimer - Timer instance + * start - Absolute expiration time + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +int hrtimer_upper_add_abs(FAR struct hrtimer_upperhalf_s *upper, + FAR struct hrtimer_s *hrtimer, + uint64_t start); + +/**************************************************************************** + * Name: hrtimer_upper_add_rel + * + * Description: + * Add a timer with a relative delay from now. + * + * Input Parameters: + * upper - Timer upper-half instance + * hrtimer - Timer instance + * increment - Relative delay in nanoseconds + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +int hrtimer_upper_add_rel(FAR struct hrtimer_upperhalf_s *upper, + FAR struct hrtimer_s *hrtimer, + uint64_t increment); + +/**************************************************************************** + * Name: hrtimer_init + * + * Description: + * Initialize a high-resolution timer instance. + * + * Input Parameters: + * hrtimer - Timer instance + * callback - Expiration callback + * args - Callback argument + * + ****************************************************************************/ + +static inline void +hrtimer_init(FAR struct hrtimer_s *hrtimer, + hrtimer_callback_t callback, + FAR void *args) +{ + hrtimer->node.expiration_time = 0; + hrtimer->upper = NULL; + hrtimer->callback = callback; + hrtimer->args = args; +} + +/**************************************************************************** + * Name: hrtimer_cancel + * + * Description: + * Cancel a running high-resolution timer. + * + * Input Parameters: + * hrtimer - Timer instance + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +int hrtimer_cancel(FAR struct hrtimer_s *hrtimer); + +/**************************************************************************** + * Name: hrtimer_start + * + * Description: + * Start a high-resolution timer in either absolute or relative mode. + * + * Input Parameters: + * hrtimer - Timer instance + * ns - Expiration time in nanoseconds + * mode - Expiration mode (absolute or relative) + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +static inline int +hrtimer_start(FAR struct hrtimer_s *hrtimer, + uint64_t ns, + const enum hrtimer_mode mode) +{ + FAR struct hrtimer_upperhalf_s *upper = &g_default_hrtimer_upperhalf; + + if (mode == HRTIMER_MODE_ABS) + { + return hrtimer_upper_add_abs(upper, hrtimer, ns); + } + else + { + return hrtimer_upper_add_rel(upper, hrtimer, ns); + } +} + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_HRTIMER_H */ diff --git a/include/nuttx/timers/arch_alarm.h b/include/nuttx/timers/arch_alarm.h index d3487cf6eaf1c..8e0d1ce677a34 100644 --- a/include/nuttx/timers/arch_alarm.h +++ b/include/nuttx/timers/arch_alarm.h @@ -29,7 +29,7 @@ #include #include - +#include /**************************************************************************** * Public Function Prototypes ****************************************************************************/ diff --git a/sched/Kconfig b/sched/Kconfig index 194dac518886b..458243884807b 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -2034,3 +2034,11 @@ config CUSTOM_SEMAPHORE_MAXVALUE ---help--- Enable to support custom max value for semaphores. When this option is enabled, the max value of a semaphore can be set + +config HRTIMER + bool "High-resolution timer (hrtimer) support" + depends on ALARM_ARCH || TIMER_ARCH + default n + ---help--- + Enable high-resolution timers (hrtimers) for precise time-based + operations within the system. diff --git a/sched/Makefile b/sched/Makefile index 92989e81253ad..2d743dfd3bb04 100644 --- a/sched/Makefile +++ b/sched/Makefile @@ -43,6 +43,7 @@ include timer/Make.defs include tls/Make.defs include wdog/Make.defs include wqueue/Make.defs +include hrtimer/Make.defs CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)sched diff --git a/sched/hrtimer/CMakeLists.txt b/sched/hrtimer/CMakeLists.txt new file mode 100644 index 0000000000000..38ebc0bc7ba49 --- /dev/null +++ b/sched/hrtimer/CMakeLists.txt @@ -0,0 +1,35 @@ +# ############################################################################## +# sched/hrtimer/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +set(CSRCS) +if(CONFIG_HRTIMER) + list( + APPEND + CSRCS + hrtimer_add.c + hrtimer_cancel.c + hrtimer_init.c + hrtimer_process.c + hrtimer_reload.c) +endif() + +target_sources(sched PRIVATE ${CSRCS}) diff --git a/sched/hrtimer/Make.defs b/sched/hrtimer/Make.defs new file mode 100644 index 0000000000000..200322673ce74 --- /dev/null +++ b/sched/hrtimer/Make.defs @@ -0,0 +1,32 @@ +############################################################################ +# sched/hrtimer/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +# Add hrtimer-related files to the build + +ifeq ($(CONFIG_HRTIMER),y) + CSRCS += hrtimer_add.c hrtimer_cancel.c hrtimer_init.c hrtimer_process.c hrtimer_reload.c +endif + +# Include hrtimer build support + +DEPPATH += --dep-path hrtimer +VPATH += :hrtimer diff --git a/sched/hrtimer/hrtimer.h b/sched/hrtimer/hrtimer.h new file mode 100644 index 0000000000000..800b542b38f25 --- /dev/null +++ b/sched/hrtimer/hrtimer.h @@ -0,0 +1,290 @@ +/**************************************************************************** + * sched/hrtimer/hrtimer.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +#ifndef __SCHED_HRTIMER_H +#define __SCHED_HRTIMER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Name: hrtimer_clock_setexpire + * + * Description: + * Program the hardware to expire at the specified time. + * + * Input Parameters: + * upper - Timer upper-half instance + * ns - Expiration time in nanoseconds + * + * Returned Value: + * OK on success; -ENOTSUP if not supported. + * + ****************************************************************************/ + +static inline int +hrtimer_clock_setexpire(FAR struct hrtimer_upperhalf_s *upper, uint64_t ns) +{ + int ret = -ENOTSUP; + struct timespec ts; + clock_nsec2time(&ts, ns); + + switch (upper->clockid) + { +#ifdef CONFIG_ALARM_ARCH + case HRTIMER_CLOCK_ALARM: + ret = up_alarm_start(&ts); + break; +#endif +#ifdef CONFIG_TIMER_ARCH + case HRTIMER_CLOCK_TIMER: + ret = up_timer_start(&ts); + break; +#endif + default: + break; + } + + return ret; +} + +/**************************************************************************** + * Name: hrtimer_clock_current + * + * Description: + * Get the current time of the underlying hardware clock. + * + * Input Parameters: + * upper - Timer upper-half instance + * ns - Location to return current time (nanoseconds) + * + * Returned Value: + * OK on success; hardware-dependent error code on failure. + * + ****************************************************************************/ + +static inline int +hrtimer_clock_current(FAR struct hrtimer_upperhalf_s *upper, uint64_t *ns) +{ + int ret = -ENOTSUP; + struct timespec ts; + + switch (upper->clockid) + { +#ifdef CONFIG_ALARM_ARCH + case HRTIMER_CLOCK_ALARM: + ret = up_alarm_gettime(&ts); + break; +#endif +#ifdef CONFIG_TIMER_ARCH + case HRTIMER_CLOCK_TIMER: + ret = up_timer_gettime(&ts); + break; +#endif + default: + break; + } + + *ns = clock_time2nsec(&ts); + return ret; +} + +/**************************************************************************** + * Name: hrtimer_clock_start + * + * Description: + * Start the hardware clock associated with the given upper-half timer + * queue. + * + * Input Parameters: + * upper - Timer upper-half instance + * + * Returned Value: + * OK on success; -ENOTSUP if not supported. + * + ****************************************************************************/ + +static inline int +hrtimer_clock_start(FAR struct hrtimer_upperhalf_s *upper) +{ + int ret = -ENOTSUP; + + switch (upper->clockid) + { +#ifdef CONFIG_ALARM_ARCH + case HRTIMER_CLOCK_ALARM: + ret = up_alarm_trigger(); + break; +#endif +#ifdef CONFIG_TIMER_ARCH + case HRTIMER_CLOCK_TIMER: + ret = up_timer_trigger(); + break; +#endif + default: + break; + } + + return ret; +} + +/**************************************************************************** + * Name: hrtimer_clock_trigger + * + * Description: + * Force trigger of the hardware clock associated with the upper-half + * timer queue. + * + * Input Parameters: + * upper - Timer upper-half instance + * + * Returned Value: + * OK on success; -ENOTSUP if not supported. + * + ****************************************************************************/ + +static inline int +hrtimer_clock_trigger(FAR struct hrtimer_upperhalf_s *upper) +{ + int ret = -ENOTSUP; + + switch (upper->clockid) + { +#ifdef CONFIG_ALARM_ARCH + case HRTIMER_CLOCK_ALARM: + ret = up_alarm_trigger(); + break; +#endif +#ifdef CONFIG_TIMER_ARCH + case HRTIMER_CLOCK_TIMER: + ret = up_timer_trigger(); + break; +#endif + default: + break; + } + + return ret; +} + +/**************************************************************************** + * Name: hrtimer_has_expired + * + * Description: + * Check whether a timer has already expired. + * + * Input Parameters: + * expiration_time - Expiration timestamp + * current_time - Current timestamp + * + * Returned Value: + * True if expired; false otherwise. + * + ****************************************************************************/ + +static inline bool +hrtimer_has_expired(uint64_t expiration_time, + uint64_t current_time) +{ + if (expiration_time > current_time) + { + return (expiration_time - current_time) > HRTIMER_MAX_INCREMENT; + } + else if (expiration_time < current_time) + { + return (current_time - expiration_time) <= HRTIMER_MAX_INCREMENT; + } + else + { + return true; + } +} + +/**************************************************************************** + * Name: hrtimer_setexpire + * + * Description: + * Program the hardware to expire at the specified time. If the time has + * already passed, trigger immediately. + * + * Input Parameters: + * upper - Timer upper-half instance + * expiration_time - Expiration timestamp + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +static inline int +hrtimer_setexpire(FAR struct hrtimer_upperhalf_s *upper, + uint64_t expiration_time) +{ + uint64_t now; + int ret = OK; + + hrtimer_clock_setexpire(upper, expiration_time); + hrtimer_clock_current(upper, &now); + + /* If expiration already passed, trigger immediately */ + + if (hrtimer_has_expired(expiration_time, now)) + { + ret = hrtimer_clock_trigger(upper); + } + + return ret; +} + +/**************************************************************************** + * Name: hrtimer_cmp + * + * Description: + * Compare two hrtimer nodes for ordering in the RB tree. + * + * Input Parameters: + * a - First node + * b - Second node + * + * Returned Value: + * <0 if a < b, 0 if equal, >0 if a > b + * + ****************************************************************************/ + +static inline int +hrtimer_cmp(struct hrtimer_node *a, struct hrtimer_node *b) +{ + return hrtimer_has_expired(b->expiration_time, a->expiration_time); +} +RB_PROTOTYPE(hrtimer_tree, hrtimer_node, entry, hrtimer_cmp); + +#endif /* __SCHED_HRTIMER_H */ diff --git a/sched/hrtimer/hrtimer_add.c b/sched/hrtimer/hrtimer_add.c new file mode 100644 index 0000000000000..fa4976f3b62a3 --- /dev/null +++ b/sched/hrtimer/hrtimer_add.c @@ -0,0 +1,170 @@ +/**************************************************************************** + * sched/hrtimer/hrtimer_add.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "hrtimer/hrtimer.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: hrtimer_upper_add_abs + * + * Description: + * Add a high-resolution timer to the active queue using an absolute + * expiration time. If this timer becomes the earliest in the queue, + * update the underlying hardware expiration time. + * + * Input Parameters: + * upper - High-resolution timer upper-half driver instance + * hrtimer - Timer instance to be queued + * start - Absolute expiration time in timer ticks + * + * Returned Value: + * OK (zero) on success; a negated errno value on failure. + * + * Assumptions: + * The caller provides valid arguments. Called from task or interrupt + * context. Internal locking is handled within this function. + * + ****************************************************************************/ + +int hrtimer_upper_add_abs(FAR struct hrtimer_upperhalf_s *upper, + FAR struct hrtimer_s *hrtimer, + uint64_t start) +{ + irqstate_t flags; + int ret = OK; + FAR struct hrtimer_node *inserted; + + DEBUGASSERT(upper != NULL); + + /* Bind this timer to the queue */ + + hrtimer->upper = upper; + hrtimer->node.expiration_time = start; + + /* Insert the timer into the RB tree */ + + flags = spin_lock_irqsave(&upper->lock); + + inserted = RB_INSERT(hrtimer_tree, &upper->tree, &hrtimer->node); + if (inserted != NULL) + { + /* Duplicate entry */ + + ret = -EINVAL; + } + else + { + /* Update hardware if this is the earliest timer */ + + if (&hrtimer->node == RB_MIN(hrtimer_tree, &upper->tree)) + { + ret = hrtimer_setexpire(upper, start); + } + } + + spin_unlock_irqrestore(&upper->lock, flags); + return ret; +} + +/**************************************************************************** + * Name: hrtimer_upper_add_rel + * + * Description: + * Add a high-resolution timer to the active queue using a relative + * expiration time. The absolute expiration time is calculated as the + * current time plus the increment. If this timer becomes the earliest + * in the queue, update the underlying hardware expiration time. + * + * Input Parameters: + * upper - High-resolution timer upper-half driver instance + * hrtimer - Timer instance to be queued + * increment - Relative time interval in timer ticks + * + * Returned Value: + * OK (zero) on success; a negated errno value on failure. + * + * Assumptions: + * The caller provides valid arguments. Called from task or interrupt + * context. Internal locking is handled within this function. + * + ****************************************************************************/ + +int hrtimer_upper_add_rel(FAR struct hrtimer_upperhalf_s *upper, + FAR struct hrtimer_s *hrtimer, + uint64_t increment) +{ + irqstate_t flags; + uint64_t now; + uint64_t expiration; + int ret = OK; + FAR struct hrtimer_node *inserted; + + DEBUGASSERT(upper != NULL); + DEBUGASSERT(increment <= HRTIMER_MAX_INCREMENT); + + /* Bind this timer to the queue */ + + hrtimer->upper = upper; + + /* Get current time from the lower-half driver */ + + hrtimer_clock_current(upper, &now); + + /* Calculate absolute expiration */ + + expiration = now + increment; + hrtimer->node.expiration_time = expiration; + + /* Insert the timer into the RB tree */ + + flags = spin_lock_irqsave(&upper->lock); + + inserted = RB_INSERT(hrtimer_tree, &upper->tree, &hrtimer->node); + if (inserted != NULL) + { + /* Duplicate entry */ + + ret = -EINVAL; + } + else + { + /* Update hardware if this is the earliest timer */ + + if (&hrtimer->node == RB_MIN(hrtimer_tree, &upper->tree)) + { + ret = hrtimer_setexpire(upper, expiration); + } + } + + spin_unlock_irqrestore(&upper->lock, flags); + return ret; +} diff --git a/sched/hrtimer/hrtimer_cancel.c b/sched/hrtimer/hrtimer_cancel.c new file mode 100644 index 0000000000000..49b6d1a73745c --- /dev/null +++ b/sched/hrtimer/hrtimer_cancel.c @@ -0,0 +1,116 @@ +/**************************************************************************** + * sched/hrtimer/hrtimer_cancel.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "hrtimer/hrtimer.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: hrtimer_cancel + * + * Description: + * Cancel a high-resolution timer. The timer is removed from the active + * queue. If the canceled timer was the earliest in the queue, then the + * expiration is updated to: + * + * 1. The next earliest timer in the queue, or + * 2. A default expiration value (current time + default increment) + * if the queue becomes empty. + * + * Input Parameters: + * hrtimer - Timer instance to be canceled + * + * Returned Value: + * OK (zero) on success; a negated errno value on failure. + * + * Assumptions: + * The caller provides a valid timer instance. May be called from task + * or interrupt context. Internal locking is handled within this + * function. + * + ****************************************************************************/ + +int hrtimer_cancel(FAR struct hrtimer_s *hrtimer) +{ + FAR struct hrtimer_upperhalf_s *upper; + FAR struct hrtimer_node *node; + irqstate_t flags; + uint64_t now; + uint64_t expiration; + int ret = OK; + + DEBUGASSERT(hrtimer != NULL); + + /* Get the queue associated with this timer */ + + upper = hrtimer->upper; + if (upper == NULL) + { + return -EINVAL; + } + + flags = spin_lock_irqsave(&upper->lock); + + /* Get the current earliest timer */ + + node = RB_MIN(hrtimer_tree, &upper->tree); + + /* Remove the timer from the queue */ + + RB_REMOVE(hrtimer_tree, &upper->tree, &hrtimer->node); + + /* If the removed timer was the earliest, update expiration */ + + if (node == &hrtimer->node) + { + /* Get the new earliest timer */ + + node = RB_MIN(hrtimer_tree, &upper->tree); + if (node != NULL) + { + /* Update to next earliest timer */ + + ret = hrtimer_setexpire(upper, node->expiration_time); + } + else + { + /* No timers remain: set expiration to a safe default */ + + hrtimer_clock_current(upper, &now); + expiration = now + HRTIMER_DEFAULT_INCREMENT; + + ret = hrtimer_setexpire(upper, expiration); + } + } + + spin_unlock_irqrestore(&upper->lock, flags); + return ret; +} diff --git a/sched/hrtimer/hrtimer_init.c b/sched/hrtimer/hrtimer_init.c new file mode 100644 index 0000000000000..52bffb2e87d5b --- /dev/null +++ b/sched/hrtimer/hrtimer_init.c @@ -0,0 +1,160 @@ +/**************************************************************************** + * sched/hrtimer/hrtimer_init.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "hrtimer/hrtimer.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Global high-resolution default timer queue. + * + * This queue manages all default high-resolution timers. It is initialized + * statically and associated with a hardware timer lower when started. + */ + +struct hrtimer_upperhalf_s g_default_hrtimer_upperhalf = +{ + .started = false, + .lock = SP_UNLOCKED, +}; + +/* Global SysTick timer instance */ + +FAR struct hrtimer_s g_hrtimer_systick_timer; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: hrtimer_systick_callback + * + * Description: + * Callback for the SysTick-based high-resolution timer. In tickless mode, + * it signals the scheduler of tick expiration. In tickful mode, it + * reloads the timer and processes scheduler tick events. + * + * Input Parameters: + * hrtimer - Pointer to the SysTick timer instance + * + * Returned Value: + * None + * + * Assumptions: + * Executed in interrupt or timer context. + * + ****************************************************************************/ + +static void hrtimer_systick_callback(FAR struct hrtimer_s *hrtimer) +{ +#ifdef CONFIG_SCHED_TICKLESS + uint64_t now = 0; + + up_alarm_gettick(&now); + nxsched_alarm_tick_expiration(now); +#else + /* Reload SysTick timer for the next tick */ + + hrtimer_reload(hrtimer, NSEC_PER_TICK); + + /* Process scheduler tick events */ + + nxsched_process_timer(); +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: hrtimer_upper_start + * + * Description: + * Start a high-resolution timer queue. If the queue has already been + * started, this function returns an error. Otherwise the queue is marked + * started, and if it is the global default queue, the SysTick timer is + * also scheduled. Finally, the hardware timer lower is started. + * + * Input Parameters: + * upper - Timer queue to be started + * clockid - Associated clock ID + * + * Returned Value: + * OK (zero) on success; a negated errno value on failure. + * + * Assumptions: + * The lower driver must be initialized before calling this function. + * + ****************************************************************************/ + +int hrtimer_upper_start(FAR struct hrtimer_upperhalf_s *upper, + htimer_clockid_t clockid) +{ + int ret = OK; + + DEBUGASSERT(upper != NULL); + + /* Return error if already started */ + + if (upper->started) + { + return -EINVAL; + } + + RB_INIT(&upper->tree); + upper->clockid = clockid; + upper->started = true; + + /* If global default queue, schedule the SysTick timer */ + + if (upper == &g_default_hrtimer_upperhalf) + { + hrtimer_init(&g_hrtimer_systick_timer, + hrtimer_systick_callback, NULL); + + ret = hrtimer_start(&g_hrtimer_systick_timer, + NSEC_PER_TICK, + HRTIMER_MODE_REL); + } + + /* Start hardware timer via lower-half ops */ + + if (ret == OK) + { + ret = hrtimer_clock_start(upper); + } + + return ret; +} + +/* Generate red-black tree implementation for high-resolution timers */ + +RB_GENERATE(hrtimer_tree, hrtimer_node, entry, hrtimer_cmp); diff --git a/sched/hrtimer/hrtimer_process.c b/sched/hrtimer/hrtimer_process.c new file mode 100644 index 0000000000000..9d913c9f5f490 --- /dev/null +++ b/sched/hrtimer/hrtimer_process.c @@ -0,0 +1,155 @@ +/**************************************************************************** + * sched/hrtimer/hrtimer_process.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "hrtimer/hrtimer.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: hrtimer_upper_process + * + * Description: + * Process expired high-resolution timers in the specified upper-half + * timer queue. This function iterates through the red-black tree of + * active timers, checks for expiration, and invokes the registered + * callback functions for each expired timer. + * + * The function stops processing in either of the following cases: + * 1. No more expired timers remain in the queue. + * 2. The maximum batch processing limit + * (MAX_HRTIMER_PROCESS_CNT) is reached. + * + * After handling expired timers, the next expiration event is + * programmed into the underlying hardware timer: + * - If there are still active timers, schedule the nearest + * expiration time. + * - If the queue is empty, schedule a fallback expiration at + * (current time + HRTIMER_DEFAULT_INCREMENT). + * + * Input Parameters: + * upper - Pointer to the high-resolution timer upper-half structure. + * Must be non-NULL and initialized. + * + * Returned Value: + * None + * + * Assumptions: + * - Must be called in a context where timer processing is valid + * (typically from timer interrupt or scheduler tick logic). + * - Caller is responsible for ensuring that the hardware timer + * lower-half driver is operational. + * - This function executes with interrupts disabled while holding + * the queue lock, but periodically restores interrupts when + * processing large numbers of timers. + * + ****************************************************************************/ + +void hrtimer_upper_process(FAR struct hrtimer_upperhalf_s *upper) +{ + FAR struct hrtimer_node *node; + FAR struct hrtimer_s *hrtimer; + uint64_t now; + uint64_t expiration_value; + uint8_t process_cnt = 0; + irqstate_t flags; + + DEBUGASSERT(upper != NULL); + + /* Enter critical section while processing timers */ + + flags = spin_lock_irqsave(&upper->lock); + + /* Get the earliest timer in the queue */ + + node = RB_MIN(hrtimer_tree, &upper->tree); + while (node != NULL) + { + /* Get current time from the hardware clock */ + + hrtimer_clock_current(upper, &now); + + /* Check if this timer has expired */ + + if (hrtimer_has_expired(node->expiration_time, now)) + { + /* Remove timer from queue and invoke its callback */ + + RB_REMOVE(hrtimer_tree, &upper->tree, node); + hrtimer = (FAR struct hrtimer_s *)node; + + DEBUGASSERT(hrtimer->callback != NULL); + hrtimer->callback(hrtimer); + + process_cnt++; + + /* To avoid holding interrupts disabled for too long, + * periodically re-enable and re-enter the critical section. + */ + + if (process_cnt >= MAX_HRTIMER_PROCESS_CNT) + { + process_cnt = 0; + spin_unlock_irqrestore(&upper->lock, flags); + flags = spin_lock_irqsave(&upper->lock); + } + } + else + { + /* Stop processing when the next timer has not yet expired */ + + break; + } + + /* Continue with the next earliest timer */ + + node = RB_MIN(hrtimer_tree, &upper->tree); + } + + /* Update the next expiration time for the hardware clock */ + + if (node != NULL) + { + (void)hrtimer_setexpire(upper, node->expiration_time); + } + else + { + hrtimer_clock_current(upper, &now); + expiration_value = now + HRTIMER_DEFAULT_INCREMENT; + + (void)hrtimer_setexpire(upper, expiration_value); + } + + /* Leave critical section */ + + spin_unlock_irqrestore(&upper->lock, flags); +} diff --git a/sched/hrtimer/hrtimer_reload.c b/sched/hrtimer/hrtimer_reload.c new file mode 100644 index 0000000000000..f92eea9a63840 --- /dev/null +++ b/sched/hrtimer/hrtimer_reload.c @@ -0,0 +1,91 @@ +/**************************************************************************** + * sched/hrtimer/hrtimer_reload.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "hrtimer/hrtimer.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: hrtimer_reload + * + * Description: + * Reload (reschedule) a high-resolution timer by advancing its expiration + * time by the specified interval in nanoseconds. This is typically used + * for periodic timers where the expiration must be continuously moved + * forward after each trigger. + * + * Input Parameters: + * hrtimer - Pointer to the high-resolution timer instance to be updated. + * Must be a valid and active timer. + * ns - Interval in nanoseconds to add to the timer's expiration time. + * Must not exceed HRTIMER_MAX_INCREMENT. + * + * Returned Value: + * OK (0) is returned on success. + * A negated errno value is returned on failure: + * -EINVAL: Invalid parameters (NULL pointer or interval too large). + * + * Assumptions: + * - Called in a context where it is safe to manipulate the timer queue. + * - Caller must ensure that the timer has been started and is associated + * with a valid upper-half timer queue. + * - This function may update the hardware expiration time if the reloaded + * timer becomes the earliest scheduled timer in the queue. + * + ****************************************************************************/ + +int hrtimer_reload(FAR struct hrtimer_s *hrtimer, uint64_t ns) +{ + FAR struct hrtimer_upperhalf_s *upper = hrtimer->upper; + int ret = OK; + + if (upper == NULL || + ns > HRTIMER_MAX_INCREMENT) + { + return -EINVAL; + } + + /* Advance the expiration time */ + + hrtimer->node.expiration_time += ns; + + /* Update hardware expiration if this is the earliest timer */ + + if ((RB_INSERT(hrtimer_tree, &upper->tree, &hrtimer->node) == NULL) && + (&hrtimer->node == RB_MIN(hrtimer_tree, &upper->tree))) + { + ret = hrtimer_setexpire(upper, hrtimer->node.expiration_time); + } + + return ret; +} diff --git a/sched/sched/sched_timerexpiration.c b/sched/sched/sched_timerexpiration.c index c6ff34fecba77..859a933f5ea64 100644 --- a/sched/sched/sched_timerexpiration.c +++ b/sched/sched/sched_timerexpiration.c @@ -40,6 +40,9 @@ #include "sched/sched.h" #include "wdog/wdog.h" #include "clock/clock.h" +#ifdef CONFIG_HRTIMER +#include +#endif #ifdef CONFIG_CLOCK_TIMEKEEPING # include "clock/clock_timekeeping.h" @@ -424,16 +427,22 @@ static clock_t nxsched_timer_start(clock_t ticks, clock_t interval) interval = interval <= (CONFIG_TIMER_ADJUST_USEC / USEC_PER_TICK) ? 0 : interval - (CONFIG_TIMER_ADJUST_USEC / USEC_PER_TICK); -#ifdef CONFIG_SCHED_TICKLESS_ALARM +#ifdef CONFIG_HRTIMER + ret = hrtimer_start(&g_hrtimer_systick_timer, + NSEC_PER_TICK * (ticks + interval), + HRTIMER_MODE_ABS); +#else +# ifdef CONFIG_SCHED_TICKLESS_ALARM /* Convert the delay to a time in the future (with respect * to the time when last stopped the timer). */ ret = up_alarm_tick_start(ticks + interval); -#else +# else /* [Re-]start the interval timer */ ret = up_timer_tick_start(interval); +# endif #endif if (ret < 0)