clock: Introduce clock_ticks_to_ns()
The clock_get_ns() API claims to return the period of a clock in nanoseconds. Unfortunately since it returns an integer and a clock's period is represented in units of 2^-32 nanoseconds, the result is often an approximation, and calculating a clock expiry deadline by multiplying clock_get_ns() by a number-of-ticks is unacceptably inaccurate. Introduce a new API clock_ticks_to_ns() which returns the number of nanoseconds it takes the clock to make a given number of ticks. This function can do the complete calculation internally and will thus give a more accurate result. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Luc Michel <luc@lmichel.fr> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-Id: <20201215150929.30311-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
This commit is contained in:
parent
7886a674f1
commit
554d523785
@ -258,6 +258,35 @@ Here is an example:
|
||||
clock_get_ns(dev->my_clk_input));
|
||||
}
|
||||
|
||||
Calculating expiry deadlines
|
||||
----------------------------
|
||||
|
||||
A commonly required operation for a clock is to calculate how long
|
||||
it will take for the clock to tick N times; this can then be used
|
||||
to set a timer expiry deadline. Use the function ``clock_ticks_to_ns()``,
|
||||
which takes an unsigned 64-bit count of ticks and returns the length
|
||||
of time in nanoseconds required for the clock to tick that many times.
|
||||
|
||||
It is important not to try to calculate expiry deadlines using a
|
||||
shortcut like multiplying a "period of clock in nanoseconds" value
|
||||
by the tick count, because clocks can have periods which are not a
|
||||
whole number of nanoseconds, and the accumulated error in the
|
||||
multiplication can be significant.
|
||||
|
||||
For a clock with a very long period and a large number of ticks,
|
||||
the result of this function could in theory be too large to fit in
|
||||
a 64-bit value. To avoid overflow in this case, ``clock_ticks_to_ns()``
|
||||
saturates the result to INT64_MAX (because this is the largest valid
|
||||
input to the QEMUTimer APIs). Since INT64_MAX nanoseconds is almost
|
||||
300 years, anything with an expiry later than that is in the "will
|
||||
never happen" category. Callers of ``clock_ticks_to_ns()`` should
|
||||
therefore generally not special-case the possibility of a saturated
|
||||
result but just allow the timer to be set to that far-future value.
|
||||
(If you are performing further calculations on the returned value
|
||||
rather than simply passing it to a QEMUTimer function like
|
||||
``timer_mod_ns()`` then you should be careful to avoid overflow
|
||||
in those calculations, of course.)
|
||||
|
||||
Changing a clock period
|
||||
-----------------------
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
#include "qom/object.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/bitops.h"
|
||||
|
||||
#define TYPE_CLOCK "clock"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Clock, CLOCK)
|
||||
@ -218,6 +220,45 @@ static inline unsigned clock_get_ns(Clock *clk)
|
||||
return CLOCK_PERIOD_TO_NS(clock_get(clk));
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_ticks_to_ns:
|
||||
* @clk: the clock to query
|
||||
* @ticks: number of ticks
|
||||
*
|
||||
* Returns the length of time in nanoseconds for this clock
|
||||
* to tick @ticks times. Because a clock can have a period
|
||||
* which is not a whole number of nanoseconds, it is important
|
||||
* to use this function when calculating things like timer
|
||||
* expiry deadlines, rather than attempting to obtain a "period
|
||||
* in nanoseconds" value and then multiplying that by a number
|
||||
* of ticks.
|
||||
*
|
||||
* The result could in theory be too large to fit in a 64-bit
|
||||
* value if the number of ticks and the clock period are both
|
||||
* large; to avoid overflow the result will be saturated to INT64_MAX
|
||||
* (because this is the largest valid input to the QEMUTimer APIs).
|
||||
* Since INT64_MAX nanoseconds is almost 300 years, anything with
|
||||
* an expiry later than that is in the "will never happen" category
|
||||
* and callers can reasonably not special-case the saturated result.
|
||||
*/
|
||||
static inline uint64_t clock_ticks_to_ns(const Clock *clk, uint64_t ticks)
|
||||
{
|
||||
uint64_t ns_low, ns_high;
|
||||
|
||||
/*
|
||||
* clk->period is the period in units of 2^-32 ns, so
|
||||
* (clk->period * ticks) is the required length of time in those
|
||||
* units, and we can convert to nanoseconds by multiplying by
|
||||
* 2^32, which is the same as shifting the 128-bit multiplication
|
||||
* result right by 32.
|
||||
*/
|
||||
mulu64(&ns_low, &ns_high, clk->period, ticks);
|
||||
if (ns_high & MAKE_64BIT_MASK(31, 33)) {
|
||||
return INT64_MAX;
|
||||
}
|
||||
return ns_low >> 32 | ns_high << 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_is_enabled:
|
||||
* @clk: a clock
|
||||
|
Loading…
Reference in New Issue
Block a user