From a6b5b05f5a692a2fcf55750c5f743532d520809f Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Thu, 25 Sep 2025 15:22:20 +0800 Subject: [PATCH] fix: make rt_tick_t 64-bit on ARCH_CPU_64BIT and improve clock time precision - Make rt_tick_t and RT_TICK_MAX 64-bit when ARCH_CPU_64BIT is defined - Add rt_muldiv_u64() and rt_muldiv_u32() for lossless counter and nanosecond conversions - Convert boottime, clock_gettime, POSIX timers, and hrtimer paths with clock source frequency instead of scaled resolution - Rewrite rt_clock_time_counter_to_ns() and rt_clock_time_ns_to_counter() with rt_muldiv_u64() for the VDSO path - Remove RT_CLOCK_TIME_RESMUL, res_scale, and scaled-resolution APIs: - res_scale was only a fixed-point multiplier for resolution math (res_scaled = 1e9 * res_scale / freq); it did not customize clock frequency - All in-tree devices always used the default RT_CLOCK_TIME_RESMUL value, so the field provided no real per-board tuning - Converting through res_scaled introduced extra rounding error compared with using get_freq() directly - Custom frequency remains available through rt_clock_time_ops.get_freq, rt_clock_time_source_init(), and rt_clock_time_device_register() - Drop RT_CLOCK_TIME_RESMUL, struct rt_clock_time_device.res_scale, rt_clock_time_get_res_scaled(), rt_clock_time_get_event_res_scaled(), and rt_clock_hrtimer_getres() - Port the original ktime fixes to the clock_time API - Keep soft-timer fallback rounding in _hrtimer_cnt_to_tick() so sub-tick timeouts schedule at least one OS tick - Update clock_time core documentation --- .../dm/hwtimer/clock_timer-rockchip_timer.c | 9 +- .../clock_time/arch/aarch64/cputimer.c | 1 - .../clock_time/arch/risc-v/virt64/cputimer.c | 1 - .../drivers/clock_time/clock_boottime.c | 37 ++-- components/drivers/clock_time/clock_hrtimer.c | 108 ++++------- .../drivers/clock_time/clock_time_core.c | 57 +----- components/drivers/clock_time/clock_timer.c | 1 - .../drivers/include/drivers/clock_time.h | 24 +-- components/drivers/rtc/dev_soft_rtc.c | 2 +- components/libc/compilers/common/ctime.c | 27 ++- .../clock_time/clock_time_core.md | 36 +--- .../clock_time/clock_time_core_zh.md | 30 +-- include/rtdef.h | 4 + include/rttypes.h | 12 ++ src/rttypes.c | 171 ++++++++++++++++++ 15 files changed, 282 insertions(+), 238 deletions(-) create mode 100644 src/rttypes.c diff --git a/bsp/rockchip/dm/hwtimer/clock_timer-rockchip_timer.c b/bsp/rockchip/dm/hwtimer/clock_timer-rockchip_timer.c index 488b48aade5..1e75874c965 100644 --- a/bsp/rockchip/dm/hwtimer/clock_timer-rockchip_timer.c +++ b/bsp/rockchip/dm/hwtimer/clock_timer-rockchip_timer.c @@ -359,16 +359,11 @@ static const struct rk_timer_data rk3399_timer_data = #ifdef RT_USING_CLOCK_TIME -uint64_t rt_clock_hrtimer_getfrq(void) +rt_uint64_t rt_clock_hrtimer_getfrq(void) { return OSC_HZ; } -uint64_t rt_clock_hrtimer_getres(void) -{ - return ((1000UL * 1000 * 1000) * RT_CLOCK_TIME_RESMUL) / OSC_HZ; -} - /** * @brief set the timeout function for hrtimer framework * @@ -377,7 +372,7 @@ uint64_t rt_clock_hrtimer_getres(void) * @param cnt the count of timer dealy * @return rt_err_t 0 forever */ -rt_err_t rt_clock_hrtimer_settimeout(unsigned long cnt) +rt_err_t rt_clock_hrtimer_settimeout(rt_uint64_t cnt) { struct hrt_timer *timer = &_timer0; struct rk_timer *time = timer->timer; diff --git a/components/drivers/clock_time/arch/aarch64/cputimer.c b/components/drivers/clock_time/arch/aarch64/cputimer.c index 58102d226f4..b6d3a73db56 100644 --- a/components/drivers/clock_time/arch/aarch64/cputimer.c +++ b/components/drivers/clock_time/arch/aarch64/cputimer.c @@ -50,7 +50,6 @@ void rt_clock_time_source_init(void) rt_uint8_t caps = RT_CLOCK_TIME_CAP_SOURCE; _aarch64_clock_dev.ops = &_aarch64_clock_ops; - _aarch64_clock_dev.res_scale = RT_CLOCK_TIME_RESMUL; _aarch64_clock_dev.caps = caps; rt_clock_time_device_register(&_aarch64_clock_dev, "clock_time_gtimer", caps); rt_clock_time_set_default_source(&_aarch64_clock_dev); diff --git a/components/drivers/clock_time/arch/risc-v/virt64/cputimer.c b/components/drivers/clock_time/arch/risc-v/virt64/cputimer.c index 7921716b7fa..62c28cde019 100644 --- a/components/drivers/clock_time/arch/risc-v/virt64/cputimer.c +++ b/components/drivers/clock_time/arch/risc-v/virt64/cputimer.c @@ -54,7 +54,6 @@ void rt_clock_time_source_init(void) rt_uint8_t caps = RT_CLOCK_TIME_CAP_SOURCE; _riscv_clock_dev.ops = &_riscv_clock_ops; - _riscv_clock_dev.res_scale = RT_CLOCK_TIME_RESMUL; _riscv_clock_dev.caps = caps; rt_clock_time_device_register(&_riscv_clock_dev, "clock_time_rdtime", caps); rt_clock_time_set_default_source(&_riscv_clock_dev); diff --git a/components/drivers/clock_time/clock_boottime.c b/components/drivers/clock_time/clock_boottime.c index 3185a049680..fbd0bbe50de 100644 --- a/components/drivers/clock_time/clock_boottime.c +++ b/components/drivers/clock_time/clock_boottime.c @@ -9,26 +9,24 @@ */ #include +#include rt_err_t rt_clock_boottime_get_us(struct timeval *tv) { rt_uint64_t cnt; - rt_uint64_t res; - rt_uint64_t ns; + rt_uint64_t freq; RT_ASSERT(tv != RT_NULL); cnt = rt_clock_time_get_counter(); - res = rt_clock_time_get_res_scaled(); - if (res == 0) + freq = rt_clock_time_get_freq(); + if (freq == 0) { return -RT_ERROR; } - ns = (cnt * res) / RT_CLOCK_TIME_RESMUL; - - tv->tv_sec = ns / (1000ULL * 1000 * 1000); - tv->tv_usec = (ns % (1000ULL * 1000 * 1000)) / 1000; + tv->tv_sec = (time_t)(cnt / freq); + tv->tv_usec = rt_muldiv_u64(cnt % freq, MICROSECOND_PER_SECOND, freq, NULL); return RT_EOK; } @@ -36,20 +34,18 @@ rt_err_t rt_clock_boottime_get_us(struct timeval *tv) rt_err_t rt_clock_boottime_get_s(time_t *t) { rt_uint64_t cnt; - rt_uint64_t res; - rt_uint64_t ns; + rt_uint64_t freq; RT_ASSERT(t != RT_NULL); cnt = rt_clock_time_get_counter(); - res = rt_clock_time_get_res_scaled(); - if (res == 0) + freq = rt_clock_time_get_freq(); + if (freq == 0) { return -RT_ERROR; } - ns = (cnt * res) / RT_CLOCK_TIME_RESMUL; - *t = ns / (1000ULL * 1000 * 1000); + *t = (time_t)(cnt / freq); return RT_EOK; } @@ -57,22 +53,19 @@ rt_err_t rt_clock_boottime_get_s(time_t *t) rt_err_t rt_clock_boottime_get_ns(struct timespec *ts) { rt_uint64_t cnt; - rt_uint64_t res; - rt_uint64_t ns; + rt_uint64_t freq; RT_ASSERT(ts != RT_NULL); cnt = rt_clock_time_get_counter(); - res = rt_clock_time_get_res_scaled(); - if (res == 0) + freq = rt_clock_time_get_freq(); + if (freq == 0) { return -RT_ERROR; } - ns = (cnt * res) / RT_CLOCK_TIME_RESMUL; - - ts->tv_sec = ns / (1000ULL * 1000 * 1000); - ts->tv_nsec = ns % (1000ULL * 1000 * 1000); + ts->tv_sec = (time_t)(cnt / freq); + ts->tv_nsec = rt_muldiv_u64(cnt % freq, NANOSECOND_PER_SECOND, freq, NULL); return RT_EOK; } diff --git a/components/drivers/clock_time/clock_hrtimer.c b/components/drivers/clock_time/clock_hrtimer.c index 6fbfcecdead..ec40582077c 100644 --- a/components/drivers/clock_time/clock_hrtimer.c +++ b/components/drivers/clock_time/clock_hrtimer.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -18,14 +19,6 @@ #define DBG_LEVEL DBG_INFO #include -#define CLOCK_TIME_NSEC_PER_SEC (1000000000ULL) - -#ifdef ARCH_CPU_64BIT -#define _HRTIMER_MAX_CNT UINT64_MAX -#else -#define _HRTIMER_MAX_CNT UINT32_MAX -#endif - static rt_list_t _timer_list = RT_LIST_OBJECT_INIT(_timer_list); static RT_DEFINE_SPINLOCK(_spinlock); @@ -34,57 +27,51 @@ rt_inline rt_clock_hrtimer_t _first_hrtimer(void) return rt_list_isempty(&_timer_list) ? RT_NULL : rt_list_first_entry(&_timer_list, struct rt_clock_hrtimer, node); } -rt_inline unsigned long _clock_time_get_cnt(void) +rt_inline rt_tick_t _clock_time_get_cnt(void) { - return rt_clock_time_get_counter(); + return (rt_tick_t)rt_clock_time_get_counter(); } -rt_inline rt_bool_t _cnt_before(unsigned long a, unsigned long b) +rt_inline rt_bool_t _cnt_before(rt_tick_t a, rt_tick_t b) { return ((rt_base_t)(a - b)) < 0; } -rt_inline rt_bool_t _cnt_after(unsigned long a, unsigned long b) +rt_inline rt_bool_t _cnt_after(rt_tick_t a, rt_tick_t b) { return _cnt_before(b, a); } -rt_weak rt_uint64_t rt_clock_hrtimer_getres(void) -{ - return rt_clock_time_get_event_res_scaled(); -} - -rt_weak unsigned long rt_clock_hrtimer_getfrq(void) +rt_weak rt_uint64_t rt_clock_hrtimer_getfrq(void) { - return (unsigned long)rt_clock_time_get_event_freq(); + return rt_clock_time_get_event_freq(); } -static rt_tick_t _hrtimer_cnt_to_tick(unsigned long cnt) +static rt_tick_t _hrtimer_cnt_to_tick(rt_uint64_t cnt) { - rt_uint64_t res = rt_clock_hrtimer_getres(); - rt_uint64_t ns; + rt_uint64_t freq = rt_clock_hrtimer_getfrq(); + rt_uint64_t rem = 0; + rt_uint64_t tick; - if (res == 0) + if (freq == 0) { return 0; } - ns = ((rt_uint64_t)cnt * res) / RT_CLOCK_TIME_RESMUL; - if (ns == 0) + tick = rt_muldiv_u64(cnt, RT_TICK_PER_SECOND, freq, &rem); + if (rem != 0) { - return 1; + tick += 1; /* round up so a non-zero cnt never collapses to a 0-tick timeout */ } - - ns = (ns * RT_TICK_PER_SECOND + CLOCK_TIME_NSEC_PER_SEC - 1) / CLOCK_TIME_NSEC_PER_SEC; - if (ns == 0) + if (tick == 0) { - return 1; + tick = 1; /* at least one tick */ } - return (rt_tick_t)ns; + return (rt_tick_t)tick; } -rt_weak rt_err_t rt_clock_hrtimer_settimeout(unsigned long cnt) +rt_weak rt_err_t rt_clock_hrtimer_settimeout(rt_uint64_t cnt) { static rt_timer_t timer = RT_NULL; static struct rt_timer _sh_rtimer; @@ -124,29 +111,18 @@ rt_weak rt_err_t rt_clock_hrtimer_settimeout(unsigned long cnt) return RT_EOK; } -static unsigned long _cnt_convert(unsigned long cnt) +static rt_uint64_t _cnt_convert(rt_tick_t cnt) { - unsigned long count; - rt_uint64_t src_res; - rt_uint64_t event_res; - rt_uint64_t result; + rt_uint64_t rtn = 0; + rt_tick_t count = cnt - _clock_time_get_cnt(); - count = cnt - _clock_time_get_cnt(); - if (count > (_HRTIMER_MAX_CNT / 2)) + if (count > (RT_TICK_MAX / 2)) { return 0; } - src_res = rt_clock_time_get_res_scaled(); - event_res = rt_clock_hrtimer_getres(); - if (src_res == 0 || event_res == 0) - { - return 0; - } - - result = ((rt_uint64_t)count * src_res) / event_res; - - return result == 0 ? 1 : (unsigned long)result; + rtn = rt_muldiv_u64(count, rt_clock_hrtimer_getfrq(), rt_clock_time_get_freq(), NULL); + return rtn == 0 ? 1 : rtn; } static void _sleep_timeout(void *parameter) @@ -177,7 +153,7 @@ static void _hrtimer_process_locked(void) while ((timer = _first_hrtimer()) != RT_NULL) { - unsigned long now = _clock_time_get_cnt(); + rt_tick_t now = _clock_time_get_cnt(); if (_cnt_before(now, timer->timeout_cnt)) { @@ -206,7 +182,7 @@ static void _hrtimer_process_locked(void) static void _set_next_timeout_locked(void) { rt_clock_hrtimer_t timer; - rt_ubase_t next_timeout_cnt; + rt_uint64_t next_timeout_cnt; rt_bool_t find_next; do @@ -258,12 +234,12 @@ void rt_clock_hrtimer_init(rt_clock_hrtimer_t timer, rt_completion_init(&timer->completion); } -rt_err_t rt_clock_hrtimer_start(rt_clock_hrtimer_t timer, unsigned long delay_cnt) +rt_err_t rt_clock_hrtimer_start(rt_clock_hrtimer_t timer, rt_tick_t delay_cnt) { rt_base_t level; RT_ASSERT(timer != RT_NULL); - RT_ASSERT(delay_cnt < (_HRTIMER_MAX_CNT / 2)); + RT_ASSERT(delay_cnt < (RT_TICK_MAX / 2)); timer->delay_cnt = delay_cnt; timer->timeout_cnt = timer->delay_cnt + _clock_time_get_cnt(); @@ -317,13 +293,13 @@ rt_err_t rt_clock_hrtimer_control(rt_clock_hrtimer_t timer, int cmd, void *arg) switch (cmd) { case RT_TIMER_CTRL_GET_TIME: - *(unsigned long *)arg = timer->delay_cnt; + *(rt_tick_t *)arg = timer->delay_cnt; break; case RT_TIMER_CTRL_SET_TIME: - RT_ASSERT((*(unsigned long *)arg) < (_HRTIMER_MAX_CNT / 2)); - timer->delay_cnt = *(unsigned long *)arg; - timer->timeout_cnt = *(unsigned long *)arg + _clock_time_get_cnt(); + RT_ASSERT((*(rt_tick_t *)arg) < (RT_TICK_MAX / 2)); + timer->delay_cnt = *(rt_tick_t *)arg; + timer->timeout_cnt = *(rt_tick_t *)arg + _clock_time_get_cnt(); break; case RT_TIMER_CTRL_SET_ONESHOT: @@ -346,7 +322,7 @@ rt_err_t rt_clock_hrtimer_control(rt_clock_hrtimer_t timer, int cmd, void *arg) break; case RT_TIMER_CTRL_GET_REMAIN_TIME: - *(unsigned long *)arg = timer->timeout_cnt; + *(rt_tick_t *)arg = timer->timeout_cnt; break; case RT_TIMER_CTRL_GET_FUNC: if (arg != RT_NULL) @@ -408,7 +384,7 @@ void rt_clock_hrtimer_delay_detach(struct rt_clock_hrtimer *timer) rt_clock_hrtimer_detach(timer); } -rt_err_t rt_clock_hrtimer_sleep(struct rt_clock_hrtimer *timer, unsigned long cnt) +rt_err_t rt_clock_hrtimer_sleep(struct rt_clock_hrtimer *timer, rt_tick_t cnt) { rt_err_t err; @@ -430,23 +406,19 @@ rt_err_t rt_clock_hrtimer_sleep(struct rt_clock_hrtimer *timer, unsigned long cn return err; } -rt_err_t rt_clock_hrtimer_ndelay(struct rt_clock_hrtimer *timer, unsigned long ns) +rt_err_t rt_clock_hrtimer_ndelay(struct rt_clock_hrtimer *timer, rt_uint64_t ns) { - rt_uint64_t res = rt_clock_time_get_res_scaled(); - if (res == 0) - { - return -RT_ERROR; - } + rt_tick_t cputimer_tick = (rt_tick_t)rt_muldiv_u64(ns, rt_clock_time_get_freq(), NANOSECOND_PER_SECOND, NULL); - return rt_clock_hrtimer_sleep(timer, (ns * RT_CLOCK_TIME_RESMUL) / res); + return rt_clock_hrtimer_sleep(timer, cputimer_tick); } -rt_err_t rt_clock_hrtimer_udelay(struct rt_clock_hrtimer *timer, unsigned long us) +rt_err_t rt_clock_hrtimer_udelay(struct rt_clock_hrtimer *timer, rt_uint64_t us) { return rt_clock_hrtimer_ndelay(timer, us * 1000); } -rt_err_t rt_clock_hrtimer_mdelay(struct rt_clock_hrtimer *timer, unsigned long ms) +rt_err_t rt_clock_hrtimer_mdelay(struct rt_clock_hrtimer *timer, rt_uint64_t ms) { return rt_clock_hrtimer_ndelay(timer, ms * 1000000); } diff --git a/components/drivers/clock_time/clock_time_core.c b/components/drivers/clock_time/clock_time_core.c index 2682270ba3d..e3adb57a6b8 100644 --- a/components/drivers/clock_time/clock_time_core.c +++ b/components/drivers/clock_time/clock_time_core.c @@ -14,8 +14,6 @@ #include -#define CLOCK_TIME_NSEC_PER_SEC (1000000000ULL) - static rt_uint64_t _clock_time_tick_get_freq(struct rt_clock_time_device *dev) { RT_UNUSED(dev); @@ -45,7 +43,6 @@ static const struct rt_clock_time_ops _clock_time_tick_ops = static struct rt_clock_time_device _clock_time_tick_dev = { .ops = &_clock_time_tick_ops, - .res_scale = RT_CLOCK_TIME_RESMUL, .caps = RT_CLOCK_TIME_CAP_SOURCE, }; @@ -57,27 +54,6 @@ rt_weak void rt_clock_time_source_init(void) return; } -static rt_uint64_t _clock_time_get_res_scaled(struct rt_clock_time_device *dev) -{ - rt_uint64_t freq; - rt_uint64_t scale; - - if (dev == RT_NULL || dev->ops == RT_NULL || dev->ops->get_freq == RT_NULL) - { - return 0; - } - - freq = dev->ops->get_freq(dev); - if (freq == 0) - { - return 0; - } - - scale = dev->res_scale ? dev->res_scale : RT_CLOCK_TIME_RESMUL; - - return (CLOCK_TIME_NSEC_PER_SEC * scale) / freq; -} - rt_err_t rt_clock_time_device_register(struct rt_clock_time_device *dev, const char *name, rt_uint8_t caps) { rt_err_t result = RT_EOK; @@ -86,10 +62,6 @@ rt_err_t rt_clock_time_device_register(struct rt_clock_time_device *dev, const c RT_ASSERT(dev->ops != RT_NULL); dev->caps = caps; - if (dev->res_scale == 0) - { - dev->res_scale = RT_CLOCK_TIME_RESMUL; - } if (name != RT_NULL) { @@ -161,11 +133,6 @@ rt_uint64_t rt_clock_time_get_counter(void) return src->ops->get_counter(src); } -rt_uint64_t rt_clock_time_get_res_scaled(void) -{ - return _clock_time_get_res_scaled(rt_clock_time_get_default_source()); -} - rt_uint64_t rt_clock_time_get_event_freq(void) { struct rt_clock_time_device *event = rt_clock_time_get_default_event(); @@ -183,40 +150,28 @@ rt_uint64_t rt_clock_time_get_event_freq(void) return event->ops->get_freq(event); } -rt_uint64_t rt_clock_time_get_event_res_scaled(void) -{ - struct rt_clock_time_device *event = rt_clock_time_get_default_event(); - - if (event == RT_NULL) - { - return rt_clock_time_get_res_scaled(); - } - - return _clock_time_get_res_scaled(event); -} - rt_uint64_t rt_clock_time_counter_to_ns(rt_uint64_t cnt) { - rt_uint64_t res = rt_clock_time_get_res_scaled(); + rt_uint64_t freq = rt_clock_time_get_freq(); - if (res == 0) + if (freq == 0) { return 0; } - return (cnt * res) / RT_CLOCK_TIME_RESMUL; + return rt_muldiv_u64(cnt, NANOSECOND_PER_SECOND, freq, NULL); } rt_uint64_t rt_clock_time_ns_to_counter(rt_uint64_t ns) { - rt_uint64_t res = rt_clock_time_get_res_scaled(); + rt_uint64_t freq = rt_clock_time_get_freq(); - if (res == 0) + if (freq == 0) { return 0; } - return (ns * RT_CLOCK_TIME_RESMUL) / res; + return rt_muldiv_u64(ns, freq, NANOSECOND_PER_SECOND, NULL); } rt_err_t rt_clock_time_set_timeout(rt_uint64_t delta) diff --git a/components/drivers/clock_time/clock_timer.c b/components/drivers/clock_time/clock_timer.c index 94e06ccb116..331305b91d0 100644 --- a/components/drivers/clock_time/clock_timer.c +++ b/components/drivers/clock_time/clock_timer.c @@ -506,7 +506,6 @@ rt_err_t rt_clock_timer_register(rt_clock_timer_t *timer, const char *name, void _clock_timer_owner = timer; _clock_timer_clock_dev.ops = &_clock_timer_clock_ops; - _clock_timer_clock_dev.res_scale = RT_CLOCK_TIME_RESMUL; _clock_timer_clock_dev.caps = caps; rt_snprintf(ct_name, sizeof(ct_name), "clock_time_%s", name); diff --git a/components/drivers/include/drivers/clock_time.h b/components/drivers/include/drivers/clock_time.h index 7374b97d005..9d351e30e98 100644 --- a/components/drivers/include/drivers/clock_time.h +++ b/components/drivers/include/drivers/clock_time.h @@ -19,8 +19,6 @@ extern "C" { #endif -#define RT_CLOCK_TIME_RESMUL (1000000ULL) - #define RT_CLOCK_TIME_CAP_SOURCE (1U << 0) #define RT_CLOCK_TIME_CAP_EVENT (1U << 1) @@ -37,7 +35,6 @@ struct rt_clock_time_device { struct rt_device parent; const struct rt_clock_time_ops *ops; - rt_uint64_t res_scale; rt_uint8_t caps; }; @@ -112,8 +109,8 @@ struct rt_clock_hrtimer char name[RT_NAME_MAX]; rt_list_t node; void *parameter; - unsigned long delay_cnt; - unsigned long timeout_cnt; + rt_tick_t delay_cnt; + rt_tick_t timeout_cnt; rt_err_t error; struct rt_completion completion; void (*timeout_func)(void *parameter); @@ -131,9 +128,7 @@ void rt_clock_time_source_init(void); rt_uint64_t rt_clock_time_get_freq(void); rt_uint64_t rt_clock_time_get_counter(void); -rt_uint64_t rt_clock_time_get_res_scaled(void); rt_uint64_t rt_clock_time_get_event_freq(void); -rt_uint64_t rt_clock_time_get_event_res_scaled(void); rt_uint64_t rt_clock_time_counter_to_ns(rt_uint64_t cnt); rt_uint64_t rt_clock_time_ns_to_counter(rt_uint64_t ns); @@ -145,9 +140,8 @@ rt_err_t rt_clock_boottime_get_us(struct timeval *tv); rt_err_t rt_clock_boottime_get_s(time_t *t); rt_err_t rt_clock_boottime_get_ns(struct timespec *ts); -rt_uint64_t rt_clock_hrtimer_getres(void); -unsigned long rt_clock_hrtimer_getfrq(void); -rt_err_t rt_clock_hrtimer_settimeout(unsigned long cnt); +rt_uint64_t rt_clock_hrtimer_getfrq(void); +rt_err_t rt_clock_hrtimer_settimeout(rt_uint64_t cnt); void rt_clock_hrtimer_process(void); void rt_clock_hrtimer_init(rt_clock_hrtimer_t timer, @@ -155,7 +149,7 @@ void rt_clock_hrtimer_init(rt_clock_hrtimer_t timer, rt_uint8_t flag, void (*timeout)(void *parameter), void *parameter); -rt_err_t rt_clock_hrtimer_start(rt_clock_hrtimer_t timer, unsigned long cnt); +rt_err_t rt_clock_hrtimer_start(rt_clock_hrtimer_t timer, rt_tick_t cnt); rt_err_t rt_clock_hrtimer_stop(rt_clock_hrtimer_t timer); rt_err_t rt_clock_hrtimer_control(rt_clock_hrtimer_t timer, int cmd, void *arg); rt_err_t rt_clock_hrtimer_detach(rt_clock_hrtimer_t timer); @@ -172,10 +166,10 @@ void rt_clock_hrtimer_delay_init(struct rt_clock_hrtimer *timer); void rt_clock_hrtimer_delay_detach(struct rt_clock_hrtimer *timer); void rt_clock_hrtimer_process(void); -rt_err_t rt_clock_hrtimer_sleep(struct rt_clock_hrtimer *timer, unsigned long cnt); -rt_err_t rt_clock_hrtimer_ndelay(struct rt_clock_hrtimer *timer, unsigned long ns); -rt_err_t rt_clock_hrtimer_udelay(struct rt_clock_hrtimer *timer, unsigned long us); -rt_err_t rt_clock_hrtimer_mdelay(struct rt_clock_hrtimer *timer, unsigned long ms); +rt_err_t rt_clock_hrtimer_sleep(struct rt_clock_hrtimer *timer, rt_tick_t cnt); +rt_err_t rt_clock_hrtimer_ndelay(struct rt_clock_hrtimer *timer, rt_uint64_t ns); +rt_err_t rt_clock_hrtimer_udelay(struct rt_clock_hrtimer *timer, rt_uint64_t us); +rt_err_t rt_clock_hrtimer_mdelay(struct rt_clock_hrtimer *timer, rt_uint64_t ms); #ifdef __cplusplus } diff --git a/components/drivers/rtc/dev_soft_rtc.c b/components/drivers/rtc/dev_soft_rtc.c index 5f8489158f1..c7c6972794e 100644 --- a/components/drivers/rtc/dev_soft_rtc.c +++ b/components/drivers/rtc/dev_soft_rtc.c @@ -257,7 +257,7 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) level = rt_spin_lock_irqsave(&_spinlock); ts->tv_sec = 0; #ifdef RT_USING_CLOCK_TIME - ts->tv_nsec = (rt_clock_time_get_res_scaled() / RT_CLOCK_TIME_RESMUL); + ts->tv_nsec = (rt_uint32_t)(NANOSECOND_PER_SECOND / rt_clock_time_get_freq()); #else ts->tv_nsec = (1000UL * 1000 * 1000) / RT_TICK_PER_SECOND; #endif diff --git a/components/libc/compilers/common/ctime.c b/components/libc/compilers/common/ctime.c index 3e1ea77fd75..22066e68fca 100644 --- a/components/libc/compilers/common/ctime.c +++ b/components/libc/compilers/common/ctime.c @@ -574,7 +574,7 @@ int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) rt_set_errno(EINVAL); return -1; } - unsigned long ns = rqtp->tv_sec * NANOSECOND_PER_SECOND + rqtp->tv_nsec; + rt_uint64_t ns = (rt_uint64_t)rqtp->tv_sec * NANOSECOND_PER_SECOND + rqtp->tv_nsec; rt_clock_boottime_get_ns(&old_ts); rt_clock_hrtimer_ndelay(&timer, ns); if (rt_get_errno() == RT_EINTR) @@ -634,7 +634,7 @@ int clock_getres(clockid_t clockid, struct timespec *res) case CLOCK_PROCESS_CPUTIME_ID: case CLOCK_THREAD_CPUTIME_ID: res->tv_sec = 0; - res->tv_nsec = (rt_clock_time_get_res_scaled() / RT_CLOCK_TIME_RESMUL); + res->tv_nsec = (rt_uint32_t)(NANOSECOND_PER_SECOND / rt_clock_time_get_freq()); return 0; default: @@ -833,7 +833,7 @@ struct timer_obj union sigval val; struct timespec interval; /* Reload value */ struct timespec value; /* Reload value */ - unsigned long reload; /* Reload value in ms */ + rt_tick_t reload; /* Reload value in ms */ rt_uint32_t status; int sigev_signo; clockid_t clockid; @@ -915,6 +915,7 @@ int timer_list_free(rt_list_t *timer_list) static void rtthread_timer_wrapper(void *timerobj) { struct timer_obj *timer; + rt_uint64_t ns; timer = (struct timer_obj *)timerobj; @@ -923,8 +924,8 @@ static void rtthread_timer_wrapper(void *timerobj) timer->status = NOT_ACTIVE; } - timer->reload = ((timer->interval.tv_sec * NANOSECOND_PER_SECOND + timer->interval.tv_nsec) * RT_CLOCK_TIME_RESMUL) / - rt_clock_time_get_res_scaled(); + ns = timer->interval.tv_sec * NANOSECOND_PER_SECOND + timer->interval.tv_nsec; + timer->reload = (rt_tick_t)rt_muldiv_u64(ns, rt_clock_time_get_freq(), NANOSECOND_PER_SECOND, NULL); if (timer->reload) { rt_clock_hrtimer_start(&timer->hrtimer, timer->reload); @@ -1143,7 +1144,6 @@ int timer_getoverrun(timer_t timerid) int timer_gettime(timer_t timerid, struct itimerspec *its) { struct timer_obj *timer; - rt_uint32_t seconds, nanoseconds; timer = _g_timerid[(rt_ubase_t)timerid]; @@ -1161,13 +1161,13 @@ int timer_gettime(timer_t timerid, struct itimerspec *its) if (timer->status == ACTIVE) { - unsigned long remain_cnt; + rt_tick_t remain_cnt; + rt_uint64_t remain_relative_cnt; + const rt_uint64_t freq = rt_clock_time_get_freq(); rt_clock_hrtimer_control(&timer->hrtimer, RT_TIMER_CTRL_GET_REMAIN_TIME, &remain_cnt); - nanoseconds = ((remain_cnt - rt_clock_time_get_counter()) * rt_clock_time_get_res_scaled()) / RT_CLOCK_TIME_RESMUL; - seconds = nanoseconds / NANOSECOND_PER_SECOND; - nanoseconds = nanoseconds % NANOSECOND_PER_SECOND; - its->it_value.tv_sec = (rt_int32_t)seconds; - its->it_value.tv_nsec = (rt_int32_t)nanoseconds; + remain_relative_cnt = (rt_uint64_t)remain_cnt - rt_clock_time_get_counter(); + its->it_value.tv_sec = (time_t)(remain_relative_cnt / freq); + its->it_value.tv_nsec = (rt_int32_t)rt_muldiv_u64(remain_relative_cnt % freq, NANOSECOND_PER_SECOND, freq, NULL); } else { @@ -1255,8 +1255,7 @@ int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, if (ns <= 0) return 0; - unsigned long res = rt_clock_time_get_res_scaled(); - timer->reload = (ns * RT_CLOCK_TIME_RESMUL) / res; + timer->reload = (rt_tick_t)rt_muldiv_u64(ns, rt_clock_time_get_freq(), NANOSECOND_PER_SECOND, NULL); timer->interval.tv_sec = value->it_interval.tv_sec; timer->interval.tv_nsec = value->it_interval.tv_nsec; timer->value.tv_sec = value->it_value.tv_sec; diff --git a/documentation/6.components/device-driver/clock_time/clock_time_core.md b/documentation/6.components/device-driver/clock_time/clock_time_core.md index e7d099eb9dc..3a56cb8990d 100644 --- a/documentation/6.components/device-driver/clock_time/clock_time_core.md +++ b/documentation/6.components/device-driver/clock_time/clock_time_core.md @@ -49,13 +49,10 @@ struct rt_clock_time_device { struct rt_device parent; const struct rt_clock_time_ops *ops; - rt_uint64_t res_scale; rt_uint8_t caps; /* RT_CLOCK_TIME_CAP_SOURCE / RT_CLOCK_TIME_CAP_EVENT */ }; ``` -- res_scale provides extra precision in the conversion pipeline. If set to 0, - RT_CLOCK_TIME_RESMUL is used by default. - caps advertises whether the device can be used as a clock source, a clock event, or both. @@ -75,7 +72,7 @@ struct rt_clock_time_device *rt_clock_time_get_default_event(void); - Purpose: register a clock_time device and its capabilities. - Parameters: - - `dev`: device object with ops and res_scale initialized. + - `dev`: device object with ops initialized. - `name`: device name; if NULL, only capability registration is performed. - `caps`: RT_CLOCK_TIME_CAP_SOURCE and/or RT_CLOCK_TIME_CAP_EVENT. - Behavior: @@ -109,7 +106,6 @@ struct rt_clock_time_device *rt_clock_time_get_default_event(void); rt_uint64_t rt_clock_time_get_freq(void); rt_uint64_t rt_clock_time_get_counter(void); rt_uint64_t rt_clock_time_get_event_freq(void); -rt_uint64_t rt_clock_time_get_event_res_scaled(void); ``` ## rt_clock_time_get_freq @@ -131,45 +127,25 @@ rt_uint64_t rt_clock_time_get_event_res_scaled(void); - Purpose: return the event device frequency in Hz. - Behavior: if no event device exists, falls back to the default source. -## rt_clock_time_get_event_res_scaled - -- Purpose: return the scaled resolution for the event device. -- Behavior: if no event device exists, falls back to the default source. - # Conversion Helpers ```c -rt_uint64_t rt_clock_time_get_res_scaled(void); rt_uint64_t rt_clock_time_counter_to_ns(rt_uint64_t cnt); rt_uint64_t rt_clock_time_ns_to_counter(rt_uint64_t ns); ``` -## rt_clock_time_get_res_scaled - -- Purpose: return the scaled resolution for the default source. -- Return values: - - Non-zero scaled resolution when the source is ready. - - 0 when the source is missing or frequency is invalid. - ## rt_clock_time_counter_to_ns - Purpose: convert a counter value to nanoseconds based on the default source. -- Notes: returns 0 when resolution is unavailable. +- Notes: returns 0 when frequency is unavailable. ## rt_clock_time_ns_to_counter - Purpose: convert nanoseconds to counter units for the default source. -- Notes: returns 0 when resolution is unavailable. - -Internally, the core computes a scaled resolution: - -``` -res_scaled = (1e9 * res_scale) / freq -``` +- Notes: returns 0 when frequency is unavailable. -Nanoseconds are then derived using the scale factor RT_CLOCK_TIME_RESMUL to -avoid floating-point math. This keeps precision stable even when freq is not a -power of ten. +Conversions use the source frequency directly with `rt_muldiv_u64()` to avoid +floating-point math and preserve precision. # Event API @@ -220,7 +196,6 @@ static const struct rt_clock_time_ops demo_src_ops = static struct rt_clock_time_device demo_src_dev = { .ops = &demo_src_ops, - .res_scale = RT_CLOCK_TIME_RESMUL, }; void rt_clock_time_source_init(void) @@ -250,7 +225,6 @@ static const struct rt_clock_time_ops demo_evt_ops = static struct rt_clock_time_device demo_evt_dev = { .ops = &demo_evt_ops, - .res_scale = RT_CLOCK_TIME_RESMUL, }; static void demo_timer_isr(void) diff --git a/documentation/6.components/device-driver/clock_time/clock_time_core_zh.md b/documentation/6.components/device-driver/clock_time/clock_time_core_zh.md index 9e81180f326..fe2ee9f94a5 100644 --- a/documentation/6.components/device-driver/clock_time/clock_time_core_zh.md +++ b/documentation/6.components/device-driver/clock_time/clock_time_core_zh.md @@ -45,12 +45,10 @@ struct rt_clock_time_device { struct rt_device parent; const struct rt_clock_time_ops *ops; - rt_uint64_t res_scale; rt_uint8_t caps; /* RT_CLOCK_TIME_CAP_SOURCE / RT_CLOCK_TIME_CAP_EVENT */ }; ``` -- res_scale 用于提高换算精度,0 表示使用 RT_CLOCK_TIME_RESMUL 默认值。 - caps 用于标识设备能力:时钟源或时钟事件。 ## 注册与默认选择 @@ -69,7 +67,7 @@ struct rt_clock_time_device *rt_clock_time_get_default_event(void); - 作用:注册 clock_time 设备及其能力。 - 参数: - - `dev`:设备对象,需初始化 ops 与 res_scale。 + - `dev`:设备对象,需初始化 ops。 - `name`:设备名;为 NULL 时仅注册能力,不进入设备框架。 - `caps`:RT_CLOCK_TIME_CAP_SOURCE / RT_CLOCK_TIME_CAP_EVENT。 - 行为: @@ -100,7 +98,6 @@ struct rt_clock_time_device *rt_clock_time_get_default_event(void); rt_uint64_t rt_clock_time_get_freq(void); rt_uint64_t rt_clock_time_get_counter(void); rt_uint64_t rt_clock_time_get_event_freq(void); -rt_uint64_t rt_clock_time_get_event_res_scaled(void); ``` ## rt_clock_time_get_freq @@ -118,41 +115,24 @@ rt_uint64_t rt_clock_time_get_event_res_scaled(void); - 作用:获取事件设备频率(Hz)。 - 行为:若无事件设备则回退使用默认时钟源。 -## rt_clock_time_get_event_res_scaled - -- 作用:获取事件设备的缩放分辨率。 -- 行为:若无事件设备则回退使用默认时钟源。 - # 换算接口 ```c -rt_uint64_t rt_clock_time_get_res_scaled(void); rt_uint64_t rt_clock_time_counter_to_ns(rt_uint64_t cnt); rt_uint64_t rt_clock_time_ns_to_counter(rt_uint64_t ns); ``` -## rt_clock_time_get_res_scaled - -- 作用:获取默认时钟源的缩放分辨率。 -- 返回值:无有效时钟源时返回 0。 - ## rt_clock_time_counter_to_ns - 作用:将计数值换算为纳秒。 -- 说明:当分辨率不可用时返回 0。 +- 说明:当频率不可用时返回 0。 ## rt_clock_time_ns_to_counter - 作用:将纳秒换算为计数值。 -- 说明:当分辨率不可用时返回 0。 - -核心使用定点缩放换算: - -``` -res_scaled = (1e9 * res_scale) / freq -``` +- 说明:当频率不可用时返回 0。 -然后用 RT_CLOCK_TIME_RESMUL 进行定点缩放,以避免浮点运算带来的精度损失。 +换算直接使用时钟源频率,并通过 `rt_muldiv_u64()` 避免浮点运算带来的精度损失。 # 事件接口 @@ -203,7 +183,6 @@ static const struct rt_clock_time_ops demo_src_ops = static struct rt_clock_time_device demo_src_dev = { .ops = &demo_src_ops, - .res_scale = RT_CLOCK_TIME_RESMUL, }; void rt_clock_time_source_init(void) @@ -233,7 +212,6 @@ static const struct rt_clock_time_ops demo_evt_ops = static struct rt_clock_time_device demo_evt_dev = { .ops = &demo_evt_ops, - .res_scale = RT_CLOCK_TIME_RESMUL, }; static void demo_timer_isr(void) diff --git a/include/rtdef.h b/include/rtdef.h index db711aadff8..77e323ab1ba 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -107,7 +107,11 @@ extern "C" { #define RT_UINT64_MAX 0xFFFFFFFFFFFFFFFFULL /**< Maximum number of UINT64 */ #endif /* RT_USING_LIBC */ +#if defined(ARCH_CPU_64BIT) +#define RT_TICK_MAX RT_UINT64_MAX /**< Maximum number of tick */ +#else #define RT_TICK_MAX RT_UINT32_MAX /**< Maximum number of tick */ +#endif /* maximum value of ipc type */ #define RT_SEM_VALUE_MAX RT_UINT16_MAX /**< Maximum number of semaphore .value */ diff --git a/include/rttypes.h b/include/rttypes.h index 110e39df0ef..23463756c06 100644 --- a/include/rttypes.h +++ b/include/rttypes.h @@ -88,7 +88,11 @@ typedef rt_ubase_t rt_uintptr_t; /**< Type for unsigned #endif /* defined(RT_USING_LIBC) && !defined(RT_USING_NANO) */ typedef rt_base_t rt_err_t; /**< Type for error number */ +#if defined(ARCH_CPU_64BIT) +typedef rt_uint64_t rt_tick_t; /**< Type for tick count */ +#else typedef rt_uint32_t rt_tick_t; /**< Type for tick count */ +#endif typedef rt_base_t rt_flag_t; /**< Type for flags */ typedef rt_ubase_t rt_dev_t; /**< Type for device */ typedef rt_base_t rt_off_t; /**< Type for offset */ @@ -258,6 +262,14 @@ typedef struct rt_spinlock rt_spinlock_t; #define RT_DEFINE_SPINLOCK(x) struct rt_spinlock x = RT_SPINLOCK_INIT +/** + * @brief lossless mul div, return `(a * b) / c` + * + * if r is not NULL then `*r = (a * b) % c` + * @return rt_uint64_t + */ +rt_uint64_t rt_muldiv_u64(rt_uint64_t a, rt_uint64_t b, rt_uint64_t c, rt_uint64_t *r); + #ifdef __cplusplus } #endif diff --git a/src/rttypes.c b/src/rttypes.c new file mode 100644 index 00000000000..e5d9e8efab6 --- /dev/null +++ b/src/rttypes.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-25 Yonggang Luo the first version + */ + +#include "rttypes.h" +#include + +/* Function to count leading zeros for an rt_uint64_t */ +static rt_int32_t u64_count_leading_zeros(rt_uint64_t n) +{ + if (n == 0) + { + return 64; + } +#ifdef __GNUC__ + return __builtin_clzll(n); +#else + rt_int32_t count = 0; + rt_uint64_t mask = + 1ULL << (sizeof(rt_uint64_t) * 8 - 1); // Most significant bit + + while ((n & mask) == 0) + { + count++; + mask >>= 1; + } + return count; +#endif +} + +/** + * https://ridiculousfish.com/blog/posts/labor-of-division-episode-v.html + * Perform a narrowing division: 128 / 64 -> 64, and 64 / 32 -> 32. + * The dividend's low and high words are given by \p numhi and \p numlo, + * respectively. The divisor is given by \p den. + * \return the quotient, and the remainder by reference in \p r, if not null. + * If the quotient would require more than 64 bits, or if denom is 0, then + * return the max value for both quotient and remainder. + * + * These functions are released into the public domain, where applicable, or the + * CC0 license. + */ +static rt_uint64_t u128_div_u64_u64(rt_uint64_t numhi, rt_uint64_t numlo, rt_uint64_t den, + rt_uint64_t *r) +{ + /* + * We work in base 2**32. + * A uint32 holds a single digit. A uint64 holds two digits. + * Our numerator is conceptually [num3, num2, num1, num0]. + * Our denominator is [den1, den0]. + */ + const rt_uint64_t b = (1ull << 32); + + /* The high and low digits of our computed quotient. */ + rt_uint32_t q1; + rt_uint32_t q0; + + /* The normalization shift factor. */ + rt_int32_t shift; + + /* + * The high and low digits of our denominator (after normalizing). + * Also the low 2 digits of our numerator (after normalizing). + */ + rt_uint32_t den1; + rt_uint32_t den0; + rt_uint32_t num1; + rt_uint32_t num0; + + /* A partial remainder. */ + rt_uint64_t rem; + + /* The estimated quotient, and its corresponding remainder (unrelated to true remainder). */ + rt_uint64_t qhat; + rt_uint64_t rhat; + + /* Variables used to correct the estimated quotient. */ + rt_uint64_t c1; + rt_uint64_t c2; + + /* Check for overflow and divide by 0. */ + numhi = numhi % den; + + /* + * Determine the normalization factor. We multiply den by this, so that its leading digit is at + * least half b. In binary this means just shifting left by the number of leading zeros, so that + * there's a 1 in the MSB. + * We also shift numer by the same amount. This cannot overflow because numhi < den. + * The expression (-shift & 63) is the same as (64 - shift), except it avoids the UB of shifting + * by 64. The funny bitwise 'and' ensures that numlo does not get shifted into numhi if shift is 0. + * clang 11 has an x86 codegen bug here: see LLVM bug 50118. The sequence below avoids it. + */ + shift = u64_count_leading_zeros(den); + den <<= shift; + numhi <<= shift; + numhi |= (numlo >> (-shift & 63)) & (-(rt_int64_t)shift >> 63); + numlo <<= shift; + + /* Extract the low digits of the numerator and both digits of the denominator. */ + num1 = (rt_uint32_t)(numlo >> 32); + num0 = (rt_uint32_t)(numlo & 0xFFFFFFFFu); + den1 = (rt_uint32_t)(den >> 32); + den0 = (rt_uint32_t)(den & 0xFFFFFFFFu); + + /* + * We wish to compute q1 = [n3 n2 n1] / [d1 d0]. + * Estimate q1 as [n3 n2] / [d1], and then correct it. + * Note while qhat may be 2 digits, q1 is always 1 digit. + */ + qhat = numhi / den1; + rhat = numhi % den1; + c1 = qhat * den0; + c2 = rhat * b + num1; + if (c1 > c2) + qhat -= (c1 - c2 > den) ? 2 : 1; + q1 = (rt_uint32_t)qhat; + + /* Compute the true (partial) remainder. */ + rem = numhi * b + num1 - q1 * den; + + /* + * We wish to compute q0 = [rem1 rem0 n0] / [d1 d0]. + * Estimate q0 as [rem1 rem0] / [d1] and correct it. + */ + qhat = rem / den1; + rhat = rem % den1; + c1 = qhat * den0; + c2 = rhat * b + num0; + if (c1 > c2) + qhat -= (c1 - c2 > den) ? 2 : 1; + q0 = (rt_uint32_t)qhat; + + /* Return remainder if requested. */ + if (r != NULL) + *r = (rem * b + num0 - q0 * den) >> shift; + return ((rt_uint64_t)q1 << 32) | q0; +} + +rt_uint64_t rt_muldiv_u64(rt_uint64_t a, rt_uint64_t b, rt_uint64_t c, rt_uint64_t *r) +{ + rt_uint64_t remainder = 0; + rt_uint64_t ret = 0; + if (c != 0) /* Handle division by zero. */ + { + rt_uint64_t a_lo = a & 0xFFFFFFFF; + rt_uint64_t a_hi = a >> 32; + rt_uint64_t b_lo = b & 0xFFFFFFFF; + rt_uint64_t b_hi = b >> 32; + + /* Perform partial products */ + rt_uint64_t p0 = a_lo * b_lo; + rt_uint64_t p1 = a_lo * b_hi; + rt_uint64_t p2 = a_hi * b_lo; + rt_uint64_t p3 = a_hi * b_hi; + + rt_uint64_t carry = (p0 >> 32) + (p1 & 0xFFFFFFFFULL) + (p2 & 0xFFFFFFFFULL); + + rt_uint64_t lo = (p0 & 0xFFFFFFFFULL) + ((carry & 0xFFFFFFFFULL) << 32); + rt_uint64_t hi = p3 + (p1 >> 32) + (p2 >> 32) + (carry >> 32); + ret = u128_div_u64_u64(hi, lo, c, &remainder); + } + if (r) + *r = remainder; + return ret; +}