On 01.11.2010 14:47, J. William Campbell wrote:
> On 10/27/2010 11:02 PM, Reinhard Meyer wrote:
>> Dear J. William Campbell,
>>> 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 fraction
al
>>
>>> 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 l
es
>> s
>>> 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.
>>
>> To get the timer mess cleaned up three things have to happen:
>>
> Hi All,
>
> I am glad somebody was still interested. I was afraid I had scared everyone 
> off.
>> 1. A consensus and documentation how it MUST be handled
>>
>> 2. Fix all timer implementations to adhere to that
>>
>> 3. Fix all timer uses to adhere to that
> I agree this is required.
>>
>> To start, RFC:
>>
>> a) udelay(u32) MUST NOT interfere with other timer functions
>> b) u32 get_timer(u32 base) MUST return (current_time - base) using
>> millisecond units
>> c) get_timer() MUST exhaust the full 32 bit range before wrapping
>> d) to ensure the function of internal high frequency wrapping
>> processes, get_timer() MUST be called at least once a second while
>> a timeout loop is run (this will typically be the case anyway)
> I have no problem with this, but it might be stricter than needed. I cant 
> imagine anybody has a timer running faster than 4 GHz, and that allows 4 
> seconds of interval.
4 GHz / 2**32 yields 1 second. Assuming no prescaler...
just theoretical anyway. A polling loop with timeout that does poll that seldom
is illogical anyway.
>> e) reset_timer() is not needed, potentially harmful if used and
>> shall be removed. This will also allow for nested timeouts, e.g.
>> inner timeout for accessing a hardware and outer timeout for having
>> the hardware perform a function
>> f) get_ticks() and get_tbclk() SHALL NOT be used to check for timeouts
>> except for cases where the better than ms resolution is really needed
> I have an issue here. I think no such use cases really exist. For things like 
> bit-banging, udelay is more what one wants. What are the counter-examples 
> where sub-millisecond resolution in a loop waiting for some expected event is 
> required?
Well, the "really needed" clause says it. I don't see it either.
>> g) get_ticks() MUST return true 64 bit time
> I hope get_ticks must not exist, at least as a "generic" globally visible 
> u-boot function. Having it present spawns confusion. People will use it 
> instead of the generic get_timer when they don't have to, creating the 
> portability problems we are trying to get rid of.
I also agree it would be best to get rid of externally available get_ticks()
and friends.
>> h) all uses of get_ticks() and get_timer() shall be fixed to use
>> get_timer() as follows:
>>
>> u32 start_ms;
>>
>> start_ms = get_timer(0);
>>
>> any_type_of_loop {
>> ...
>> /* check for timeout */
>> if (get_timer(start_ms) >= TIMEOUT_IN_MS)
>> /* handle timeout */
>> ...
>> }
>> Alternative, but in contrast to current get_timer() implementation
>> is to drop the get_timer parameter and do the subtraction in the if.
> No problem here.
How much code uses get_ticks, or get_timer, or just uses a counted
loop of small udelays? I have seen all variants but never made stats
of that.
We probably should leave get_timer() as it is.
>>
>> --> get_timer(base) is a bit of a misnomer, it should be better named
>> something like get_ms_since(base).
>>
>> _Observation:_
>>
>> It seems possible to move udelay() and get_timer() and static helper
>> functions into a common, ARCH and SoC independent file, provided that
>> the ARCH/SoC/board dependant implementations of get_ticks() runs
>> at >= 1 MHz and is true 64 bits. IF above d) is met get_ticks might be
>> replaced by a u32 function to be used by the common udelay() and
>> get_timer() implementation. That would allow all timer code to use 32
>> bit arithmetic only.
> I see no down-side in requiring d) to be true. It is clearly no problem for 
> udelay, as the caller gives up control anyway. I can't imagine a wait for 
> event loop that takes a second to execute.
> It may be that "common" C code is not really a good idea, in that different 
> CPUs may want to convert timer ticks to millisec/microsec in different ways, 
> based on clock rates and available CPU capabilities (like having or not 
> having a 32 bit divide, needing to deal with clock rates with repeating 
> fractional parts etc.). If ticks per microsecond is an integer and a 32 bit 
> divide is available, the code is trivial. If we must avoid the divide and/or 
> the ticks per microsecond is not an integer, things get somewhat messier but 
> not impossible. If we don't care about the divide taking a bit of time, 
> generic code is pretty easy to write even allowing for fractional ticks per 
> nicrosecond. Usually, the high-rate timer can be read without requiring a 
> call to another routine. This might be a good idea in that people will not 
> then "grow" another timer structure on top of the read timer routine!
> It is for sure true that if we are worried about efficiency, there are at 
> most three or four versions of the ticks to microseconds or milliseconds code 
> required for all the different cases, so it is not so bad to write them out 
> and let the users decide which one fits their cpu.
For efficiency it would help if CONFIG_SYS_HZ would be allowed to be in the 
range
of 1000 - 2000 Hz (or 700 - 1400), so on any hardware a simple shift would do 
for
get_timer(). Of course the constant CONFIG_SYS_HZ should be replaced by a 
function
like get_timer_hz() to allow for runtime calculation of the best fit.

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

Reply via email to