Timing

Every tile in a system has a reference clock. On XMOS XS1 devices this is defined to always be a 100MHz clock. In xC, timers give the program access to this clock as shown in the figure below.

Timers
images/timers-crop.png

The reference clock has an associated 32-bit counter. Note that the current counter value of the clock is not the same on every tile, but if all tiles are on the same PCB the clocks are usually synchronized.

A timer variable can be declared at any point in an xC program:

timer t;

To get the current value of the timer counter the :> operator is used:

uint32_t time;

t :> time;  // this reads the current timer value into the variable 'time'

With this functionality a program can, for example, measure elapsed time:

uint32_t start_time, end_time;

t :> start_time;

// Do something here

t :> end_time;
printf("Number of timer ticks elapsed: %u", end_time - start_time);

Remember the range of the 32-bit counter. At 100MHz, you can only meaningfully measure up to 232 - 1 ticks (about 42 seconds).

Timers can cause events when the counter reaches a certain value. These events can be reacted to in a select statement. The syntax for cases like this is:

case timer when timerafter ( value ) :> [ var | void ] :

For example, the following select will event based on a timer:

uint32_t x;
timer t;
...
select {
  case t when timerafter(x) :> void:
     // handler the timer event
     ...
     break;
}

This event will trigger if the counter is greater than the value held in x. In this example void is after the :> symbol. This means that the actual counter value at the time of the event is ignored. Alternatively, the value could be read into a new variable:

case t when timerafter(x) :> int y:

The ability to react to timing events allows a program to perform actions periodically. For example, the following loop has a case that executes some code every 1ms:

timer t;
uint32_t time;
const uint32_t period = 100000; // 100000 timer ticks = 1ms

// get the initial timer value
t :> time;
while(1) {
  select {
  case t when timerafter(time) :> void:
    // perform periodic task
    ...
    time += period;
    break;
  }
}

This form of periodic event handling is particular useful when combined with other events handlers in the same select construct. It is also useful in defining combinable functions (see Combinable functions), which can run periodic tasks along with others on the same logical core.

See Also