Author Topic: Correct timing by timer + overflow count  (Read 13757 times)

0 Members and 1 Guest are viewing this topic.

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Correct timing by timer + overflow count
« on: September 08, 2015, 09:45:07 am »
Hi all,

This is more of an MCU programming question, that was mentioned in Jack Ganssle's latest newsletter. He presented the naive way of measuring time beyond a timer's bit-size limit: whenever the timer overflows, in the overflow interrupt, you increase a separate variable. Then, when you need to know the time, you take that variable and the current timer value, and do the math. 

Jack said this usually works but is guaranteed to fail eventually. I assume that's because between the time you access the variable and the time you access the timer value, an overflow may occur and the overall result will be false. 

This issue was actually presented as sort of an advertisement to one of Jack's seminars, so I hope I don't harm his business by asking this here  ;) - how does one overcome the problem?

It seems to me that the only really safe way is to halt the timer itself right before the reading. However, this pause will interfere with the accuracy of the timing, unless I do exact cycle-counting on the process (or activate a separate timer just for that) and add the missing time to the timer afterwards. This doesn't feel very elegant.

So, is there a better way?

Thanks!
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Correct timing by timer + overflow count
« Reply #1 on: September 08, 2015, 10:07:56 am »
You'll need a capture time (on that stops by hardware) otherwise your extra bit hack will have interrupt jitter.
But, an RTOS might make the jitter predictable. Which gives an known error you can put on your spec sheet.

https://en.wikipedia.org/wiki/Interrupt_latency
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 9951
  • Country: nz
Re: Correct timing by timer + overflow count
« Reply #2 on: September 08, 2015, 10:12:52 am »
Use a stm32 and enable hardware master/slave over all of the timers :D

That's like 6x 16bit timers and one 32bit timer

A 128 bit counter at 48mhz overflows after 7,089,215,977,519,551,322,153,637,654,828 seconds
« Last Edit: September 08, 2015, 10:37:05 am by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline Chris C

  • Frequent Contributor
  • **
  • Posts: 259
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #3 on: September 08, 2015, 10:13:06 am »
1) Disable the timer interrupt (preferably all interrupts, so we can execute the following in a predictably timely fashion).
2) Get a copy of the timer value and the overflow count.
3) Check the interrupt flag.  If set, then we know we have to add one to our copy of the overflow count, since the interrupt hasn't yet been able to do it.  The timer value is where uncertainly creeps in, as we don't know if the value overflowed prior to, or after, previously copying its value - and if it was at 999 of 1,000, adding another 1,000 via the overflow count would give us an incorrect result.  But we know for certain it's overflowed NOW!  So copy the timer value again, problem solved.
4) Re-enable interrupts.
5) Use the copy of value and overflow count to calculate our final time.

I figured this out on my own, the first time I encountered this problem.  There may be other ways to do it.  Jack has no monopoly on the solution. ;)
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #4 on: September 08, 2015, 10:33:37 am »
3) Check the interrupt flag.  If set, then we know we have to add one to our copy of the overflow count, since the interrupt hasn't yet been able to do it.  The timer value is where uncertainly creeps in, as we don't know if the value overflowed prior to, or after, previously copying its value - and if it was at 999 of 1,000, adding another 1,000 via the overflow count would give us an incorrect result.  But we know for certain it's overflowed NOW!  So copy the timer value again, problem solved.

Yes! That last step, of re-reading the timer value if the interrupt flag is set, is what I missed in my attempt to solve this earlier. Thanks!

Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline Chris C

  • Frequent Contributor
  • **
  • Posts: 259
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #5 on: September 08, 2015, 10:34:51 am »
Here's an alternate solution.  It doesn't require disabling interrupts.  But does require a long enough timer interval, and short enough interrupts, that the following can ALWAYS run to completion in half the interval.  If you cannot guarantee that, it's fallible, which is why I usually use the prior approach.

