Commit d0a981b2d50919bea53986f28234ee7402597f7c
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 +7,7 @@ | ||
| 7 | */ | 7 | */ |
| 8 | #include "hw.h" | 8 | #include "hw.h" |
| 9 | #include "qemu-timer.h" | 9 | #include "qemu-timer.h" |
| 10 | - | 10 | +#include "host-utils.h" |
| 11 | 11 | ||
| 12 | struct ptimer_state | 12 | struct ptimer_state |
| 13 | { | 13 | { |
| @@ -78,9 +78,39 @@ uint64_t ptimer_get_count(ptimer_state *s) | @@ -78,9 +78,39 @@ uint64_t ptimer_get_count(ptimer_state *s) | ||
| 78 | } else { | 78 | } else { |
| 79 | uint64_t rem; | 79 | uint64_t rem; |
| 80 | uint64_t div; | 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 | rem = s->next_event - now; | 95 | rem = s->next_event - now; |
| 83 | div = s->period; | 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 | counter = rem / div; | 114 | counter = rem / div; |
| 85 | } | 115 | } |
| 86 | } else { | 116 | } else { |