cancel
Showing results for 
Search instead for 
Did you mean: 

Systick only runs when CPU is active

RSext
Associate II

I've got a strange situation. I've enabled systick, but it doesn't keep time unless the CPU is active. Key excerpts:

main.c:
SysTickInit(sys_clocks.SYSCLK_Frequency);
void SysTickInit(unsigned base_freq) {
  
  SysTick->CTRL = 0; // Turn it off.
  // SysTick->CTRL &= ~SYSTICK_CLKSOURCE_HCLK; // HCLK/8 
  LL_InitTick(base_freq, 250);
 
  SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; // Turn it on... 
  return;
}

void SysTickHandler(void) {
 
  // ***************************************
  // Increment a local system time. 
  g_ulSystemTimeMS += SYSTICKMS;

This is set up for a 250Hz ticker. It keeps correct time when the CPU is fully occupied, but it doesn't seem to work correctly when the system is idling, with or without WFI.

I've also got a 1MHz timer that I can use for an independent check.

What I'm seeing is that the sys ticker only works if I am running a while (1) { ; } loop on the CPU. When I do that, it keeps perfect time.

If I let the CPU idle, I get a total of 7 interrupts over a 10 second period.

I've dug through the reference manual, and I can find little if any documentation about the clock config for the systicker. As best as I can tell, there are no RCC registers to control its sleep behavior and it lives inside the CPU complex.

I see the same behavior with or without a DSB barrier around the update to the global timebase.

I see the same thing regardless of the state of the clock select bit in the systick control register.

The docs refer to the systick clock as being sourced from HCLK, the AHB bus clock. That might be the same AHB clock that drives. What I do know is that the sys ticker produces correct time when setup based upon 520MHz, which is not the HCLK.

12 REPLIES 12
TDK
Guru

SysTick is a Cortex feature which might explain why it's not documented in the RM so much. Behavior no doubt depends on which low power mode you enter. If it stops the cpu clock, it should also stop the systick clock.

0693W00000KbYLbQAN.png 

You can switch the clock source to a timer that still runs in the low power mode if you want to keep receiving interrupts.

If you feel a post has answered your question, please click "Accept as Solution".
Danish1
Lead II

Further to what TDK said, as well as the Reference Manual which describes all the peripherals that ST add, you need to refer to the Programming Manual for your stm32. This describes the arm processor core and all the peripherals (including SysTick) that Arm designed.

Hope this helps,

Danish

RSext
Associate II

Danish,

Thanks for trying to help. This Systick behavior is unlike any of the the 5-10 Cortex-M devices that I've worked with in the past 10 years, including ST's own M0 and M3 parts.

Clock-gated systick is useless. I'm sure thats not how its supposed to work. It seems more likely that I've overlooked something. If the RM diagram is correct, the clock gating happens upstream of the systick clock, which can't be right. I have checked the errata.

The only implementation-defined behavior in ARMs SysTick is the clock source. What I have observed is that systick is clocked off of the 520MHz sysclock, and not sysclock/8 as indicated in the diagram from the reference manual. The systick clock select bit (CLKSOURCE) doesn't seem to work.

Looking at the ARM verilog for the CPU, I can see that there is a Systick clock enable line ( STCLKEN ), so maybe things have changed from older designs and you can clock-gate it. Looking around the internet, I can see that there are some minor disagreements between the ARM CPU implementers guide and public docs ( with regards to STCALIB ).

This is all very odd. Systick has worked flawlessly for me in the past on many devices. I must be missing something.

Danish1
Lead II

520 MHz - so this is high-performance stm32h723/725/730/733/735

What do you mean by "idling" given that it's not while (1) { ; }

And with / without WFI.

For me an idle loop is:

while (1) { WFI; }

And without WFI it becomes

while (1) { ; }

Are you running an RTOS, which (when it doesn't have any tasks) will idle with WFI? So maybe your test of With/Without WFI isn't valid because the arm core will sit at the RTOS's WFI instead.

Hope this helps,

Danish

RSext
Associate II

My system is bare metal, with an exception/kernel layer and a non-priviledged thread/user layer. I use the NVIC instead of an RTOS, and vend a kernel interface to the thread mode code via system calls layer ( https://github.com/rbsexton/sockpuppet ).

It's a an lwIP based platform that has hundreds of millions of operational hours in the field. I'm doing the initial port from TI to STM32.

The thread mode logic is:

while ( there are runnable tasks  ) {
   run the next task;
   if ( no more runnable tasks ) {
      wfi ( or nop )  
   } 
 
Some special details omitted. 

I've got control of the whole stack, and I've tried it with and without WFI.

I must be missing something. There is no way that this part has a clock-gated systick. That would break all kinds of software stacks.

I know that's only pseudo-code, but it's possible to temporarily run out of runnable tasks, for example until another network packet arrives. So I don't see why you should drop out of the while loop.

while (true) {
   if ( no more runnable tasks ) {
      wfi; ( or { ; } )
   } else
      run the next task;
}

We're getting pretty far afield and you don't have all the context. This is a cooperative multitasking system so there is no while(1) loop. That pseudo-code is for yield().

JPeac.1
Senior

If I let the CPU idle, I get a total of 7 interrupts over a 10 second period.

The only obvious difference is with WFI you need an interrupt to wake up. That begs the obvious question, is the SYSTICK interrupt enabled when you use the WFI? Check the SYSTICK and NVIC settings in the debugger before executing WFI. It may be the interrupts you expect are not what's enabled.

This is a problem in certain RTOS implementations when the preemption tick (SYSTICK) irq is disabled for a known, lengthy idle interval where the CPU is halted with a WFI. This is to prevent wakeups while no task is known in advance to be scheduled for execution, so no preemption needed. Either a longer duration timer (i.e. RTC) or external event is used to exit WFI, followed by re-enabling the SYSTICK interrupt.

Granted you don't have an RTOS but in a sense you are solving the same issue. Worst case, try setting up another timer, with interrupt but longer interval compared to SYSTICK, start it before the WFI, and see if it occurs first. If so, the question is why the NVIC does not see the SYSTICK irq.

Jack Peacock

I may have something more fundamental going on here. I removed the two instances of WFI from my existing code, and I've switched to experimenting with an existing known good 1us timer ( TIMER2) that has an existing IRQ handler. I can verify that its running at the correct frequency by doing wristwatch experiments and checking the value.

I set the counter overflow to 1s and added a little code to the interrupt handler to toggle an LED.

The debug shell is polling for user input on UART0 via a spin loop and a SVC call.

What I observe is that I get an LED toggle every time I press a key - that suggests to me that the Timer2 interrupt handler is only running after the CPU enters Exception mode and writes to a peripheral. I've only got the beginnings of a firmware stack here, so everything is running out of DTCM. Hopefully the caches are not involved.

Maybe this is a system configuration issue. I'll look at the STMCube examples and see if there is something I've missed.

Thanks for the ideas.