Skip to content

Tickless idle fixes/improvement #59

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

Merged
merged 30 commits into from
Oct 3, 2022
Merged
Changes from 2 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0c7b04b
Fix tickless idle when stopping systick on zero...
jefftenney May 11, 2020
967acc9
Fix imminent tick rescheduled after tickless idle
jefftenney May 11, 2020
316b656
Fix tickless idle with alternate systick clocking
jefftenney May 21, 2020
18222af
Improve comments and name of preprocessor symbol
jefftenney Jun 23, 2020
a10184f
Revert introduction of 2nd name for NVIC register
jefftenney Jul 31, 2020
d904c9a
Merge remote-tracking branch 'upstream/master' into tickless-idle-imp…
jefftenney Sep 11, 2020
607f166
Merge branch 'master' into tickless-idle-improvements
cobusve Oct 22, 2020
85015c7
Merge branch 'master' into tickless-idle-improvements
cobusve Oct 23, 2020
aded661
Merge branch 'main' into tickless-idle-improvements
jefftenney Feb 9, 2021
cfabb61
Replicate to other Cortex M ports
jefftenney Feb 10, 2021
9ae578b
Revert changes to IAR-CM0-portmacro.h
jefftenney Feb 11, 2021
9b8fd32
Merge branch 'main' into tickless-idle-improvements
abhidixi11 May 5, 2021
a7c975a
Merge branch 'main' into tickless-idle-improvements
jefftenney Aug 17, 2021
6b10420
Merge branch 'main' into tickless-idle-improvements
n9wxu Dec 29, 2021
c271c2c
Merge branch 'main' into tickless-idle-improvements
n9wxu Dec 29, 2021
3fd2de8
Merge branch 'main' into tickless-idle-improvements
n9wxu Jan 7, 2022
493b3cf
Merge branch 'main' into tickless-idle-improvements
n9wxu Jan 10, 2022
62f68ec
Merge branch 'main' into tickless-idle-improvements
jefftenney Jan 29, 2022
5e6d000
Handle edge cases with slow SysTick clock
jefftenney Jan 29, 2022
8192434
Merge branch 'main' into tickless-idle-improvements
n9wxu Feb 1, 2022
ac5a74d
Merge branch 'main' into tickless-idle-improvements
jefftenney Feb 9, 2022
fa5fe39
Merge branch 'main' into tickless-idle-improvements
n9wxu Feb 16, 2022
9db8350
Merge branch 'main' into tickless-idle-improvements
n9wxu Feb 18, 2022
ce077f6
Merge branch 'main' into tickless-idle-improvements
n9wxu Feb 24, 2022
4327f77
Merge branch 'main' into tickless-idle-improvements
jefftenney Apr 8, 2022
df539cc
Merge branch 'main' into tickless-idle-improvements
n9wxu Aug 11, 2022
50a7bb5
Merge branch 'main' into tickless-idle-improvements
alfred2g Aug 16, 2022
b5e874f
Merge branch 'main' into tickless-idle-improvements
n9wxu Sep 21, 2022
0dc2a36
Merge branch 'main' into tickless-idle-improvements
n9wxu Sep 26, 2022
8b9fc40
Merge branch 'main' into tickless-idle-improvements
n9wxu Oct 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 68 additions & 44 deletions portable/GCC/ARM_CM4F/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,19 @@
#error This port can only be used when the project options are configured to enable hardware floating point support.
#endif

#ifndef configSYSTICK_CLOCK_HZ
#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ
/* Ensure the SysTick is clocked at the same frequency as the core. */
#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
#else
/* The way the SysTick is clocked is not modified in case it is not the same
as the core. */
#define portNVIC_SYSTICK_CLK_BIT ( 0 )
#endif

/* Constants required to manipulate the core. Registers first... */
#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) )
#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) )
#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) )
#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) )
#define portNVIC_ICSR_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) )
/* ...then bits in the registers. */
#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL )
#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL )
#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL )
#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL )

/* Constants used to detect a Cortex-M7 r0p1 core, which should use the ARM_CM7
Expand Down Expand Up @@ -101,6 +94,19 @@ occurred while the SysTick counter is stopped during tickless idle
calculations. */
#define portMISSED_COUNTS_FACTOR ( 45UL )