1) Get a copy of the timer value (V1) and the overflow count (OC).
2) Get another, separate copy of the timer value (V2).  If V2 < V1, then resolve ambiguity by setting V1=V2, and getting OC again.
3) Compute time based on V1 and OC.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: Correct timing by timer + overflow count
« Reply #6 on: September 08, 2015, 10:38:37 am »
Old-timer timer trick from 8-bit days for accessing long timers through small registers was to read the least most significant word(s) then least significant word, then read the most significant word again. If the most significant word changes you've sampled it during a roll-over, so repeat reading the least significant and most significant words again. The most significant word should be stable. Worst case is you'll stall for a few a handful of instructions.

If you do have to reread the registers you can roll take a few counts off the value to make allowance for the extra instruction cycles it took.
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #7 on: September 08, 2015, 10:47:35 am »
Use a stm32 and master/slave all of the timers to each other :D

That's like 6x 16bit timers and one 32bit timer

A 128 bit counter at 48mhz overflows after 7,089,215,977,519,551,322,153,637,654,828 seconds

Quite safe for everyday use, I guess, but if I want my system to outlast the universe, there may still be an issue  ;D

I actually got a cheap STM32 board lately and a programmer for it, but proper documentation for beginners seems hard to find. I guess I'll have to do some serious datasheet reading.


Thanks Chris C and hmaster_nz for the other suggestion. It sounds reasonable too  :)

Edit: Oh, and thanks Jeroen3, however I currently don't work with RTOSs.
« Last Edit: September 08, 2015, 10:49:17 am by igendel »
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #8 on: September 08, 2015, 12:11:16 pm »
This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting added.
I posted some example code to do this on an AVR last year.
http://nerdralph.blogspot.ca/2014/07/writing-avr-interrupt-service-routines.html
Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #9 on: September 08, 2015, 12:42:18 pm »
This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting

No, if I understand you correctly, that doesn't solve the problem Ganssle was talking about.

Let's say the timer is 8-bit. To get the time, I read it, disable interrupts, read the counter in RAM etc.
Now suppose I caught the timer when it was 255, one nanosecond before it overflowed. There is a possibility, I think, that the overflow interrupt will trip before my "main" code gets the chance to disable it. So it runs, the counter is +1'ed, timer now 0, and only then I get to disable the interrupt and read the (updated) counter. So I get the new RAM value but the old timer value, and that's a bug.

Same if I read the RAM counter first and the timer later.
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline Brutte

  • Frequent Contributor
  • **
  • Posts: 614
Re: Correct timing by timer + overflow count
« Reply #10 on: September 08, 2015, 02:29:13 pm »
I have asked a more generic question on avrfreaks some time ago.
By contrast, I was not interested in extending one timer register but rather whole timer.
I have found that implementations can be categorized in terms of "permissible delays" and range from "ridiculous" up to "timer period".
All (not buggy) extensions require runtime resolution of events which is PITA but doable. An example for resolution of only two events for AVR8 is available on the linked page.

Quote
This is pretty basic.
That is neither pretty nor basic.
 

Offline Chris C

  • Frequent Contributor
  • **
  • Posts: 259
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #11 on: September 09, 2015, 12:38:29 am »
By contrast, I was not interested in extending one timer register but rather whole timer.

Virtual timers could accurately be called the heart of my custom PIC programming environment.

It takes a single real timer, and expands it to up to 16 virtual timers; with up to 32-bit resolution, regardless of what the real timer is.  It's bug-free, lean, can defer its own maintenance tasks in order to not delay an imminent event, and emulates interrupt chaining even if the MCU lacks that feature.  Glitchless PWM is built-in.  So is MIBAM, which can make every pin on an MCU capable of LED dimming, regardless of the number of pins, for about 5% CPU utilization.

Other functions can be hard coded in, or provided via a virtual interrupt (callback).  If any function will run too long, it can use what I call a "bridge" to pass data down to a lower priority interrupt; which does the real processing, and no longer interferes with other virtual timers.

