-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ARMv8M: Ensure SysTick timer is disabled when modifying SysTick regs #660
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -393,7 +393,7 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; | |
#if ( configUSE_TICKLESS_IDLE == 1 ) | ||
__attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) | ||
{ | ||
uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; | ||
uint32_t ulControlStatusValue, ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; | ||
TickType_t xModifiableIdleTime; | ||
|
||
/* Make sure the SysTick reload value does not overflow the counter. */ | ||
|
@@ -422,7 +422,7 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; | |
* is accounted for as best it can be, but using the tickless mode will | ||
* inevitably result in some tiny drift of the time maintained by the | ||
* kernel with respect to calendar time. */ | ||
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT ); | ||
portNVIC_SYSTICK_CTRL_REG &= ~( portNVIC_SYSTICK_ENABLE_BIT ); | ||
|
||
/* Use the SysTick current-value register to determine the number of | ||
* SysTick decrements remaining until the next tick interrupt. If the | ||
|
@@ -458,8 +458,7 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; | |
/* Set the new reload value. */ | ||
portNVIC_SYSTICK_LOAD_REG = ulReloadValue; | ||
|
||
/* Clear the SysTick count flag and set the count value back to | ||
* zero. */ | ||
/* Clear the SysTick count flag and set the count value back to 0. */ | ||
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; | ||
|
||
/* Restart SysTick. */ | ||
|
@@ -497,17 +496,29 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; | |
__asm volatile ( "dsb" ); | ||
__asm volatile ( "isb" ); | ||
|
||
/* Disable the SysTick clock without reading the | ||
* portNVIC_SYSTICK_CTRL_REG register to ensure the | ||
* portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again, | ||
* the time the SysTick is stopped for is accounted for as best it can | ||
/* | ||
* Read the contents of the portNVIC_SYSTICK_CTRL_REG register into | ||
* ulControlStatusValue to ensure portNVIC_SYSTICK_COUNT_FLAG_BIT is taken | ||
* into account later on. | ||
*/ | ||
ulControlStatusValue = portNVIC_SYSTICK_CTRL_REG; | ||
|
||
/* Disable the SysTick timer. */ | ||
ulControlStatusValue &= ~( portNVIC_SYSTICK_ENABLE_BIT ); | ||
portNVIC_SYSTICK_CTRL_REG = ulControlStatusValue; | ||
|
||
/* Update ulControlStatusValue */ | ||
ulControlStatusValue |= portNVIC_SYSTICK_CTRL_REG; | ||
|
||
/* | ||
* The time the SysTick is stopped for is accounted for as best it can | ||
* be, but using the tickless mode will inevitably result in some tiny | ||
* drift of the time maintained by the kernel with respect to calendar | ||
* time*/ | ||
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT ); | ||
* time. | ||
*/ | ||
|
||
/* Determine whether the SysTick has already counted to zero. */ | ||
if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) | ||
if( ( ulControlStatusValue & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) | ||
{ | ||
uint32_t ulCalculatedLoadValue; | ||
|
||
|
@@ -531,6 +542,9 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; | |
* function exits, the tick value maintained by the tick is stepped | ||
* forward by one less than the time spent waiting. */ | ||
ulCompleteTickPeriods = xExpectedIdleTime - 1UL; | ||
|
||
/* Clear portNVIC_SYSTICK_COUNT_FLAG_BIT */ | ||
ulControlStatusValue &= ~( portNVIC_SYSTICK_COUNT_FLAG_BIT ); | ||
} | ||
else | ||
{ | ||
|
@@ -568,34 +582,64 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; | |
portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; | ||
} | ||
|
||
/* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, | ||
/* | ||
* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, | ||
* then set portNVIC_SYSTICK_LOAD_REG back to its standard value. If | ||
* the SysTick is not using the core clock, temporarily configure it to | ||
* use the core clock. This configuration forces the SysTick to load | ||
* use the core clock. This configuration forces the SysTick to load | ||
* from portNVIC_SYSTICK_LOAD_REG immediately instead of at the next | ||
* cycle of the other clock. Then portNVIC_SYSTICK_LOAD_REG is ready | ||
* to receive the standard value immediately. */ | ||
* cycle of the external clock. Then portNVIC_SYSTICK_LOAD_REG is ready | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I belive the other clock can also be internal - if a peripheral time is considered internal - but I may be wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would you prefer the term "implementation-defined clock?" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ARM documentation refers to this as the "IMPLEMENTATION DEFINED external reference clock." |
||
* to receive the standard value immediately. | ||
*/ | ||
|
||
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; | ||
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; | ||
|
||
#if ( portNVIC_SYSTICK_CLK_BIT_CONFIG == portNVIC_SYSTICK_CLK_BIT ) | ||
{ | ||
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; | ||
|
||
/* Enable SysTick */ | ||
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; | ||
} | ||
#else | ||
{ | ||
/* The temporary usage of the core clock has served its purpose, | ||
* as described above. Resume usage of the other clock. */ | ||
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT; | ||
/* Temporarily switch to core clock */ | ||
ulControlStatusValue |= portNVIC_SYSTICK_CLK_BIT; | ||
portNVIC_SYSTICK_CTRL_REG = ulControlStatusValue; | ||
|
||
/* Enable SysTick */ | ||
ulControlStatusValue |= portNVIC_SYSTICK_ENABLE_BIT; | ||
portNVIC_SYSTICK_CTRL_REG = ulControlStatusValue; | ||
|
||
/* Read portNVIC_SYSTICK_CTRL_REG into ulControlStatusValue */ | ||
ulControlStatusValue |= portNVIC_SYSTICK_CTRL_REG; | ||
|
||
/* Disable SysTick */ | ||
ulControlStatusValue &= ~( portNVIC_SYSTICK_ENABLE_BIT ); | ||
portNVIC_SYSTICK_CTRL_REG = ulControlStatusValue; | ||
|
||
/* Update ulControlStatusValue */ | ||
ulControlStatusValue |= portNVIC_SYSTICK_CTRL_REG; | ||
|
||
if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) | ||
/* Switch back to external clock. */ | ||
ulControlStatusValue &= ~( portNVIC_SYSTICK_CLK_BIT ); | ||
portNVIC_SYSTICK_CTRL_REG = ulControlStatusValue; | ||
|
||
if( ( ulControlStatusValue & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) | ||
{ | ||
/* The partial tick period already ended. Be sure the SysTick | ||
* counts it only once. */ | ||
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0; | ||
|
||
/* Clear portNVIC_SYSTICK_COUNT_FLAG_BIT */ | ||
ulControlStatusValue &= ~( portNVIC_SYSTICK_COUNT_FLAG_BIT ); | ||
} | ||
|
||
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; | ||
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; | ||
|
||
/* Enable SysTick */ | ||
ulControlStatusValue |= ( portNVIC_SYSTICK_ENABLE_BIT | portNVIC_SYSTICK_INT_BIT ); | ||
portNVIC_SYSTICK_CTRL_REG = ulControlStatusValue; | ||
} | ||
#endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ | ||
|
||
|
@@ -620,13 +664,26 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU | |
} | ||
#endif /* configUSE_TICKLESS_IDLE */ | ||
|
||
/* Stop and reset the SysTick. */ | ||
portNVIC_SYSTICK_CTRL_REG = 0UL; | ||
/* Stop the SysTick timer, clear the value. */ | ||
portNVIC_SYSTICK_CTRL_REG &= ~( portNVIC_SYSTICK_ENABLE_BIT ); | ||
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; | ||
|
||
/* Configure SysTick to interrupt at the requested rate. */ | ||
/* Configure SysTick to interrupt at the requested rate with the request clock source. */ | ||
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; | ||
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; | ||
|
||
/* Set or clear the portNVIC_SYSTICK_CLK_BIT as required. */ | ||
|
||
#if ( portNVIC_SYSTICK_CLK_BIT_CONFIG == portNVIC_SYSTICK_CLK_BIT ) | ||
paulbartell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_CLK_BIT; | ||
} | ||
#else /* portNVIC_SYSTICK_CLK_BIT_CONFIG == 0 */ | ||
{ | ||
portNVIC_SYSTICK_CTRL_REG &= ~( portNVIC_SYSTICK_CLK_BIT_CONFIG ); | ||
} | ||
#endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ | ||
|
||
portNVIC_SYSTICK_CTRL_REG |= ( portNVIC_SYSTICK_ENABLE_BIT | portNVIC_SYSTICK_INT_BIT ); | ||
} | ||
/*-----------------------------------------------------------*/ | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original code:
The new code:
Therefore I think the original code is (more) correct, and the updated code is incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RichardBarry The remaining bits of SYSTICK_CTRL are labeled RES0. According to the arm glossary it is implementation-defined whether or not writes or zero or one to these bits have any side effects.
Given this, the read-modify-write operations better comply with ARM's recommendations in the Armv8-M Architecture Reference Manual and Glossary.