Qemu omap gp-timer bug
在u-boot中,采用函数udelay来进行延时。这是一个单纯等待的算法。在beagle board的u-boot中,udelay会不断去读OMAP general purpose timer的count值,然后根据general purpose timer的输入时钟来得到已经经过的时间。
在u-boot中,gp timer2的输入时钟为系统时钟,beagle board中为13MHZ。并且时钟经过了256分频。因此,每一秒钟的时间,gp timer2的count的值会增加1*(13MHZ/256)。
对于qemu来说,必须要保证当u-boot去读gptimer寄存器值的时候,返回的count值是正确的。也就是说,必须是实际经过的时间*13MHZ/256。
在qemu中,ticks_per_sec的值为1000000000。函数qemu_get_clock可以得到当前时间的ticks。因此,当u-boot来读gptimer寄存器值的时候,返回的count值应该为:
(qemu_get_clock/ticks_per_sec)*(13MHZ/256)
也就是
(qemu_get_clock*13MHZ)/(ticks_per_sec*256)
见函数omap_gp_timer_read
distance = muldiv64(distance, timer->rate, timer->ticks_per_sec);
这里,timer->ticks_per_sec= ticks_per_sec*256。
可是问题的关键在于,函数muldiv64的原型为
/* compute with 96 bit intermediate result: (a*b)/c */uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
也就是说,参数b和c都是32bit的。而timer->ticks_per_sec已经超过了32bit。导致distance的计算出错,后果是u-boot中的延时函数是不准的。
解决方法:
首先判断一下ticks_per_secis有没有超过32bit,然后根据情况分别计算。
/*if ticks_per_secis bigger than 32bit, we can not use muldiv64 anymore!*/if (timer->ticks_per_sec>0xffffffff){distance = distance/(ticks_per_sec/1000); /*distance ms*/rate = timer->rate >> (timer->pre ? timer->ptv + 1 : 0);distance = muldiv64(distance, rate, 1000);}elsedistance = muldiv64(distance, timer->rate, timer->ticks_per_sec);
现在U-boot中的时间是准确的了。