/* Let the user override the default SysTick clock rate. If defined by the
user, this symbol must equal the SysTick clock rate when the CLK bit is 0 in the
configuration register. */
#ifndef configSYSTICK_CLOCK_HZ
#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ
/* Ensure the SysTick is clocked at the same frequency as the core. */
#define portNVIC_SYSTICK_CLK_BIT_SETTING ( portNVIC_SYSTICK_CLK_BIT )
#else
/* Select the option to clock SysTick not at the same frequency as the core.
The clock used is often a divided version of the core clock. */
#define portNVIC_SYSTICK_CLK_BIT_SETTING ( 0 )
#endif

/* Let the user override the pre-loading of the initial LR with the address of
prvTaskExitError() in case it messes up unwinding of the stack in the
debugger. */
Expand Down Expand Up @@ -518,21 +524,6 @@ void xPortSysTickHandler( void )
xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
}

/* Stop the SysTick momentarily. 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_ENABLE_BIT;

/* Calculate the reload value required to wait xExpectedIdleTime
tick periods. -1 is used because this code will execute part way
through one of the tick periods. */
ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
if( ulReloadValue > ulStoppedTimerCompensation )
{
ulReloadValue -= ulStoppedTimerCompensation;
}

/* Enter a critical section but don't use the taskENTER_CRITICAL()
method as that will mask interrupts that should exit sleep mode. */
__asm volatile( "cpsid i" ::: "memory" );
Expand All @@ -543,23 +534,44 @@ void xPortSysTickHandler( void )
to be unsuspended then abandon the low power entry. */
if( eTaskConfirmSleepModeStatus() == eAbortSleep )
{
/* Restart from whatever is left in the count register to complete
this tick period. */
portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;

/* Restart SysTick. */
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;

/* Reset the reload register to the value required for normal tick
periods. */
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;

/* Re-enable interrupts - see comments above the cpsid instruction()
above. */
__asm volatile( "cpsie i" ::: "memory" );
}
else
{
/* Stop the SysTick momentarily. 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_SETTING | portNVIC_SYSTICK_INT_BIT );

/* Calculate the reload value required to wait xExpectedIdleTime
tick periods. -1 is used because this code normally executes part
way through the first tick period. But if the SysTick IRQ is now
pending, then clear the IRQ, suppressing the first tick, and correct
the reload value to reflect that the second tick period is already
underway. The expected idle time is always at least two ticks. */
ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * (xExpectedIdleTime - 1UL) );
if( ( portNVIC_ICSR_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 )
{
portNVIC_ICSR_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT;

/* Skip the correction described above if SysTick happened to
stop on zero. In that case, there are still
ulTimerCountsForOneTick ticks left in the second tick period,
not zero, so the value in portNVIC_SYSTICK_CURRENT_VALUE_REG
already includes the correction normally done here. */
if( portNVIC_SYSTICK_CURRENT_VALUE_REG != 0 )
{
ulReloadValue -= ulTimerCountsForOneTick;
}
}
if( ulReloadValue > ulStoppedTimerCompensation )
{
ulReloadValue -= ulStoppedTimerCompensation;
}

/* Set the new reload value. */
portNVIC_SYSTICK_LOAD_REG = ulReloadValue;

Expand Down Expand Up @@ -607,7 +619,7 @@ void xPortSysTickHandler( void )
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 | portNVIC_SYSTICK_INT_BIT );
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_SETTING | portNVIC_SYSTICK_INT_BIT );

/* Determine if the SysTick clock has already counted to zero and
been set back to the current reload value (the reload back being
Expand All @@ -626,7 +638,8 @@ void xPortSysTickHandler( void )

/* Don't allow a tiny value, or values that have somehow
underflowed because the post sleep hook did something
that took too long. */
that took too long or because the SysTick current-value register
is zero. */
if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
{
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
Expand Down Expand Up @@ -656,13 +669,24 @@ void xPortSysTickHandler( void )
portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
}

/* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG
again, then set portNVIC_SYSTICK_LOAD_REG back to its standard
value. */
/* 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
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. */
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
vTaskStepTick( ulCompleteTickPeriods );
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
#if( portNVIC_SYSTICK_CLK_BIT_SETTING != portNVIC_SYSTICK_CLK_BIT )
{
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_SETTING | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
}
#endif /* portNVIC_SYSTICK_CLK_BIT_SETTING */
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;

/* Step the tick to account for any tick periods that elapsed. */
vTaskStepTick( ulCompleteTickPeriods );

/* Exit with interrupts enabled. */
__asm volatile( "cpsie i" ::: "memory" );
Expand Down Expand Up @@ -693,7 +717,7 @@ __attribute__(( weak )) void vPortSetupTimerInterrupt( void )

/* Configure SysTick to interrupt at the requested rate. */
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_SETTING | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}
/*-----------------------------------------------------------*/

Expand Down