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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user