It can be used to poll pins to implement virtual external interrupts, frequency counters, duty cycle measurement, quadrature decoding, and so on.  That obviously requires a trade-off between poll rate and CPU utilization, but it's still handy.  I can partially port my environment to a new MCU, and add native hardware support for these functions when it actually becomes necessary, or at my leisure.  And after that, I can still use it to extend what hardware peripherals are there, using hardware for the most demanding tasks, and software for the least.

And it can produce "0-tick error" when generating a single, larger timer.  Though I'm not sure how often that would actually be useful.  I feel that would usually be better addressed by starting with at least a 16-bit MCU in the first place.

It seems your search for prior art was limited to a single forum, and probably also too narrow in looking specifically for a timer extension.  You'll have better luck by Googling for finished and tested interrupt-driven software PWM implementations.  Which seem to be more common.  And if you think about it, they are nothing but virtual timers being used for a single purpose. 

Still, it seems relatively few people attempt anything of this nature.  Or get it right on the first try.  So by all means, publish your project!
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #12 on: September 09, 2015, 04:41:32 am »
This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting

No, if I understand you correctly, that doesn't solve the problem Ganssle was talking about.

Let's say the timer is 8-bit. To get the time, I read it, disable interrupts, read the counter in RAM etc.
Now suppose I caught the timer when it was 255, one nanosecond before it overflowed. There is a possibility, I think, that the overflow interrupt will trip before my "main" code gets the chance to disable it. So it runs, the counter is +1'ed, timer now 0, and only then I get to disable the interrupt and read the (updated) counter. So I get the new RAM value but the old timer value, and that's a bug.

Same if I read the RAM counter first and the timer later.

You obviously didn't look at my code and I'm guessing you didn't even read the whole blog post that explains the code.
The getseconds() function is only a few lines of C.
Now if you need a microsecond-resolution timer things get a bit more complicated, but you never said that is a requirement.

Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Correct timing by timer + overflow count
« Reply #13 on: September 09, 2015, 05:34:57 am »
Still, it seems relatively few people attempt anything of this nature.  Or get it right on the first try.  So by all means, publish your project!
Virtual timers are often included in operating systems, few people have time to roll their own.
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #14 on: September 09, 2015, 06:37:28 am »
You obviously didn't look at my code and I'm guessing you didn't even read the whole blog post that explains the code.
The getseconds() function is only a few lines of C.
Now if you need a microsecond-resolution timer things get a bit more complicated, but you never said that is a requirement.

It might be my own daftness, but even now after re-reading the post you linked to, I don't see how it relates to the very specific issue I raised in the original post.
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline Brutte

  • Frequent Contributor
  • **
  • Posts: 614
Re: Correct timing by timer + overflow count
« Reply #15 on: September 09, 2015, 10:34:47 am »
Virtual timers could accurately be called the heart of my custom PIC programming environment.
That was not the subject of the Muse.
Quote
You'll have better luck by Googling for finished and tested interrupt-driven software PWM implementations. 
Nor software PWM.
As stated before:
Quote
all implementations can be categorized in terms of "permissible delays" and range from "ridiculous" up to "timer period".
the software PWM falls into "ridiculous" category because to obtain a "0-tick error" you would have to pinpoint toggling code in exact instruction clock.
Quote
Still, it seems relatively few people attempt anything of this nature.  Or get it right on the first try.  So by all means, publish your project!
I have already abandoned that sinking ship.
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #16 on: September 09, 2015, 12:05:05 pm »
You obviously didn't look at my code and I'm guessing you didn't even read the whole blog post that explains the code.
The getseconds() function is only a few lines of C.
Now if you need a microsecond-resolution timer things get a bit more complicated, but you never said that is a requirement.

It might be my own daftness, but even now after re-reading the post you linked to, I don't see how it relates to the very specific issue I raised in the original post.
My solution provides a glitch-free timer.  Isn't that what you want?
If you want to do it the more complicated way, reading the timer register and combining it with the overflow count, it's a bit more code, but still not rocket science.  After cli(), if the overflow interrupt flag is set and the timer register value is < 128 then add 1 to the overflow value.
Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #17 on: September 09, 2015, 03:36:25 pm »
My solution provides a glitch-free timer.  Isn't that what you want?

