Search code examples
timeroverflowmicrocontroller

Timer overflow race condition


I am using a micro controller with a 16 bit timer counting up. The current value can be read from a register. However I need a 32 bit counter. Every time the timer overflows, it generates an interrupt. My current solution looks like the code below. Every time the timer overflows, a variable counter_high is incremented. The current counter value is read as a combination of counter_high and the timer register.

volatile uint16_t counter_high = 0;

uint32_t get_counter(void)
{
    return (counter_high << 16) | timer->counter;
}

void timer_overflow(void)
{
    counter_high++;
}

This seems to work. However I have started to wonder what happens if the timer overflows while get_counter() is executed? I could get the old value of counter_high combined with the new value of timer->counter or vice versa.

Is there a best practice for preventing this problem?


Solution

  • Read counter_high before and after reading timer->counter. If the value read for counter_high does not change then you know that timer->counter did not rollover between the reads and therefore you can trust the value you read from timer->counter.

    However if counter_high changed between the two reads then you know timer->counter rolled over sometime between the two reads. This means you cannot trust the value you read from timer->counter because you don't know whether you read it before or after the rollover. But now you know timer->counter just recently rolled over so you can read it again and know that it is not about to rollover a second time.

    uint32_t get_counter(void)
    {
        uint32_t first_counter_high = counter_high;
        uint32_t counter_low = timer->counter;
        uint32_t second_counter_high = counter_high;
        if (first_counter_high != second_counter_high)
        {
            counter_low = timer->counter;  // Read timer->counter again, after rollover.
        }
        return (second_counter_high << 16) | counter_low;
    }