Skip to main content
Jurrien de Klerk
Associate
August 11, 2017
Question

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

  • August 11, 2017
  • 16 replies
  • 3997 views
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
This topic has been closed for replies.

16 replies

waclawek.jan
Super User
August 11, 2017
Posted on August 11, 2017 at 11:12

How do you measure it?

JW

Jurrien de Klerk
Associate
August 11, 2017
Posted on August 11, 2017 at 11:21

Thanks for the reply waclawek.jan. First I measured it using a trace tool in my IDE (TrueSTUDIO), but during my experiment I started to suspect that the deviation measured might be in the tool. Therefore, I changed to the clock cycle count register in the debug peripheral (DWT->CYCCNT), this gave me the same result.

waclawek.jan
Super User
August 11, 2017
Posted on August 11, 2017 at 12:34

Need more details.

JW

Jurrien de Klerk
Associate
August 11, 2017
Posted on August 11, 2017 at 13:27

The systick handler is as follow:

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

and the SysTick_Config call looks like:

// SystemCoreClock equals 168000000
if(SysTick_Config(SystemCoreClock / 1000)) {
 // Error ...
 return;
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

And finally the main loop is just a while(1) {} loop. I just rechecked the values of the experiment, and now they oscillate around

167980, but they are always below the 168000. I compiled this with the lowest optimization level (-O0), it should not matter expect that some values might be optimized out. To see the values I have a breakpoint on line 6 of the first code fragment.

Please let me know if I forgot an important detail.

waclawek.jan
Super User
August 11, 2017
Posted on August 11, 2017 at 15:19

Guess how many cycles elapse between these two reads to

DWT->CYCCNT...:
uint32_t inteval = DWT->CYCCNT - count;
 count = DWT->CYCCNT;

:-) JW
Brian TIDAL
ST Technical Moderator
August 11, 2017
Posted on August 11, 2017 at 15:15

Hi,

can you check the SysTick Reload value (SysTick->LOAD) and SystemCoreClock value? The reload value is derived from SystemCoreClock value: reload = ((SystemCoreClock / 1000) -1)

On my side, on STM32F429I-DISC1 discovery board with HCLK=168MHz (*), SystemCoreClock=168000000 and SysTick->LOAD=167999.

(*) Clock config: HSE=8MHz, PLL source=HSE, PLL_M=4 N=168 P=2.

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
Jurrien de Klerk
Associate
August 15, 2017
Posted on August 15, 2017 at 14:51

Thanks for your response, its much appreciated :-).

Waclawek.Jan

‌ . Unfortunately my life isn't so easy. I performed the test with several different clock frequency settings and different SysTick timer intervals, all resulted in different deviations. Also, the deviation depends on whether the mcu is active or not.

TISSERAND.Bruno

‌ I checked the

SysTick->LOAD. For me it's the 167999 as well.

waclawek.jan
Super User
August 15, 2017
Posted on August 15, 2017 at 15:53

Jurrien,

don't forget that

DWT->CYCCNT

is ticking continuously, all the time the program is executing. Imagine it's a real clock.

In the above snippet, you first read

DWT->CYCCNT

to calculate the time difference. Say, it's 16:

The you read it again to start measuring the time between two interrupts. But it took time to calculate the time difference, so say you read in 16:

Then the next interrupt chimes in and you read

DWT->CYCCNT

to calculate the time difference. Say, it's 17:

What is the span between two interrupts in this example?

JW

Jurrien de Klerk
Associate
August 15, 2017
Posted on August 15, 2017 at 17:00

Jan,

I have to admit, I did not look up the actual clock count for the load- and subtract instruction, but let assume (for a worst case calculation) the instructions take 3 cycles. (I suspect that the subtract will only be on cycle and the load instruction 2, but this would only make it worst). We have 1 instruction for reading count, 1 for DWT->CYCCNT and one for subtracting. So we execute 3 instruction, times 3 cycles is smaller then 20 cycles (168000 -

167980 = 20)

. But maybe more importantly, the amount of cycles should be constant right? And it isn't.

I realize now that giving the code snippet was a bad idea. I encountered the problem some weeks ago. Back then, I had a program doing nothing expect for executing the SysTick irq. I measured the cycle count using an build in tool of Atollic's TrueSTUDIO, the SWV Exception Trace Log. I was surprised by the result so I tried several different configuration by changing the clock frequency and SysTick interval. Still using the SWV Exception Trace Log I measured different deviation depending on the configuration.

At first, I thought that this issue might be related to the measurement tool, so I had a discussion with the support for TrueSTUDIO. After a while we figured that, using the DWT->CYCCNT, we could show that this problem is not in the measurement tool. And so I ended on this forum.

waclawek.jan
Super User
August 15, 2017
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
 }
}

Jurrien de Klerk
Associate
August 16, 2017
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.

Jurrien de Klerk
Associate
August 23, 2017
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

Jan Waclawek
Visitor II
August 23, 2017
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

waclawek.jan
Super User
August 23, 2017
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;
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?