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 > 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 les 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: 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 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) 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 g) get_ticks() MUST return true 64 bit time 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. --> 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. Best Regards, Reinhard _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot