This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 4b31291043258abddfa7ce3bda671f572d1603ec
Author: ouyangxiangzhen <[email protected]>
AuthorDate: Wed Jul 23 17:53:14 2025 +0800

    timers/oneshot: Add clkdev fast-path.
    
    This commit add fast-path for oneshot clkdev APIs.
    
    Signed-off-by: ouyangxiangzhen <[email protected]>
---
 drivers/timers/Kconfig         | 11 ++++++
 include/nuttx/timers/oneshot.h | 84 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 91 insertions(+), 4 deletions(-)

diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig
index 62416e868af..112db9c84fa 100644
--- a/drivers/timers/Kconfig
+++ b/drivers/timers/Kconfig
@@ -130,6 +130,17 @@ config ONESHOT_COUNT
                This option enables the oneshot implementation to be based on 
the
                new clock device driver interfaces.
 
+config ONESHOT_FAST_DIVISION
+       bool # Convert Clock Count Using Invariant-divisor Division
+       default n
+       depends on ONESHOT_COUNT
+       ---help---
+               This option will enable the invariant-divisor division 
optimization in
+               the clock device driver implementation, which can improve 
performance
+               for certain architectures. It is recommended to enable it on 
32-bit
+               architecture that do not support hardware division and 64-bit
+               architecture that hardware division is slow.
+
 config ALARM_ARCH
        bool "Alarm Arch Implementation"
        select ARCH_HAVE_TICKLESS
diff --git a/include/nuttx/timers/oneshot.h b/include/nuttx/timers/oneshot.h
index 2626eb20597..b2d5b35f0a3 100644
--- a/include/nuttx/timers/oneshot.h
+++ b/include/nuttx/timers/oneshot.h
@@ -239,6 +239,13 @@ struct oneshot_lowerhalf_s
 
 #ifdef CONFIG_ONESHOT_COUNT
   uint32_t frequency;
+
+  uint32_t cnt2nsec_mult;
+  uint32_t cnt2nsec_shift;
+#endif
+
+#ifdef CONFIG_ONESHOT_FAST_DIVISION
+  invdiv_param64_t invdiv_freq;
 #endif
 
   /* Private lower half data may follow */
@@ -277,9 +284,78 @@ static inline_function
 void oneshot_count_init(FAR struct oneshot_lowerhalf_s *lower,
                         uint32_t frequency)
 {
+  clkcnt_t result;
   DEBUGASSERT(lower && frequency);
 
   lower->frequency = frequency;
+
+  clkcnt_best_multshift(frequency, NSEC_PER_SEC,
+                        &lower->cnt2nsec_mult,
+                        &lower->cnt2nsec_shift);
+
+  /* Ensure the maximum error of the mult-shift is less than 5ns. */
+
+  result = clkcnt_delta_cnt2nsec_fast(frequency, lower->cnt2nsec_mult,
+                                      lower->cnt2nsec_shift);
+
+  ASSERT(NSEC_PER_SEC - 5 <= result && NSEC_PER_SEC + 5 >= result);
+
+#  ifdef CONFIG_ONESHOT_FAST_DIVISION
+  /* invdiv requires the invariant-divsor > 1. */
+
+  ASSERT(frequency > 1);
+
+  invdiv_init_param64(frequency, &lower->invdiv_freq);
+#  endif
+}
+
+static inline_function
+uint32_t oneshot_delta_cnt2nsec(FAR struct oneshot_lowerhalf_s *lower,
+                                clkcnt_t delta)
+{
+  DEBUGASSERT(delta <= lower->frequency);
+
+  /* Here we use a multiply-shift method to convert the clock
+   * count to nanoseconds. This will reduce at least one divsion
+   * operation and improve the performance. Note that this is an
+   * approximate method that trades accuracy for performance, it may lead
+   * to 1-3 nanoseconds of error when converting the cycles that
+   * represent less than 1 second. If extremely high resolution time is
+   * required, then this option should be disabled.
+   */
+
+  return clkcnt_delta_cnt2nsec_fast(delta, lower->cnt2nsec_mult,
+                                    lower->cnt2nsec_shift);
+}
+
+static inline_function
+clock_t oneshot_delta_cnt2tick(FAR struct oneshot_lowerhalf_s *lower,
+                               clkcnt_t delta)
+{
+  uint32_t nsec;
+
+  DEBUGASSERT(delta <= lower->frequency);
+
+  /* Be careful of using mult-shift fast converting here.
+   * Since ticks are realted to the scheduling, inaccurate converting
+   * results may lead to wrong scheduling.
+   */
+
+  nsec = clkcnt_delta_cnt2nsec_fast(delta, lower->cnt2nsec_mult,
+                                    lower->cnt2nsec_shift);
+
+  return div_const(nsec, NSEC_PER_TICK);
+}
+
+static inline_function
+uint64_t oneshot_cnt2sec(FAR struct oneshot_lowerhalf_s *lower,
+                         clkcnt_t cnt)
+{
+#  ifdef CONFIG_ONESHOT_FAST_DIVISION
+  return clkcnt_delta_cnt2time_invdiv(cnt, 1, &lower->invdiv_freq);
+#  else
+  return clkcnt_cnt2sec(cnt, lower->frequency);
+#  endif
 }
 #endif
 
@@ -321,10 +397,10 @@ int oneshot_current(FAR struct oneshot_lowerhalf_s *lower,
 #ifdef CONFIG_ONESHOT_COUNT
   clkcnt_t cnt  = lower->ops->current(lower);
   uint32_t freq = lower->frequency;
-  uint64_t sec  = clkcnt_cnt2sec(cnt, freq);
+  uint64_t sec  = oneshot_cnt2sec(lower, cnt);
 
   cnt          -= sec * freq;
-  ts->tv_nsec   = clkcnt_delta_cnt2nsec(cnt, freq);
+  ts->tv_nsec   = oneshot_delta_cnt2nsec(lower, cnt);
   ts->tv_sec    = sec;
 #else
   ret = lower->ops->current(lower, ts);
@@ -520,10 +596,10 @@ int oneshot_tick_current(FAR struct oneshot_lowerhalf_s 
*lower,
 #ifdef CONFIG_ONESHOT_COUNT
   clkcnt_t cnt  = lower->ops->current(lower);
   uint32_t freq = lower->frequency;
-  uint64_t sec  = clkcnt_cnt2sec(cnt, freq);
+  uint64_t sec  = oneshot_cnt2sec(lower, cnt);
 
   cnt   -= sec * freq;
-  *tick  = sec * TICK_PER_SEC + clkcnt_delta_cnt2tick(cnt, freq);
+  *tick  = sec * TICK_PER_SEC + oneshot_delta_cnt2tick(lower, cnt);
 #else
   struct timespec ts;
 

Reply via email to