genode/repos/base/include/drivers/timer/util.h
Christian Helmuth 73d18261dc Fix calculation in timer_ticks_to_us()
Added missing factoring of the upper-half division remainder into the
lower-half calculation.

Fixes #5243
2024-06-20 12:54:30 +02:00

64 lines
2.1 KiB
C++

/*
* \brief Utilities for timer drivers
* \author Martin Stein
* \date 2017-08-23
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _DRIVERS__TIMER__UTIL_H_
#define _DRIVERS__TIMER__UTIL_H_
namespace Genode {
enum { TIMER_MIN_TICKS_PER_MS = 1000ULL };
/*
* Translate timer ticks to microseconds without losing precicision
*
* There are hardware timers whose frequency can't be expressed as
* ticks-per-microsecond integer-value because only a ticks-per-millisecond
* integer-value is precise enough. We don't want to use expensive
* floating-point values here but nonetheless want to translate from ticks
* to time with microseconds precision. Thus, we split the input in two and
* translate both parts separately. This way, we can raise precision by
* shifting the values to their optimal bit position. Afterwards, the
* results are shifted back and merged together again. Note, the remainder
* of the upper-half division is factored into the lower-half calculation
* (as upper-half value).
*
* Please ensure that the assertion
* "ticks_per_ms >= TIMER_MIN_TICKS_PER_MS" is true when calling this
* method!
*/
template <typename RESULT_T, typename TICS_PER_MS_T>
RESULT_T timer_ticks_to_us(RESULT_T const ticks,
TICS_PER_MS_T const ticks_per_ms)
{
enum:RESULT_T {
HALF_WIDTH = (sizeof(RESULT_T) << 2),
MSB_MASK = (~(RESULT_T)0) << HALF_WIDTH,
LSB_MASK = (~(RESULT_T)0) >> HALF_WIDTH,
MSB_RSHIFT = 10,
LSB_LSHIFT = HALF_WIDTH - MSB_RSHIFT,
};
/* upper half */
RESULT_T const msb0 = ((ticks & MSB_MASK) >> MSB_RSHIFT) * 1000;
RESULT_T const msb = (msb0 / ticks_per_ms) << MSB_RSHIFT;
RESULT_T const rem = (msb0 % ticks_per_ms) << MSB_RSHIFT;
/* lower half */
RESULT_T const lsb0 = ((ticks & LSB_MASK) << LSB_LSHIFT) * 1000
+ (rem << LSB_LSHIFT);
RESULT_T const lsb = (lsb0 / ticks_per_ms) >> LSB_LSHIFT;
return msb + lsb;
}
}
#endif /* _DRIVERS__TIMER__UTIL_H_ */