As a by-product, perhaps. My original question was about the technique of counting time specifically using overflow counting + timer value.

If you want to do it the more complicated way, reading the timer register and combining it with the overflow count, it's a bit more code, but still not rocket science.  After cli(), if the overflow interrupt flag is set and the timer register value is < 128 then add 1 to the overflow value.

Yes, in the vein of some answers given above. It all boils down to checking, in retrospect, whether an overflow occurred while I was "taking time";  the exact details of the implementation seem to be a matter of taste and resource constraints. Thanks!
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: Correct timing by timer + overflow count
« Reply #18 on: September 09, 2015, 11:40:04 pm »
My solution provides a glitch-free timer.  Isn't that what you want?
If you want to do it the more complicated way, reading the timer register and combining it with the overflow count, it's a bit more code, but still not rocket science.  After cli(), if the overflow interrupt flag is set and the timer register value is < 128 then add 1 to the overflow value.

I didn't read your code, but based on the description "This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting added" are you sure that it is glitch free?

Right column is RAM_Counter:Timer Counter for a 16 bit counter, left column is the instructions being run....

0000:FFFF disable interrupts
0000:FFFF read timer.   Gives xxxx:FFFFF for timer value (the upper half comes from RAM)
 --- timer rolls over here ---
0000:0000 read RAM  - GIves 0000:FFFF for timer value
0000:0000 check timer's IRQ flag = true
0000:0000 enable interrupts
-- IRQ happens --
0001:0000 IRQ flag is set, so inc reading from RAM (0000:FFFF => 00001:FFFF)

Or how about:

0000:FFFF disable interrupts
0000:0000 read timer.   Gives xxxx:FFFF for timer value (the upper half comes from RAM)
0000:0000 read RAM  - GIves 0000:FFFF for timer value
 --- timer rolls over here ---
0000:0000 check timer's IRQ flag = true
0000:0000 enable interrupts
-- IRQ happens --
0001:0000 IRQ flag is set, so inc reading from RAM (0000:FFFF => 00001:FFFF)

I guess you are now about to suggest that if an IRQ was flagged you look at the value from the h/w timer, and decide if the IRQ was raised before of after the timer was read (based on the high bit).

Why not just keep it simple...

   read RAM value into reg1
   read H/W timer into reg2
   read RAM value into reg3
   if reg3 != reg1
      clear reg2 to zero
   use reg3:reg2 as timer value

You don't even need to disable interrupts...
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #19 on: September 10, 2015, 02:52:54 am »
If you want to do it the more complicated way, reading the timer register and combining it with the overflow count, it's a bit more code, but still not rocket science.  After cli(), if the overflow interrupt flag is set and the timer register value is < 128 then add 1 to the overflow value.

Yes, in the vein of some answers given above. It all boils down to checking, in retrospect, whether an overflow occurred while I was "taking time";  the exact details of the implementation seem to be a matter of taste and resource constraints. Thanks!

I can only think of one way to do it, and it is straightforward enough that I doubt there's a better way: (code tested only in my head)
Code: [Select]
__uint24 get_ticks()
{
  uint16_t ovfl;
  uint8_t timerval;
  cli();
  ovfl = _overflows;
  timerval = TCNT0;
  if ( (TIFR0 & (1<<TOV0)) && (timerval < 128) ) ovfl++;
  sei();
  return ( (ovfl << 8) | timerval );
}
Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26907
  • Country: nl
    • NCT Developments
Re: Correct timing by timer + overflow count
« Reply #20 on: September 10, 2015, 04:19:26 am »
This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting
No, if I understand you correctly, that doesn't solve the problem Ganssle was talking about.

Let's say the timer is 8-bit. To get the time, I read it, disable interrupts, read the counter in RAM etc.
Now suppose I caught the timer when it was 255, one nanosecond before it overflowed. There is a possibility, I think, that the overflow interrupt will trip before my "main" code gets the chance to disable it. So it runs, the counter is +1'ed, timer now 0, and only then I get to disable the interrupt and read the (updated) counter. So I get the new RAM value but the old timer value, and that's a bug.

Same if I read the RAM counter first and the timer later.
Interesting problem... The timer interrupt adds extra difficulties due to parallel process synchronisation. It is not a particular timer problem but a process synchronisation problem. If you don't use a timer interrupt but just poll the counter from a main loop you can create a global time counter in a very simple way. Every time you read a value from the counter which is less than the previous value an overflow has occured. If you need the instant time just call the routine which determines the time. The only requirement is that the counter is polled often enough so that an overflow do not happen unnoticed.
« Last Edit: September 10, 2015, 04:36:17 am by nctnico »
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #21 on: September 10, 2015, 11:24:30 am »
I can only think of one way to do it, and it is straightforward enough that I doubt there's a better way: (code tested only in my head)
Code: [Select]
__uint24 get_ticks()
{
  uint16_t ovfl;
  uint8_t timerval;
  cli();
  ovfl = _overflows;
  timerval = TCNT0;
  if ( (TIFR0 & (1<<TOV0)) && (timerval < 128) ) ovfl++;
  sei();
  return ( (ovfl << 8) | timerval );
}

Why do you check whether timerval < 128?
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3240
  • Country: gb
Re: Correct timing by timer + overflow count
« Reply #22 on: September 10, 2015, 11:48:08 am »
Why do you check whether timerval < 128?

Think about what would happen if the overflow occurred after TCNT0 had been read.
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #23 on: September 10, 2015, 12:00:49 pm »
Why do you check whether timerval < 128?

Think about what would happen if the overflow occurred after TCNT0 had been read.

Actually that's another bug in the code above. If the OF flag is raised, it should re-read TCNT0 to get its updated value, not just increase the overflow count. This has nothing to do with the number 128. [edit: perhaps no need to re-read TCNT0, but the 128 check is still redundant]
« Last Edit: September 10, 2015, 12:03:44 pm by igendel »
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #24 on: September 11, 2015, 12:39:14 am »
Why do you check whether timerval < 128?

Think about what would happen if the overflow occurred after TCNT0 had been read.

Actually that's another bug in the code above. If the OF flag is raised, it should re-read TCNT0 to get its updated value, not just increase the overflow count. This has nothing to do with the number 128. [edit: perhaps no need to re-read TCNT0, but the 128 check is still redundant]

Sounds like it is rocket science to you.   :palm:
Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #25 on: September 11, 2015, 01:02:34 am »
Sounds like it is rocket science to you.   :palm:

Kind as you are, that still doesn't answer the question - what's the point of comparing to 128.
Under what circumstances, in the code you wrote, will the OF flag be set AND timerval will be >= 128?

[Added: to prevent further insults, please be aware that this is just "part 1" of the question]
« Last Edit: September 11, 2015, 01:07:15 am by igendel »
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #26 on: September 11, 2015, 01:28:39 am »
Ok, sorry for the constant editing, I think I finally figured it out.
Still, a code like this, without that comparison, is clearer and will probably give more accurate results:

Code: [Select]
  cli();
  ovfl = _overflows;
  timerval = TCNT0;
  if (TIFR0 & (1<<TOV0)) {
    ovfl++;
    timerval = TCNT0;
  }
  sei();
  return ( (ovfl << 8) | timerval ); // return result is quite accurate, no time wasted on comparison
« Last Edit: September 11, 2015, 01:47:15 am by igendel »
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #27 on: September 11, 2015, 02:33:25 am »
Ok, sorry for the constant editing, I think I finally figured it out.
Still, a code like this, without that comparison, is clearer and will probably give more accurate results:

Code: [Select]
  cli();
  ovfl = _overflows;
  timerval = TCNT0;
  if (TIFR0 & (1<<TOV0)) {
    ovfl++;
    timerval = TCNT0;
  }
  sei();
  return ( (ovfl << 8) | timerval ); // return result is quite accurate, no time wasted on comparison
That way is worse because it adds jitter when the overflow flag is set.
And although avoiding the comparison to 128 is a wash (maybe 1 extra cycle) vs reloading TCNT0 on the 8-bit AVR, on other MCUs the IO read lantency can be slower than the comparison.

p.s. I think I went pretty easy on you after you falsely accused me of posting buggy code.  Suggesting you are not as smart as me isn't an insult if it is true. :-)
Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline Chris C

  • Frequent Contributor
  • **
  • Posts: 259
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #28 on: September 11, 2015, 03:35:19 am »
That way is worse because it adds jitter when the overflow flag is set.
And although avoiding the comparison to 128 is a wash (maybe 1 extra cycle) vs reloading TCNT0 on the 8-bit AVR, on other MCUs the IO read lantency can be slower than the comparison.

Then again, on some MCUs the "disable all interrupts" facility does not disable those at the very highest priority.  That priority is reserved for hard real-time tasks, like NTSC generation for instance.  And if that priority is used, not only is jitter reduction in get_ticks() pointless; but you may run into a situation where the timer interrupt flag is set and TCNT0>=128, yet hasn't rolled over a full second time.

Introducing hardware-specific and unusual scenarios does complicate things a bit, and I don't blame [igendel] for questioning why you did it that way.
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #29 on: September 11, 2015, 08:44:16 am »
p.s. I think I went pretty easy on you after you falsely accused me of posting buggy code.  Suggesting you are not as smart as me isn't an insult if it is true. :-)

Yeah, sorry about that. I guess I was inclined to doubt you, since your very first response in this thread was irrelevant to the question, and then you falsely accused me of not reading carefully.
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #30 on: September 11, 2015, 11:56:43 am »
p.s. I think I went pretty easy on you after you falsely accused me of posting buggy code.  Suggesting you are not as smart as me isn't an insult if it is true. :-)

Yeah, sorry about that. I guess I was inclined to doubt you, since your very first response in this thread was irrelevant to the question, and then you falsely accused me of not reading carefully.

If you are serious, then I'll be adding you to my ignore preferences.  You ended your initial post with, "So, is there a better way? "
My first response was a better way to do correct timing.  You respond saying my solution doesn't solve the problem, and then when you realize it avoids the problem altogether while providing a glitch-free timer, you say it was irrelevant to the question.  Kinda reminds me of the ex-wife, "I don't care what I said, that's not what I meant!" :-)

And since you admitted you had to re-read my blog post and code to understand how it really works, then you really didn't read it carefully the first time.

Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #31 on: September 11, 2015, 01:25:39 pm »
You ended your initial post with, "So, is there a better way? "

A better way => to overcome the specific overflow-while-reading issue, that I detailed so clearly throughout the post.

Please do add me to your ignore list. It appears both of us will benefit from it.
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline C

  • Super Contributor
  • ***
  • Posts: 1346
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #32 on: September 11, 2015, 03:47:54 pm »
Hi guys

I may be wrong, but I see some problems with the general idea.

First
 remember that C and other high level languages can reorder the machine code they can produce.
A = A + 1
could have additional instructions inserted between the machine code steps needed to accomplish this. The compiler is adding additional time for change to happen which expands the time window where the wrong value could be read.

Second
  If the A from above needs more than one instruction to read/write the value, the time window expands again.

just having to use two machine code instructions for a read modify write adds a time window for a change to happen. 

Turning off interrupts can cause harm to other interrupt based tasks.

So when the change is made in interrupt code, the main code will see the change happen at same time. But if the read of A takes more than one machine code instruction, you still have a problem.

A different way to handle this would be to put a serial number on the A value.
read the serial number, read A, read the serial number.
If first serial number = second serial number then A should be good.
The serial number is changed in interrupt code and is one machine code instruction in size for read/write.

Now the value of A could be many machine code instructions in size for a read.

Here you are dealing with a hardware timer and ram value so
loop: serial_number1 = serial_number
         read hardware timer
         read ram value
         serial number2 = serial_number
         if serial number1 <> serial number2 then loop

Think on possibilities,
Any time you have interrupt code changing a value that takes more than one instruction to read you have a problem.

You have a choice of having a specific fix or a general fix.

C

 
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26907
  • Country: nl
    • NCT Developments
Re: Correct timing by timer + overflow count
« Reply #33 on: September 11, 2015, 06:31:39 pm »
This is why you have critical sections and or mutexes. Asynchronous parallel processes are a common occurence for software engineers.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #34 on: September 12, 2015, 07:29:55 am »
Quote
remember that C and other high level languages can reorder the machine code they can produce.
The opportunities for that should be somewhat limited given the number of "volatile" values in use.  The compiler can't re-order accesses to volatile values, right?

I had this exact problem with a SAMD10 implementation of a microsecond counter recently.  Obscured by rather lengthy synchronization issues and weird reset behavior, too.  ( http://www.avrfreaks.net/forum/samd10-rtc )

It IS that "re-read the counter" that really seems to get you.   I ended up with this code, that seems to work, and includes what I found to be useful debugging hooks:

Code: [Select]
uint32_t micros()
{
  uint32_t thismicros;
  CpuCriticalVar();
  CpuEnterCritical();

#if DEBUG
  rtctick_microscalled++;
#endif
  thismicros = RTC->MODE0.COUNT.reg;
  if (RTC->MODE0.INTFLAG.bit.CMP0) {
    /*
     * Check for a ms interrupt pending but not yet
     * serviced, and correct for it.
     */
    thismicros = RTC->MODE0.COUNT.reg;  //re-read
    thismicros += 1000;
  }
  thismicros += 1000*millisecondcount;
   
#if DEBUG
  if (thismicros < rtctick_lastmicros) {
    // We shouldn't get here.  But allow it to BKPT
    rtctick_backwards++;
    rtctick_trace[rtctick_tracei].current = thismicros;
    rtctick_trace[rtctick_tracei].last = rtctick_lastmicros;
    rtctick_tracei = (rtctick_tracei+1) & 7;
  }
  rtctick_lastmicros = thismicros;
#endif
   
  CpuExitCritical();
  return thismicros;
}
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3240
  • Country: gb
Re: Correct timing by timer + overflow count
« Reply #35 on: September 12, 2015, 08:07:45 am »
Quote
remember that C and other high level languages can reorder the machine code they can produce.
The opportunities for that should be somewhat limited given the number of "volatile" values in use.  The compiler can't re-order accesses to volatile values, right

You'd think not, but I had this exact problem in avr-gcc.  I had to use a memory barrier to force it to work as intended.
 

Offline donotdespisethesnake

  • Super Contributor
  • ***
  • Posts: 1093
  • Country: gb
  • Embedded stuff
Re: Correct timing by timer + overflow count
« Reply #36 on: September 12, 2015, 08:30:45 am »
Quote
remember that C and other high level languages can reorder the machine code they can produce.
The opportunities for that should be somewhat limited given the number of "volatile" values in use.  The compiler can't re-order accesses to volatile values, right?

volatile used to be a cheap way to get synchronization, but in fact the C spec is hazy on the subject (like many other things). At least volatile accesses can be reordered within a sequence point, and volatile can be be re-ordered with respect to non-volatile accesses. Modern gcc exploits more of this behavior, so traditional assumptions about volatile should not be relied on.

The general rule is that "a single-threaded application should see no difference in behavior" which is great for desktop apps, not great for embedded. Memory barriers can help, but you may still need proper synchronization primitives to get correct behavior.

I've struggled against gcc, and so apparently have Linux kernel team. In one case, to get the code and timing I wanted I gave up with C and used assembler. Otherwise keeping it in C I have to accept some compromise, and generally avoid "tricky" code which requires a specific sequence to work correctly. Even if code works during test, it can easily break depending on optimization when code is added or changed.

It's bad enough when I found bugs in the hardware timers, and then C compiler adds other weirdness on top. "Keep it simple" is a good approach.

Some interesting links :
http://blog.regehr.org/archives/28
https://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming
https://lwn.net/Articles/233479/
« Last Edit: September 12, 2015, 08:35:27 am by donotdespisethesnake »
Bob
"All you said is just a bunch of opinions."
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Correct timing by timer + overflow count
« Reply #37 on: September 12, 2015, 01:03:51 pm »
Volatile insists that each operation with the variables uses the variable from memory. Instead of a cached one in the registers.
Example:
Code: [Select]
int d; // d is global, otherwise it won't get a memory location
int main(void) {
volatile int vd;
vd++;
vd++;
vd++;
__DMB();
d++;
d++;
d++;
}
Will have 3 read-modify-write cycles. As compared to a non volatile one where it will only have 1 read, 3 modifies, and 1 write.
That makes optimizing much more complicated. But there might still be things to do. For example, I used a NOP instead of an DMB first. But it was inserted between the last two vd++'s. Not wat I typed in C.
Code: [Select]
    34:         vd++;
0x080002A6 9800      LDR      r0,[sp,#0x00]
0x080002A8 1C40      ADDS     r0,r0,#1
0x080002AA 9000      STR      r0,[sp,#0x00]
    35:         vd++;
0x080002AC 9800      LDR      r0,[sp,#0x00]
0x080002AE 1C40      ADDS     r0,r0,#1
0x080002B0 9000      STR      r0,[sp,#0x00]
    36:         vd++;
    37:         
0x080002B2 9800      LDR      r0,[sp,#0x00]
0x080002B4 1C40      ADDS     r0,r0,#1
0x080002B6 9000      STR      r0,[sp,#0x00]
    38:         __DMB();
    39:         
0x080002B8 F3BF8F5F  DMB.W   
    40:         d++;
    41:         d++;
0x080002BC 4903      LDR      r1,[pc,#12]  ; @0x080002CC
0x080002BE 6808      LDR      r0,[r1,#0x00]
    42:         d++;
0x080002C0 1CC0      ADDS     r0,r0,#3
0x080002C2 6008      STR      r0,[r1,#0x00]
    43: }
0x080002C4 2000      MOVS     r0,#0x00
(yes, its arm)
Registers are volatile defined in the header. Or at least they should be!
« Last Edit: September 12, 2015, 01:07:21 pm by Jeroen3 »
 

Offline C

  • Super Contributor
  • ***
  • Posts: 1346
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #38 on: September 12, 2015, 04:24:25 pm »
Thank you  Jeroen3 your code shows exactly part of what I was trying to get at.

Your
  vd++;
shows part what I was trying to say. This is not a new problem.
Back in the days of S-100 & VME buses where you could have many processors on the buss, it was easy to get caught.
You have three instructions for each vd++  So if you had a second processor accessing this same value, you still have a problem.
The timing could end up like this
Processor     code
Code: [Select]

1             34:         vd++;
2             34:         vd++;
1             0x080002A6 9800      LDR      r0,[sp,#0x00]
2             0x080002A6 9800      LDR      r0,[sp,#0x00]
1             0x080002A8 1C40      ADDS     r0,r0,#1
2             0x080002A8 1C40      ADDS     r0,r0,#1
1             0x080002AA 9000      STR      r0,[sp,#0x00]
2             0x080002AA 9000      STR      r0,[sp,#0x00]
Now instead of VD increasing by 2 it increased by 1 and the second was hidden with no errors or notice.
With three instructions you do not have a single buss cycle so a second processor could access the value.
The 68000 had an instruction "TAS"  Test and SET instruction. The memory buss was held so the memory read & memory Write were back to back and indivisible.

Above I used a second processor, but DMA could do this also.
The hardware could be the second processor.
If Processor #2 is interrupt code you have just bunched the three for #2 together where one of the #2's instructions are now.

The C language is bad news in a lot of ways, But the C programmer is just as bad.
 C lost the "Keep it Simple" a very long time ago.

C
 

 



 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf