I have a 32 bit hardware timer that I'd like to extend to 64 bit effective length in software.
In my embedded system, I have available a 32-bit hardware "core timer" (CT) that ticks at ~ 40 MHz, so it rolls over in about 107 seconds.
That's great for precise timing of periods up to 107 seconds. But I'd like to do equally precise timing of longer periods.
It also has a 32-bit "period" register - when the CT value matches the period register, an interrupt is generated.
My ISR looks like this (simplified for clarity):
const UINT32 ONE_MILLISECOND = TICK_RATE/1000;
UINT64 SwRTC;
void CT_ISR(void) {
PeriodRegister += ONE_MILLISECOND;
SwRTC += ONE_MILLISECOND;
ClearCTInterrupt();
}
So, now I have a 64 bit "SwRTC" that can be used to measure longer periods, but only to a precision of 1 millisecond, plus the 32-bit hardware timer that is precise to 1/40 MHz (25 nanoseconds). Both use the same units (TICK_RATE).
How can I combine both to get a 64 bit timer that's equally precise, while still getting interrupts at 1000 Hz?
My first try looked like this:
UINT64 RTC(void){
UINT64 result;
DisableInterrupts(); // to allow atomic operations
result = (SwRTC & 0xFFFFFFFF00000000ull) + ReadCoreTimer();
EnableInterrupts();
return result;
}
But that's no good, because if the CT rolls over while interrupts are disabled then I'll get a result with a small number in the low-order 32 bits, but without the high-order bits having been incremented by the ISR.
Maybe something like this would work - read it twice and return the higher value:
UINT64 RTC(void){
UINT64 result1, result2;
DisableInterrupts(); // to allow atomic operations
result1 = (SwRTC & 0xFFFFFFFF00000000ull) + ReadCoreTimer();
EnableInterrupts();
DisableInterrupts(); // again
result2 = (SwRTC & 0xFFFFFFFF00000000ull) + ReadCoreTimer();
EnableInterrupts();
if (result1 > result2)
return result1;
else
return result2;
}
I'm not sure if that'll work or if there is a hidden problem there I've missed.
What is the best way to do this?
(Some may ask why I need to time such long periods so precisely in the first place. It's mainly for simplicity - I don't want to use 2 different timing methods depending on the period; I'd prefer to use the same method all the time.)
I think I've almost solved this myself:
UINT64 Rtc(void){
UINT64 softwareTimer = SwRTC;
UINT32 lowOrderBits = softwareTimer; // just take low-order 32 bits
UINT64 coreTimer = ReadCoreTimer();
if (lowOrderBits > coreTimer) // if CT has rolled over since SwRTC was updated
softwareTimer += 0x100000000; // then increment high-order 32 bits of software count
return (softwareTimer & 0xFFFFFFFF00000000ull) + coreTimer;
}
This first reads the 64-bit software timer, then the 32-bit hardware timer.
The hardware timer (updated every 25 nS) should always be >= the low-order 32-bits of the software timer (updated only every 1 mS).
If it's not, that indicates the hardware timer rolled over since the software timer was read.
So, in that case I increment the high-order word of the software timer.
Then just combine the high-order 32 bits from the software time with the low-order 32 bits from the hardware timer.
One nice side effect is there's no need to disable interrupts.
The only problem I can see is, what if compiler optimization re-orders the code so that the hardware timer gets read first? Then I could get an interrupt that increments the software timer before I have a chance to read it.
At first I thought I could fix that by disabling interrupts while reading both timers, but what if the compiler re-orders the code so the DisableInterrupts() comes too late?