Reputation: 41985
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
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
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