timers/oneshot: Fix converted tv_nsec > NSEC_PER_SEC.

In rare case, the round-nearest behavior in the clkcnt_best_multshift
may result in converted tv_nsec > NSEC_PER_SEC. This commit fixed the
issue.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
This commit is contained in:
ouyangxiangzhen 2025-12-16 10:52:13 +08:00 committed by Matteo Golin
parent 99ca0dbf9d
commit f14461db2e
2 changed files with 11 additions and 4 deletions

View file

@ -84,7 +84,7 @@ void clkcnt_best_multshift(uint32_t freq, uint32_t scale,
{
uint32_t logfreq = log2floor(freq);
/* Be careful of the round-nearest behavior here.
/* Do not use the round-nearest behavior here.
* It may lead to the converted result is larger than the
* integer division.
* In rare cases, this can cause the converted tick to be greater
@ -103,7 +103,7 @@ void clkcnt_best_multshift(uint32_t freq, uint32_t scale,
* affecting the accuracy of time compensation.
*/
*mult = (((uint64_t)scale << logfreq) + (freq >> 1)) / freq;
*mult = ((uint64_t)scale << logfreq) / freq;
*shift = logfreq;
}

View file

@ -44,6 +44,8 @@
* Pre-processor Definitions
****************************************************************************/
#define ONESHOT_NSEC_TOLERANT 5
/* IOCTL commands ***********************************************************/
/* These commands are used by applications to access the oneshot lower-half
@ -293,12 +295,15 @@ void oneshot_count_init(FAR struct oneshot_lowerhalf_s *lower,
&lower->cnt2nsec_mult,
&lower->cnt2nsec_shift);
/* Ensure the maximum error of the mult-shift is less than 5ns. */
/* Ensure the maximum error of the mult-shift is less than
* ONESHOT_NSEC_TOLERANT.
*/
result = clkcnt_delta_cnt2nsec_fast(frequency, lower->cnt2nsec_mult,
lower->cnt2nsec_shift);
ASSERT(NSEC_PER_SEC - 5 <= result && NSEC_PER_SEC + 5 >= result);
ASSERT(NSEC_PER_SEC - ONESHOT_NSEC_TOLERANT <= result &&
NSEC_PER_SEC >= result);
# ifdef CONFIG_ONESHOT_FAST_DIVISION
/* invdiv requires the invariant-divsor > 1. */
@ -402,6 +407,8 @@ int oneshot_current(FAR struct oneshot_lowerhalf_s *lower,
cnt -= sec * freq;
ts->tv_nsec = oneshot_delta_cnt2nsec(lower, cnt);
ts->tv_sec = sec;
DEBUGASSERT(cnt < freq && ts->tv_nsec < NSEC_PER_SEC);
#else
ret = lower->ops->current(lower, ts);
#endif