Commit d0a981b2d50919bea53986f28234ee7402597f7c

Authored by pbrook
1 parent bbeea539

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
Showing 1 changed file with 31 additions and 1 deletions
hw/ptimer.c
... ... @@ -7,7 +7,7 @@
7 7 */
8 8 #include "hw.h"
9 9 #include "qemu-timer.h"
10   -
  10 +#include "host-utils.h"
11 11  
12 12 struct ptimer_state
13 13 {
... ... @@ -78,9 +78,39 @@ uint64_t ptimer_get_count(ptimer_state *s)
78 78 } else {
79 79 uint64_t rem;
80 80 uint64_t div;
  81 + uint32_t frac;
  82 + int clz1, clz2;
  83 + int shift;
  84 +
  85 + /* We need to divide time by period, where time is stored in
  86 + rem (64-bit integer) and period is stored in period/period_frac
  87 + (64.32 fixed point).
  88 +
  89 + Doing full precision division is hard, so scale values and
  90 + do a 64-bit division. The result should be rounded down,
  91 + so that the rounding error never causes the timer to go
  92 + backwards.
  93 + */
81 94  
82 95 rem = s->next_event - now;
83 96 div = s->period;
  97 +
  98 + clz1 = clz64(rem);
  99 + clz2 = clz64(div);
  100 + shift = clz1 < clz2 ? clz1 : clz2;
  101 +
  102 + rem <<= shift;
  103 + div <<= shift;
  104 + if (shift >= 32) {
  105 + div |= ((uint64_t)s->period_frac << (shift - 32));
  106 + } else {
  107 + if (shift != 0)
  108 + div |= (s->period_frac >> (32 - shift));
  109 + /* Look at remaining bits of period_frac and round div up if
  110 + necessary. */
  111 + if ((uint32_t)(s->period_frac << shift))
  112 + div += 1;
  113 + }
84 114 counter = rem / div;
85 115 }
86 116 } else {
... ...