Qemu omap gp-timer bug

Nov 28th, 2008 | Posted by yajin | Filed under OMAP, emulation

在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);
 }
else
  distance = muldiv64(distance, timer->rate, timer->ticks_per_sec);

现在U-boot中的时间是准确的了。

Tags:
No comments yet.