Forums

timestamp in ms and 64-bit counter

Started by pozz February 6, 2020
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.

extern volatile uint32_t ticks_high;

uint64_t
GetTick(void)
{
   uint32_t h1 = ticks_high;
   uint32_t l1 = hwcnt_get();
   uint32_t h2 = ticks_high;

   if (h1 == h2) return ((uint64_t)h1 << 32) | l1;
   else          return ((uint64_t)h1 << 32);
}

Is it correct in single-tasking? In the else branch, I decided to set 
the low part to zero. I think it's acceptable, because if h1!=h2, hw 
counter has just wrapped-around, so it is 0... maybe 1.

What about preemptive multi-tasking? What happens if GetTick() is 
preempted by another higher-priority task that calls GetTick()?

I think it's better to fix the else branch, because the higher-priority 
task could take more than a few milliseconds, so the previous assumption 
that hw counter is 0 (maybe 1) can be incorrect. The fix could be:

uint64_t
GetTick(void)
{
   uint32_t h1 = ticks_high;
   uint32_t l1 = hwcnt_get();
   uint32_t h2 = ticks_high;

   if (h1 == h2) return ((uint64_t)h1 << 32) | l1;
   else          return ((uint64_t)h1 << 32) | hwcnt_get();
}
On 06/02/2020 13:43, pozz wrote:
> 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. > > extern volatile uint32_t ticks_high; > > uint64_t > GetTick(void) > { > &#2013266080; uint32_t h1 = ticks_high; > &#2013266080; uint32_t l1 = hwcnt_get(); > &#2013266080; uint32_t h2 = ticks_high; > > &#2013266080; if (h1 == h2) return ((uint64_t)h1 << 32) | l1; > &#2013266080; else&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080; return ((uint64_t)h1 << 32); > }
I presume your second line should be "return ((uint64_t)h2 << 32);"
> > Is it correct in single-tasking? In the else branch, I decided to set > the low part to zero. I think it's acceptable, because if h1!=h2, hw > counter has just wrapped-around, so it is 0... maybe 1.
That is a bit of an assumption, but is probably okay. I tend to use a loop: uint64_t getTick(void) { uint32_t high1 = ticks_high; while (true) { uint32_t low = hwcnt_get(); uint32_t high2 = ticks_high; if (high1 == high2) { return ((uint64_t) high1 << 32) | low; } high1 = high2; } } That will be more accurate if there is a risk of multiple low-part ticks while executing getTick() (due to interrupts, other tasks, etc.).
> > What about preemptive multi-tasking? What happens if GetTick() is > preempted by another higher-priority task that calls GetTick()? > > I think it's better to fix the else branch, because the higher-priority > task could take more than a few milliseconds, so the previous assumption > that hw counter is 0 (maybe 1) can be incorrect. The fix could be: > > uint64_t > GetTick(void) > { > &#2013266080; uint32_t h1 = ticks_high; > &#2013266080; uint32_t l1 = hwcnt_get(); > &#2013266080; uint32_t h2 = ticks_high; > > &#2013266080; if (h1 == h2) return ((uint64_t)h1 << 32) | l1; > &#2013266080; else&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080; return ((uint64_t)h1 << 32) | hwcnt_get(); > }
Again, fix it to use h2. That would be better, IMHO. The loop version is useful for faster timers and lower resolution. With a 32-bit millisecond counter, you wrap after 98 days (IIRC). So two reads are the most you will need unless your task is pre-empted for about 3 months, and I suspect that would indicate a more serious design fault!
Il 06/02/2020 15:10, David Brown ha scritto:
> On 06/02/2020 13:43, pozz wrote: >> 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. >> >> extern volatile uint32_t ticks_high; >> >> uint64_t >> GetTick(void) >> { >> &#2013266080; uint32_t h1 = ticks_high; >> &#2013266080; uint32_t l1 = hwcnt_get(); >> &#2013266080; uint32_t h2 = ticks_high; >> >> &#2013266080; if (h1 == h2) return ((uint64_t)h1 << 32) | l1; >> &#2013266080; else&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080; return ((uint64_t)h1 << 32); >> } > > I presume your second line should be "return ((uint64_t)h2 << 32);"
Oh yes, my error.
>> Is it correct in single-tasking? In the else branch, I decided to set >> the low part to zero. I think it's acceptable, because if h1!=h2, hw >> counter has just wrapped-around, so it is 0... maybe 1. > > That is a bit of an assumption, but is probably okay. > > I tend to use a loop: > > uint64_t getTick(void) { > uint32_t high1 = ticks_high; > while (true) { > uint32_t low = hwcnt_get(); > uint32_t high2 = ticks_high; > if (high1 == high2) { > return ((uint64_t) high1 << 32) | low; > } > high1 = high2; > } > } > > That will be more accurate if there is a risk of multiple low-part ticks > while executing getTick() (due to interrupts, other tasks, etc.).
In single-tasking there aren't other tasks and interrupts should take less than 1ms... at least, I usually tend to have very fast ISRs. Anyway your implementation is good for multi-tasking too.
>> What about preemptive multi-tasking? What happens if GetTick() is >> preempted by another higher-priority task that calls GetTick()? >> >> I think it's better to fix the else branch, because the higher-priority >> task could take more than a few milliseconds, so the previous assumption >> that hw counter is 0 (maybe 1) can be incorrect. The fix could be: >> >> uint64_t >> GetTick(void) >> { >> &#2013266080; uint32_t h1 = ticks_high; >> &#2013266080; uint32_t l1 = hwcnt_get(); >> &#2013266080; uint32_t h2 = ticks_high; >> >> &#2013266080; if (h1 == h2) return ((uint64_t)h1 << 32) | l1; >> &#2013266080; else&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080;&#2013266080; return ((uint64_t)h1 << 32) | hwcnt_get(); >> } > > Again, fix it to use h2.
Yes.
> That would be better, IMHO. > > The loop version is useful for faster timers and lower resolution. With > a 32-bit millisecond counter, you wrap after 98 days (IIRC). So two > reads are the most you will need unless your task is pre-empted for > about 3 months, and I suspect that would indicate a more serious design > fault! >
Of course!
On Thursday, February 6, 2020 at 7:43:35 AM UTC-5, pozz wrote:
> 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. > > extern volatile uint32_t ticks_high; > > uint64_t > GetTick(void) > { > uint32_t h1 = ticks_high; > uint32_t l1 = hwcnt_get(); > uint32_t h2 = ticks_high; > > if (h1 == h2) return ((uint64_t)h1 << 32) | l1; > else return ((uint64_t)h1 << 32); > } > > Is it correct in single-tasking? In the else branch, I decided to set > the low part to zero. I think it's acceptable, because if h1!=h2, hw > counter has just wrapped-around, so it is 0... maybe 1. > > What about preemptive multi-tasking? What happens if GetTick() is > preempted by another higher-priority task that calls GetTick()? > > I think it's better to fix the else branch, because the higher-priority > task could take more than a few milliseconds, so the previous assumption > that hw counter is 0 (maybe 1) can be incorrect. The fix could be: > > uint64_t > GetTick(void) > { > uint32_t h1 = ticks_high; > uint32_t l1 = hwcnt_get(); > uint32_t h2 = ticks_high; > > if (h1 == h2) return ((uint64_t)h1 << 32) | l1; > else return ((uint64_t)h1 << 32) | hwcnt_get(); > }
All this seems to be more complex than it needs to be. You guys are focused 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 code 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) which is incremented when needed in the GetTick code. uint64_t GetTick(void) { static uint32_t ticks_high; uint32_t ticks_hw= hwcnt_get(); static uint32_t ticks_last; if (ticks_last > ticks_hw) ticks_high++; ticks_last = ticks_hw; return ((uint64_t)ticks_high << 32) | ticks_hw; } I'm not so conversant in C and I'm not familiar with the conventions of using time variables. Clearly the time will need to be initialized by some means and ticks_high would need to be initialized to correspond to the current time/date, unless this is a run time variable only tracking time since boot up. Is the "call this code at least once in 3 years" requirement reasonable? In the systems I design that would not be a problem. -- Rick C. - Get 1,000 miles of free Supercharging - Tesla referral code - https://ts.la/richard11209
Rick C wrote:
> On Thursday, February 6, 2020 at 7:43:35 AM UTC-5, pozz wrote: > > All this seems to be more complex than it needs to be. You guys are focused 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.
2 ^ 32 / (24 * 60 * 60 * 1000) = 49.71026962... (days) > [snipped] regards, Bernd
On Thu, 6 Feb 2020 10:02:45 -0800 (PST), Rick C
<gnuarm.deletethisbit@gmail.com> wrote:

>On Thursday, February 6, 2020 at 7:43:35 AM UTC-5, pozz wrote: >> 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. >> >> extern volatile uint32_t ticks_high; >> >> uint64_t >> GetTick(void) >> { >> uint32_t h1 = ticks_high; >> uint32_t l1 = hwcnt_get(); >> uint32_t h2 = ticks_high; >> >> if (h1 == h2) return ((uint64_t)h1 << 32) | l1; >> else return ((uint64_t)h1 << 32); >> } >> >> Is it correct in single-tasking? In the else branch, I decided to set >> the low part to zero. I think it's acceptable, because if h1!=h2, hw >> counter has just wrapped-around, so it is 0... maybe 1. >> >> What about preemptive multi-tasking? What happens if GetTick() is >> preempted by another higher-priority task that calls GetTick()? >> >> I think it's better to fix the else branch, because the higher-priority >> task could take more than a few milliseconds, so the previous assumption >> that hw counter is 0 (maybe 1) can be incorrect. The fix could be: >> >> uint64_t >> GetTick(void) >> { >> uint32_t h1 = ticks_high; >> uint32_t l1 = hwcnt_get(); >> uint32_t h2 = ticks_high; >> >> if (h1 == h2) return ((uint64_t)h1 << 32) | l1; >> else return ((uint64_t)h1 << 32) | hwcnt_get(); >> } > >All this seems to be more complex than it needs to be. You guys are focused 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 code can be used.
A 1Khz 32-bit counter rolls over about every 49.7 days.
On Thursday, February 6, 2020 at 2:12:56 PM UTC-5, Bernd Linsel wrote:
> Rick C wrote: > > On Thursday, February 6, 2020 at 7:43:35 AM UTC-5, pozz wrote: > > > > All this seems to be more complex than it needs to be. You guys are focused 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. > > 2 ^ 32 / (24 * 60 * 60 * 1000) = 49.71026962... (days) > > > [snipped] > > regards, > Bernd
Yes, I forgot to divide by 24 hours. Nevermind... lol Actually, it still works, just a shorter minimum time. -- Rick C. + Get 1,000 miles of free Supercharging + Tesla referral code - https://ts.la/richard11209
On Thu, 06 Feb 2020 13:43:30 +0100, pozz wrote:

> 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. > > extern volatile uint32_t ticks_high; > > uint64_t GetTick(void) > { > uint32_t h1 = ticks_high; > uint32_t l1 = hwcnt_get(); > uint32_t h2 = ticks_high; > > if (h1 == h2) return ((uint64_t)h1 << 32) | l1; > else return ((uint64_t)h1 << 32); > } > > Is it correct in single-tasking? In the else branch, I decided to set > the low part to zero. I think it's acceptable, because if h1!=h2, hw > counter has just wrapped-around, so it is 0... maybe 1. > > What about preemptive multi-tasking? What happens if GetTick() is > preempted by another higher-priority task that calls GetTick()? > > I think it's better to fix the else branch, because the higher-priority > task could take more than a few milliseconds, so the previous assumption > that hw counter is 0 (maybe 1) can be incorrect. The fix could be: > > uint64_t GetTick(void) > { > uint32_t h1 = ticks_high; > uint32_t l1 = hwcnt_get(); > uint32_t h2 = ticks_high; > > if (h1 == h2) return ((uint64_t)h1 << 32) | l1; > else return ((uint64_t)h1 << 32) | hwcnt_get(); > }
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. -- Chisolm Texas-American
On 06/02/2020 19:02, Rick C wrote:
> On Thursday, February 6, 2020 at 7:43:35 AM UTC-5, pozz wrote: >> 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. >> >> extern volatile uint32_t ticks_high; >> >> uint64_t >> GetTick(void) >> { >> uint32_t h1 = ticks_high; >> uint32_t l1 = hwcnt_get(); >> uint32_t h2 = ticks_high; >> >> if (h1 == h2) return ((uint64_t)h1 << 32) | l1; >> else return ((uint64_t)h1 << 32); >> } >> >> Is it correct in single-tasking? In the else branch, I decided to set >> the low part to zero. I think it's acceptable, because if h1!=h2, hw >> counter has just wrapped-around, so it is 0... maybe 1. >> >> What about preemptive multi-tasking? What happens if GetTick() is >> preempted by another higher-priority task that calls GetTick()? >> >> I think it's better to fix the else branch, because the higher-priority >> task could take more than a few milliseconds, so the previous assumption >> that hw counter is 0 (maybe 1) can be incorrect. The fix could be: >> >> uint64_t >> GetTick(void) >> { >> uint32_t h1 = ticks_high; >> uint32_t l1 = hwcnt_get(); >> uint32_t h2 = ticks_high; >> >> if (h1 == h2) return ((uint64_t)h1 << 32) | l1; >> else return ((uint64_t)h1 << 32) | hwcnt_get(); >> } > > All this seems to be more complex than it needs to be. You guys are > focused 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 code 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) which is incremented when needed in the GetTick code. > > uint64_t > GetTick(void) > { > static uint32_t ticks_high; > uint32_t ticks_hw= hwcnt_get(); > static uint32_t ticks_last; > > if (ticks_last > ticks_hw) ticks_high++; > ticks_last = ticks_hw; > return ((uint64_t)ticks_high << 32) | ticks_hw; > } > > I'm not so conversant in C and I'm not familiar with the conventions > of using time variables. Clearly the time will need to be > initialized by some means and ticks_high would need to be initialized > to correspond to the current time/date, unless this is a run time > variable only tracking time since boot up. > > Is the "call this code at least once in 3 years" requirement > reasonable? In the systems I design that would not be a problem. >
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. I think this should do it: uint64_t GetTick(void) { static volatile uint32_t ticks_high; static volatile uint32_t ticks_last; uint32_t ticks_hw = hwcnt_get(); if (ticks_last > ticks_hw) { uint32_t old_processor_state = disableInterrupts(); ticks_hw = hwcnt_get(); if (ticks_last > ticks_hw) { ticks_high++; ticks_last = ticks_hw; } restoreInterrupts(old_processor_state); } return ((uint64_t)ticks_high << 32) | ticks_hw; }
Il 06/02/2020 22:34, Joe Chisolm ha scritto:
> On Thu, 06 Feb 2020 13:43:30 +0100, pozz wrote: > >> 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. >> >> extern volatile uint32_t ticks_high; >> >> uint64_t GetTick(void) >> { >> uint32_t h1 = ticks_high; >> uint32_t l1 = hwcnt_get(); >> uint32_t h2 = ticks_high; >> >> if (h1 == h2) return ((uint64_t)h1 << 32) | l1; >> else return ((uint64_t)h1 << 32); >> } >> >> Is it correct in single-tasking? In the else branch, I decided to set >> the low part to zero. I think it's acceptable, because if h1!=h2, hw >> counter has just wrapped-around, so it is 0... maybe 1. >> >> What about preemptive multi-tasking? What happens if GetTick() is >> preempted by another higher-priority task that calls GetTick()? >> >> I think it's better to fix the else branch, because the higher-priority >> task could take more than a few milliseconds, so the previous assumption >> that hw counter is 0 (maybe 1) can be incorrect. The fix could be: >> >> uint64_t GetTick(void) >> { >> uint32_t h1 = ticks_high; >> uint32_t l1 = hwcnt_get(); >> uint32_t h2 = ticks_high; >> >> if (h1 == h2) return ((uint64_t)h1 << 32) | l1; >> else return ((uint64_t)h1 << 32) | hwcnt_get(); >> } > > In your timer ISR > low32 += 1 > if (low32 == 0) high32 += 1;
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.
> 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. >
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.