Skip to content

Commit cfabb61

Browse files
committed
Replicate to other Cortex M ports
Also set a new fiddle factor based on tests with a CM4F. I used gcc, optimizing at -O1. Users can fine-tune as needed. Also add configSYSTICK_CLOCK_HZ to the CM0 ports to be just like the other Cortex M ports. This change allowed uniformity in the default tickless implementations across all Cortex M ports. And CM0 is likely to benefit from configSYSTICK_CLOCK_HZ, especially considering new CM0 devices with very fast CPU clock speeds.
1 parent aded661 commit cfabb61

File tree

25 files changed

+2622
-1592
lines changed

25 files changed

+2622
-1592
lines changed

portable/ARMv8M/non_secure/port.c

Lines changed: 124 additions & 79 deletions
Large diffs are not rendered by default.

portable/CCS/ARM_CM3/port.c

Lines changed: 106 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -36,27 +36,18 @@
3636
#error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
3737
#endif
3838

39-
#ifndef configSYSTICK_CLOCK_HZ
40-
#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ
41-
/* Ensure the SysTick is clocked at the same frequency as the core. */
42-
#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
43-
#else
44-
45-
/* The way the SysTick is clocked is not modified in case it is not the same
46-
* as the core. */
47-
#define portNVIC_SYSTICK_CLK_BIT ( 0 )
48-
#endif
49-
5039
/* Constants required to manipulate the core. Registers first... */
5140
#define portNVIC_SYSTICK_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000e010 ) )
5241
#define portNVIC_SYSTICK_LOAD_REG ( *( ( volatile uint32_t * ) 0xe000e014 ) )
5342
#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) )
5443
#define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) )
5544
/* ...then bits in the registers. */
45+
#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
5646
#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
5747
#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
5848
#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL )
5949
#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL )
50+
#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL )
6051
#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL )
6152

6253
#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
@@ -84,12 +75,26 @@
8475
/* A fiddle factor to estimate the number of SysTick counts that would have
8576
* occurred while the SysTick counter is stopped during tickless idle
8677
* calculations. */
87-
#define portMISSED_COUNTS_FACTOR ( 45UL )
78+
#define portMISSED_COUNTS_FACTOR ( 94UL )
8879

8980
/* For strict compliance with the Cortex-M spec the task start address should
9081
* have bit-0 clear, as it is loaded into the PC on exit from an ISR. */
9182
#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL )
9283

84+
/* Let the user override the default SysTick clock rate. If defined by the
85+
* user, this symbol must equal the SysTick clock rate when the CLK bit is 0 in the
86+
* configuration register. */
87+
#ifndef configSYSTICK_CLOCK_HZ
88+
#define configSYSTICK_CLOCK_HZ ( configCPU_CLOCK_HZ )
89+
/* Ensure the SysTick is clocked at the same frequency as the core. */
90+
#define portNVIC_SYSTICK_CLK_BIT_CONFIG ( portNVIC_SYSTICK_CLK_BIT )
91+
#else
92+
93+
/* Select the option to clock SysTick not at the same frequency as the core.
94+
* The clock used is often a divided version of the core clock. */
95+
#define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 )
96+
#endif
97+
9398
/*
9499
* Setup the timer to generate the tick interrupts. The implementation in this
95100
* file is weak to allow application writers to change the timer used to
@@ -354,7 +359,7 @@ void xPortSysTickHandler( void )
354359
#pragma WEAK( vPortSuppressTicksAndSleep )
355360
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
356361
{
357-
uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements;
362+
uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft;
358363
TickType_t xModifiableIdleTime;
359364

360365
/* Make sure the SysTick reload value does not overflow the counter. */
@@ -363,22 +368,6 @@ void xPortSysTickHandler( void )
363368
xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
364369
}
365370

366-
/* Stop the SysTick momentarily. The time the SysTick is stopped for
367-
* is accounted for as best it can be, but using the tickless mode will
368-
* inevitably result in some tiny drift of the time maintained by the
369-
* kernel with respect to calendar time. */
370-
portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;
371-
372-
/* Calculate the reload value required to wait xExpectedIdleTime
373-
* tick periods. -1 is used because this code will execute part way
374-
* through one of the tick periods. */
375-
ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
376-
377-
if( ulReloadValue > ulStoppedTimerCompensation )
378-
{
379-
ulReloadValue -= ulStoppedTimerCompensation;
380-
}
381-
382371
/* Enter a critical section but don't use the taskENTER_CRITICAL()
383372
* method as that will mask interrupts that should exit sleep mode. */
384373
__asm( " cpsid i");
@@ -389,23 +378,49 @@ void xPortSysTickHandler( void )
389378
* to be unsuspended then abandon the low power entry. */
390379
if( eTaskConfirmSleepModeStatus() == eAbortSleep )
391380
{
392-
/* Restart from whatever is left in the count register to complete
393-
* this tick period. */
394-
portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;
395-
396-
/* Restart SysTick. */
397-
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
398-
399-
/* Reset the reload register to the value required for normal tick
400-
* periods. */
401-
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
402-
403-
/* Re-enable interrupts - see comments above __disable_interrupt()
404-
* call above. */
381+
/* Re-enable interrupts - see comments above the cpsid instruction
382+
* above. */
405383
__asm( " cpsie i");
406384
}
407385
else
408386
{
387+
/* Stop the SysTick momentarily. The time the SysTick is stopped for
388+
* is accounted for as best it can be, but using the tickless mode will
389+
* inevitably result in some tiny drift of the time maintained by the
390+
* kernel with respect to calendar time. */
391+
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
392+
393+
/* Use the SysTick current-value register to determine the number of
394+
* SysTick decrements remaining until the next tick interrupt. If the
395+
* current-value register is zero, then there are actually
396+
* ulTimerCountsForOneTick decrements remaining, not zero, because the
397+
* SysTick requests the interrupt when decrementing from 1 to 0. */
398+
ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG;
399+
400+
if( ulSysTickDecrementsLeft == 0 )
401+
{
402+
ulSysTickDecrementsLeft = ulTimerCountsForOneTick;
403+
}
404+
405+
/* Calculate the reload value required to wait xExpectedIdleTime
406+
* tick periods. -1 is used because this code normally executes part
407+
* way through the first tick period. But if the SysTick IRQ is now
408+
* pending, then clear the IRQ, suppressing the first tick, and correct
409+
* the reload value to reflect that the second tick period is already
410+
* underway. The expected idle time is always at least two ticks. */
411+
ulReloadValue = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
412+
413+
if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 )
414+
{
415+
portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT;
416+
ulReloadValue -= ulTimerCountsForOneTick;
417+
}
418+
419+
if( ulReloadValue > ulStoppedTimerCompensation )
420+
{
421+
ulReloadValue -= ulStoppedTimerCompensation;
422+
}
423+
409424
/* Set the new reload value. */
410425
portNVIC_SYSTICK_LOAD_REG = ulReloadValue;
411426

@@ -434,8 +449,8 @@ void xPortSysTickHandler( void )
434449
configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
435450

436451
/* Re-enable interrupts to allow the interrupt that brought the MCU
437-
* out of sleep mode to execute immediately. see comments above
438-
* __disable_interrupt() call above. */
452+
* out of sleep mode to execute immediately. See comments above
453+
* the cpsid instruction above. */
439454
__asm( " cpsie i");
440455
__asm( " dsb");
441456
__asm( " isb");
@@ -455,26 +470,22 @@ void xPortSysTickHandler( void )
455470
* be, but using the tickless mode will inevitably result in some tiny
456471
* drift of the time maintained by the kernel with respect to calendar
457472
* time*/
458-
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT );
473+
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
459474

460-
/* Determine if the SysTick clock has already counted to zero and
461-
* been set back to the current reload value (the reload back being
462-
* correct for the entire expected idle time) or if the SysTick is yet
463-
* to count to zero (in which case an interrupt other than the SysTick
464-
* must have brought the system out of sleep mode). */
475+
/* Determine whether the SysTick has already counted to zero. */
465476
if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
466477
{
467478
uint32_t ulCalculatedLoadValue;
468479

469-
/* The tick interrupt is already pending, and the SysTick count
470-
* reloaded with ulReloadValue. Reset the
471-
* portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick
472-
* period. */
480+
/* The tick interrupt ended the sleep (or is now pending), and
481+
* a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG
482+
* with whatever remains of the new tick period. */
473483
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );
474484

475485
/* Don't allow a tiny value, or values that have somehow
476486
* underflowed because the post sleep hook did something
477-
* that took too long. */
487+
* that took too long or because the SysTick current-value register
488+
* is zero. */
478489
if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
479490
{
480491
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
@@ -489,11 +500,29 @@ void xPortSysTickHandler( void )
489500
}
490501
else
491502
{
492-
/* Something other than the tick interrupt ended the sleep.
493-
* Work out how long the sleep lasted rounded to complete tick
503+
/* Something other than the tick interrupt ended the sleep. */
504+
505+
/* Use the SysTick current-value register to determine the
506+
* number of SysTick decrements remaining until the expected idle
507+
* time would have ended. */
508+
ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG;
509+
#if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT )
510+
{
511+
/* If the SysTick is not using the core clock, the current-
512+
* value register might still be zero here. In that case, the
513+
* SysTick didn't load from the reload register, and there are
514+
* ulReloadValue + 1 decrements remaining, not zero. */
515+
if( ulSysTickDecrementsLeft == 0 )
516+
{
517+
ulSysTickDecrementsLeft = ulReloadValue + 1UL;
518+
}
519+
}
520+
#endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */
521+
522+
/* Work out how long the sleep lasted rounded to complete tick
494523
* periods (not the ulReload value which accounted for part
495524
* ticks). */
496-
ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;
525+
ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft;
497526

498527
/* How many complete tick periods passed while the processor
499528
* was waiting? */
@@ -504,13 +533,26 @@ void xPortSysTickHandler( void )
504533
portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
505534
}
506535

507-
/* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG
508-
* again, then set portNVIC_SYSTICK_LOAD_REG back to its standard
509-
* value. */
536+
/* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again,
537+
* then set portNVIC_SYSTICK_LOAD_REG back to its standard value. If
538+
* the SysTick is not using the core clock, temporarily configure it to
539+
* use the core clock. This configuration forces the SysTick to load
540+
* from portNVIC_SYSTICK_LOAD_REG immediately instead of at the next
541+
* cycle of the other clock. Then portNVIC_SYSTICK_LOAD_REG is ready
542+
* to receive the standard value immediately. */
510543
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
511-
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
512-
vTaskStepTick( ulCompleteTickPeriods );
544+
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
513545
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
546+
#if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT )
547+
{
548+
/* The temporary usage of the core clock has served its purpose,
549+
* as described above. Resume usage of the other clock. */
550+
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT;
551+
}
552+
#endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */
553+
554+
/* Step the tick to account for any tick periods that elapsed. */
555+
vTaskStepTick( ulCompleteTickPeriods );
514556

515557
/* Exit with interrupts enabled. */
516558
__asm( " cpsie i");
@@ -542,7 +584,7 @@ void vPortSetupTimerInterrupt( void )
542584

543585
/* Configure SysTick to interrupt at the requested rate. */
544586
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
545-
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
587+
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
546588
}
547589
/*-----------------------------------------------------------*/
548590

0 commit comments

Comments
 (0)