[U-Boot] [RFC] Review of U-Boot timer API

Graeme Russ
2011-05-23T01:00:35Z

Hi Bill,

On Mon, May 23, 2011 at 10:14 AM, J. William Campbell
<jwilliamcampbell@comcast.net> wrote:
> 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

[Snip first method]

> 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.
>
> the vaues in gd are as follows:
>
> u32 prev_timer;
> u32 timer_in_ms;
>
> /*
> ? ?One tick of the 3.576 MHz timer corresponds to 1/3.576/1000 ms,
> ? ?or 0.000279642 ms. Scale the fraction by 65536 (16 bit shift),
> ? ?you get 37532.9217
> */
> u32 get_raw_ms(void)
> {
> ?u32 ?t_save;
> ?u32 ?temp;
>
> ?/* read the hardware 32 bit timer running at 3.576 MHz */
> ?read_timer_atomic(t_save);
> ?t_save ? ? ? ? -= gd->prev_timer; /* get "delta time" since last call */
> ?gd->prev_timer += t_save; /* assume we will use all of the counts */
>
> ?/*
> ? * This first while loop is entered for any delta time > about 18.3 ms. The
> ? * while loop will execute twice 2.734% of the time, otherwise once.
> ? */
> ?while (t_save > 65535)
> ?{
> ? ?temp = t_save >> 16; ? /* extract msb */
> ? ?temp ?= ((temp * 37532) + ((temp * 60404) >> 16)) >> 11;

Where does 60404 come from?

> ? ?/* temp ?= (temp * 37532) >> 11; */
> ? ?gd->timer_in_ms += temp;
> ? ?t_save ? ? ? ? ?-= temp * 3576;
> ?}
> ?/*
> ? * This second while loop is entered for 94.837% of all possible delta
> times,
> ? * 0 through 0XFFFFFFFF. The second while loop will execute twice 0.037% of
> ? * the time, otherwise once.
> ? */
> ?while (t_save >= 3576)
> ?{
> ? ?temp ?= (t_save * 37532) >> (16 + 11);
> ? ?if (temp == 0)
> ? ? ?temp = 1; ? ? /* we know that 1 works for sure */
> ? ?gd->timer_in_ms += temp;
> ? ?t_save ? ? ? ? ?-= temp * 3576;
> ?}
> ?gd->prev_timer -= t_save; /* restore any counts we didn't use this time */
> ?return gd->timer_in_ms;
> }
>
> I have tested this code and it seems to work fine for me. I have attached a
> more readable copy as "example .c" for those who wish to play around with
> it. In my original post, I had a version of this code that could be used
> with different clock rates. I can provide the same functionality with this
> code, for CPUs/systems where the clock rate is unknown at compile time or is
> variable. I can also address the error encountered by non-integer ticks per
> millisecond, if people really think the error is enough to matter, but I
> won't post it unless people want to see it.

Provided the hardware counter has high enough resolution (in this case an
extra 12 bits meaning the counter must be at least 44 bits) I assume this
methog guarantees full 32-bit resolution with correct roll-over, but if
the hardware counter is only 32-bit we run into problems. And does this
solution work for, say, the entire range of a 64-bit hardware counter
including when it wraps?

Questions:
- If the hardware supports a native 64-bit counter, could we have a
u64 get_raw_ticks64() HAL API and 'libify' the above function using
either a 'tick rate' in gd or a get_tick_rate() HAL function
- Can we reduce the HAL to u32 get_raw_ticks() and make some assumptions
about how often get_raw_ms() is called (maybe in the main loop for
example - We already call the watchdog periodically anyway)

Regards,

Graeme