Gauthier
Gauthier

Reputation: 41985

Timestamps based on hardware timer with overflow counter

I want to implement a timestamp functionality for my msp430-based platform.

My aim is to use a hardware timer, and count the number of times it overflows, to generate a long timestamp value (typically an uint32 for overflow counter, combined with the uint16 value of the hardware timer).

This is what I have:

I run into problems when I take into consideration the timing of interrupts.


The first naive implementation I had:

uint16_t timer_value = timer_value_get();

__istate_t istate = interrupt_disable();
uint64_t overflow_count_local = overflow_count; // the volatile incremented on interrupt
interrupt_restore(istate);

return (overflow_count_local << 16u) + timer_value;    

This is going to fail when an overflow occurs after getting the timer value, but before the interrupts are disabled. overflow_count_local would then be 1 greater than what it was upon assigning timer_value.


I have tried to add other checks to detect this possible interrupt

uint16_t timer_value = timer_value_get();

__istate_t istate = interrupt_disable();
uint16_t second_timer_value = timer_value_get();

uint64_t overflow_count_local = overflow_count; // the volatile incremented on interrupt
interrupt_restore(istate);

if (second_timer_value < timer_value) {
    // A HW timer overflow occured just before disabling interrupts.
    overflow_count_local--;
}

return (overflow_count_local << 16u) + timer_value;    

This is not either going to work, this time because the timer might have overflown after disabling the interrupts, but before assigning second_timer_value. This would then make overflow_count_local one too little.


However I try to turn this around, there seems to always be a case which is not covered. Is there a known way to make this work?

Some limitations:

Upvotes: 2

Views: 1215

Answers (2)

Clifford
Clifford

Reputation: 93556

The following algorithm is lock free (no interrupt disable required):

Get overflow then timer (in that order) until overflow is the same either side of the timer read.

uint32_t hi ;
uint16_t lo ;
do
{
    hi = overflow_count ;
    lo = timer_value_get() ;

} while( hi != overflow_count )

return (hi << 16 ) | lo ; 

This should normally take zero or one iteration at most, unless there is an exceptionally long context switch to another thread or interrupt during which the timer overflowed again.

Upvotes: 3

kkrambo
kkrambo

Reputation: 7057

Maybe something like this. I don't see a need to disable interrupts with this.

timer_value_1 = timer_value_get();
overflow_count_snapshot = overflow_count;
timer_value_2 = timer_value_get();
if (timer_value_2 < timer_value_1)
{
    return (timer_value_2 + (overflow_count << 16)); // not the snapshot
}
else
{
    return (timer_value_2 + (overflow_count_snapshot << 16)); // you could use timer_value_1 or 2
}

Upvotes: 1

Related Questions