You are here: DENX Home »  U-Boot » Tasks » TaskTimerAPI

The (new) U-Boot Timer API

Being worked on by: Graeme Russ <graeme.russ@gmail.com>

The 'Old' API

Overview

U-Boot currently has a timer API consisting of the following functions:
  • void udelay(u32 usec)
    • Delays execution foy 'usec' microseconds
    • Intended for sub-second delays

  • u32 get_timer(u32 base)
    • Returns the number of milliseconds since the epoch 'base'

  • u32 set_timer(u32 msec)
    • Sets the internal millisecond timer to a value of 'msec'
    • get_timer(msec) called immediately after set_timer(msec) returns 0 (assuming the internal timer has not 'ticked' between the two calls)

  • void reset_timer()
    • Sets the internal millisecond timer to a value of 0
    • On some platforms (Nios2 in particular), the low-level timer is also reset resulting in subsequent calls to get_timer(0) that are guaranteed to return at most the number of milliseconds elapsed since reset_timer()

Problems and Limitations

  • The time base for the API is not clearly defined. It is assumed to be milliseconds, but not all platforms are known to guarantee this. The issue is further confounded by CONFIG_SYS_HZ
  • Use of set_timer() and reset_timer() prohibit mult-level timing loops. If an inner timing loop uses set_timer() or reset_timer(), the outer timing loop will be impacted - This particularly prohibits timing of operations involving the CFI Flash driver
  • Many implementations of udelay() call reset_timer() which can break timeout loops which invoke udelay()
  • For ARM, [get,set,reset]_timer() are mostly thin wrappers around the platform specific [get,set,reset]_timer_masked() - This in itself is not a problem, however there is platform independent code in /drivers/ which call the [get,set,reset]_timer_masked() functions directly
  • A lot of code duplication exists (particularly in arch/arm/)
  • Lots of other nigglies at the platform specific level wink

The 'New' API Proposal

At the core of the new API is two user-level functions per defined time base:
  • u32 time_<time base>_now() returns the current number of 'time base' 'ticks' that have elapsed since the timer subsystem was initialised
  • u32 time_<time base>_delta(u32 from, u32 to, u8 delta_type) returns the number of 'time base' 'ticks' between 'from' and 'to' taking into account the limitations of the platforms available resolution of 'time base'

Three 'time bases' are initially proposed:
  • ms - milliseconds
  • us - microseconds
  • ticks - raw ticks

User API (/lib/timer.c)

  • u32 time_ms_now(void)
    • Returns the current value of the millisecond timer

  • u32 time_ms_delta_[min,raw,max](u32 from, u32 to)
    • Returns the time difference, in milliseconds, between two arbitrary millisecond times
    • Maximum delta is ~49 days
    • Utilizes weak function u32 time_ms_resolution(void) (defaults to 1)
    • delta_type is one of:
      • time_ms_delta_raw() - Returns the raw number of milliseconds between 'from' and 'to'
      • time_ms_delta_max() - Returns the maximum number of milliseconds between 'from' and 'to'
      • time_ms_delta_min() - Returns the minimum number of milliseconds between 'from' and 'to'

    • Example - time_ms_resolution() not overridden (returns 1):
      • time_ms_delta_min(1, 1) returns 0
      • time_ms_delta_raw(1, 1) returns 0
      • time_ms_delta_max(1, 1) returns 1
      • time_ms_delta_min(1, 2) returns 0
      • time_ms_delta_raw(1, 2) returns 1
      • time_ms_delta_max(1, 2) returns 2
      • time_ms_delta_min(1, 3) returns 1
      • time_ms_delta_raw(1, 3) returns 2
      • time_ms_delta_max(1, 3) returns 3

    • Examples - time_ms_resolution() overridden to return 10:
      • time_ms_delta_min(10, 10) returns 0
      • time_ms_delta_raw(10, 10) returns 0
      • time_ms_delta_max(10, 10) returns 10
      • time_ms_delta_min(10, 20) returns 0
      • time_ms_delta_raw(10, 20) returns 10
      • time_ms_delta_max(10, 20) returns 20
      • time_ms_delta_min(10, 30) returns 10
      • time_ms_delta_raw(10, 30) returns 20
      • time_ms_delta_max(10, 30) returns 30

    • A little picture might help explain the above numbers
      • Platform tick counter is limited to 10ms resolution
      • Each character below is 1ms in real-time

                         %        #^        !&        @
          -----|---------|---------|---------|---------|------
               0         10        20        30        40

      • time_ms_now() at # returns 10
      • time_ms_now() at ^ returns 20
      • BUT, <1ms may have passed in real time
      • Therefore, time_ms_delta_min(#, ^) = 0
      • time_ms_now() at ! also returns 20
      • Therefore, there is no way to differentiate between ^ and !
      • Therefore, time_ms_delta_min(#, !) = 0
      • time_ms_now() at % returns 10
      • Therefore, there is no way to differentiate between % and #
      • Therefore, time_ms_delta_min(%, ^) = 0
      • Therefore, time_ms_delta_min(%, !) = 0
      • Even though UP TO 20ms pass between % and !, the fact that there is no way to differentiate between % and # and between ^ and ! there is NO WAY to tell if even one complete millisecond has passed
      • As noted, up to 20ms (actually, ever so slightly less) can pass between % and !
      • Therefore, time_ms_delta_max(%, !) = 20
      • Again, there is no way to differentiate between % and # and between ^ and !
      • Therefore, time_ms_delta_max(#, ^) = 20
      • Also, up to 10ms can pass between % and #
      • Therefore, time_ms_delta_max(%, #) = 10
      • time_ms_now() at & returns 30
      • At least 10ms has past between # and &
      • Therefore, time_ms_delta_min(#, &) = 10 AND
      • Again, there is no way to differentiate between % and #
      • Therefore, time_ms_delta_min(%, &) = 10
      • time_ms_now() at @ returns 30
      • No way to differentiate between & and @
      • Therefore, time_ms_delta_min(%, @) = 10 (Even though up to 30 milliseconds may have passed in real time!)
      • time_ms_delta_max(%, @) = 30
      • time_ms_delta_max(#, &) = 30

  • u32 time_us_now(void)
    • Same as time_ms_now() but returns microseconds
  • u32 time_us_delta_[min,raw,max](u32 from, u32 to)
    • Same as time_ms_now() but delta is microseconds
    • Utilizes weak function u32 time_us_resolution(void) (defaults to 1)
    • Maximum delta is ~71.58 minutes

  • u64 time_ticks_now(void)
    • Same as time_ms_now() but returns raw platforms ticks
  • u64 time_ticks_delta_[min,raw,max](u64 from, u64 to)
    • Same as time_ms_now() but delta is raw platform ticks
    • time_ticks_resolution(void) not required (always 1 by definition)
    • Maximum delta is highly platform dependent
    • Note the use of u64 rather than u32 - Some platforms are known to overflow a 32-bit raw tick counter in <4 seconds

Using the User API

  1. Timeout Detection - Don't want to timeout early, so need to know the minimum time elapsed

        u32 start;
        u32 now;
        u32 timeout = 5 /* milliseconds */

        start_execute_command();

        start = timer_ms_now();

        while (!command_complete()) {
                now = timer_ms_now();
                if (time_ms_delta_min(start, now)> timeout)
                        handle_timeout();

                udelay(100);
        }
  1. Timing Duration of an Activity - Useful for performance profiling, so need to know the maximum possible time taken

        u32 start;
        u32 end;
        u32 duration;

        start = timer_ms_now();

        do_something_complicated();

        end = timer_ms_now();

        duration = time_ms_delta_max(start, end);

        printf("execution time: %ld millisec\n", duration);

A Note about Accuracy and 'Recursion'

The U-Boot Timer API make no guarantees regarding accuracy - It is provided as a means of performing operations that are known to require a minimum amount of time (hardware timeouts such as Flash operations in particular). Platform implementors must ensure that for any two calls to time_ms_now(), AT LEAST x milliseconds pass (in real time) where 'x' is the difference in the return value of the two calls - It is acceptable for the underlying hardware timer to run slow, but not fast.

Any further accuracy guarantees are highly platform specific. Individual platform implementors are free to document accuracy claims, but this is by no means a requirement.

Another important aspect of the Timer API is the ability to run timing loops inside other timing loops (recursive timing)

Behind the Scenes of the User API

  • In /lib/time.c
        u32 time_ms_delta_min(u32 from, u32 to, u8 round)
        {
                u32 delta = to - from;

                /* round down */
                if (delta < time_ms_resolution())
                        return 0;

                return delta - time_ms_resolution();
        }

        u32 time_ms_delta_raw(u32 from, u32 to, u8 round)
        {
                return = to - from;
        }

        u32 time_ms_delta_max(u32 from, u32 to, u8 round)
        {
                u32 delta = to - from;

                return delta + time_ms_resolution();
        }

        __attribute__((weak))
        u32 time_ms_resolution(void) {return 1;}

  • Similarly for time_us_delta() and time_us_resolution()

-- Main.GraemeRuss - 03 Jun 2011
Copyright © 2002-2022 by DENX Software Engineering GmbH ImprintTerms & ConditionsPrivacyFeedback
This website is using cookies. More info. That's Fine