[U-Boot] Timer implementations

J. William Campbell jwilliamcampbell at comcast.net
Tue Oct 26 23:19:29 CEST 2010


On 10/26/2010 6:33 AM, Reinhard Meyer wrote:
> Dear Wolfgang Denk,
>>> Then the define CONFIG_SYS_HZ should not be in every<board>.h since that
>>> suggests that a board developer has some freedom there...
>> Agreed - there are historical reasons this has ever been changable at
>> all.
>>
>>> and MOST IMPORTANT that some implementations of udelay() might
>>> call reset_timer() and therefore break an outer timeout loop !!!
>> Such implementations are inherently broken and need to be fixed.
> Found such in arm926ejs/omap... But then, that timer is multiple-broken:
> relocation broken (uses static data), returns 32 a bit value in get_ticks(),
> returns CONFIG_SYS_HZ in get_tbclk() instead of the rate get_ticks()
> increments...
>
> PXA:
> void udelay_masked (unsigned long usec)
> {
> 	unsigned long long tmp;
> 	ulong tmo;
>
> 	tmo = us_to_tick(usec);
> 	tmp = get_ticks() + tmo;	/* get current timestamp */
>
> 	while (get_ticks()<  tmp)
> 		/* loop till event _OR FOREVER is tmp happens to be>  32 bit_ */
> 		 /*NOP*/;
>
> }
>
> unsigned long long get_ticks(void)
> {
> 	return readl(OSCR);
> }
> - not any better :( -- its the same code that AT91 had before I fixed it.
>
>>> It is also open if reset_timer() does actually reset the hardware timer
>>> (e.g. tbu/tbl at PPC) - which would be messing up any time difference
>>> calculation using get_ticks() - or does emulate that by remembering
>>> the hardware value and subtracting it later in every subsequent
>>> get_timer() call?
>> This is an implementation detail.
> IF we require that get_ticks() and get_timer() shall not interfere with
> each other and IF both are based on the same hardware timer only the
> second method is available (same if the hardware timer is not easyly
> resettable).
>
>>> 2. get_ticks() and friends operate at a higher rate (tbu/tbl for PPC).
>>> Since they are defined as having 64 bits they MUST not wrap at 32 bits,
>>> i.e. if the hardware provides only 32 bits, the upper 32 bits must be
>>> emulated by software.
>> Right.
>>
>>> Otherwise we have to document that get_ticks() cannot be used to get
>>> 64 bit time differences.
>> No. Such an implementation is broken and needs fixing.
> Original AT91 timer.c was like that, and I think other ARMs where this was
> copied around should be looked at... I don't know when get_timer() became
> 64 bits, but it seems that some implementations just did change the return
> type: uint64 get_timer(void) {return (uint64)timer_val_32;}
Hi All,

      I am pretty sure the migration to 64 bits was caused by 1) people 
not understanding that the timer operating on time DIFFERENCES would 
work fine even if the underlying timer wrapped around (most probable 
problem) and possibly 2) broken timer functions causing bogus timeouts, 
improperly "fixed" by switching to 64 bits.

I think u-boot could get along just fine with only 2 time related 
functions,  uint_32 get_timer(uint_32 base) and udelay(uint 32 delay).  
udelay will only work on "small" values of delay, on the order of 
milliseconds. It is to be used when a "short" but "precise" delay in 
microsecond resolution is required.  Users of get_timer must understand 
that it is only valid if it is called "often enough", i.e. at least once 
per period of the underlying timer. This is required because u-boot  
does not want to rely on interrupts as a timer update method. Therefore, 
all uses of get_timer must 1) call it once initially to get a start 
value, and 2) call get_timer at least once per period  of the underlying 
hardware counter. This underlying period is guaranteed to be at least 
4.29 seconds (32 bit counter at 4 GHz). Note that this does NOT mean 
that the total wait must be less than 4.29 seconds, only that the rate 
at which the elapsed time is TESTED must be adequate.

In order to implement this functionality, at least one hardware timer of 
some kind is required. An additional software "timer" in 1 ms resolution 
may be useful in maintaining the software time. If the hardware timer 
update rate is programmable, u-boot MAY set the update rate on 
initialization On initialization, u-boot MAY reset the hardware timer 
and MAY reset any associated software timer. The hardware timer MAY be 
started on initialization. On each call to get_timer(), u-boot MUST 
start the hardware timer if it was not started already. On calls to 
get_timer, u-boot MUST NOT reset the hardware timer if it was already 
started. The software timer MAY be reset if u-boot can unambiguously 
determine that  more than 4.29 seconds has elapsed since the last call 
to get_timer.

The simplest case for implementing this scheme is if two programmable 
timers exist that can be set to 1ms and 1us.  The timers are initialized 
at start-up, get_timer just returns the 32 bit 1 ms timer and udelay 
just waits for the number of ticks required on the second timer to 
elapse. The most common harder case is where there is only one timer 
available, it is running at 1 us per tick or faster, and we cannot 
control the rate. udelay is still easy, because we can convert the 
(small) delay in us to a delay in ticks by a 32 bit multiply that will 
not overflow 32 bits even if we have quite a few fractional bits in the 
tics per microsecond value. The elapsed ticks required is the (delay in 
us * us/per tick) >>  # fractional bits in us/per tick. If that is not 
close enough for you, you can do it as (delay in us * (integer part of 
us/tick)) + ((delay in us * (fractional part)us/tick) >> # fraction 
bits). For "nice" numbers, like any integral number of MHz, there is no 
fractional part. Only numbers like 66 MHz, or 1.666 GHz require messing 
with the fractional part.
For get_timer, it is a bit harder. The program must keep two 32 bit 
global variables, the timer reading "last time" and the software timer 
in 1 ms resolution. Whenever get_timer is called, it must increase the 
software timer by the number of ms that have elapsed since the previous 
update and record the corresponding timer reading as the new "last 
time". Note that if the number of ms elapsed is not an integer (a common 
case), the value recorded as the "last time" must be decreased by the 
number of ticks not included in the 1 ms resolution software timer.  
There are many different ways to accomplish update, depending on what 
hardware math capabilities are available, and whether one thinks 
efficiency is important here. Conceptually, you convert the elapsed time 
in ticks into an equivalent number of ms, add that number to the 
software timer, store the current value of the hardware timer in last 
time, and subtract any "remainder" ticks from that value.  If the 
elapsed time is less that one ms, do no update of "last hardware time" 
and return the current software counter. If the elapsed time is greater 
than 4.29 seconds, reset the software counter to 0, record the current 
hardware counter time and return the current software counter. In 
between, do the math, which will fit into 32 bits.

If this idea seems like a good one, I can provide more detail on the 
conversions for various hardware capabilities is people want. Comments 
welcome.

Best Regards,

Bill Campbell
>>> If you really closely look at the various implementations of those
>>> timers, you will easyly see the wide variations implemented there.
>> Yes, I am aware of this :-(
> I will start beautifying the AT91 timer - its already quite there,
> except for a possible timer wrap problem in udelay() after a few billion
> years :)
>
> Best Regards,
> Reinhard
>
>



More information about the U-Boot mailing list