From e13dc950d1205a3ef474158829d1a842edc9c277 Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Thu, 25 Sep 2025 15:22:20 +0800 Subject: [PATCH] Fixes clock_gettime and rt_ktime_boottime_get_ns Currently the return value of clock_gettime and rt_ktime_boottime_get_ns are restricted by rt_ktime_cputimer_getres, as the value of rt_ktime_cputimer_getres are not exactly represent the frequency of the clock, directly using rt_ktime_cputimer_getfrq will ensure the return value of rt_ktime_boottime_get_ns be more exact. And also add function rt_muldiv_u64 rt_muldiv_u32 for not losing precision when convert between cputimer and hrtimer and nanoseconds. Because rt_ktime_boottime_get_ns depends on rt_ktime_cputimer_getcnt and rt_ktime_cputimer_getcnt depends rt_tick_get So the rt_tick_t should be 64bit for long running program. Use `#if defined(ARCH_CPU_64BIT)` is for future allow rt_tick_t can be 64bit on 32bit CPU. Tick count should not be restricted to the CPU Signed-off-by: Yonggang Luo --- .../driver/hwtimer/hwtimer-rockchip_timer.c | 9 +- components/drivers/ktime/inc/ktime.h | 46 ++--- .../drivers/ktime/src/aarch64/cputimer.c | 15 +- components/drivers/ktime/src/boottime.c | 21 +- components/drivers/ktime/src/cputimer.c | 9 +- components/drivers/ktime/src/hrtimer.c | 59 +++--- .../ktime/src/risc-v/virt64/cputimer.c | 11 +- components/drivers/rtc/dev_soft_rtc.c | 2 +- components/libc/compilers/common/ctime.c | 27 ++- include/rtdef.h | 4 + include/rttypes.h | 20 ++ src/rttypes.c | 186 ++++++++++++++++++ 12 files changed, 289 insertions(+), 120 deletions(-) create mode 100644 src/rttypes.c diff --git a/bsp/rockchip/rk3500/driver/hwtimer/hwtimer-rockchip_timer.c b/bsp/rockchip/rk3500/driver/hwtimer/hwtimer-rockchip_timer.c index 33ad9cd51bd..46ffd860d8c 100644 --- a/bsp/rockchip/rk3500/driver/hwtimer/hwtimer-rockchip_timer.c +++ b/bsp/rockchip/rk3500/driver/hwtimer/hwtimer-rockchip_timer.c @@ -328,16 +328,11 @@ static const struct rk_timer_data rk3399_timer_data = #ifdef RT_USING_KTIME -uint64_t rt_ktime_hrtimer_getfrq(void) +rt_uint64_t rt_ktime_hrtimer_getfrq(void) { return (24 * 1000 * 1000UL); } -uint64_t rt_ktime_hrtimer_getres(void) -{ - return ((1000UL * 1000 * 1000) * RT_KTIME_RESMUL) / (24 * 1000 * 1000UL); -} - /** * @brief set the timeout function for hrtimer framework * @@ -346,7 +341,7 @@ uint64_t rt_ktime_hrtimer_getres(void) * @param cnt the count of timer dealy * @return rt_err_t 0 forever */ -rt_err_t rt_ktime_hrtimer_settimeout(unsigned long cnt) +rt_err_t rt_ktime_hrtimer_settimeout(rt_uint64_t cnt) { struct hrt_timer *timer = &_timer0; struct rk_timer *time = timer->timer; diff --git a/components/drivers/ktime/inc/ktime.h b/components/drivers/ktime/inc/ktime.h index a430f85a175..20050980910 100644 --- a/components/drivers/ktime/inc/ktime.h +++ b/components/drivers/ktime/inc/ktime.h @@ -18,16 +18,14 @@ #include "rtthread.h" -#define RT_KTIME_RESMUL (1000000ULL) - struct rt_ktime_hrtimer { rt_uint8_t flag; /**< compatible to tick timer's flag */ 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); @@ -58,26 +56,19 @@ rt_err_t rt_ktime_boottime_get_s(time_t *t); */ rt_err_t rt_ktime_boottime_get_ns(struct timespec *ts); -/** - * @brief Get cputimer resolution - * - * @return (resolution * RT_KTIME_RESMUL) - */ -rt_uint64_t rt_ktime_cputimer_getres(void); - /** * @brief Get cputimer frequency * * @return frequency */ -unsigned long rt_ktime_cputimer_getfrq(void); +rt_uint32_t rt_ktime_cputimer_getfrq(void); /** * @brief Get cputimer the value of the cnt counter * * @return cnt */ -unsigned long rt_ktime_cputimer_getcnt(void); +rt_tick_t rt_ktime_cputimer_getcnt(void); /** * @brief Init cputimer @@ -86,18 +77,11 @@ unsigned long rt_ktime_cputimer_getcnt(void); void rt_ktime_cputimer_init(void); /** - * @brief Get hrtimer resolution - * - * @return (resolution * RT_KTIME_RESMUL) - */ -rt_uint64_t rt_ktime_hrtimer_getres(void); - -/** - * @brief Get hrtimer frequency + * @brief Get hrtimer frequency, you should re-implemented it in hrtimer device driver * * @return frequency */ -unsigned long rt_ktime_hrtimer_getfrq(void); +rt_uint64_t rt_ktime_hrtimer_getfrq(void); /** * @brief set hrtimer interrupt timeout count (cnt), you should re-implemented it in hrtimer device driver @@ -105,7 +89,7 @@ unsigned long rt_ktime_hrtimer_getfrq(void); * @param cnt: hrtimer requires a timing cnt value * @return rt_err_t */ -rt_err_t rt_ktime_hrtimer_settimeout(unsigned long cnt); +rt_err_t rt_ktime_hrtimer_settimeout(rt_uint64_t cnt); /** * @brief called in hrtimer device driver isr routinue, it will process the timeouts @@ -117,7 +101,13 @@ void rt_ktime_hrtimer_init(rt_ktime_hrtimer_t timer, rt_uint8_t flag, void (*timeout)(void *parameter), void *parameter); -rt_err_t rt_ktime_hrtimer_start(rt_ktime_hrtimer_t timer, unsigned long cnt); +/** + * @brief start the hrtimer + * + * @param cnt the cputimer cnt value + * @return rt_err_t + */ +rt_err_t rt_ktime_hrtimer_start(rt_ktime_hrtimer_t timer, rt_tick_t cnt); rt_err_t rt_ktime_hrtimer_stop(rt_ktime_hrtimer_t timer); rt_err_t rt_ktime_hrtimer_control(rt_ktime_hrtimer_t timer, int cmd, void *arg); rt_err_t rt_ktime_hrtimer_detach(rt_ktime_hrtimer_t timer); @@ -140,7 +130,7 @@ void rt_ktime_hrtimer_process(void); * @param cnt: the cputimer cnt value * @return rt_err_t */ -rt_err_t rt_ktime_hrtimer_sleep(struct rt_ktime_hrtimer *timer, unsigned long cnt); +rt_err_t rt_ktime_hrtimer_sleep(struct rt_ktime_hrtimer *timer, rt_tick_t cnt); /** * @brief sleep by ns @@ -148,7 +138,7 @@ rt_err_t rt_ktime_hrtimer_sleep(struct rt_ktime_hrtimer *timer, unsigned long cn * @param ns: ns * @return rt_err_t */ -rt_err_t rt_ktime_hrtimer_ndelay(struct rt_ktime_hrtimer *timer, unsigned long ns); +rt_err_t rt_ktime_hrtimer_ndelay(struct rt_ktime_hrtimer *timer, rt_uint64_t ns); /** * @brief sleep by us @@ -156,7 +146,7 @@ rt_err_t rt_ktime_hrtimer_ndelay(struct rt_ktime_hrtimer *timer, unsigned long n * @param us: us * @return rt_err_t */ -rt_err_t rt_ktime_hrtimer_udelay(struct rt_ktime_hrtimer *timer, unsigned long us); +rt_err_t rt_ktime_hrtimer_udelay(struct rt_ktime_hrtimer *timer, rt_uint64_t us); /** * @brief sleep by ms @@ -164,6 +154,6 @@ rt_err_t rt_ktime_hrtimer_udelay(struct rt_ktime_hrtimer *timer, unsigned long u * @param ms: ms * @return rt_err_t */ -rt_err_t rt_ktime_hrtimer_mdelay(struct rt_ktime_hrtimer *timer, unsigned long ms); +rt_err_t rt_ktime_hrtimer_mdelay(struct rt_ktime_hrtimer *timer, rt_uint64_t ms); #endif diff --git a/components/drivers/ktime/src/aarch64/cputimer.c b/components/drivers/ktime/src/aarch64/cputimer.c index 005848eccdc..41ebdb4277b 100644 --- a/components/drivers/ktime/src/aarch64/cputimer.c +++ b/components/drivers/ktime/src/aarch64/cputimer.c @@ -11,21 +11,16 @@ #include "gtimer.h" #include "ktime.h" -static volatile unsigned long _init_cnt = 0; +static volatile rt_uint64_t _init_cnt = 0; -rt_uint64_t rt_ktime_cputimer_getres(void) +rt_uint32_t rt_ktime_cputimer_getfrq(void) { - return ((1000ULL * 1000 * 1000) * RT_KTIME_RESMUL) / rt_hw_get_gtimer_frq(); + return (rt_uint32_t)rt_hw_get_gtimer_frq(); } -unsigned long rt_ktime_cputimer_getfrq(void) +rt_tick_t rt_ktime_cputimer_getcnt(void) { - return rt_hw_get_gtimer_frq(); -} - -unsigned long rt_ktime_cputimer_getcnt(void) -{ - return rt_hw_get_cntpct_val() - _init_cnt; + return (rt_tick_t)(rt_hw_get_cntpct_val() - _init_cnt); } void rt_ktime_cputimer_init(void) diff --git a/components/drivers/ktime/src/boottime.c b/components/drivers/ktime/src/boottime.c index 8e69141b68d..04a74c5074b 100644 --- a/components/drivers/ktime/src/boottime.c +++ b/components/drivers/ktime/src/boottime.c @@ -10,16 +10,15 @@ #include "ktime.h" -#define __KTIME_MUL ((1000ULL * 1000 * 1000) / RT_TICK_PER_SECOND) - rt_weak rt_err_t rt_ktime_boottime_get_us(struct timeval *tv) { RT_ASSERT(tv != RT_NULL); - rt_uint64_t ns = (rt_ktime_cputimer_getcnt() * rt_ktime_cputimer_getres()) / RT_KTIME_RESMUL; + rt_tick_t cnt = rt_ktime_cputimer_getcnt(); + rt_uint32_t freq = rt_ktime_cputimer_getfrq(); - 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_u32((rt_uint32_t)(cnt % freq), MICROSECOND_PER_SECOND, freq, NULL); return RT_EOK; } @@ -28,9 +27,10 @@ rt_weak rt_err_t rt_ktime_boottime_get_s(time_t *t) { RT_ASSERT(t != RT_NULL); - rt_uint64_t ns = (rt_ktime_cputimer_getcnt() * rt_ktime_cputimer_getres()) / RT_KTIME_RESMUL; + rt_tick_t cnt = rt_ktime_cputimer_getcnt(); + rt_uint32_t freq = rt_ktime_cputimer_getfrq(); - *t = ns / (1000ULL * 1000 * 1000); + *t = (time_t)(cnt / freq); return RT_EOK; } @@ -39,10 +39,11 @@ rt_weak rt_err_t rt_ktime_boottime_get_ns(struct timespec *ts) { RT_ASSERT(ts != RT_NULL); - rt_uint64_t ns = (rt_ktime_cputimer_getcnt() * rt_ktime_cputimer_getres()) / RT_KTIME_RESMUL; + rt_tick_t cnt = rt_ktime_cputimer_getcnt(); + rt_uint32_t freq = rt_ktime_cputimer_getfrq(); - 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_u32((rt_uint32_t)(cnt % freq), NANOSECOND_PER_SECOND, freq, NULL); return RT_EOK; } diff --git a/components/drivers/ktime/src/cputimer.c b/components/drivers/ktime/src/cputimer.c index ee19b236dc6..ac326e13f92 100644 --- a/components/drivers/ktime/src/cputimer.c +++ b/components/drivers/ktime/src/cputimer.c @@ -10,17 +10,12 @@ #include "ktime.h" -rt_weak rt_uint64_t rt_ktime_cputimer_getres(void) -{ - return ((1000ULL * 1000 * 1000) * RT_KTIME_RESMUL) / RT_TICK_PER_SECOND; -} - -rt_weak unsigned long rt_ktime_cputimer_getfrq(void) +rt_weak rt_uint32_t rt_ktime_cputimer_getfrq(void) { return RT_TICK_PER_SECOND; } -rt_weak unsigned long rt_ktime_cputimer_getcnt(void) +rt_weak rt_tick_t rt_ktime_cputimer_getcnt(void) { return rt_tick_get(); } diff --git a/components/drivers/ktime/src/hrtimer.c b/components/drivers/ktime/src/hrtimer.c index ca579a5e988..2bfa14df829 100644 --- a/components/drivers/ktime/src/hrtimer.c +++ b/components/drivers/ktime/src/hrtimer.c @@ -19,12 +19,6 @@ #include "ktime.h" -#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); @@ -33,17 +27,12 @@ rt_inline rt_ktime_hrtimer_t _first_hrtimer(void) return rt_list_isempty(&_timer_list) ? RT_NULL : rt_list_first_entry(&_timer_list, struct rt_ktime_hrtimer, node); } -rt_weak rt_uint64_t rt_ktime_hrtimer_getres(void) -{ - return ((1000ULL * 1000 * 1000) * RT_KTIME_RESMUL) / RT_TICK_PER_SECOND; -} - -rt_weak unsigned long rt_ktime_hrtimer_getfrq(void) +rt_weak rt_uint64_t rt_ktime_hrtimer_getfrq(void) { return RT_TICK_PER_SECOND; } -rt_weak rt_err_t rt_ktime_hrtimer_settimeout(unsigned long cnt) +rt_weak rt_err_t rt_ktime_hrtimer_settimeout(rt_uint64_t cnt) { static rt_timer_t timer = RT_NULL; static struct rt_timer _sh_rtimer; @@ -53,11 +42,11 @@ rt_weak rt_err_t rt_ktime_hrtimer_settimeout(unsigned long cnt) if (timer == RT_NULL) { timer = &_sh_rtimer; - rt_timer_init(timer, "shrtimer", (void (*)(void *))rt_ktime_hrtimer_process, RT_NULL, cnt, RT_TIMER_FLAG_ONE_SHOT); + rt_timer_init(timer, "shrtimer", (void (*)(void *))rt_ktime_hrtimer_process, RT_NULL, (rt_tick_t)cnt, RT_TIMER_FLAG_ONE_SHOT); } else { - rt_tick_t tick = cnt; + rt_tick_t tick = (rt_tick_t)cnt; rt_timer_control(timer, RT_TIMER_CTRL_SET_TIME, &tick); rt_timer_control(timer, RT_TIMER_CTRL_SET_PARM, RT_NULL); } @@ -75,16 +64,16 @@ rt_weak rt_err_t rt_ktime_hrtimer_settimeout(unsigned long cnt) * @brief convert cnt from cputimer cnt to hrtimer cnt * * @param cnt - * @return unsigned long + * @return rt_uint64_t */ -static unsigned long _cnt_convert(unsigned long cnt) +static rt_uint64_t _cnt_convert(rt_tick_t cnt) { - unsigned long rtn = 0; - unsigned long count = cnt - rt_ktime_cputimer_getcnt(); - if (count > (_HRTIMER_MAX_CNT / 2)) + rt_uint64_t rtn = 0; + rt_tick_t count = cnt - rt_ktime_cputimer_getcnt(); + if (count > (RT_TICK_MAX / 2)) return 0; - rtn = (count * rt_ktime_cputimer_getres()) / rt_ktime_hrtimer_getres(); + rtn = rt_muldiv_u64(count, rt_ktime_hrtimer_getfrq(), rt_ktime_cputimer_getfrq(), NULL); return rtn == 0 ? 1 : rtn; /* at least 1 */ } @@ -140,7 +129,7 @@ static void _hrtimer_process_locked(void) static void _set_next_timeout_locked(void) { rt_ktime_hrtimer_t timer; - rt_ubase_t next_timeout_hrtimer_cnt; + rt_uint64_t next_timeout_hrtimer_cnt; rt_bool_t find_next; do @@ -193,13 +182,13 @@ void rt_ktime_hrtimer_init(rt_ktime_hrtimer_t timer, rt_completion_init(&timer->completion); } -rt_err_t rt_ktime_hrtimer_start(rt_ktime_hrtimer_t timer, unsigned long delay_cnt) +rt_err_t rt_ktime_hrtimer_start(rt_ktime_hrtimer_t timer, rt_tick_t delay_cnt) { rt_base_t level; /* parameter check */ 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 + rt_ktime_cputimer_getcnt(); @@ -255,13 +244,13 @@ rt_err_t rt_ktime_hrtimer_control(rt_ktime_hrtimer_t timer, int cmd, void *arg) { 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 + rt_ktime_cputimer_getcnt(); + RT_ASSERT((*(rt_tick_t *)arg) < (RT_TICK_MAX / 2)); + timer->delay_cnt = *(rt_tick_t *)arg; + timer->timeout_cnt = *(rt_tick_t *)arg + rt_ktime_cputimer_getcnt(); break; case RT_TIMER_CTRL_SET_ONESHOT: @@ -286,7 +275,7 @@ rt_err_t rt_ktime_hrtimer_control(rt_ktime_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: arg = (void *)timer->timeout_func; @@ -351,7 +340,7 @@ void rt_ktime_hrtimer_delay_detach(struct rt_ktime_hrtimer *timer) rt_ktime_hrtimer_detach(timer); } -rt_err_t rt_ktime_hrtimer_sleep(struct rt_ktime_hrtimer *timer, unsigned long cnt) +rt_err_t rt_ktime_hrtimer_sleep(struct rt_ktime_hrtimer *timer, rt_tick_t cnt) { rt_err_t err; @@ -369,18 +358,18 @@ rt_err_t rt_ktime_hrtimer_sleep(struct rt_ktime_hrtimer *timer, unsigned long cn return err; } -rt_err_t rt_ktime_hrtimer_ndelay(struct rt_ktime_hrtimer *timer, unsigned long ns) +rt_err_t rt_ktime_hrtimer_ndelay(struct rt_ktime_hrtimer *timer, rt_uint64_t ns) { - rt_uint64_t res = rt_ktime_cputimer_getres(); - return rt_ktime_hrtimer_sleep(timer, (ns * RT_KTIME_RESMUL) / res); + rt_tick_t cputimer_tick = (rt_tick_t)rt_muldiv_u64(ns, rt_ktime_cputimer_getfrq(), NANOSECOND_PER_SECOND, NULL); + return rt_ktime_hrtimer_sleep(timer, cputimer_tick); } -rt_err_t rt_ktime_hrtimer_udelay(struct rt_ktime_hrtimer *timer, unsigned long us) +rt_err_t rt_ktime_hrtimer_udelay(struct rt_ktime_hrtimer *timer, rt_uint64_t us) { return rt_ktime_hrtimer_ndelay(timer, us * 1000); } -rt_err_t rt_ktime_hrtimer_mdelay(struct rt_ktime_hrtimer *timer, unsigned long ms) +rt_err_t rt_ktime_hrtimer_mdelay(struct rt_ktime_hrtimer *timer, rt_uint64_t ms) { return rt_ktime_hrtimer_ndelay(timer, ms * 1000000); } diff --git a/components/drivers/ktime/src/risc-v/virt64/cputimer.c b/components/drivers/ktime/src/risc-v/virt64/cputimer.c index 70c133aa2e7..0df3104cad2 100644 --- a/components/drivers/ktime/src/risc-v/virt64/cputimer.c +++ b/components/drivers/ktime/src/risc-v/virt64/cputimer.c @@ -12,21 +12,16 @@ static volatile unsigned long _init_cnt = 0; -rt_uint64_t rt_ktime_cputimer_getres(void) -{ - return ((1000ULL * 1000 * 1000) * RT_KTIME_RESMUL) / CPUTIME_TIMER_FREQ; -} - -unsigned long rt_ktime_cputimer_getfrq(void) +rt_uint32_t rt_ktime_cputimer_getfrq(void) { return CPUTIME_TIMER_FREQ; } -unsigned long rt_ktime_cputimer_getcnt(void) +rt_tick_t rt_ktime_cputimer_getcnt(void) { unsigned long time_elapsed; __asm__ __volatile__("rdtime %0" : "=r"(time_elapsed)); - return time_elapsed - _init_cnt; + return (rt_tick_t)(time_elapsed - _init_cnt); } void rt_ktime_cputimer_init(void) diff --git a/components/drivers/rtc/dev_soft_rtc.c b/components/drivers/rtc/dev_soft_rtc.c index cd5a37ee027..ce17ee5292e 100644 --- a/components/drivers/rtc/dev_soft_rtc.c +++ b/components/drivers/rtc/dev_soft_rtc.c @@ -153,7 +153,7 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) { struct timespec *ts = (struct timespec *)args; ts->tv_sec = 0; - ts->tv_nsec = (rt_ktime_cputimer_getres() / RT_KTIME_RESMUL); + ts->tv_nsec = (rt_uint32_t)(NANOSECOND_PER_SECOND / rt_ktime_cputimer_getfrq()); break; } #else diff --git a/components/libc/compilers/common/ctime.c b/components/libc/compilers/common/ctime.c index 159667bbcfa..76680df572d 100644 --- a/components/libc/compilers/common/ctime.c +++ b/components/libc/compilers/common/ctime.c @@ -555,7 +555,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_ktime_boottime_get_ns(&old_ts); rt_ktime_hrtimer_ndelay(&timer, ns); if (rt_get_errno() == RT_EINTR) @@ -615,7 +615,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_ktime_cputimer_getres() / RT_KTIME_RESMUL); + res->tv_nsec = (rt_uint32_t)(NANOSECOND_PER_SECOND / rt_ktime_cputimer_getfrq()); return 0; default: @@ -805,7 +805,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; @@ -887,6 +887,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; @@ -895,8 +896,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_KTIME_RESMUL) / - rt_ktime_cputimer_getres(); + ns = timer->interval.tv_sec * NANOSECOND_PER_SECOND + timer->interval.tv_nsec; + timer->reload = (rt_tick_t)rt_muldiv_u64(ns, rt_ktime_cputimer_getfrq(), NANOSECOND_PER_SECOND, NULL); if (timer->reload) { rt_ktime_hrtimer_start(&timer->hrtimer, timer->reload); @@ -1115,7 +1116,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]; @@ -1133,13 +1133,13 @@ int timer_gettime(timer_t timerid, struct itimerspec *its) if (timer->status == ACTIVE) { - unsigned long remain_cnt; + rt_tick_t remain_cnt; + rt_tick_t remain_relative_cnt; + const rt_uint32_t freq = rt_ktime_cputimer_getfrq(); rt_ktime_hrtimer_control(&timer->hrtimer, RT_TIMER_CTRL_GET_REMAIN_TIME, &remain_cnt); - nanoseconds = ((remain_cnt - rt_ktime_cputimer_getcnt()) * rt_ktime_cputimer_getres()) / RT_KTIME_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 = remain_cnt - rt_ktime_cputimer_getcnt(); + its->it_value.tv_sec = (time_t)(remain_relative_cnt / freq); + its->it_value.tv_nsec = rt_muldiv_u32((rt_uint32_t)(remain_relative_cnt % freq), (rt_uint32_t)NANOSECOND_PER_SECOND, freq, NULL); } else { @@ -1227,8 +1227,7 @@ int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, if (ns <= 0) return 0; - unsigned long res = rt_ktime_cputimer_getres(); - timer->reload = (ns * RT_KTIME_RESMUL) / res; + timer->reload = (rt_tick_t)rt_muldiv_u64(ns, rt_ktime_cputimer_getfrq(), 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/include/rtdef.h b/include/rtdef.h index f3e10fe51f3..0629e7a3e8d 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 cfbaec8bdb8..d2e8aa8cd39 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 */ @@ -250,6 +254,22 @@ 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); + +/** + * @brief lossless mul div, return `(a * b) / c` + * + * if r is not NULL then `*r = (a * b) % c` + * @return rt_uint32_t + */ +rt_uint32_t rt_muldiv_u32(rt_uint32_t a, rt_uint32_t b, rt_uint32_t c, rt_uint32_t *r); + #ifdef __cplusplus } #endif diff --git a/src/rttypes.c b/src/rttypes.c new file mode 100644 index 00000000000..48f96f94e55 --- /dev/null +++ b/src/rttypes.c @@ -0,0 +1,186 @@ +/* + * 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; +} + +rt_uint32_t rt_muldiv_u32(rt_uint32_t a, rt_uint32_t b, rt_uint32_t c, rt_uint32_t *r) +{ + rt_uint32_t remainder = 0; + rt_uint32_t ret = 0; + if (c != 0) /* Handle division by zero. */ + { + rt_uint64_t mul = (rt_uint64_t)a * (rt_uint64_t)b; + ret = mul / c; + remainder = mul % c; + } + if (r) + *r = remainder; + return ret; +}