# High resolution system timer in STM32

I have some software running on an STM32F103 (I'd also like to be able to use the F4) where I'd like to be able to 'timestamp' events (such as interrupts on external pins) to the nearest microsecond (if not better).

As the software will be running for some time I want to use a 64 bit timer, and to me it made sense to use the built in SysTick timer. I have created SysTickMajor (a 64 bit counter) which increments every time SysTick overflows, and a function getSystemTime which combines this counter and the current value of SysTick to get me an accurate 64 bit time:

#define SYSTICK_RANGE 0x1000000 // 24 bit
volatile long long SysTickMajor = SYSTICK_RANGE;

voif onInit(void) {
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
SysTick_Config(SYSTICK_RANGE-1);
/* Super priority for SysTick - is done over absolutely everything. */
NVIC_InitStructure.NVIC_IRQChannel = SysTick_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

void SysTick_Handler(void) {
SysTickMajor+=SYSTICK_RANGE;
}

long long getSystemTime() {
long long t1 = SysTickMajor;
long long time = (long long)SysTick->VAL;
long long t2 = SysTickMajor;
// times are different and systick has rolled over while reading
if (t1!=t2 && time > (SYSTICK_RANGE>>1))
return t2 - time;
return t1-time;
}


The only problem is this doesn't appear to be 100% accurate - for instance, if I do the following:

bool toggle = false;
while (true) {
toggle = !toggle;
LED1.write(toggle);
long long t = getSystemTime()() + 1000000;
while (getSystemTime() < t);
}


I won't get a good square wave - occasionally there will be glitches (even though the SysTick interrupt is finished very quickly), because at points the time returned by getSystemTime is completely wrong.

UPDATE: When this happens (I am recording the time value on an interrupt triggered by an external event) I dump the last 3 values to serial. Repeatably I get things like:

>0x278-E2281A
0x278-E9999A
0x278-0001E5

>0x5BE-F11F51
0x5BE-F89038
0x5BE-0000FB


I've stuck dashes in to depict which bits come from SysTick.

Now just to avoid doubt, I've used the code from starblue below. I've also changed it to use a 32 bit value for SysTickMajor, and I've ANDed out SysTick->VAL, just in case.

It appears that the SysTick_Handler is not preempting the other interrupt!

Now I've checked over this and I did have the Preemption priorities wrong previously, but now I set NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); then set up the interrupts - SysTick preemption priority 0, and the other interrupt is 7.

Is there something else I have to do to get preemption to work?

UPDATE2: I created a tiny test case for this exact issue (SysTick not preempting other interrupts), and have put it here:

STM32 Interrupt Priority (preemption) Problems

So what's wrong with the code above? Is there a better way to get a good high resolution timer? It seems like something obvious that people would want to do but I'm struggling to find examples.

• Maybe you will try DMA? Increase some variable on timer overflow. Jul 16, 2013 at 18:50
• Do you have an example of incrementing a variable with DMA? Just a note: There should really be interrupt disable/enable around each mention of SysTickMajor in getSystemTime - however even this doesn't stop the occasional glitch Jul 16, 2013 at 19:23
• Your main mistake is that you run while (getSystemTime() < t); : this function longs for some time, that's why you won't be able to get time perfectly. Work on interrupts or make atomic operation of time calculation (SysTick interrupt would modify some long long variable by DMA or other mechanism, and last your line may be like while(SystemTime - SystemTime0 < 1000000) Jul 17, 2013 at 4:54
• It's not like it's a little bit off - occasionally the pulse could be anywhere between 0 and the length it was supposed to be. I have now modified the question after I tried some other things. However if you do have a solution which involves DMA (I'm not sure how it can be done?) I'd be very interested. Jul 17, 2013 at 18:31

As you've discovered, handling counter overflow with an interrupt handler is tricky and race-prone. Even with your priority boost, you'll get the wrong answer if interrupts are disabled while you read the counter. There's a much simpler technique which works if you're not reading the clock from interrupt context (note that I'm using the DWT cycle counter here, which is built into the processor thus fast and simple):

uint64_t last_cycle_count_64 = 0;

// Call at least every 2^32 cycles (every 59.6 seconds @ 72 MHz).
// Do not call from interrupt context!
uint64_t GetCycleCount64() {
last_cycle_count_64 += DWT->CYCCNT - (uint32_t)(last_cycle_count_64);
return last_cycle_count_64;
}


If you need to call it from interrupt context (or with a preemptive OS):

volatile uint64_t last_cycle_count_64 = 0;

// Call at least every 2^32 cycles (every 59.6 seconds @ 72 MHz).
uint64_t GetCycleCount64() {
asm volatile ("cpsid i");  // Disable interrupts.
int64_t r = last_cycle_count_64;
r += DWT->CYCCNT - (uint32_t)(r);
last_cycle_count_64 = r;
return r;
}


You may need to enable the cycle counter first:

DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;  // Set bit 0.

• Nice - so the very act of reading the counter clears it? I guess potentially this could be called on the SysTick timer overflow IRQ, which would ensure that it never got to the point of overflowing. May 22, 2015 at 8:26
• No, reading the counter does not clear it. This code simply updates a larger 64-bit counter using the current value of the smaller 32-bit counter. In principle, you could do the same thing with the SysTick register, but you'd have to perform your update more frequently. Jul 22, 2018 at 3:52
• This general method seems like a good one if you make a custom timer for it. However, the DWT->CYCCNT counter only increments when the debug probe is attached. If you want to use it with the debug probe detached you'll need to enable it separately. However doing so this may allow an attacker access to all the debug registers so it seems inadvisable to rely on DWT->CYCCNT.
– Evan
Aug 29, 2019 at 21:23
• Evan states: "the DWT->CYCCNT counter only increments when the debug probe is attached". At least on my platform (STM32F407), the CYCCNT counter appears to increment regardless of whether a debug probe is attached. I'm looking at the ARMv7-M Architecture Reference Manual (DDI0403E_d), and I don't see any mention of requiring a debug probe. I do see that the counter halts when the processor is halted, which is typically desireable: "CYCCNT does not increment when the processor is halted in Debug state." Sep 3, 2019 at 17:56
• Ah, you do have to enable CYCCNT by setting the low bit of DWT_CTRL. Added a description of this to the answer. Sep 3, 2019 at 18:06

• You don't know whether the interrupt that increased SysTickMajor happened before or after you read the timer value into time.

• Operations on 64-bit values are not atomic on a 32-bit system. So the value for SysTickMajor could be updated by the interrupt between reading the two 32-bit words.

I would do it as follows:

major0 = SysTickMajor;
minor1 = SysTick->VAL;
major1 = SysTickMajor;

minor2 = SysTick->VAL;
major2 = SysTickMajor;

if ( major0 == major1 )
{
time = major1 + minor1;
}
else
{
time = major2 + minor2;
}


Here you assume that if SysTickMajor changed during the first block of assignments it won't change again in the second block. (This assumption could be wrong if this code is interrupted during this routine and can't run for a long time.)

• Thanks - I've now started using your code (see modified question) but I still have the problem! I've even swapped the 64 bit values down to 32 bit in order to be sure that wasn't an issue. It now looks like the SysTick interrupt is not being called when SysTick overflows, rather that there being a synchronization issue. Jul 17, 2013 at 18:29
• I prefer to read low/high/low and repeat if needed. Among other things, that approach will work correctly even if "logical carry propagation" is slow. Suppose, for example, that when by the time the processor reads the data from SysTick->VAL it has already started executing the instruction to read SysTickMajor into major1. The minor1 value could reflect a timer that has wrapped, even though major1 would not. Using low/high/low, such things won't happen unless the relative timing difference between high- and low-word actions exceeds the time between the two low-word reads. Jul 17, 2013 at 19:12

I just found the answer from a very helpful poster on the STM32 forum:

The following isn't correct. SysTick is a 'System Handler', and as such the priority isn't set in this way at all:

  NVIC_InitStructure.NVIC_IRQChannel = SysTick_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // Highest priority
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);


It's actually set with:

  NVIC_SetPriority(SysTick_IRQn, 0);


Calling that code instead solves the problem!

So my version of getSystemTime could have had a problem, and starblue's method is nicer than mine - however the actual cause of the problems was the above.

Just thought I'd add one other possible improvement to getSystemTime that I tried as well:

do {
major0 = SysTickMajor;
minor = SysTick->VAL;
major1 = SysTickMajor;
while (major0 != major1);
return major0 - minor;


This will effectively negate the problem where you could (somehow) end up with the SysTick being implemented twice in very quick succession.

The previous answers are great. I like this one the most:

do {
major0 = SysTickMajor;
minor = SysTick->VAL;
major1 = SysTickMajor;
while (major0 != major1);
return major0 - minor;


You can optimize it a bit more, since you know that if you have a rollover of SysTickMajor that you're not going to get another rollover "a long time." You should be safe doing this:

major = SysTickMajor;
minor = SysTick->VAL;
if (major != SysTickMajor)
return SysTickMajor - SysTick->Val;
return major - minor;


The above assumes, of course, that SysTickMajor and VAL are declared as volatile. Several years ago while in a previous job, I discovered that unfortunately the above is still not perfect on all processors. I was using an STM32F407, which has a cache and is pipelined. Sometimes the fetch of the second SysTickMajor would arrive before the VAL even though they were issued in order. Enter the memory barrier (dmb instruction):

major = SysTickMajor;
minor = SysTick->VAL;
__asm__ volatile("dmb");
if (major != SysTickMajor)
return SysTickMajor - SysTick->Val;
return major - minor;


In my case, I was using a 32-bit TIM2 and a TIM2 rollover ISR which increments the upper 32 bits of my 64-bit cycle counter but I think it should act the same as what you are doing with SysTick.