cancel
Showing results for 
Search instead for 
Did you mean: 

Why is the SysTick timer off by a few clock cycles?

Jurrien de Klerk
Associate II
Posted on August 11, 2017 at 08:36

Hello all,

Sometime ago I was looking into configuring the SysTick timer for an stm32f407 mcu. As the CMSIS lib provides a function for this, this is kind of straight forward. To verify the configuration I measured the clock cycle count.  When I was looking whether I configured the timer correctly, I discovered something I don't understand; the count between SysTick interrupts was slightly less then expected. I configured the SysTick to hit every 1 ms with a system clock of a 168 MHz, so I would expect that the interrupt would hit every 168000 clock ticks. However, I measure 167996.

Does anyone has an idea why this is?

Best regards,

Jurrien

#systick-config #interrupts
16 REPLIES 16
Posted on August 15, 2017 at 17:26

the instructions take 3cycles.

They don't except very specific case (well-controlled asm code run from a well-controlled memory position under well-controlled setup), and it can be off by far, 20 may be a reasonably good number.

Remember, the 32-bitters are not microcontrollers, these are SoC, the processor thrown together with peripherals, bound by a fabric of buses and clocks. If you want to go down to single cycles, be prepared for a fair (read: huge) amount of reading - start with the ARMv7 Architecture Reference Manual, continue with Cortex-M4 TRM, and then do a very thorough reading of the STM32F4xx RM and DS. Be prepared to be wiser and at the same time confused more than you are now. No smiley.

But maybe more importantly, the amount of cycles should be constant right?

Again, there is at least a dozen of sources of interrupt latencies in a 'F4xx, resulting in jitter. Again, it's up to the basic reading as above.

Should you do it properly (i.e. a single point read to

DWT->CYCCNT

, a long-term average would match the expectation.

JW

extern 'C' void SysTick_Handler(void)
{
 static uint32_t count = DWT->CYCCNT;
 uint32_t current_count;
 uint32_t inteval;
 current_count = DWT->CYCCNT; // single-point read
 interval = current_count - count;
 count = current_count;
 if(inteval) {
 // useless, but should prevent the compiler from optimizing interval away
 }
}

Posted on August 16, 2017 at 08:49

They don't except very specific case (well-controlled asm code run from a well-controlled memory position under well-controlled setup), and it can be off by far, 20 may be a reasonably good number.

Fair enough. I went back to the original experiment; do nothing except the SysTick handler. Then I used your proposal for reading the instruction count (copy-pasted the code; note that I made a typo in my original code snippet which you copied, 'intevel' should be 'interval' otherwise it does not compile). Interestingly my magic number returned. interval equals 167996. Always (so no deviation).

I suspect that, when the debug support peripheral creates a package to send over the SWV, it uses to DWT->CYCCNT to provide the 'time stamp'. Therefore, I suspect that attolic's Exception Trace Log tool provided the correct values, which in tern means that I measured per configuration (and I had 12 different configurations) 1000 times the same SysTick interval, which was constantly lower then the expected value. Per configuration, the amount of cycles it was off was constant and always less then the expected value.

The inaccuracy I measured is only off by less then 25 ns, which is probably fine for what ever application you build. However, it just sounds smelly to me. I also do understand that the SysTick unit is part of pretty much every ARM mcu, so its quite unlikely that the actual SysTick peripheral is off by anything, so I guess there is some error in how I measure. However, I would really like to know what.

Posted on August 16, 2017 at 12:48

I suspect that, when the debug support peripheral creates a package to send over the SWV, it uses to DWT->CYCCNT to provide the 'time stamp'.

Yes, that's the original purpose of DWT->CYCCNT.

167996. Always (so no deviation).

This is hummm, then.

JW

Posted on August 21, 2017 at 16:08

So I tried and the SysTick interrupt *is* cycle-precise as set in SysTick registers.

However, if there is a debug stop (breakpoint, manual pause), in that particular cycle the DWT_CYCCNT exhibited the behaviour you've shown, ie. SysTick appears to be shorted by a couple of cycles. The exact difference depends apparently on the exact procedure the debugger performs, e.g. it was different between the manual stop and the breakpoint case.

According to ARM® v7-M Architecture Reference Manual (is this supposed to be abbreviated as ARMv7M ARM? 🙂 ), both SysTick and DWT_CYCCNT are stopped in Debugging mode; apparently the exact moment when they stop or restart is different between the two.

This is an on-chip debugger, not a full-fledged hardware emulator; so it *is* intrusive and has a bunch of imperfections.

JW

Jurrien de Klerk
Associate II
Posted on August 23, 2017 at 08:34

Thanks JW for further looking into this .So basically you are saying that some cycles will be 'lost' when initiating a break for debug?

I did a quick test to verify this: I used the single point read of the DWT->CYCCNT code snippet you supplied and changed the 'useless' if-statement with a compare aganst168000.

void SysTick_Handler(void)
{
 static uint32_t count = DWT->CYCCNT;
 uint32_t current_count;
 uint32_t interval;
 current_count = DWT->CYCCNT; // single-point read
 interval = current_count - count;
 count = current_count;
 if(interval != 168000) {
 interval--;
 // useless, but should prevent the compiler from optimizing interval away
 }
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Then, to avoid using the debugger to get the infoI set a break point at line 11of this code snippet ('interval--;'). Theoretically, once the code is running, this break point should not hit right? But it did :-(. And again, the interval value is 167997 consistently.

Thanks,

Jurrien

Posted on August 23, 2017 at 09:44

The first time the debugger stops, the 'mismatch' occurs. As a consequence it stops again in that if() so it occurs again, etc.

Devise the test so that it ignores the first mismatch after reset.

There's still chance that the debugger does something intrusive (stops execution here and there) on its on behalf. Devise the test so that it runs without the debugger, e.g. toggling a pin in that if() and observing on a LA.

JW

Posted on August 23, 2017 at 11:49


volatile uint32_t interval;
volatile uint8_t a[0x100];
uint32_t aidx;
void SysTick_Handler(void);
void SysTick_Handler(void) {
 static uint32_t count;
 static _Bool not_first_time = 0;
 uint32_t cc;
 cc = *DWT_CYCCNT;
 interval = cc - count;
 count = cc;
 a[aidx] = interval; aidx++; if (aidx >= sizeof(a)/sizeof(a[0])) aidx = 0;
 if (not_first_time && (interval != 168000)) {
 __asm('nop');
 }
 not_first_time = 1;
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?