# Timer

The unit at [drv/timer.fs] rests on a driver that provides a "ticks ( -- n )"
word which yields a number that increases approximately (but at least)
"uspertick" microseconds.

This subsystem provides a convenience layer on top of that to facilitate the
measurement of time through those "ticks".

The layer itself is excessively simple. The idea is that you take a "tickstamp"
by reading "ticks" and saving the result, then use that tickstamp later to
measure how much time has elapsed since the tickstamp was taken.

The system timer isn't designed to be very accurate. A "tick" is guaranteed to
represent *at least* "uspertick" microseconds, but can be a bit longer.

Moreover, special care is taken by the system timer to ensure that we wait at
least the amount of time that is specified. Indeed, a problem can arise if we
call "ticks" right before the counter increases, which means that you can get
the next "tick" before "uspertick" has elapsed. To avoid this, "elapsedXX?" and
"waitXX" will add one tick to their target.

"uspertick" can't be less than 1, which means that you can rely on a 32-bit
overflow being at least 70 minutes. This means that you shouldn't measure more
than 70 minutes with this timer.

Overflow is not a problem as long as tickstamps are subtracted to each other
before doing a comparison (and that you don't measure more than 70 minutes worth
of microseconds), but you should avoid comparing tickstamps directly. Something
like "tickstamp 10 + ticks <" is *not* ok, because it will fail on overflow.
Always do "ticks tickstamp -" before comparison, or use "elapsed?".

## Waiting and "idle"

The "idle ( -- )" word is called when we want to run [sys/loop]. We specifically
don't do this in "waitus" and "waitms" because timers are used in drivers and
those drivers are often called in [sys/loop].

TODO: add "sleep ( us -- )" to the driver protocol, which would either be a noop
(waste cycles) or halt the CPU (conserve energy) for at most "us" microseconds.

## Driver Protocol

ticks ( -- n )
  Yield a running tick counter.

uspertick ( -- n )
  The number of microseconds, at minimum, that elapse between two ticks.

## API

elapsedus? ( targetus tickstamp -- f )
  Yield whether "targetus" has elapsed since "tickstamp" was taken.

elapsedms? ( targetms tickstamp -- f )
  Same as "elapsedus?", but in milliseconds.

?timeoutus ( targetus tickstamp -- ) *I*
  Shortcut for: elapsedus? ?abort"..."

?timeoutms ( targetms tickstamp -- ) *I*
  Shortcut for: elapsedms? ?abort"..."

waitus ( us -- )
  Block until "us" microseconds have elapsed.

waitms ( ms -- )
  Block until "ms" milliseconds have elapsed.

ifelapsedus ( us -- )
  Creates a "if" block that maintains its own tickstamp storage and executes at
  most every "us" microseconds. Example usage:

    : foo begin [ 500 ] ifelapsedus ."hello" then again ;

  Calling "foo" would enter an infinite loop where "hello" is printed
  approximately every 500 microseconds.

ifelapsedms ( ms -- )
  Like "ifelapsedus" but for milliseconds.

## C API

Timer API can be exposed to C by loading drv/timer.h, which add these functions:

uint ticks();
uint iselapsedus(uint tickstamp, uint targetus);
uint iselapsedms(uint tickstamp, uint targetms);
void waitus(uint us);
void waitms(uint ms);
