Dear J. William Campbell,
> On 5/22/2011 1:15 AM, Reinhard Meyer wrote:
>> Dear J. William Campbell,
>>
>> please demonstrate for me (and others), by a practical example,
>> how _any_ arithmetic (even less with just shifts and multiplies)
>> can convert a free running 3.576 MHz (wild example) free running
>> 32 bit counter (maybe software extended to 64 bits) into a ms
>> value that will properly wrap from 2**32-1 to 0 ?
> Hi All
> I accept the challenge! I will present two ways to do this, one using a 32 
> bit by 16 bit divide, and one using only multiplies.
> This first method is "exact", in that there is no difference in performance 
> from a "hardware" counter ticking at the 1 ms rate. This is accomplished by 
> operating the 1 ms counter based on the delta time in the hardware time base. 
> It is necessary to call this routine often enough that the hardware counter 
> does not wrap more than once between calls. This is not really a problem, as 
> this time is 1201 seconds or so. If the routine is not called for a long 
> time, or at the first call, it will return a timer_in_ms value that will work 
> for all subsequent calls that are within a hardware rollover interval. Since 
> the timer in ms is a 32 bit number anyway. The same rollover issue will exist 
> if you "software extend" the timer to 64 bits. You must assume 1 rollover. If 
> it is more than 1, the timer is wrong.
>
>
> The variables in the gd are
> u32 prev_timer;
> u32 timer_in_ms;
> u16 timer_remainder;
>
> /* gd->timer remainder must be initialized to 0 (actually, an number less 
> than 3576, but 0 is nice). Other two variables don't matter but can be 
> initialized if desired */
>
> u32 get_raw_ms()
> {
> u32 delta;
> u32 t_save;
>
> read(t_save); /* atomic read of the hardware 32 bit timer running at 3.576 
> MHz */
> delta_t = (t_save - gd->prev_timer) ;
>
> gd->prev_timer = t_save;
> /*
> Hopefully, the following two lines only results in one hardware divide when 
> optimized. If your CPU has no hardware divide, or if it slow, see second 
> method .
> */
> gd->timer_in_ms += delta_t / 3576; /* convert elapsed time to ms */
> gd->timer_remainder += delta_t % 3576; /* add in remaining part not included 
> above */
> if (gd->timer_remainder >= 3576) /* a carry has been detected */
> {
> ++gd->timer_in_ms;
> gd->timer_remainder -= 3576; /* fix remainder for the carry above */
> }
>
> return(gd->timer_in_ms)
> }

Thank you! Basically this is similar to a Bresenham Algorithm.

>
> This approach works well when the number of ticks per ms is an exact number 
> representable as a small integer, as it is in this case. It is exact with a 
> clock rate of 600 MHz, but is not exact for a clock rate of 666 MHz. 666667 
> is not an exact estimate of ticks per ms, It is off by 0.00005 % That should 
> be acceptable for use as a timeout delay. The accumulated error in a 10 
> second delay should be less than 0.5 ms.

I would think the non exact cases result in such a small error that can be
tolerated. We are using the ms tick for timeouts, not for providing a clock
or exact delays. We should just round up when calculating the divider.

Hence the hick-ups that result when this is not called frequent enough to
prevent a multiple rollover of the raw value between calls do not matter
either (they should be just documented).

>
> There is a way that the divide above can be approximated by multiplying by an 
> appropriate fraction, taking the resulting delta t in ms, multiplying it by 
> 3576, and subtracting the product from the original delta to get the 
> remainder. This is the way to go if your CPU divides slowly or not at all. 
> This approach is presented below.
>
[...]

Optimizations would be up to the implementer of such a hardware and work
only if the divider is a compile time constant. Often the divider will be
run time determined (AT91 for example).

Reinhard
_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to