I need a timestamp in millisecond in linux epoch. It is a number that doesn't fit in a 32-bits number.
I'm using a 32-bit MCU (STM32L4R9...) so I don't have a 64-bits hw counter. I need to create a mixed sw/hw 64-bits counter. It's very simple, I configure a 32-bits hw timer to run at 1kHz and increment an uint32_t variable in timer overflow ISR.
Now I need to implement a GetTick() function that returns a uint64_t. I know it could be difficult, because of race conditions. One solutions is to disable interrupts, but I remember another solution.
All this seems to be more complex than it needs to be. You guys are focuse d on limitations when things happen fast. Do you know how slow things can happen?
A 32 bit counter incremented at 1 kHz will roll over every 3 years or so. If you can assure that the GetTick is called once every 3 years simpler cod e can be used.
Have a 64 bit counter value which is the 32 bit counter incremented by the
1kHz interrupt and another 32 bit counter (the high part of the 64 bits) wh ich is incremented when needed in the GetTick code.
In your timer ISR low32 += 1 if (low32 == 0) high32 += 1;
in your GetTick()
do { l1 = low32; h1 = high32; while (l1 > low32);
return your or'd h1 and l1
It still can be incorrect with out disabling interrupts. Consider: timer interrupt priority 1 dma interrupt priority 2 your code lowest priority In the middle of your h1 == h2 the dma interrupt goes off. In the dma routine your timer interrupt goes off You unwind back to your code and your h2 and possibly h1 are incorrect.
The potential problem with this is if the code is used re-entrantly - i.e., you have multiple threads that each use GetTick() and more than one calls it during the cross-over point. It's highly unlikely to happen - there is only a risk once every 49 days (I got that wrong too). But you don't want to leave these little potential race conditions in the code - you'll never find them in testing, but they always happen at your most important customer site.
I think it's quite likely that the code already has a 1 KHz interrupt routine doing other things, so incrementing a "high" counter there would not be an issue. But if there is no interrupt for other purposes, then it is a nice idea to do the update during the GetTick call like this. However, you need locking (or a more advanced lockfree solution, but that would likely be a fair bit less efficient on a microcontroller. It might be worth considering on a multi-processor system).
The easiest method is disabling interrupts in a double-checked lock. If your system timing can't handle a few cycles of disabled interrupts every 49 days, you should be worrying about the rest of the design.
low32 is the hw counter that runs at 1kHz and generates and interrupt every 2^32 ms. It isn't a software variable, it's a register.
You are supposing to use an hw counter that generates interrupt at 1kHz, but this is my case.
I couldn't get your point. Ok, when the low priority code is back after interrupts, h1, h2 and l1 could be incorrect, but they are coherent among them, so the result of GetTick() is still valid, maybe slightly inaccurate.
Actually, there is no need for a 1 kHz interrupt. I believe the OP has a h ardware counter ticking at 1 kHz so the overflow event would be 49 days. T here may be a faster interrupt for some other purpose, but not needed for t he hardware timer which may well be run on a low power oscillator and backu p battery.
My intent was to do something just plain simple, but in multitasking system it is not so simple. I don't typically use complications like interrupts. I do FPGA work where I roll the hardware for the peripherals as well as d esigning the instruction set for the processor. I seldom use multitasking other than potentially interrupts which usually don't need to use locking a nd such. If resources are not shared locking is not required.
That works.
--
Rick C.
-- Get 1,000 miles of free Supercharging
Joe is saying read the LSB then the MSB, then read the LSB again to be sure it hasn't changed due to a tick. If it has, try again until it doesn't change.
If LSB has changed, MSB might have. If LSB hasn't changed, and you haven't waited for an entire cycle of the LSB register, then you know MSB is good.
If you've repeated the loop until LSB cycled to exactly where you were before, you have bigger problems.
However, Joe got it wrong. The do... while loop should use not-equal, not >. Otherwise if you read LSB as it rolls over l1 is < low32, and you're broken because you didn't detect that.
If the two reads of the high tick counter are different, then the point in time when the high tick counter had its higher value, and the lower tick counter was 0 had to have occurred between the two reads of the high tick counter, so it is a valid time point that occurred during the call to GetTick(). The fact that it might be 'stale' compared to the return time is no different than if it returned a totally accurate time-stamp, but just after the return the higher priority task interrupt this task.
(Note, you need to use h2, not h1, to build the 64 bit time stamp, h1 with a 0 bottom word is likely outside your call window, as you likely did the first read with the counter at max, and the second after it wrapped to 0.)
Bugs that *could* happen every 49 days are critical, because they could really happen even more rarely.
If you have a ms resolution, I think it's better to work with a 16-bits hw counter, coupled with a 32-bits sw counter.
In this way, the potential bug could happen much more frequently (every minute) and the 48-bits timestamp could represents datetime until... I don't know exactly, but I don't worry what it is.
We don't know that - the OP hasn't told us all the details. An interrupt that hits every millisecond (or some other regular time), used as part of the global timebase and for executing regular functions, is very common. Maybe he has something like this, maybe not.
Fair enough. And we don't know if there is multi-tasking involved here or not. If GetTick is only called from one thread, there is no problem. Also if he has cooperative multitasking rather than pre-emptive, there will be no problem. Your suggested solution is good (IMHO), but it is important to understand its restrictions too.
That is a useful thought. It is very important to write code in a way that it can be tested. And even then, remember that testing can only prove the /presence/ of bugs, never to prove their /absence/.
Another trick during testing is to speed up the timers. If you can make the 1 kHz timer run at 1 MHz for testing, you'll get similar benefits.
"I'm using a 32-bit MCU (STM32L4R9...) so I don't have a 64-bits hw counter. I need to create a mixed sw/hw 64-bits counter. It's very simple, I configure a 32-bits hw timer to run at 1kHz and increment an uint32_t variable in timer overflow ISR."
--
Rick C.
-+ Get 1,000 miles of free Supercharging
ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here.
All logos and trade names are the property of their respective owners.