Measuring Time Differences When Timer Overflows

This is just a quick note on one simple truth perhaps every embedded developer knows, however I still happened to make this mistake, even though I am aware of it. This way, I will hopefully remember it.

Imagine you have a timer variable holding current value of time in milliseconds. Such variable will overflow in about 49.71 days in case it is 32-bit unsigned integer. This is nowhere long enough time to ignore the possibility of it happening. Let's also say you have another 32-bit unsigned integer variable holding time of some previous event and your task is to figure out whether some time period elapsed since the previous event. Would you approach this problem as follows:

if ((last_time + period_length) >= now)
{
    // ...
}

Or as follows:

if ((now - last_time) >= period_length)
{
    // ...
}

Of course the latter is correct, as it does not have any overflow issues, provided the measured period is sufficiently small with respect to the range of the time variables.

One way to reason about this, I found to be useful, is to realize that unsigned addition/subtraction logic is identical to two's complement one. Hence, by taking an example, where

unsigned last_time = 0xfffffffe;
unsigned now = 1;
unsigned period_length = 4;

By substituting into the latter expression we will get following.

if ((1 - 0xfffffffe) >= 4)

This in turn can be expressed in two's complement as following.

if ((1 - (-2)) >= 4)

By applying the elementary school mathematics skills, one can see that the result is correct - the period has not yet elapsed at time 1.

Now let's try to do the same with former expression. By substituting the example values we will get:

if ((0xfffffffe + 4) >= 1)

And by interpreting numbers as 32-bit two's complement numbers we will get:

if ((-2 + 4) >= 1)

It is easy to see that the last expression falsely reports that the period already elapsed at time 1.

While, this approach to reason about the presented expressions is useful, it is not yet complete, as it will break down once either side of the comparison is negative. This is due to the fact that the comparison logic is not identical for signed and unsigned numbers. The code will still perform unsigned comparison, so the numbers should be converted to unsigned representation after performing subtraction/addition. This was conveniently not necessary above, as the results were positive integers.

Note on AVR-GCC

The presented solution will break on AVR-GCC compiler if the time variables are intended to be native 8-bit unsigned integers, as compilers are free to perform usual arithmetic conversions on them:

If both operands have arithmetic type, the usual arithmetic conversions are performed on them.

I presume the issue is that AVR-GCC is configured to consifer 16-bit to be native with of number by default, since it is the with of pointers on the platform. Solution is to explicitly state that you want to perform the arithmetic in 8-bits, e.g. as follows:

uint8_t time_since_last_event = now - last_time;
if (time_since_last_event >= period)
{
    \\ ...
}