public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
From: J. William Campbell <jwilliamcampbell@comcast.net>
To: u-boot@lists.denx.de
Subject: [U-Boot] [RFC] Review of U-Boot timer API
Date: Sat, 21 May 2011 23:57:49 -0700	[thread overview]
Message-ID: <4DD8B3ED.4000408@comcast.net> (raw)
In-Reply-To: <4DD8908E.7040501@emk-elektronik.de>

On 5/21/2011 9:26 PM, Reinhard Meyer wrote:
> Dear Graeme Russ,
>> Hi All,
>>
>> I've just had a good look through the timer API code with the view of
>> rationalising it and fixing up some of the inconsistencies. What I found
>> was a little scarier than I expected! Anyway, here is a write-up of what I
>> found - Please comment
> We have been at this discussion a multiple of times :) but never reached a consent.
>
> However, at current master, I have reduced at91 architecture to only use
> get_timer(base), set_timer() never existed and reset_timer() has been removed.
>
> As it showed up recently, common cfi code still calls reset_timer() - which certainly
> can be fixed with little effort...
>
>> At the lowest level, the U-Boot timer API consists of a unsigned 32-bit
>> free running timestamp which increments every millisecond (wraps around
>> every 4294967 seconds, or about every 49.7 days). The U-Boot timer API
>> allows:
>>    - Time deltas to be measured between arbitrary code execution points
>> 	ulong start_time;
>> 	ulong elapsed_time;
>>
>> 	start_time = get_timer(0);
>> 	...
>> 	elapsed_time = get_timer(start_time);
>>
>>    - Repetition of code for a specified duration
>> 	ulong start_time;
>>
>> 	start_time = get_timer(0);
>>
>> 	while (get_timer(start_time)<   REPEAT_TIME) {
>> 		...
>> 	}
>>
>>    - Device timeout detection
>> 	ulong start_time;
>>
>> 	send_command_to_device();
>> 	start = get_timer (0);
>> 	while (device_is_busy()) {
>> 		if (get_timer(start)>   TIMEOUT)
>> 			return ERR_TIMOUT;
>> 		udelay(1);
>> 	}
>> 	return ERR_OK;
> correct.
>
>> The U-Boot timer API is not a 'callback' API and cannot 'trigger' a
>> function call after a pre-determined time.
> that would be too complex to implement and of little use in a single task
> system. u-boot can do fine with polling.
>
>> NOTE: http://lists.denx.de/pipermail/u-boot/2010-June/073024.html appears
>> to imply the following implementation of get_timer() is wrong:
>>
>> 	ulong get_timer(ulong base)
>> 	{
>> 		return get_timer_masked() - base;
>> 	}
> Is is not wrong as long as get_timer_masked() returns the full 32 bit space
> of numbers and 0xffffffff is followed by 0x00000000. Most implementations
> probably do NOT have this property.
>
>> U-Boot Timer API Details
>> ========================
>> There are currently three functions in the U-Boot timer API:
>> 	ulong get_timer(ulong start_time)
> As you point out in the following, this is the only function required.
> However it REQUIRES that the internal timer value must exploit the full
> 32 bit range of 0x00000000 to 0xffffffff before it wraps back to 0x00000000.
>
>> 	void set_timer(ulong preset)
>> 	void reset_timer(void)
>>
>> get_timer() returns the number of milliseconds since 'start_time'. If
>> 'start_time' is zero, therefore, it returns the current value of the
>> free running counter which can be used as a reference for subsequent
>> timing measurements.
>>
>> set_timer() sets the free running counter to the value of 'preset'
>> reset_timer() sets the free running counter to the value of zero[1]. In
>> theory, the following examples are all identical
>>
>> 	----------------------------------------------------
>> 	ulong start_time;
>> 	ulong elapsed_time;
>>
>> 	start_time = get_timer(0);
>> 	...
>> 	elapsed_time = get_timer(start_time);
>> 	----------------------------------------------------
>> 	ulong elapsed_time;
>>
>> 	reset_timer();
>> 	...
>> 	elapsed_time = get_timer(0);
>> 	----------------------------------------------------
>> 	ulong elapsed_time;
>>
>> 	set_timer(0);
>> 	...
>> 	elapsed_time = get_timer(0);
>> 	----------------------------------------------------
>>
>> [1] arch/arm/cpu/arm926ejs/at91/ and arch/arm/cpu/arm926ejs/davinci/ are
>> exceptions, they set the free running counter to get_ticks() instead
> Not anymore on at91.
>> Architecture Specific Peculiarities
>> ===================================
>> ARM
>>    - Generally define get_timer_masked() and reset_timer_masked()
>>    - [get,reset]_timer_masked() are exposed outside arch\arm which is a bad
>>      idea as no other arches define these functions - build breakages are
>>      possible although the external files are most likely ARM specific (for
>>      now!)
>>    - Most CPUs define their own versions of the API get/set functions which
>>      are wrappers to the _masked equivalents. These all tend to be the same.
>>      The API functions could be moved into arch/arm/lib and made weak for
>>      the rare occasions where they need to be different
>>    - Implementations generally look sane[2] except for the following:
>>      - arm_intcm - No timer code (unused CPU arch?)
>>      - arm1136/mx35 - set_timer() is a NOP
>>      - arm926ejs/at91 - reset_timer() sets counter to get_ticks()
>>                         no implelemtation of set_timer()
> See current master for actual implementation!
>>      - arm926ejs/davinci - reset_timer() sets counter to get_ticks()
>>                            no implelemtation of set_timer()
>>      - arm926ejs/mb86r0x - No implementation of set_timer()
>>      - arm926ejs/nomadik - No implementation of set_timer()
>>      - arm946es - No timer code (unused CPU arch?)
>>      - ixp - No implementation of set_timer()
>>            - If CONFIG_TIMER_IRQ is defined, timer is incremented by an
>>              interrupt routine - reset_timer() writes directly to the
>>              counter without interrupts disables - Could cause corruption
>>              if reset_timer() is not atomic
>>            - If CONFIG_TIMER_IRQ is not defined, reset_timer() appears to
>>              not be implemented
>>      - pxa - set_timer() is a NOP
>>      - sa1100 - get_timer() does not subtract the argument
>>
>> nios, powerpc, sparc, x86
>>    - All look sane[2]
>>
>> avr32
>>    - Not obvious, but looks sane[2]
>>
>> blackfin
>>    - Does not implement set_timer()
>>    - Provides a 64-bit get_ticks() which simply returns 32-bit get_timer(0)
>>    - reset_timer() calls timer_init()
>>    - Looks sane[2]
>>
>> m68k
>>    - Looks sane[2] if CONFIG_MCFTMR et al are defined. If CONFIG_MCFPIT is
>>      defined instead, reset_timer() is unimplemented and build breakage will
>>      result if cfi driver is included in the build - No configurations use
>>      this define, so that code is dead anyway
>>
>> microblaze
>>    - Only sane[2] if CONFIG_SYS_INTC_0 and CONFIG_SYS_TIMER_0 are both defined.
>>      Doing so enables a timer interrupt which increments the internal
>>      counter. Otherwise, it is incremented when get_timer() is called which
>>      will lead to horrible (i.e. none at all) accuracy
>>
>> mips
>>    - Not obvious, but looks sane[2]
>>
>> sh
>>    - Generally looks sane[2]
>>    - Writes 0xffff to the CMCOR_0 control register when resetting to zero,
>>      but writes the actual 'set' value when not zero
>>    - Uses a 32-bit microsecond based timebase:
>> 	static unsigned long get_usec (void)
>> 	{
>> 	...
>> 	}
>>
>> 	get_timer(ulong base)
>> 	{
>> 		return (get_usec() / 1000) - base;
>> 	}
>>
>>    - Timer will wrap after ~4295 seconds (71 minutes)
>>
>> [2] Sane means something close to:
>> 	void reset_timer (void)
>> 	{
>> 		timestamp = 0;
>> 	}
>>
>> 	ulong get_timer(ulong base)
>> 	{
>> 		return timestamp - base;
>> 	}
>>
>> 	void set_timer(ulong t)
>> 	{
>> 		timestamp = t;
>> 	}
>>
>> Rationalising the API
>> =====================
>> Realistically, the value of the free running timer at the start of a timing
>> operation is irrelevant (even if the counter wraps during the timed period).
>> Moreover, using reset_timer() and set_timer() makes nested usage of the
>> timer API impossible. So in theory, the entire API could be reduced to
>> simply get_timer()
> Full ACK here !!!
>> 0. Fix arch/arm/cpu/sa1100/timer.c:get_timer()
>> ----------------------------------------------
>> This appears to be the only get_timer() which does not subtract the
>> argument from timestamp
>>
>> 1. Remove set_timer()
>> ---------------------
>> regex "[^e]set_timer\s*\([^)]*\);" reveals 14 call sites for set_timer()
>> and all except arch/sh/lib/time_sh2:[timer_init(),reset_timer()] are
>> set_timer(0). The code in arch/sh is trivial to resolve in order to
>> remove set_timer()
>>
>> 2. Remove reset_timer()
>> -----------------------------------------------
>> regex "[\t ]*reset_timer\s*\([^)]*\);" reveals 22 callers across the
>> following groups:
>>    - timer_init() - Make reset_timer() static or fold into timer_init()
>>    - board_init_r() - Unnecessary
>>    - arch/m68k/lib/time.c:wait_ticks() - convert[3]
>>    - Board specific flash drivers - convert[3]
>>    - drivers/block/mg_disk.c:mg_wait() - Unnecessary
>>    - drivers/mtd/cfi_flash.c:flash_status_check() - Unnecessary
>>    - drivers/mtd/cfi_flash.c:flash_status_poll() - Unnecessary
>>
>> [3] These instance can be trivially converted to use one of the three
>> examples at the beginning of this document
>>
>> 3. Remove reset_timer_masked()
>> ------------------------------
>> This is only implemented in arm but has propagated outside arch/arm and
>> into board/ and drivers/ (bad!)
>>
>> regex "[\t ]*reset_timer_masked\s*\([^)]*\);" reveals 135 callers!
>>
>> A lot are in timer_init() and reset_timer(), but the list includes:
>>    - arch/arm/cpu/arm920t/at91rm9200/spi.c:AT91F_SpiWrite()
>>    - arch/arm/cpu/arm926ejs/omap/timer.c:__udelay()
>>    - arch/arm/cpu/arm926ejs/versatile/timer.c:__udelay()
>>    - arch/arm/armv7/s5p-common/timer.c:__udelay()
>>    - arch/arm/lh7a40x/timer.c:__udelay()
>>    - A whole bunch of board specific flash drivers
>>    - board/mx1ads/syncflash.c:flash_erase()
>>    - board/trab/cmd_trab.c:do_burn_in()
>>    - board/trab/cmd_trab.c:led_blink()
>>    - board/trab/cmd_trab.c:do_temp_log()
>>    - drivers/mtd/spi/eeprom_m95xxx.c:spi_write()
>>    - drivers/net/netarm_eth.c:na_mii_poll_busy()
>>    - drivers/net/netarm_eth.c:reset_eth()
>>    - drivers/spi/atmel_dataflash_spi.c/AT91F_SpiWrite()
> Fixed in current master.
>> Most of these instance can be converted to use one of the three examples
>> at the beginning of this document, but __udelay() will need closer
>> examination
>>
>> This is preventing nesting of timed code - Any code which uses udelay()
>> has the potential to foul up outer-loop timers. The problem is somewhat
>> unique to ARM though
>>
>> 4. Implement microsecond API - get_usec_timer()
>> -----------------------------------------------
>>    - Useful for profiling
>>    - A 32-bit microsecond counter wraps in 71 minutes - Probably OK for most
>>      U-Boot usage scenarios
>>    - By default could return get_timer() * 1000 if hardware does not support
>>      microsecond accuracy - Beware of results>   32 bits!
>>    - If hardware supports microsecond resolution counters, get_timer() could
>>      simply use get_usec_timer() / 1000
> That is wrong. Dividing 32 bits by any number will result in a result that does not
> exploit the full 32 bit range, i.e. wrap from (0xffffffff/1000) to 0x00000000,
> which makes time differences go wrong when they span across such a wrap!
Hi All,
       True, as previously discussed.
>>    - get_usec_timer_64() could offer a longer period (584942 years!)
> Correct. And a "must be" when one uses such division.
>
> But you have to realize that most hardware does not provide a simple means to
> implement a timer that runs in either exact microseconds or exact milliseconds.
Also True.
> Powerpc for example has a 64 bit free running hardware counter at CPU clock,
> which can be in the GHz range, making the lower 32 bits overflow within seconds,
> so the full 64 bits MUST BE used to obtain a millisecond timer by division.
       This statement, as written, is false. While it is true that the 
power PC (and others) have a 64 bit counter that runs at a many 
megahertz rate, it is NOT true that all 64 bits must be used to obtain a 
millisecond timer. A millisecond timer can be produced by using the 33 
bits of the 64 bit counter whose lsb is <= 1 ms and > 0.5 ms. This value 
can be extracted by a right shift and masking of the 64 bit counter. Two 
32 bit multiplies and adds will produce a correct monotonic 1 ms timer. 
This is certainly the best way to go on systems that do not have a 
hardware 64 bit by 32 bit divide. See the thread "ARM timing code 
refactoring" for an example of how this can be done. This timer will 
NEVER result in a wrong value, although it does loose  1 ms every 1193 
hours.
> arm/at91 has a timer that can be made to appear like a 32 bit free running counter
> at some fraction of cpu clock (can be brought into a few MHz value by a prescaler)
> and the current implementation extends this to 64 bits by software, so it is
> similar to powerpc.
>
> A get timer() simply uses this 64 bit value by dividing it by (tickrate/1000).
Or as above on the PPC
> Of course this results in a wrong wrap "gigaseconds" after the timer has been started,
> but certainly this can be ignored...
FWIW, this does not happen with the shift and multiply approach.
> On any account, I see only the following two functions to be implemented for use by
> other u-boot code parts:
>
> 1. void udelay(u32 microseconds) with the following properties:
> - must not affect get_timer() results
> - must not delay less than the given time, may delay longer
> (this might be true especially for very small delay values)
> - shall not be used for delays in the seconds range and longer
> (or any other limit we see practical)
> Note: a loop doing "n" udelays of "m" microseconds might take _much_ longer than
> "n * m" microseconds and therefore is the wrong approach to implement a timeout.
> get_timer() must be used for any such timeouts instead!
>
> 2. u32 get_timer(u32 base) with the following properties:
> - must return the elapsed milliseconds since base
> - must work without wrapping problems for at least several hours
>
> Best Regards,
> Reinhard
I think this is a correct appreciation of what is needed.

Best Regards.
Bill Campbell
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
>
>

  parent reply	other threads:[~2011-05-22  6:57 UTC|newest]

Thread overview: 101+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-05-21 12:38 [U-Boot] [RFC] Review of U-Boot timer API Graeme Russ
     [not found] ` <4DD7DB64.70605@comcast.net>
2011-05-22  0:06   ` Graeme Russ
2011-05-22  0:43     ` J. William Campbell
2011-05-22  4:26 ` Reinhard Meyer
2011-05-22  6:23   ` Graeme Russ
2011-05-22  7:21     ` J. William Campbell
2011-05-22  7:44       ` Graeme Russ
2011-05-22  8:15       ` Reinhard Meyer
2011-05-23  0:02         ` Graeme Russ
2011-05-23  0:20           ` J. William Campbell
2011-05-23  0:14         ` J. William Campbell
2011-05-23  1:00           ` Graeme Russ
     [not found]             ` <4DD9B608.7080307@comcast.net>
2011-05-23  1:42               ` Graeme Russ
2011-05-23  5:02                 ` J. William Campbell
2011-05-23  5:25                   ` Graeme Russ
2011-05-23  6:29                     ` Albert ARIBAUD
2011-05-23 10:53                       ` Graeme Russ
2011-05-23 16:22                       ` J. William Campbell
2011-05-23 12:09                 ` Wolfgang Denk
2011-05-23 12:29                   ` Graeme Russ
2011-05-23 13:19                     ` Wolfgang Denk
2011-05-23 17:30                       ` J. William Campbell
2011-05-23 18:24                         ` Albert ARIBAUD
2011-05-23 19:18                         ` Wolfgang Denk
2011-05-23 18:27                       ` J. William Campbell
2011-05-23 19:33                         ` Wolfgang Denk
2011-05-23 20:26                           ` J. William Campbell
2011-05-23 21:51                             ` Wolfgang Denk
2011-05-23 20:48                       ` Graeme Russ
2011-05-23  3:26           ` Reinhard Meyer
2011-05-23  5:20             ` J. William Campbell
2011-05-22  6:57   ` J. William Campbell [this message]
2011-05-23 12:13     ` Wolfgang Denk
2011-05-24  3:42 ` Mike Frysinger
2011-05-24  4:07 ` Graeme Russ
2011-05-24  4:24   ` Mike Frysinger
2011-05-24  4:35     ` Graeme Russ
2011-05-24  5:31       ` Reinhard Meyer
2011-05-24  5:43         ` Graeme Russ
2011-05-24  6:11           ` Albert ARIBAUD
2011-05-24  7:10             ` Graeme Russ
2011-05-24 14:15               ` Wolfgang Denk
2011-05-24 14:12             ` Wolfgang Denk
2011-05-24 15:23               ` J. William Campbell
2011-05-24 19:09                 ` Wolfgang Denk
2011-05-24 13:29           ` Scott McNutt
2011-05-24 14:19             ` Wolfgang Denk
2011-05-24 16:51               ` Graeme Russ
2011-05-24 18:59                 ` J. William Campbell
2011-05-24 19:31                   ` Wolfgang Denk
2011-05-24 19:19                 ` Wolfgang Denk
2011-05-24 22:32                   ` J. William Campbell
2011-05-25  5:17                     ` Wolfgang Denk
2011-05-25 16:50                       ` J. William Campbell
2011-05-25 19:56                         ` Wolfgang Denk
2011-05-25  0:17                   ` Graeme Russ
2011-05-25  2:53                     ` J. William Campbell
2011-05-25  3:21                       ` Graeme Russ
2011-05-25  5:28                       ` Wolfgang Denk
2011-05-25  6:06                         ` Graeme Russ
2011-05-25  8:08                           ` Wolfgang Denk
2011-05-25  8:38                             ` Graeme Russ
2011-05-25 11:37                               ` Wolfgang Denk
2011-05-25 11:52                                 ` Graeme Russ
2011-05-25 12:26                                   ` Wolfgang Denk
2011-05-25 12:42                                     ` Graeme Russ
2011-05-25 12:59                                       ` Wolfgang Denk
2011-05-25 13:14                                         ` Graeme Russ
2011-05-25 13:38                                           ` Wolfgang Denk
2011-05-25 21:11                                             ` Graeme Russ
2011-05-25 21:16                                               ` Wolfgang Denk
2011-05-25 23:13                                                 ` Graeme Russ
2011-05-26  0:15                                                   ` J. William Campbell
2011-05-26  0:33                                                     ` Graeme Russ
2011-05-26  4:19                                                   ` Reinhard Meyer
2011-05-26  4:40                                                     ` Graeme Russ
2011-05-26  5:03                                                       ` J. William Campbell
2011-05-26  5:16                                                         ` Wolfgang Denk
2011-05-26  5:25                                                           ` Graeme Russ
2011-05-26  5:55                                                             ` Albert ARIBAUD
2011-05-26  6:18                                                               ` Graeme Russ
2011-05-26  6:36                                                               ` Reinhard Meyer
2011-05-26  8:48                                                             ` Wolfgang Denk
2011-05-26  9:02                                                               ` Graeme Russ
2011-05-26  4:54                                                     ` J. William Campbell
2011-05-25  5:25                     ` Wolfgang Denk
2011-05-25  6:02                       ` Graeme Russ
2011-05-25  8:06                         ` Wolfgang Denk
2011-05-25  8:26                           ` Graeme Russ
2011-05-25 11:32                             ` Wolfgang Denk
2011-05-25 11:53                               ` Graeme Russ
2011-05-25 12:27                                 ` Wolfgang Denk
2011-05-25 12:36                 ` Scott McNutt
2011-05-25 12:43                   ` Graeme Russ
2011-05-25 13:08                     ` Scott McNutt
2011-05-25 13:16                       ` Graeme Russ
2011-05-25 13:46                         ` Scott McNutt
2011-05-25 14:21                           ` Graeme Russ
2011-05-25 19:46                             ` Wolfgang Denk
2011-05-25 20:40                               ` J. William Campbell
2011-05-25 20:48                                 ` Wolfgang Denk

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4DD8B3ED.4000408@comcast.net \
    --to=jwilliamcampbell@comcast.net \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox