mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-09-06 18:05:37 +00:00
Avoid rounding problems in ptimer_get_count
Signed-off-by: Paul Brook <paul@codesourcery.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6961 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
bbeea539aa
commit
d0a981b2d5
32
hw/ptimer.c
32
hw/ptimer.c
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
#include "hw.h"
|
||||
#include "qemu-timer.h"
|
||||
|
||||
#include "host-utils.h"
|
||||
|
||||
struct ptimer_state
|
||||
{
|
||||
|
@ -78,9 +78,39 @@ uint64_t ptimer_get_count(ptimer_state *s)
|
|||
} else {
|
||||
uint64_t rem;
|
||||
uint64_t div;
|
||||
uint32_t frac;
|
||||
int clz1, clz2;
|
||||
int shift;
|
||||
|
||||
/* We need to divide time by period, where time is stored in
|
||||
rem (64-bit integer) and period is stored in period/period_frac
|
||||
(64.32 fixed point).
|
||||
|
||||
Doing full precision division is hard, so scale values and
|
||||
do a 64-bit division. The result should be rounded down,
|
||||
so that the rounding error never causes the timer to go
|
||||
backwards.
|
||||
*/
|
||||
|
||||
rem = s->next_event - now;
|
||||
div = s->period;
|
||||
|
||||
clz1 = clz64(rem);
|
||||
clz2 = clz64(div);
|
||||
shift = clz1 < clz2 ? clz1 : clz2;
|
||||
|
||||
rem <<= shift;
|
||||
div <<= shift;
|
||||
if (shift >= 32) {
|
||||
div |= ((uint64_t)s->period_frac << (shift - 32));
|
||||
} else {
|
||||
if (shift != 0)
|
||||
div |= (s->period_frac >> (32 - shift));
|
||||
/* Look at remaining bits of period_frac and round div up if
|
||||
necessary. */
|
||||
if ((uint32_t)(s->period_frac << shift))
|
||||
div += 1;
|
||||
}
|
||||
counter = rem / div;
|
||||
}
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue