public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
* [U-Boot] [RFC] Review of U-Boot timer API
@ 2011-05-21 12:38 Graeme Russ
       [not found] ` <4DD7DB64.70605@comcast.net>
                   ` (3 more replies)
  0 siblings, 4 replies; 101+ messages in thread
From: Graeme Russ @ 2011-05-21 12:38 UTC (permalink / raw)
  To: u-boot

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

Regards,

Graeme

--------

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;

The U-Boot timer API is not a 'callback' API and cannot 'trigger' a
function call after a pre-determined time.

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;
	}

U-Boot Timer API Details
========================
There are currently three functions in the U-Boot timer API:
	ulong get_timer(ulong start_time)
	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

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()
   - 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()

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()

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
 - get_usec_timer_64() could offer a longer period (584942 years!)

^ permalink raw reply	[flat|nested] 101+ messages in thread

end of thread, other threads:[~2011-05-26  9:02 UTC | newest]

Thread overview: 101+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox