I think I've found a bug in the Timer component's GetCountMicroSeconds when it's called repeatedly at a high rate.
Setup: Timer component, 16-bit, Timer 3, Prescaler 1:8, AVR ATmega2560. Built the simplest possible test project I could - just the Timer component plus one IOC interrupt on a digital pin, nothing else in the project at all. On every edge I call GetCountMicroSeconds, store the result, then StopCounter followed by StartCounter(1). Driving the pin with a signal generator at "high rate" of 55.56Hz and printing every result over serial.
A good chunk of the readings (roughly a third in my test) come back close to 2147483648 (2^31) instead of the correct elapsed microsecond value, while the calls around them are fine. It's not tied to any particular duration - short and long intervals both get hit - so it looks rate-dependent rather than an overflow at some fixed time.
I had a look through the GetRawCount flowchart to see if I could figure out why. It builds the extended tick count like this:
FCR_RETVAL = TIM_x_COUNTER;
Return = Return << BitDepth;
Return = Return | Timer_x_CountRegLow;
Return = Return | (Timer_x_CountRegHigh << 8);
TIM_x_COUNTER is the software overflow counter that gets bumped in the timer's overflow ISR, and it's read separately from the live hardware count register, with nothing disabling interrupts in between. So if the overflow interrupt fires in that gap, you end up combining the overflow count from one side of the rollover with the hardware register from the other side, which would give you a badly torn result. That would explain why it's intermittent and rate-dependent rather than a fixed overflow point - it only bites when there's a real chance of the overflow interrupt landing in that window.
Might be worth wrapping that read (and the equivalent one in StopCounter) in a short critical section, or doing a read-check-retry so both halves always come from the same side of a rollover.
Separately, not sure if related: GetCountMicroSeconds itself converts through a 32-bit float (FVar = InstructionTime * FLOAT Count, then Return = FVar * 1000.0) which could lose precision on large counts, but the main issue looks like the race above
Attached is the project file and Tera Term's log
Edit:
Update: Found harder evidence for this. I added a raw tick count alongside the microsecond value (calling GetRawCount directly, before any conversion), and the fault shows up there too — RawCount occasionally comes back negative (e.g. -30970, -3331, -27893, -18166, -24823...) even though it's a u32. A raw tick count obviously can't be negative, so this is the actual point of corruption — once that small negative value gets treated as unsigned, it wraps to a huge number near 4.3 billion, and GetCountMicroSeconds's ×0.5us/tick scaling turns that into the ~2^31 values I originally reported. Same fault, caught one stage earlier in the calculation.
The size of these negative numbers is the interesting part: they're all under about 31,000 in magnitude, and my Timer setup's own RolloverTime property is 0.032768 seconds — exactly 65536 ticks at my prescaler. So these look like "off by one hardware rollover" errors, not random corruption.
My best guess at the mechanism: the edge interrupt (RX_ISR) runs as a genuine ISR, and on AVR an ISR runs with global interrupts disabled by default. If the hardware timer wraps right as the edge interrupt fires, the timer's own overflow ISR (which increments the software rollover counter GetRawCount relies on) can't run until RX_ISR finishes — so GetRawCount ends up combining an already-wrapped hardware register with a rollover counter that's one behind, undercounting by exactly one rollover period. That would explain both the negative sign and the consistent magnitude.
Attaching the updated test project (adds a RawCount variable/print alongside LastElapsedUs) and the TeraTerm log showing the negative RawCount values lined up against the corresponding ~2^31 LastElapsedUs readings.
Timer component GetCountMicroSeconds
-
medelec35
- Valued Contributor
- Posts: 2377
- http://meble-kuchenne.info.pl
- Joined: Wed Dec 02, 2020 11:07 pm
- Has thanked: 755 times
- Been thanked: 817 times
Timer component GetCountMicroSeconds
- Attachments
-
- UART GetRawCount Results.txt
- (3.62 KiB) Downloaded 3 times
-
- Timer Component IOC test GetRawCount.fcfx
- (15.17 KiB) Downloaded 4 times
-
- UART Result.txt
- (910 Bytes) Downloaded 4 times
-
- Timer Component IOC test.fcfx
- (13.96 KiB) Downloaded 4 times
Martin