# Timer This unit rests on a driver that provides a "ticks ( -- n )" word which yields a number that increases at approximately (but at least) "uspertick" microseconds intervals. 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. For a longer time frame, see [lib/time]. 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?". ## Snoozing The timer driver must also provide "snooze ( -- )" which waits for an indeterminate amount of time in power saving mode. If the CPU has some kind of "halt" instruction, then it can do that. Otherwise, a noop is fine, but it will waste power. The idea is to halt the CPU until the next interrupt. This will generally be a very small amount of time, since a typical system has a lot of interrupts firing all the time. The general ballpark for how long a "snooze" should last is 10ms. Less is good too, because "snooze" is likely to be in a loop anyway, but it shouldn't be much more than that. ## Initialization Units in Dusk are generally all explicitly initialized, but because timers are sometimes needed in bootstrap code, this unit is implicitly initialized by its implementation units. This means that loading any driver implementation automatically initialized drv/timer to it, unless it has already been initialized. Until a timer implementation is loaded, "ticks" will abort with error. ## The default timer To accurately count time, an actual hardware timer is needed. That's why this is a driver. When this unit is loaded, in order to have consumers of this unit somewhat work, we have a default implementation that increases ticks by 1 each time it's called. Because the default value of "uspertick" is 1, this means that calling ticks elapses 1us. Of course, this is not a proper timer and your system should setup one, but when you don't have one, this unit still "kinda works". ## Examples ticks begin MYTIMEOUTUS over elapsedus? if ... else ... then ... until drop ticks begin MYTIMEOUTMS over ?timeoutms"timeout!" ... until drop ## API ### Values uspertick The number of microseconds, at minimum, that elapse between two ticks. Is 1 when the unit is uninitialized. ### Words ticks ( -- n:uint ) Yield a running tick counter. snooze ( -- ) Put the CPU in power saving state for an indeterminate amount of time, normally a few microseconds at most. elapsedus ( tickstamp -- us ) Yields the number of microseconds elapsed since tickstamp. elapsedms ( tickstamp -- ms ) Yields the number of milliseconds elapsed since tickstamp. elapsedus? ( targetus:uint tickstamp:uint -- f:uint ) Yield whether "targetus" has elapsed since "tickstamp" was taken. elapsedms? ( targetms:uint tickstamp:uint -- f:uint ) Same as "elapsedus?", but in milliseconds. ?timeoutus" ( targetus tickstamp -- ) *I* Shortcut for: elapsedus? ?abort"..." ?timeoutms" ( targetms tickstamp -- ) *I* Shortcut for: elapsedms? ?abort"..." waitus ( us:uint -- ) Block until "us" microseconds have elapsed. Does *not* call snooze, this is a CPU busywait. waitms ( ms:uint -- ) Block until "ms" milliseconds have elapsed, calling "snooze" in between the loop iterations. ifelapsedus ( us -- ) *I* 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 -- ) *I* Like "ifelapsedus" but for milliseconds. timer$ ( ticks-xt snooze-xt uspertick -- ) Realias "ticks" and "snooze" to the specified XTs and set uspertick. ?timer$ ( ticks-xt snooze-xt uspertick -- ) If the unit is uninitialized, do the same as "timer$". Otherwise, ignore initialization.