diff --git a/portable/ARMv8M/non_secure/port.c b/portable/ARMv8M/non_secure/port.c index d746923df..9976daee4 100644 --- a/portable/ARMv8M/non_secure/port.c +++ b/portable/ARMv8M/non_secure/port.c @@ -78,20 +78,13 @@ #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) -#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 a the core. */ - #define portNVIC_SYSTICK_CLK_BIT ( 0 ) -#endif /*-----------------------------------------------------------*/ /** @@ -199,7 +192,7 @@ * have occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /*-----------------------------------------------------------*/ /** @@ -258,6 +251,20 @@ #define portINITIAL_CONTROL_UNPRIVILEGED ( 0x3 ) #define portINITIAL_CONTROL_PRIVILEGED ( 0x2 ) +/** + * @brief 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /** * @brief 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 @@ -386,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; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -395,22 +402,6 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; 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" ); @@ -418,26 +409,52 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; __asm volatile ( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler - * to be un-suspended then abandon the low power entry. */ + * 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -448,12 +465,11 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can - * set its parameter to 0 to indicate that its implementation - * contains its own wait for interrupt or wait for event - * instruction, and so wfi should not be executed again. However, - * the original expected idle time variable must remain unmodified, - * so a copy is taken. */ + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -467,48 +483,44 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. See comments above + * out of sleep mode to execute immediately. See comments above * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Disable interrupts again because the clock is about to be stopped - * and interrupts that execute while the clock is stopped will - * increase any slippage between the time maintained by the RTOS and - * calendar time. */ + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ __asm volatile ( "cpsid i" ::: "memory" ); __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 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_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 + * 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 ); - /* Determine if the SysTick clock has already counted to zero and - * been set back to the current reload value (the reload back being - * correct for the entire expected idle time) or if the SysTick is - * yet to count to zero (in which case an interrupt other than the - * SysTick must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,17 +528,36 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* As the pending tick will be processed as soon as this - * function exits, the tick value maintained by the tick is - * stepped forward by one less than the time spent waiting. */ + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -537,13 +568,39 @@ 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, 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -556,11 +613,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -569,7 +626,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; } /*-----------------------------------------------------------*/ @@ -694,10 +751,10 @@ static void prvTaskExitError( void ) static void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ { #if ( configENABLE_TRUSTZONE == 1 ) - { - /* Enable non-secure access to the FPU. */ - SecureInit_EnableNSFPUAccess(); - } + { + /* Enable non-secure access to the FPU. */ + SecureInit_EnableNSFPUAccess(); + } #endif /* configENABLE_TRUSTZONE */ /* CP10 = 11 ==> Full access to FPU i.e. both privileged and @@ -810,22 +867,22 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO ulR0 = pulCallerStackAddress[ 0 ]; #if ( configENABLE_MPU == 1 ) - { - /* Read the CONTROL register value. */ - __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); + { + /* Read the CONTROL register value. */ + __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); - /* The task that raised the SVC is privileged if Bit[0] - * in the CONTROL register is 0. */ - ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); + /* The task that raised the SVC is privileged if Bit[0] + * in the CONTROL register is 0. */ + ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); - } + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); + } #else /* if ( configENABLE_MPU == 1 ) */ - { - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); - } + { + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); + } #endif /* configENABLE_MPU */ configASSERT( xSecureContext != securecontextINVALID_CONTEXT_ID ); @@ -833,6 +890,7 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO break; case portSVC_FREE_SECURE_CONTEXT: + /* R0 contains TCB being freed and R1 contains the secure * context handle to be freed. */ ulR0 = pulCallerStackAddress[ 0 ]; @@ -845,21 +903,21 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO case portSVC_START_SCHEDULER: #if ( configENABLE_TRUSTZONE == 1 ) - { - /* De-prioritize the non-secure exceptions so that the - * non-secure pendSV runs at the lowest priority. */ - SecureInit_DePrioritizeNSExceptions(); + { + /* De-prioritize the non-secure exceptions so that the + * non-secure pendSV runs at the lowest priority. */ + SecureInit_DePrioritizeNSExceptions(); - /* Initialize the secure context management system. */ - SecureContext_Init(); - } + /* Initialize the secure context management system. */ + SecureContext_Init(); + } #endif /* configENABLE_TRUSTZONE */ #if ( configENABLE_FPU == 1 ) - { - /* Setup the Floating Point Unit (FPU). */ - prvSetupFPU(); - } + { + /* Setup the Floating Point Unit (FPU). */ + prvSetupFPU(); + } #endif /* configENABLE_FPU */ /* Setup the context of the first task so that the first task starts @@ -904,105 +962,105 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO /* Simulate the stack frame as it would be created by a context switch * interrupt. */ #if ( portPRELOAD_REGISTERS == 0 ) + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ + *pxTopOfStack = portINITIAL_EXC_RETURN; + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ - *pxTopOfStack = portINITIAL_EXC_RETURN; - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #else /* portPRELOAD_REGISTERS */ + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ - pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #endif /* portPRELOAD_REGISTERS */ return pxTopOfStack; @@ -1016,10 +1074,10 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; #if ( configENABLE_MPU == 1 ) - { - /* Setup the Memory Protection Unit (MPU). */ - prvSetupMPU(); - } + { + /* Setup the Memory Protection Unit (MPU). */ + prvSetupMPU(); + } #endif /* configENABLE_MPU */ /* Start the timer that generates the tick ISR. Interrupts are disabled diff --git a/portable/CCS/ARM_CM3/port.c b/portable/CCS/ARM_CM3/port.c index 35c832b64..257aefa55 100644 --- a/portable/CCS/ARM_CM3/port.c +++ b/portable/CCS/ARM_CM3/port.c @@ -38,27 +38,18 @@ #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ #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_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 ) #define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) @@ -86,12 +77,24 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /* For strict compliance with the Cortex-M spec the task start address should * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -213,66 +216,66 @@ static void prvTaskExitError( void ) BaseType_t xPortStartScheduler( void ) { #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -356,7 +359,7 @@ void xPortSysTickHandler( void ) #pragma WEAK( vPortSuppressTicksAndSleep ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -365,22 +368,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( " cpsid i"); @@ -391,23 +378,49 @@ 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 __disable_interrupt() - * call above. */ + /* Re-enable interrupts - see comments above the cpsid instruction + * above. */ __asm( " cpsie i"); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -436,8 +449,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the cpsid instruction above. */ __asm( " cpsie i"); __asm( " dsb"); __asm( " isb"); @@ -457,27 +470,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -491,11 +500,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -506,13 +534,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm( " cpsie i"); @@ -531,11 +585,11 @@ void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -544,7 +598,7 @@ 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ diff --git a/portable/CCS/ARM_CM4F/port.c b/portable/CCS/ARM_CM4F/port.c index c5036606b..1028d8398 100644 --- a/portable/CCS/ARM_CM4F/port.c +++ b/portable/CCS/ARM_CM4F/port.c @@ -42,27 +42,18 @@ #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ #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_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 ) #define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) @@ -95,12 +86,24 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /* For strict compliance with the Cortex-M spec the task start address should * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -232,66 +235,66 @@ static void prvTaskExitError( void ) BaseType_t xPortStartScheduler( void ) { #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -381,7 +384,7 @@ void xPortSysTickHandler( void ) #pragma WEAK( vPortSuppressTicksAndSleep ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -390,22 +393,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( " cpsid i"); @@ -416,23 +403,49 @@ 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 __disable_interrupt() - * call above. */ + /* Re-enable interrupts - see comments above the cpsid instruction + * above. */ __asm( " cpsie i"); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -461,8 +474,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the cpsid instruction above. */ __asm( " cpsie i"); __asm( " dsb"); __asm( " isb"); @@ -482,27 +495,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,11 +525,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -531,13 +559,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm( " cpsie i"); @@ -556,11 +610,11 @@ void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -569,7 +623,7 @@ 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ diff --git a/portable/GCC/ARM_CM0/port.c b/portable/GCC/ARM_CM0/port.c index 18d073fa1..198307307 100644 --- a/portable/GCC/ARM_CM0/port.c +++ b/portable/GCC/ARM_CM0/port.c @@ -34,27 +34,19 @@ #include "FreeRTOS.h" #include "task.h" -#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 NVIC. */ #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_INT_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000ed04 ) ) #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) +#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_PENDSVSET_BIT ( 1UL << 28UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) @@ -69,7 +61,19 @@ * occurred while the SysTick counter is stopped during tickless idle * calculations. */ #ifndef portMISSED_COUNTS_FACTOR - #define portMISSED_COUNTS_FACTOR ( 45UL ) + #define portMISSED_COUNTS_FACTOR ( 94UL ) +#endif + +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) #endif /* Let the user override the pre-loading of the initial LR with the address of @@ -391,11 +395,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -404,7 +408,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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ @@ -412,7 +416,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) __attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -421,22 +425,6 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( 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" ); @@ -447,23 +435,49 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -492,8 +506,8 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); @@ -513,27 +527,23 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -547,11 +557,30 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -562,15 +591,41 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( 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_LOAD_REG = ulTimerCountsForOneTick - 1UL; + 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; + } + #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; - /* Exit with interrpts enabled. */ + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ + vTaskStepTick( ulCompleteTickPeriods ); + + /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); } } diff --git a/portable/GCC/ARM_CM23/non_secure/port.c b/portable/GCC/ARM_CM23/non_secure/port.c index d746923df..9976daee4 100644 --- a/portable/GCC/ARM_CM23/non_secure/port.c +++ b/portable/GCC/ARM_CM23/non_secure/port.c @@ -78,20 +78,13 @@ #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) -#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 a the core. */ - #define portNVIC_SYSTICK_CLK_BIT ( 0 ) -#endif /*-----------------------------------------------------------*/ /** @@ -199,7 +192,7 @@ * have occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /*-----------------------------------------------------------*/ /** @@ -258,6 +251,20 @@ #define portINITIAL_CONTROL_UNPRIVILEGED ( 0x3 ) #define portINITIAL_CONTROL_PRIVILEGED ( 0x2 ) +/** + * @brief 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /** * @brief 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 @@ -386,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; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -395,22 +402,6 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; 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" ); @@ -418,26 +409,52 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; __asm volatile ( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler - * to be un-suspended then abandon the low power entry. */ + * 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -448,12 +465,11 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can - * set its parameter to 0 to indicate that its implementation - * contains its own wait for interrupt or wait for event - * instruction, and so wfi should not be executed again. However, - * the original expected idle time variable must remain unmodified, - * so a copy is taken. */ + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -467,48 +483,44 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. See comments above + * out of sleep mode to execute immediately. See comments above * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Disable interrupts again because the clock is about to be stopped - * and interrupts that execute while the clock is stopped will - * increase any slippage between the time maintained by the RTOS and - * calendar time. */ + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ __asm volatile ( "cpsid i" ::: "memory" ); __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 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_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 + * 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 ); - /* Determine if the SysTick clock has already counted to zero and - * been set back to the current reload value (the reload back being - * correct for the entire expected idle time) or if the SysTick is - * yet to count to zero (in which case an interrupt other than the - * SysTick must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,17 +528,36 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* As the pending tick will be processed as soon as this - * function exits, the tick value maintained by the tick is - * stepped forward by one less than the time spent waiting. */ + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -537,13 +568,39 @@ 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, 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -556,11 +613,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -569,7 +626,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; } /*-----------------------------------------------------------*/ @@ -694,10 +751,10 @@ static void prvTaskExitError( void ) static void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ { #if ( configENABLE_TRUSTZONE == 1 ) - { - /* Enable non-secure access to the FPU. */ - SecureInit_EnableNSFPUAccess(); - } + { + /* Enable non-secure access to the FPU. */ + SecureInit_EnableNSFPUAccess(); + } #endif /* configENABLE_TRUSTZONE */ /* CP10 = 11 ==> Full access to FPU i.e. both privileged and @@ -810,22 +867,22 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO ulR0 = pulCallerStackAddress[ 0 ]; #if ( configENABLE_MPU == 1 ) - { - /* Read the CONTROL register value. */ - __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); + { + /* Read the CONTROL register value. */ + __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); - /* The task that raised the SVC is privileged if Bit[0] - * in the CONTROL register is 0. */ - ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); + /* The task that raised the SVC is privileged if Bit[0] + * in the CONTROL register is 0. */ + ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); - } + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); + } #else /* if ( configENABLE_MPU == 1 ) */ - { - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); - } + { + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); + } #endif /* configENABLE_MPU */ configASSERT( xSecureContext != securecontextINVALID_CONTEXT_ID ); @@ -833,6 +890,7 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO break; case portSVC_FREE_SECURE_CONTEXT: + /* R0 contains TCB being freed and R1 contains the secure * context handle to be freed. */ ulR0 = pulCallerStackAddress[ 0 ]; @@ -845,21 +903,21 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO case portSVC_START_SCHEDULER: #if ( configENABLE_TRUSTZONE == 1 ) - { - /* De-prioritize the non-secure exceptions so that the - * non-secure pendSV runs at the lowest priority. */ - SecureInit_DePrioritizeNSExceptions(); + { + /* De-prioritize the non-secure exceptions so that the + * non-secure pendSV runs at the lowest priority. */ + SecureInit_DePrioritizeNSExceptions(); - /* Initialize the secure context management system. */ - SecureContext_Init(); - } + /* Initialize the secure context management system. */ + SecureContext_Init(); + } #endif /* configENABLE_TRUSTZONE */ #if ( configENABLE_FPU == 1 ) - { - /* Setup the Floating Point Unit (FPU). */ - prvSetupFPU(); - } + { + /* Setup the Floating Point Unit (FPU). */ + prvSetupFPU(); + } #endif /* configENABLE_FPU */ /* Setup the context of the first task so that the first task starts @@ -904,105 +962,105 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO /* Simulate the stack frame as it would be created by a context switch * interrupt. */ #if ( portPRELOAD_REGISTERS == 0 ) + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ + *pxTopOfStack = portINITIAL_EXC_RETURN; + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ - *pxTopOfStack = portINITIAL_EXC_RETURN; - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #else /* portPRELOAD_REGISTERS */ + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ - pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #endif /* portPRELOAD_REGISTERS */ return pxTopOfStack; @@ -1016,10 +1074,10 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; #if ( configENABLE_MPU == 1 ) - { - /* Setup the Memory Protection Unit (MPU). */ - prvSetupMPU(); - } + { + /* Setup the Memory Protection Unit (MPU). */ + prvSetupMPU(); + } #endif /* configENABLE_MPU */ /* Start the timer that generates the tick ISR. Interrupts are disabled diff --git a/portable/GCC/ARM_CM23_NTZ/non_secure/port.c b/portable/GCC/ARM_CM23_NTZ/non_secure/port.c index d746923df..9976daee4 100644 --- a/portable/GCC/ARM_CM23_NTZ/non_secure/port.c +++ b/portable/GCC/ARM_CM23_NTZ/non_secure/port.c @@ -78,20 +78,13 @@ #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) -#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 a the core. */ - #define portNVIC_SYSTICK_CLK_BIT ( 0 ) -#endif /*-----------------------------------------------------------*/ /** @@ -199,7 +192,7 @@ * have occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /*-----------------------------------------------------------*/ /** @@ -258,6 +251,20 @@ #define portINITIAL_CONTROL_UNPRIVILEGED ( 0x3 ) #define portINITIAL_CONTROL_PRIVILEGED ( 0x2 ) +/** + * @brief 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /** * @brief 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 @@ -386,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; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -395,22 +402,6 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; 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" ); @@ -418,26 +409,52 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; __asm volatile ( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler - * to be un-suspended then abandon the low power entry. */ + * 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -448,12 +465,11 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can - * set its parameter to 0 to indicate that its implementation - * contains its own wait for interrupt or wait for event - * instruction, and so wfi should not be executed again. However, - * the original expected idle time variable must remain unmodified, - * so a copy is taken. */ + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -467,48 +483,44 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. See comments above + * out of sleep mode to execute immediately. See comments above * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Disable interrupts again because the clock is about to be stopped - * and interrupts that execute while the clock is stopped will - * increase any slippage between the time maintained by the RTOS and - * calendar time. */ + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ __asm volatile ( "cpsid i" ::: "memory" ); __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 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_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 + * 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 ); - /* Determine if the SysTick clock has already counted to zero and - * been set back to the current reload value (the reload back being - * correct for the entire expected idle time) or if the SysTick is - * yet to count to zero (in which case an interrupt other than the - * SysTick must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,17 +528,36 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* As the pending tick will be processed as soon as this - * function exits, the tick value maintained by the tick is - * stepped forward by one less than the time spent waiting. */ + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -537,13 +568,39 @@ 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, 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -556,11 +613,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -569,7 +626,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; } /*-----------------------------------------------------------*/ @@ -694,10 +751,10 @@ static void prvTaskExitError( void ) static void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ { #if ( configENABLE_TRUSTZONE == 1 ) - { - /* Enable non-secure access to the FPU. */ - SecureInit_EnableNSFPUAccess(); - } + { + /* Enable non-secure access to the FPU. */ + SecureInit_EnableNSFPUAccess(); + } #endif /* configENABLE_TRUSTZONE */ /* CP10 = 11 ==> Full access to FPU i.e. both privileged and @@ -810,22 +867,22 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO ulR0 = pulCallerStackAddress[ 0 ]; #if ( configENABLE_MPU == 1 ) - { - /* Read the CONTROL register value. */ - __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); + { + /* Read the CONTROL register value. */ + __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); - /* The task that raised the SVC is privileged if Bit[0] - * in the CONTROL register is 0. */ - ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); + /* The task that raised the SVC is privileged if Bit[0] + * in the CONTROL register is 0. */ + ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); - } + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); + } #else /* if ( configENABLE_MPU == 1 ) */ - { - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); - } + { + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); + } #endif /* configENABLE_MPU */ configASSERT( xSecureContext != securecontextINVALID_CONTEXT_ID ); @@ -833,6 +890,7 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO break; case portSVC_FREE_SECURE_CONTEXT: + /* R0 contains TCB being freed and R1 contains the secure * context handle to be freed. */ ulR0 = pulCallerStackAddress[ 0 ]; @@ -845,21 +903,21 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO case portSVC_START_SCHEDULER: #if ( configENABLE_TRUSTZONE == 1 ) - { - /* De-prioritize the non-secure exceptions so that the - * non-secure pendSV runs at the lowest priority. */ - SecureInit_DePrioritizeNSExceptions(); + { + /* De-prioritize the non-secure exceptions so that the + * non-secure pendSV runs at the lowest priority. */ + SecureInit_DePrioritizeNSExceptions(); - /* Initialize the secure context management system. */ - SecureContext_Init(); - } + /* Initialize the secure context management system. */ + SecureContext_Init(); + } #endif /* configENABLE_TRUSTZONE */ #if ( configENABLE_FPU == 1 ) - { - /* Setup the Floating Point Unit (FPU). */ - prvSetupFPU(); - } + { + /* Setup the Floating Point Unit (FPU). */ + prvSetupFPU(); + } #endif /* configENABLE_FPU */ /* Setup the context of the first task so that the first task starts @@ -904,105 +962,105 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO /* Simulate the stack frame as it would be created by a context switch * interrupt. */ #if ( portPRELOAD_REGISTERS == 0 ) + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ + *pxTopOfStack = portINITIAL_EXC_RETURN; + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ - *pxTopOfStack = portINITIAL_EXC_RETURN; - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #else /* portPRELOAD_REGISTERS */ + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ - pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #endif /* portPRELOAD_REGISTERS */ return pxTopOfStack; @@ -1016,10 +1074,10 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; #if ( configENABLE_MPU == 1 ) - { - /* Setup the Memory Protection Unit (MPU). */ - prvSetupMPU(); - } + { + /* Setup the Memory Protection Unit (MPU). */ + prvSetupMPU(); + } #endif /* configENABLE_MPU */ /* Start the timer that generates the tick ISR. Interrupts are disabled diff --git a/portable/GCC/ARM_CM3/port.c b/portable/GCC/ARM_CM3/port.c index ac882e926..f41f215b3 100644 --- a/portable/GCC/ARM_CM3/port.c +++ b/portable/GCC/ARM_CM3/port.c @@ -41,27 +41,18 @@ #define configKERNEL_INTERRUPT_PRIORITY 255 #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_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 ) #define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) @@ -89,12 +80,24 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /* For strict compliance with the Cortex-M spec the task start address should * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 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. */ @@ -267,66 +270,66 @@ BaseType_t xPortStartScheduler( void ) configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -455,7 +458,7 @@ void xPortSysTickHandler( void ) __attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -464,22 +467,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" ); @@ -490,23 +477,49 @@ 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -535,8 +548,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); @@ -556,27 +569,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -590,11 +599,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -605,13 +633,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -629,11 +683,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -642,7 +696,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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ diff --git a/portable/GCC/ARM_CM33/non_secure/port.c b/portable/GCC/ARM_CM33/non_secure/port.c index d746923df..9976daee4 100644 --- a/portable/GCC/ARM_CM33/non_secure/port.c +++ b/portable/GCC/ARM_CM33/non_secure/port.c @@ -78,20 +78,13 @@ #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) -#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 a the core. */ - #define portNVIC_SYSTICK_CLK_BIT ( 0 ) -#endif /*-----------------------------------------------------------*/ /** @@ -199,7 +192,7 @@ * have occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /*-----------------------------------------------------------*/ /** @@ -258,6 +251,20 @@ #define portINITIAL_CONTROL_UNPRIVILEGED ( 0x3 ) #define portINITIAL_CONTROL_PRIVILEGED ( 0x2 ) +/** + * @brief 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /** * @brief 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 @@ -386,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; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -395,22 +402,6 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; 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" ); @@ -418,26 +409,52 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; __asm volatile ( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler - * to be un-suspended then abandon the low power entry. */ + * 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -448,12 +465,11 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can - * set its parameter to 0 to indicate that its implementation - * contains its own wait for interrupt or wait for event - * instruction, and so wfi should not be executed again. However, - * the original expected idle time variable must remain unmodified, - * so a copy is taken. */ + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -467,48 +483,44 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. See comments above + * out of sleep mode to execute immediately. See comments above * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Disable interrupts again because the clock is about to be stopped - * and interrupts that execute while the clock is stopped will - * increase any slippage between the time maintained by the RTOS and - * calendar time. */ + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ __asm volatile ( "cpsid i" ::: "memory" ); __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 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_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 + * 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 ); - /* Determine if the SysTick clock has already counted to zero and - * been set back to the current reload value (the reload back being - * correct for the entire expected idle time) or if the SysTick is - * yet to count to zero (in which case an interrupt other than the - * SysTick must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,17 +528,36 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* As the pending tick will be processed as soon as this - * function exits, the tick value maintained by the tick is - * stepped forward by one less than the time spent waiting. */ + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -537,13 +568,39 @@ 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, 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -556,11 +613,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -569,7 +626,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; } /*-----------------------------------------------------------*/ @@ -694,10 +751,10 @@ static void prvTaskExitError( void ) static void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ { #if ( configENABLE_TRUSTZONE == 1 ) - { - /* Enable non-secure access to the FPU. */ - SecureInit_EnableNSFPUAccess(); - } + { + /* Enable non-secure access to the FPU. */ + SecureInit_EnableNSFPUAccess(); + } #endif /* configENABLE_TRUSTZONE */ /* CP10 = 11 ==> Full access to FPU i.e. both privileged and @@ -810,22 +867,22 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO ulR0 = pulCallerStackAddress[ 0 ]; #if ( configENABLE_MPU == 1 ) - { - /* Read the CONTROL register value. */ - __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); + { + /* Read the CONTROL register value. */ + __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); - /* The task that raised the SVC is privileged if Bit[0] - * in the CONTROL register is 0. */ - ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); + /* The task that raised the SVC is privileged if Bit[0] + * in the CONTROL register is 0. */ + ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); - } + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); + } #else /* if ( configENABLE_MPU == 1 ) */ - { - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); - } + { + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); + } #endif /* configENABLE_MPU */ configASSERT( xSecureContext != securecontextINVALID_CONTEXT_ID ); @@ -833,6 +890,7 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO break; case portSVC_FREE_SECURE_CONTEXT: + /* R0 contains TCB being freed and R1 contains the secure * context handle to be freed. */ ulR0 = pulCallerStackAddress[ 0 ]; @@ -845,21 +903,21 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO case portSVC_START_SCHEDULER: #if ( configENABLE_TRUSTZONE == 1 ) - { - /* De-prioritize the non-secure exceptions so that the - * non-secure pendSV runs at the lowest priority. */ - SecureInit_DePrioritizeNSExceptions(); + { + /* De-prioritize the non-secure exceptions so that the + * non-secure pendSV runs at the lowest priority. */ + SecureInit_DePrioritizeNSExceptions(); - /* Initialize the secure context management system. */ - SecureContext_Init(); - } + /* Initialize the secure context management system. */ + SecureContext_Init(); + } #endif /* configENABLE_TRUSTZONE */ #if ( configENABLE_FPU == 1 ) - { - /* Setup the Floating Point Unit (FPU). */ - prvSetupFPU(); - } + { + /* Setup the Floating Point Unit (FPU). */ + prvSetupFPU(); + } #endif /* configENABLE_FPU */ /* Setup the context of the first task so that the first task starts @@ -904,105 +962,105 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO /* Simulate the stack frame as it would be created by a context switch * interrupt. */ #if ( portPRELOAD_REGISTERS == 0 ) + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ + *pxTopOfStack = portINITIAL_EXC_RETURN; + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ - *pxTopOfStack = portINITIAL_EXC_RETURN; - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #else /* portPRELOAD_REGISTERS */ + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ - pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #endif /* portPRELOAD_REGISTERS */ return pxTopOfStack; @@ -1016,10 +1074,10 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; #if ( configENABLE_MPU == 1 ) - { - /* Setup the Memory Protection Unit (MPU). */ - prvSetupMPU(); - } + { + /* Setup the Memory Protection Unit (MPU). */ + prvSetupMPU(); + } #endif /* configENABLE_MPU */ /* Start the timer that generates the tick ISR. Interrupts are disabled diff --git a/portable/GCC/ARM_CM33_NTZ/non_secure/port.c b/portable/GCC/ARM_CM33_NTZ/non_secure/port.c index d746923df..9976daee4 100644 --- a/portable/GCC/ARM_CM33_NTZ/non_secure/port.c +++ b/portable/GCC/ARM_CM33_NTZ/non_secure/port.c @@ -78,20 +78,13 @@ #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) -#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 a the core. */ - #define portNVIC_SYSTICK_CLK_BIT ( 0 ) -#endif /*-----------------------------------------------------------*/ /** @@ -199,7 +192,7 @@ * have occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /*-----------------------------------------------------------*/ /** @@ -258,6 +251,20 @@ #define portINITIAL_CONTROL_UNPRIVILEGED ( 0x3 ) #define portINITIAL_CONTROL_PRIVILEGED ( 0x2 ) +/** + * @brief 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /** * @brief 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 @@ -386,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; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -395,22 +402,6 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; 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" ); @@ -418,26 +409,52 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; __asm volatile ( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler - * to be un-suspended then abandon the low power entry. */ + * 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -448,12 +465,11 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can - * set its parameter to 0 to indicate that its implementation - * contains its own wait for interrupt or wait for event - * instruction, and so wfi should not be executed again. However, - * the original expected idle time variable must remain unmodified, - * so a copy is taken. */ + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -467,48 +483,44 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. See comments above + * out of sleep mode to execute immediately. See comments above * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Disable interrupts again because the clock is about to be stopped - * and interrupts that execute while the clock is stopped will - * increase any slippage between the time maintained by the RTOS and - * calendar time. */ + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ __asm volatile ( "cpsid i" ::: "memory" ); __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 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_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 + * 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 ); - /* Determine if the SysTick clock has already counted to zero and - * been set back to the current reload value (the reload back being - * correct for the entire expected idle time) or if the SysTick is - * yet to count to zero (in which case an interrupt other than the - * SysTick must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,17 +528,36 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* As the pending tick will be processed as soon as this - * function exits, the tick value maintained by the tick is - * stepped forward by one less than the time spent waiting. */ + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -537,13 +568,39 @@ 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, 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -556,11 +613,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -569,7 +626,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; } /*-----------------------------------------------------------*/ @@ -694,10 +751,10 @@ static void prvTaskExitError( void ) static void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ { #if ( configENABLE_TRUSTZONE == 1 ) - { - /* Enable non-secure access to the FPU. */ - SecureInit_EnableNSFPUAccess(); - } + { + /* Enable non-secure access to the FPU. */ + SecureInit_EnableNSFPUAccess(); + } #endif /* configENABLE_TRUSTZONE */ /* CP10 = 11 ==> Full access to FPU i.e. both privileged and @@ -810,22 +867,22 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO ulR0 = pulCallerStackAddress[ 0 ]; #if ( configENABLE_MPU == 1 ) - { - /* Read the CONTROL register value. */ - __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); + { + /* Read the CONTROL register value. */ + __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); - /* The task that raised the SVC is privileged if Bit[0] - * in the CONTROL register is 0. */ - ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); + /* The task that raised the SVC is privileged if Bit[0] + * in the CONTROL register is 0. */ + ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); - } + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); + } #else /* if ( configENABLE_MPU == 1 ) */ - { - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); - } + { + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); + } #endif /* configENABLE_MPU */ configASSERT( xSecureContext != securecontextINVALID_CONTEXT_ID ); @@ -833,6 +890,7 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO break; case portSVC_FREE_SECURE_CONTEXT: + /* R0 contains TCB being freed and R1 contains the secure * context handle to be freed. */ ulR0 = pulCallerStackAddress[ 0 ]; @@ -845,21 +903,21 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO case portSVC_START_SCHEDULER: #if ( configENABLE_TRUSTZONE == 1 ) - { - /* De-prioritize the non-secure exceptions so that the - * non-secure pendSV runs at the lowest priority. */ - SecureInit_DePrioritizeNSExceptions(); + { + /* De-prioritize the non-secure exceptions so that the + * non-secure pendSV runs at the lowest priority. */ + SecureInit_DePrioritizeNSExceptions(); - /* Initialize the secure context management system. */ - SecureContext_Init(); - } + /* Initialize the secure context management system. */ + SecureContext_Init(); + } #endif /* configENABLE_TRUSTZONE */ #if ( configENABLE_FPU == 1 ) - { - /* Setup the Floating Point Unit (FPU). */ - prvSetupFPU(); - } + { + /* Setup the Floating Point Unit (FPU). */ + prvSetupFPU(); + } #endif /* configENABLE_FPU */ /* Setup the context of the first task so that the first task starts @@ -904,105 +962,105 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO /* Simulate the stack frame as it would be created by a context switch * interrupt. */ #if ( portPRELOAD_REGISTERS == 0 ) + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ + *pxTopOfStack = portINITIAL_EXC_RETURN; + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ - *pxTopOfStack = portINITIAL_EXC_RETURN; - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #else /* portPRELOAD_REGISTERS */ + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ - pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #endif /* portPRELOAD_REGISTERS */ return pxTopOfStack; @@ -1016,10 +1074,10 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; #if ( configENABLE_MPU == 1 ) - { - /* Setup the Memory Protection Unit (MPU). */ - prvSetupMPU(); - } + { + /* Setup the Memory Protection Unit (MPU). */ + prvSetupMPU(); + } #endif /* configENABLE_MPU */ /* Start the timer that generates the tick ISR. Interrupts are disabled diff --git a/portable/GCC/ARM_CM4F/port.c b/portable/GCC/ARM_CM4F/port.c index ae05a3276..c62d16049 100644 --- a/portable/GCC/ARM_CM4F/port.c +++ b/portable/GCC/ARM_CM4F/port.c @@ -38,27 +38,18 @@ #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_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 @@ -101,7 +92,19 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) + +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 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 @@ -303,66 +306,66 @@ BaseType_t xPortStartScheduler( void ) configASSERT( portCPUID != portCORTEX_M7_r0p0_ID ); #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -517,7 +520,7 @@ void xPortSysTickHandler( void ) __attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -526,22 +529,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" ); @@ -552,23 +539,49 @@ 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -597,8 +610,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); @@ -618,27 +631,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -652,11 +661,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -667,13 +695,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -691,11 +745,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -704,7 +758,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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ diff --git a/portable/GCC/ARM_CM7/r0p1/port.c b/portable/GCC/ARM_CM7/r0p1/port.c index 130e1878d..7bfaa1c3c 100644 --- a/portable/GCC/ARM_CM7/r0p1/port.c +++ b/portable/GCC/ARM_CM7/r0p1/port.c @@ -38,27 +38,18 @@ #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_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 ) #define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) @@ -95,7 +86,19 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) + +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 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 @@ -291,66 +294,66 @@ BaseType_t xPortStartScheduler( void ) configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -507,7 +510,7 @@ void xPortSysTickHandler( void ) __attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -516,22 +519,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" ); @@ -542,23 +529,49 @@ 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -587,8 +600,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); @@ -608,27 +621,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -642,11 +651,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -657,13 +685,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -681,11 +735,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -694,7 +748,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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ diff --git a/portable/IAR/ARM_CM0/port.c b/portable/IAR/ARM_CM0/port.c index 6fbc8529a..4e40442a1 100644 --- a/portable/IAR/ARM_CM0/port.c +++ b/portable/IAR/ARM_CM0/port.c @@ -37,26 +37,18 @@ #include "FreeRTOS.h" #include "task.h" -#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 NVIC. */ #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_INT_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000ed04 ) ) #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) +#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_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) @@ -82,7 +74,7 @@ static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; * occurred while the SysTick counter is stopped during tickless idle * calculations. */ #ifndef portMISSED_COUNTS_FACTOR - #define portMISSED_COUNTS_FACTOR ( 45UL ) + #define portMISSED_COUNTS_FACTOR ( 94UL ) #endif /* The number of SysTick increments that make up one tick period. */ @@ -102,6 +94,18 @@ static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; static uint32_t ulStoppedTimerCompensation = 0; #endif /* configUSE_TICKLESS_IDLE */ +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -256,11 +260,11 @@ __weak void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -269,7 +273,7 @@ __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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ @@ -277,7 +281,7 @@ __weak void vPortSetupTimerInterrupt( void ) __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -286,22 +290,6 @@ __weak void vPortSetupTimerInterrupt( 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. */ __disable_interrupt(); @@ -312,23 +300,49 @@ __weak void vPortSetupTimerInterrupt( 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 __disable_interrupt() + /* Re-enable interrupts - see comments above the __disable_interrupt() * call above. */ __enable_interrupt(); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -357,8 +371,8 @@ __weak void vPortSetupTimerInterrupt( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the __disable_interrupt() call above. */ __enable_interrupt(); __DSB(); __ISB(); @@ -378,27 +392,23 @@ __weak void vPortSetupTimerInterrupt( 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -412,11 +422,30 @@ __weak void vPortSetupTimerInterrupt( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -427,15 +456,41 @@ __weak void vPortSetupTimerInterrupt( 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_LOAD_REG = ulTimerCountsForOneTick - 1UL; + 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; + } + #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; - /* Exit with interrpts enabled. */ + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ + vTaskStepTick( ulCompleteTickPeriods ); + + /* Exit with interrupts enabled. */ __enable_interrupt(); } } diff --git a/portable/IAR/ARM_CM0/portmacro.h b/portable/IAR/ARM_CM0/portmacro.h index fc9eaa406..56127dfa7 100644 --- a/portable/IAR/ARM_CM0/portmacro.h +++ b/portable/IAR/ARM_CM0/portmacro.h @@ -26,7 +26,6 @@ * */ - #ifndef PORTMACRO_H #define PORTMACRO_H @@ -80,8 +79,8 @@ /* Scheduler utilities. */ extern void vPortYield( void ); - #define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) - #define portNVIC_PENDSVSET 0x10000000 + #define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) + #define portNVIC_PENDSVSET 0x10000000 #define portYIELD() vPortYield() #define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET #define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) diff --git a/portable/IAR/ARM_CM23/non_secure/port.c b/portable/IAR/ARM_CM23/non_secure/port.c index d746923df..9976daee4 100644 --- a/portable/IAR/ARM_CM23/non_secure/port.c +++ b/portable/IAR/ARM_CM23/non_secure/port.c @@ -78,20 +78,13 @@ #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) -#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 a the core. */ - #define portNVIC_SYSTICK_CLK_BIT ( 0 ) -#endif /*-----------------------------------------------------------*/ /** @@ -199,7 +192,7 @@ * have occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /*-----------------------------------------------------------*/ /** @@ -258,6 +251,20 @@ #define portINITIAL_CONTROL_UNPRIVILEGED ( 0x3 ) #define portINITIAL_CONTROL_PRIVILEGED ( 0x2 ) +/** + * @brief 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /** * @brief 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 @@ -386,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; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -395,22 +402,6 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; 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" ); @@ -418,26 +409,52 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; __asm volatile ( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler - * to be un-suspended then abandon the low power entry. */ + * 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -448,12 +465,11 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can - * set its parameter to 0 to indicate that its implementation - * contains its own wait for interrupt or wait for event - * instruction, and so wfi should not be executed again. However, - * the original expected idle time variable must remain unmodified, - * so a copy is taken. */ + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -467,48 +483,44 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. See comments above + * out of sleep mode to execute immediately. See comments above * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Disable interrupts again because the clock is about to be stopped - * and interrupts that execute while the clock is stopped will - * increase any slippage between the time maintained by the RTOS and - * calendar time. */ + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ __asm volatile ( "cpsid i" ::: "memory" ); __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 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_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 + * 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 ); - /* Determine if the SysTick clock has already counted to zero and - * been set back to the current reload value (the reload back being - * correct for the entire expected idle time) or if the SysTick is - * yet to count to zero (in which case an interrupt other than the - * SysTick must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,17 +528,36 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* As the pending tick will be processed as soon as this - * function exits, the tick value maintained by the tick is - * stepped forward by one less than the time spent waiting. */ + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -537,13 +568,39 @@ 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, 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -556,11 +613,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -569,7 +626,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; } /*-----------------------------------------------------------*/ @@ -694,10 +751,10 @@ static void prvTaskExitError( void ) static void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ { #if ( configENABLE_TRUSTZONE == 1 ) - { - /* Enable non-secure access to the FPU. */ - SecureInit_EnableNSFPUAccess(); - } + { + /* Enable non-secure access to the FPU. */ + SecureInit_EnableNSFPUAccess(); + } #endif /* configENABLE_TRUSTZONE */ /* CP10 = 11 ==> Full access to FPU i.e. both privileged and @@ -810,22 +867,22 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO ulR0 = pulCallerStackAddress[ 0 ]; #if ( configENABLE_MPU == 1 ) - { - /* Read the CONTROL register value. */ - __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); + { + /* Read the CONTROL register value. */ + __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); - /* The task that raised the SVC is privileged if Bit[0] - * in the CONTROL register is 0. */ - ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); + /* The task that raised the SVC is privileged if Bit[0] + * in the CONTROL register is 0. */ + ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); - } + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); + } #else /* if ( configENABLE_MPU == 1 ) */ - { - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); - } + { + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); + } #endif /* configENABLE_MPU */ configASSERT( xSecureContext != securecontextINVALID_CONTEXT_ID ); @@ -833,6 +890,7 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO break; case portSVC_FREE_SECURE_CONTEXT: + /* R0 contains TCB being freed and R1 contains the secure * context handle to be freed. */ ulR0 = pulCallerStackAddress[ 0 ]; @@ -845,21 +903,21 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO case portSVC_START_SCHEDULER: #if ( configENABLE_TRUSTZONE == 1 ) - { - /* De-prioritize the non-secure exceptions so that the - * non-secure pendSV runs at the lowest priority. */ - SecureInit_DePrioritizeNSExceptions(); + { + /* De-prioritize the non-secure exceptions so that the + * non-secure pendSV runs at the lowest priority. */ + SecureInit_DePrioritizeNSExceptions(); - /* Initialize the secure context management system. */ - SecureContext_Init(); - } + /* Initialize the secure context management system. */ + SecureContext_Init(); + } #endif /* configENABLE_TRUSTZONE */ #if ( configENABLE_FPU == 1 ) - { - /* Setup the Floating Point Unit (FPU). */ - prvSetupFPU(); - } + { + /* Setup the Floating Point Unit (FPU). */ + prvSetupFPU(); + } #endif /* configENABLE_FPU */ /* Setup the context of the first task so that the first task starts @@ -904,105 +962,105 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO /* Simulate the stack frame as it would be created by a context switch * interrupt. */ #if ( portPRELOAD_REGISTERS == 0 ) + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ + *pxTopOfStack = portINITIAL_EXC_RETURN; + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ - *pxTopOfStack = portINITIAL_EXC_RETURN; - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #else /* portPRELOAD_REGISTERS */ + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ - pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #endif /* portPRELOAD_REGISTERS */ return pxTopOfStack; @@ -1016,10 +1074,10 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; #if ( configENABLE_MPU == 1 ) - { - /* Setup the Memory Protection Unit (MPU). */ - prvSetupMPU(); - } + { + /* Setup the Memory Protection Unit (MPU). */ + prvSetupMPU(); + } #endif /* configENABLE_MPU */ /* Start the timer that generates the tick ISR. Interrupts are disabled diff --git a/portable/IAR/ARM_CM23_NTZ/non_secure/port.c b/portable/IAR/ARM_CM23_NTZ/non_secure/port.c index d746923df..9976daee4 100644 --- a/portable/IAR/ARM_CM23_NTZ/non_secure/port.c +++ b/portable/IAR/ARM_CM23_NTZ/non_secure/port.c @@ -78,20 +78,13 @@ #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) -#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 a the core. */ - #define portNVIC_SYSTICK_CLK_BIT ( 0 ) -#endif /*-----------------------------------------------------------*/ /** @@ -199,7 +192,7 @@ * have occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /*-----------------------------------------------------------*/ /** @@ -258,6 +251,20 @@ #define portINITIAL_CONTROL_UNPRIVILEGED ( 0x3 ) #define portINITIAL_CONTROL_PRIVILEGED ( 0x2 ) +/** + * @brief 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /** * @brief 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 @@ -386,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; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -395,22 +402,6 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; 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" ); @@ -418,26 +409,52 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; __asm volatile ( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler - * to be un-suspended then abandon the low power entry. */ + * 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -448,12 +465,11 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can - * set its parameter to 0 to indicate that its implementation - * contains its own wait for interrupt or wait for event - * instruction, and so wfi should not be executed again. However, - * the original expected idle time variable must remain unmodified, - * so a copy is taken. */ + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -467,48 +483,44 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. See comments above + * out of sleep mode to execute immediately. See comments above * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Disable interrupts again because the clock is about to be stopped - * and interrupts that execute while the clock is stopped will - * increase any slippage between the time maintained by the RTOS and - * calendar time. */ + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ __asm volatile ( "cpsid i" ::: "memory" ); __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 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_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 + * 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 ); - /* Determine if the SysTick clock has already counted to zero and - * been set back to the current reload value (the reload back being - * correct for the entire expected idle time) or if the SysTick is - * yet to count to zero (in which case an interrupt other than the - * SysTick must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,17 +528,36 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* As the pending tick will be processed as soon as this - * function exits, the tick value maintained by the tick is - * stepped forward by one less than the time spent waiting. */ + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -537,13 +568,39 @@ 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, 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -556,11 +613,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -569,7 +626,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; } /*-----------------------------------------------------------*/ @@ -694,10 +751,10 @@ static void prvTaskExitError( void ) static void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ { #if ( configENABLE_TRUSTZONE == 1 ) - { - /* Enable non-secure access to the FPU. */ - SecureInit_EnableNSFPUAccess(); - } + { + /* Enable non-secure access to the FPU. */ + SecureInit_EnableNSFPUAccess(); + } #endif /* configENABLE_TRUSTZONE */ /* CP10 = 11 ==> Full access to FPU i.e. both privileged and @@ -810,22 +867,22 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO ulR0 = pulCallerStackAddress[ 0 ]; #if ( configENABLE_MPU == 1 ) - { - /* Read the CONTROL register value. */ - __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); + { + /* Read the CONTROL register value. */ + __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); - /* The task that raised the SVC is privileged if Bit[0] - * in the CONTROL register is 0. */ - ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); + /* The task that raised the SVC is privileged if Bit[0] + * in the CONTROL register is 0. */ + ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); - } + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); + } #else /* if ( configENABLE_MPU == 1 ) */ - { - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); - } + { + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); + } #endif /* configENABLE_MPU */ configASSERT( xSecureContext != securecontextINVALID_CONTEXT_ID ); @@ -833,6 +890,7 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO break; case portSVC_FREE_SECURE_CONTEXT: + /* R0 contains TCB being freed and R1 contains the secure * context handle to be freed. */ ulR0 = pulCallerStackAddress[ 0 ]; @@ -845,21 +903,21 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO case portSVC_START_SCHEDULER: #if ( configENABLE_TRUSTZONE == 1 ) - { - /* De-prioritize the non-secure exceptions so that the - * non-secure pendSV runs at the lowest priority. */ - SecureInit_DePrioritizeNSExceptions(); + { + /* De-prioritize the non-secure exceptions so that the + * non-secure pendSV runs at the lowest priority. */ + SecureInit_DePrioritizeNSExceptions(); - /* Initialize the secure context management system. */ - SecureContext_Init(); - } + /* Initialize the secure context management system. */ + SecureContext_Init(); + } #endif /* configENABLE_TRUSTZONE */ #if ( configENABLE_FPU == 1 ) - { - /* Setup the Floating Point Unit (FPU). */ - prvSetupFPU(); - } + { + /* Setup the Floating Point Unit (FPU). */ + prvSetupFPU(); + } #endif /* configENABLE_FPU */ /* Setup the context of the first task so that the first task starts @@ -904,105 +962,105 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO /* Simulate the stack frame as it would be created by a context switch * interrupt. */ #if ( portPRELOAD_REGISTERS == 0 ) + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ + *pxTopOfStack = portINITIAL_EXC_RETURN; + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ - *pxTopOfStack = portINITIAL_EXC_RETURN; - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #else /* portPRELOAD_REGISTERS */ + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ - pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #endif /* portPRELOAD_REGISTERS */ return pxTopOfStack; @@ -1016,10 +1074,10 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; #if ( configENABLE_MPU == 1 ) - { - /* Setup the Memory Protection Unit (MPU). */ - prvSetupMPU(); - } + { + /* Setup the Memory Protection Unit (MPU). */ + prvSetupMPU(); + } #endif /* configENABLE_MPU */ /* Start the timer that generates the tick ISR. Interrupts are disabled diff --git a/portable/IAR/ARM_CM3/port.c b/portable/IAR/ARM_CM3/port.c index 92b211c1b..f8e6a1b59 100644 --- a/portable/IAR/ARM_CM3/port.c +++ b/portable/IAR/ARM_CM3/port.c @@ -41,27 +41,18 @@ #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ #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_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 ) #define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) @@ -89,7 +80,7 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /* For strict compliance with the Cortex-M spec the task start address should * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ @@ -102,6 +93,18 @@ #define configKERNEL_INTERRUPT_PRIORITY 255 #endif +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -216,66 +219,66 @@ BaseType_t xPortStartScheduler( void ) configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -358,7 +361,7 @@ void xPortSysTickHandler( void ) __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -367,22 +370,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. */ __disable_interrupt(); @@ -393,23 +380,49 @@ 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 __disable_interrupt() + /* Re-enable interrupts - see comments above the __disable_interrupt() * call above. */ __enable_interrupt(); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -438,8 +451,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the __disable_interrupt() call above. */ __enable_interrupt(); __DSB(); __ISB(); @@ -459,27 +472,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -493,11 +502,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -508,13 +536,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __enable_interrupt(); @@ -532,11 +586,11 @@ __weak void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -545,7 +599,7 @@ __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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ diff --git a/portable/IAR/ARM_CM33/non_secure/port.c b/portable/IAR/ARM_CM33/non_secure/port.c index d746923df..9976daee4 100644 --- a/portable/IAR/ARM_CM33/non_secure/port.c +++ b/portable/IAR/ARM_CM33/non_secure/port.c @@ -78,20 +78,13 @@ #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) -#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 a the core. */ - #define portNVIC_SYSTICK_CLK_BIT ( 0 ) -#endif /*-----------------------------------------------------------*/ /** @@ -199,7 +192,7 @@ * have occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /*-----------------------------------------------------------*/ /** @@ -258,6 +251,20 @@ #define portINITIAL_CONTROL_UNPRIVILEGED ( 0x3 ) #define portINITIAL_CONTROL_PRIVILEGED ( 0x2 ) +/** + * @brief 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /** * @brief 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 @@ -386,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; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -395,22 +402,6 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; 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" ); @@ -418,26 +409,52 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; __asm volatile ( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler - * to be un-suspended then abandon the low power entry. */ + * 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -448,12 +465,11 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can - * set its parameter to 0 to indicate that its implementation - * contains its own wait for interrupt or wait for event - * instruction, and so wfi should not be executed again. However, - * the original expected idle time variable must remain unmodified, - * so a copy is taken. */ + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -467,48 +483,44 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. See comments above + * out of sleep mode to execute immediately. See comments above * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Disable interrupts again because the clock is about to be stopped - * and interrupts that execute while the clock is stopped will - * increase any slippage between the time maintained by the RTOS and - * calendar time. */ + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ __asm volatile ( "cpsid i" ::: "memory" ); __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 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_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 + * 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 ); - /* Determine if the SysTick clock has already counted to zero and - * been set back to the current reload value (the reload back being - * correct for the entire expected idle time) or if the SysTick is - * yet to count to zero (in which case an interrupt other than the - * SysTick must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,17 +528,36 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* As the pending tick will be processed as soon as this - * function exits, the tick value maintained by the tick is - * stepped forward by one less than the time spent waiting. */ + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -537,13 +568,39 @@ 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, 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -556,11 +613,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -569,7 +626,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; } /*-----------------------------------------------------------*/ @@ -694,10 +751,10 @@ static void prvTaskExitError( void ) static void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ { #if ( configENABLE_TRUSTZONE == 1 ) - { - /* Enable non-secure access to the FPU. */ - SecureInit_EnableNSFPUAccess(); - } + { + /* Enable non-secure access to the FPU. */ + SecureInit_EnableNSFPUAccess(); + } #endif /* configENABLE_TRUSTZONE */ /* CP10 = 11 ==> Full access to FPU i.e. both privileged and @@ -810,22 +867,22 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO ulR0 = pulCallerStackAddress[ 0 ]; #if ( configENABLE_MPU == 1 ) - { - /* Read the CONTROL register value. */ - __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); + { + /* Read the CONTROL register value. */ + __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); - /* The task that raised the SVC is privileged if Bit[0] - * in the CONTROL register is 0. */ - ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); + /* The task that raised the SVC is privileged if Bit[0] + * in the CONTROL register is 0. */ + ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); - } + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); + } #else /* if ( configENABLE_MPU == 1 ) */ - { - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); - } + { + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); + } #endif /* configENABLE_MPU */ configASSERT( xSecureContext != securecontextINVALID_CONTEXT_ID ); @@ -833,6 +890,7 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO break; case portSVC_FREE_SECURE_CONTEXT: + /* R0 contains TCB being freed and R1 contains the secure * context handle to be freed. */ ulR0 = pulCallerStackAddress[ 0 ]; @@ -845,21 +903,21 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO case portSVC_START_SCHEDULER: #if ( configENABLE_TRUSTZONE == 1 ) - { - /* De-prioritize the non-secure exceptions so that the - * non-secure pendSV runs at the lowest priority. */ - SecureInit_DePrioritizeNSExceptions(); + { + /* De-prioritize the non-secure exceptions so that the + * non-secure pendSV runs at the lowest priority. */ + SecureInit_DePrioritizeNSExceptions(); - /* Initialize the secure context management system. */ - SecureContext_Init(); - } + /* Initialize the secure context management system. */ + SecureContext_Init(); + } #endif /* configENABLE_TRUSTZONE */ #if ( configENABLE_FPU == 1 ) - { - /* Setup the Floating Point Unit (FPU). */ - prvSetupFPU(); - } + { + /* Setup the Floating Point Unit (FPU). */ + prvSetupFPU(); + } #endif /* configENABLE_FPU */ /* Setup the context of the first task so that the first task starts @@ -904,105 +962,105 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO /* Simulate the stack frame as it would be created by a context switch * interrupt. */ #if ( portPRELOAD_REGISTERS == 0 ) + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ + *pxTopOfStack = portINITIAL_EXC_RETURN; + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ - *pxTopOfStack = portINITIAL_EXC_RETURN; - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #else /* portPRELOAD_REGISTERS */ + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ - pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #endif /* portPRELOAD_REGISTERS */ return pxTopOfStack; @@ -1016,10 +1074,10 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; #if ( configENABLE_MPU == 1 ) - { - /* Setup the Memory Protection Unit (MPU). */ - prvSetupMPU(); - } + { + /* Setup the Memory Protection Unit (MPU). */ + prvSetupMPU(); + } #endif /* configENABLE_MPU */ /* Start the timer that generates the tick ISR. Interrupts are disabled diff --git a/portable/IAR/ARM_CM33_NTZ/non_secure/port.c b/portable/IAR/ARM_CM33_NTZ/non_secure/port.c index d746923df..9976daee4 100644 --- a/portable/IAR/ARM_CM33_NTZ/non_secure/port.c +++ b/portable/IAR/ARM_CM33_NTZ/non_secure/port.c @@ -78,20 +78,13 @@ #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) -#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 a the core. */ - #define portNVIC_SYSTICK_CLK_BIT ( 0 ) -#endif /*-----------------------------------------------------------*/ /** @@ -199,7 +192,7 @@ * have occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /*-----------------------------------------------------------*/ /** @@ -258,6 +251,20 @@ #define portINITIAL_CONTROL_UNPRIVILEGED ( 0x3 ) #define portINITIAL_CONTROL_PRIVILEGED ( 0x2 ) +/** + * @brief 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /** * @brief 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 @@ -386,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; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -395,22 +402,6 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; 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" ); @@ -418,26 +409,52 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; __asm volatile ( "isb" ); /* If a context switch is pending or a task is waiting for the scheduler - * to be un-suspended then abandon the low power entry. */ + * 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() + /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -448,12 +465,11 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can - * set its parameter to 0 to indicate that its implementation - * contains its own wait for interrupt or wait for event - * instruction, and so wfi should not be executed again. However, - * the original expected idle time variable must remain unmodified, - * so a copy is taken. */ + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -467,48 +483,44 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. See comments above + * out of sleep mode to execute immediately. See comments above * the cpsid instruction above. */ __asm volatile ( "cpsie i" ::: "memory" ); __asm volatile ( "dsb" ); __asm volatile ( "isb" ); /* Disable interrupts again because the clock is about to be stopped - * and interrupts that execute while the clock is stopped will - * increase any slippage between the time maintained by the RTOS and - * calendar time. */ + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ __asm volatile ( "cpsid i" ::: "memory" ); __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 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_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 + * 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 ); - /* Determine if the SysTick clock has already counted to zero and - * been set back to the current reload value (the reload back being - * correct for the entire expected idle time) or if the SysTick is - * yet to count to zero (in which case an interrupt other than the - * SysTick must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,17 +528,36 @@ PRIVILEGED_DATA static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; /* As the pending tick will be processed as soon as this - * function exits, the tick value maintained by the tick is - * stepped forward by one less than the time spent waiting. */ + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -537,13 +568,39 @@ 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, 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm volatile ( "cpsie i" ::: "memory" ); @@ -556,11 +613,11 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and reset the SysTick. */ @@ -569,7 +626,7 @@ __attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FU /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; } /*-----------------------------------------------------------*/ @@ -694,10 +751,10 @@ static void prvTaskExitError( void ) static void prvSetupFPU( void ) /* PRIVILEGED_FUNCTION */ { #if ( configENABLE_TRUSTZONE == 1 ) - { - /* Enable non-secure access to the FPU. */ - SecureInit_EnableNSFPUAccess(); - } + { + /* Enable non-secure access to the FPU. */ + SecureInit_EnableNSFPUAccess(); + } #endif /* configENABLE_TRUSTZONE */ /* CP10 = 11 ==> Full access to FPU i.e. both privileged and @@ -810,22 +867,22 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO ulR0 = pulCallerStackAddress[ 0 ]; #if ( configENABLE_MPU == 1 ) - { - /* Read the CONTROL register value. */ - __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); + { + /* Read the CONTROL register value. */ + __asm volatile ( "mrs %0, control" : "=r" ( ulControl ) ); - /* The task that raised the SVC is privileged if Bit[0] - * in the CONTROL register is 0. */ - ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); + /* The task that raised the SVC is privileged if Bit[0] + * in the CONTROL register is 0. */ + ulIsTaskPrivileged = ( ( ulControl & portCONTROL_PRIVILEGED_MASK ) == 0 ); - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); - } + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, ulIsTaskPrivileged, pxCurrentTCB ); + } #else /* if ( configENABLE_MPU == 1 ) */ - { - /* Allocate and load a context for the secure task. */ - xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); - } + { + /* Allocate and load a context for the secure task. */ + xSecureContext = SecureContext_AllocateContext( ulR0, pxCurrentTCB ); + } #endif /* configENABLE_MPU */ configASSERT( xSecureContext != securecontextINVALID_CONTEXT_ID ); @@ -833,6 +890,7 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO break; case portSVC_FREE_SECURE_CONTEXT: + /* R0 contains TCB being freed and R1 contains the secure * context handle to be freed. */ ulR0 = pulCallerStackAddress[ 0 ]; @@ -845,21 +903,21 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO case portSVC_START_SCHEDULER: #if ( configENABLE_TRUSTZONE == 1 ) - { - /* De-prioritize the non-secure exceptions so that the - * non-secure pendSV runs at the lowest priority. */ - SecureInit_DePrioritizeNSExceptions(); + { + /* De-prioritize the non-secure exceptions so that the + * non-secure pendSV runs at the lowest priority. */ + SecureInit_DePrioritizeNSExceptions(); - /* Initialize the secure context management system. */ - SecureContext_Init(); - } + /* Initialize the secure context management system. */ + SecureContext_Init(); + } #endif /* configENABLE_TRUSTZONE */ #if ( configENABLE_FPU == 1 ) - { - /* Setup the Floating Point Unit (FPU). */ - prvSetupFPU(); - } + { + /* Setup the Floating Point Unit (FPU). */ + prvSetupFPU(); + } #endif /* configENABLE_FPU */ /* Setup the context of the first task so that the first task starts @@ -904,105 +962,105 @@ void vPortSVCHandler_C( uint32_t * pulCallerStackAddress ) /* PRIVILEGED_FUNCTIO /* Simulate the stack frame as it would be created by a context switch * interrupt. */ #if ( portPRELOAD_REGISTERS == 0 ) + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ + *pxTopOfStack = portINITIAL_EXC_RETURN; + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack -= 9; /* R11..R4, EXC_RETURN. */ - *pxTopOfStack = portINITIAL_EXC_RETURN; - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #else /* portPRELOAD_REGISTERS */ + { + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ + + #if ( configENABLE_MPU == 1 ) { - pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ - *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x12121212UL; /* R12 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x03030303UL; /* R3 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x02020202UL; /* R2 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x01010101UL; /* R1 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x11111111UL; /* R11 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x10101010UL; /* R10 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x09090909UL; /* R09 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x08080808UL; /* R08 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x07070707UL; /* R07 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x06060606UL; /* R06 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x05050505UL; /* R05 */ - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) 0x04040404UL; /* R04 */ - pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXC_RETURN; /* EXC_RETURN */ - #if ( configENABLE_MPU == 1 ) - { - pxTopOfStack--; - - if( xRunPrivileged == pdTRUE ) - { - *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - else - { - *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ - } - } - #endif /* configENABLE_MPU */ - - pxTopOfStack--; - *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ - - #if ( configENABLE_TRUSTZONE == 1 ) - { - pxTopOfStack--; - *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ - } - #endif /* configENABLE_TRUSTZONE */ + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_PRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_UNPRIVILEGED; /* Slot used to hold this task's CONTROL value. */ + } } + #endif /* configENABLE_MPU */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxEndOfStack; /* Slot used to hold this task's PSPLIM value. */ + + #if ( configENABLE_TRUSTZONE == 1 ) + { + pxTopOfStack--; + *pxTopOfStack = portNO_SECURE_CONTEXT; /* Slot used to hold this task's xSecureContext value. */ + } + #endif /* configENABLE_TRUSTZONE */ + } #endif /* portPRELOAD_REGISTERS */ return pxTopOfStack; @@ -1016,10 +1074,10 @@ BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; #if ( configENABLE_MPU == 1 ) - { - /* Setup the Memory Protection Unit (MPU). */ - prvSetupMPU(); - } + { + /* Setup the Memory Protection Unit (MPU). */ + prvSetupMPU(); + } #endif /* configENABLE_MPU */ /* Start the timer that generates the tick ISR. Interrupts are disabled diff --git a/portable/IAR/ARM_CM4F/port.c b/portable/IAR/ARM_CM4F/port.c index a870d273b..3c9e0065f 100644 --- a/portable/IAR/ARM_CM4F/port.c +++ b/portable/IAR/ARM_CM4F/port.c @@ -45,27 +45,18 @@ #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ #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_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 @@ -104,12 +95,24 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /* For strict compliance with the Cortex-M spec the task start address should * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -247,66 +250,66 @@ BaseType_t xPortStartScheduler( void ) configASSERT( portCPUID != portCORTEX_M7_r0p0_ID ); #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -395,7 +398,7 @@ void xPortSysTickHandler( void ) __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -404,22 +407,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. */ __disable_interrupt(); @@ -430,23 +417,49 @@ 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 __disable_interrupt() + /* Re-enable interrupts - see comments above the __disable_interrupt() * call above. */ __enable_interrupt(); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -475,8 +488,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the __disable_interrupt() call above. */ __enable_interrupt(); __DSB(); __ISB(); @@ -496,27 +509,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -530,11 +539,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -545,13 +573,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __enable_interrupt(); @@ -569,11 +623,11 @@ __weak void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -582,7 +636,7 @@ __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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ diff --git a/portable/IAR/ARM_CM7/r0p1/port.c b/portable/IAR/ARM_CM7/r0p1/port.c index 93ab626e5..a7300b2f2 100644 --- a/portable/IAR/ARM_CM7/r0p1/port.c +++ b/portable/IAR/ARM_CM7/r0p1/port.c @@ -45,27 +45,18 @@ #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ #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_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 ) #define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) @@ -98,12 +89,24 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /* For strict compliance with the Cortex-M spec the task start address should * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -235,66 +238,66 @@ BaseType_t xPortStartScheduler( void ) configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -383,7 +386,7 @@ void xPortSysTickHandler( void ) __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -392,22 +395,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. */ __disable_interrupt(); @@ -418,23 +405,49 @@ 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 __disable_interrupt() + /* Re-enable interrupts - see comments above the __disable_interrupt() * call above. */ __enable_interrupt(); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -463,8 +476,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the __disable_interrupt() call above. */ __enable_interrupt(); __DSB(); __ISB(); @@ -484,27 +497,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -518,11 +527,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -533,13 +561,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __enable_interrupt(); @@ -557,11 +611,11 @@ __weak void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -570,7 +624,7 @@ __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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } /*-----------------------------------------------------------*/ diff --git a/portable/MikroC/ARM_CM4F/port.c b/portable/MikroC/ARM_CM4F/port.c index 028140572..a8e7e7ca8 100644 --- a/portable/MikroC/ARM_CM4F/port.c +++ b/portable/MikroC/ARM_CM4F/port.c @@ -34,28 +34,18 @@ #include "FreeRTOS.h" #include "task.h" - -#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_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 ) #define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) @@ -88,7 +78,19 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) + +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 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 @@ -298,70 +300,70 @@ BaseType_t xPortStartScheduler( void ) configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* The kernel interrupt priority should be set to the lowest + * priority. */ + configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* The kernel interrupt priority should be set to the lowest - * priority. */ - configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -510,7 +512,7 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -519,22 +521,6 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO 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 { @@ -551,18 +537,7 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO * 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() + /* Re-enable interrupts - see comments above the cpsid instruction * above. */ __asm { "cpsie i" @@ -570,6 +545,43 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -604,8 +616,8 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the cpsid instruction above. */ __asm { "cpsie i" }; @@ -637,27 +649,23 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO * 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -671,11 +679,30 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -686,13 +713,39 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __asm { @@ -714,11 +767,11 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Reset SysTick. */ @@ -727,7 +780,7 @@ void xPortSysTickHandler( void ) iv IVT_INT_SysTick ics ICS_AUTO /* 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } #endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */ diff --git a/portable/RVDS/ARM_CM0/port.c b/portable/RVDS/ARM_CM0/port.c index bb6dfcd31..50cd9ba3c 100644 --- a/portable/RVDS/ARM_CM0/port.c +++ b/portable/RVDS/ARM_CM0/port.c @@ -45,6 +45,8 @@ #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) #define portMIN_INTERRUPT_PRIORITY ( 255UL ) #define portNVIC_PENDSV_PRI ( portMIN_INTERRUPT_PRIORITY << 16UL ) #define portNVIC_SYSTICK_PRI ( portMIN_INTERRUPT_PRIORITY << 24UL ) @@ -59,12 +61,24 @@ * occurred while the SysTick counter is stopped during tickless idle * calculations. */ #ifndef portMISSED_COUNTS_FACTOR - #define portMISSED_COUNTS_FACTOR ( 45UL ) + #define portMISSED_COUNTS_FACTOR ( 94UL ) #endif /* Constants used with memory barrier intrinsics. */ #define portSY_FULL_READ_WRITE ( 15 ) +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* Legacy macro for backward compatibility only. This macro used to be used to * replace the function that configures the clock used to generate the tick * interrupt (prvSetupTimerInterrupt()), but now the function is declared weak so @@ -360,18 +374,20 @@ void xPortSysTickHandler( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - ulTimerCountsForOneTick = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ); + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ - /* Stop and reset the SysTick. */ + /* Stop and clear the SysTick. */ portNVIC_SYSTICK_CTRL_REG = 0UL; portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - portNVIC_SYSTICK_LOAD_REG = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + 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 ); } #endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */ @@ -381,7 +397,7 @@ void xPortSysTickHandler( void ) __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -390,22 +406,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. */ __disable_irq(); @@ -416,23 +416,49 @@ 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 __disable_irq() call - * above. */ + /* Re-enable interrupts - see comments above the __disable_irq() + * call above. */ __enable_irq(); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -443,10 +469,10 @@ void xPortSysTickHandler( void ) /* Restart SysTick. */ portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can * set its parameter to 0 to indicate that its implementation contains * its own wait for interrupt or wait for event instruction, and so wfi - * should not be executed again. However, the original expected idle + * should not be executed again. However, the original expected idle * time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); @@ -461,8 +487,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the __disable_irq() call above. */ __enable_irq(); __dsb( portSY_FULL_READ_WRITE ); __isb( portSY_FULL_READ_WRITE ); @@ -482,27 +508,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -516,11 +538,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -531,15 +572,41 @@ void xPortSysTickHandler( void ) portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; } - /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD - * again, then set portNVIC_SYSTICK_LOAD 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_LOAD_REG = ulTimerCountsForOneTick - 1UL; + 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; + } + #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; - /* Exit with interrpts enabled. */ + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ + vTaskStepTick( ulCompleteTickPeriods ); + + /* Exit with interrupts enabled. */ __enable_irq(); } } diff --git a/portable/RVDS/ARM_CM3/port.c b/portable/RVDS/ARM_CM3/port.c index 6b5efb27e..b47d3b7e3 100644 --- a/portable/RVDS/ARM_CM3/port.c +++ b/portable/RVDS/ARM_CM3/port.c @@ -42,17 +42,6 @@ #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ #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 - /* Legacy macro for backward compatibility only. This macro used to be used to * replace the function that configures the clock used to generate the tick * interrupt (prvSetupTimerInterrupt()), but now the function is declared weak so @@ -68,10 +57,12 @@ #define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) ) #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 ) #define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) @@ -99,12 +90,24 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /* For strict compliance with the Cortex-M spec the task start address should * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -262,70 +265,70 @@ __asm void prvStartFirstTask( void ) BaseType_t xPortStartScheduler( void ) { #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* The kernel interrupt priority should be set to the lowest + * priority. */ + configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* The kernel interrupt priority should be set to the lowest - * priority. */ - configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -450,7 +453,7 @@ void xPortSysTickHandler( void ) __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -459,22 +462,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. */ __disable_irq(); @@ -485,23 +472,49 @@ 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 __disable_irq() call - * above. */ + /* Re-enable interrupts - see comments above the __disable_irq() + * call above. */ __enable_irq(); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -530,8 +543,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the __disable_irq() call above. */ __enable_irq(); __dsb( portSY_FULL_READ_WRITE ); __isb( portSY_FULL_READ_WRITE ); @@ -551,27 +564,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -585,11 +594,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -600,13 +628,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __enable_irq(); @@ -627,11 +681,11 @@ void xPortSysTickHandler( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -640,7 +694,7 @@ void xPortSysTickHandler( 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } #endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */ diff --git a/portable/RVDS/ARM_CM4F/port.c b/portable/RVDS/ARM_CM4F/port.c index 443ec3e27..40e3ac958 100644 --- a/portable/RVDS/ARM_CM4F/port.c +++ b/portable/RVDS/ARM_CM4F/port.c @@ -42,17 +42,6 @@ #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ #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 - /* Legacy macro for backward compatibility only. This macro used to be used to * replace the function that configures the clock used to generate the tick * interrupt (prvSetupTimerInterrupt()), but now the function is declared weak so @@ -68,10 +57,12 @@ #define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) ) #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 @@ -110,12 +101,24 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /* For strict compliance with the Cortex-M spec the task start address should * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -324,70 +327,70 @@ BaseType_t xPortStartScheduler( void ) configASSERT( portCPUID != portCORTEX_M7_r0p0_ID ); #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* The kernel interrupt priority should be set to the lowest + * priority. */ + configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* The kernel interrupt priority should be set to the lowest - * priority. */ - configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -542,7 +545,7 @@ void xPortSysTickHandler( void ) __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -551,22 +554,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. */ __disable_irq(); @@ -577,23 +564,49 @@ 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 __disable_irq() call - * above. */ + /* Re-enable interrupts - see comments above the __disable_irq() + * call above. */ __enable_irq(); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -622,8 +635,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the __disable_irq() call above. */ __enable_irq(); __dsb( portSY_FULL_READ_WRITE ); __isb( portSY_FULL_READ_WRITE ); @@ -643,27 +656,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -677,11 +686,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -692,13 +720,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __enable_irq(); @@ -719,11 +773,11 @@ void xPortSysTickHandler( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -732,7 +786,7 @@ void xPortSysTickHandler( 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } #endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */ diff --git a/portable/RVDS/ARM_CM7/r0p1/port.c b/portable/RVDS/ARM_CM7/r0p1/port.c index 910214c1f..fe869df44 100644 --- a/portable/RVDS/ARM_CM7/r0p1/port.c +++ b/portable/RVDS/ARM_CM7/r0p1/port.c @@ -42,17 +42,6 @@ #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ #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 - /* The __weak attribute does not work as you might expect with the Keil tools * so the configOVERRIDE_DEFAULT_TICK_CONFIGURATION constant must be set to 1 if * the application writer wants to provide their own implementation of @@ -68,10 +57,12 @@ #define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) ) #define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) /* ...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 ) #define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) @@ -104,12 +95,24 @@ /* A fiddle factor to estimate the number of SysTick counts that would have * occurred while the SysTick counter is stopped during tickless idle * calculations. */ -#define portMISSED_COUNTS_FACTOR ( 45UL ) +#define portMISSED_COUNTS_FACTOR ( 94UL ) /* For strict compliance with the Cortex-M spec the task start address should * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) +/* 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_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -308,70 +311,70 @@ __asm void prvEnableVFP( void ) BaseType_t xPortStartScheduler( void ) { #if ( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* The kernel interrupt priority should be set to the lowest + * priority. */ + configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { - volatile uint32_t ulOriginalPriority; - volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); - volatile uint8_t ucMaxPriorityValue; - - /* Determine the maximum priority from which ISR safe FreeRTOS API - * functions can be called. ISR safe functions are those that end in - * "FromISR". FreeRTOS maintains separate thread and ISR API functions to - * ensure interrupt entry is as fast and simple as possible. - * - * Save the interrupt priority value that is about to be clobbered. */ - ulOriginalPriority = *pucFirstUserPriorityRegister; - - /* Determine the number of priority bits available. First write to all - * possible bits. */ - *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; - - /* Read the value back to see how many bits stuck. */ - ucMaxPriorityValue = *pucFirstUserPriorityRegister; - - /* The kernel interrupt priority should be set to the lowest - * priority. */ - configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); - - /* Use the same mask on the maximum system call priority. */ - ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; - - /* Calculate the maximum acceptable priority group value for the number - * of bits read back. */ - ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; - - while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) - { - ulMaxPRIGROUPValue--; - ucMaxPriorityValue <<= ( uint8_t ) 0x01; - } - - #ifdef __NVIC_PRIO_BITS - { - /* Check the CMSIS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); - } - #endif - - #ifdef configPRIO_BITS - { - /* Check the FreeRTOS configuration that defines the number of - * priority bits matches the number of priority bits actually queried - * from the hardware. */ - configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); - } - #endif - - /* Shift the priority group value back to its position within the AIRCR - * register. */ - ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; - ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; - - /* Restore the clobbered interrupt priority register to its original - * value. */ - *pucFirstUserPriorityRegister = ulOriginalPriority; + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + * priority bits matches the number of priority bits actually queried + * from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } #endif /* configASSERT_DEFINED */ /* Make PendSV and SysTick the lowest priority interrupts. */ @@ -528,7 +531,7 @@ void xPortSysTickHandler( void ) __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -537,22 +540,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. */ __disable_irq(); @@ -563,23 +550,49 @@ 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 __disable_irq() call - * above. */ + /* Re-enable interrupts - see comments above the __disable_irq() + * call above. */ __enable_irq(); } 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_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* 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 = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + /* Set the new reload value. */ portNVIC_SYSTICK_LOAD_REG = ulReloadValue; @@ -608,8 +621,8 @@ void xPortSysTickHandler( void ) configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* Re-enable interrupts to allow the interrupt that brought the MCU - * out of sleep mode to execute immediately. see comments above - * __disable_interrupt() call above. */ + * out of sleep mode to execute immediately. See comments above + * the __disable_irq() call above. */ __enable_irq(); __dsb( portSY_FULL_READ_WRITE ); __isb( portSY_FULL_READ_WRITE ); @@ -629,27 +642,23 @@ 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_CONFIG | 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 - * correct for the entire expected idle time) or if the SysTick is yet - * to count to zero (in which case an interrupt other than the SysTick - * must have brought the system out of sleep mode). */ + /* Determine whether the SysTick has already counted to zero. */ if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt is already pending, and the SysTick count - * reloaded with ulReloadValue. Reset the - * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick - * period. */ + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); /* Don't allow a tiny value, or values that have somehow * underflowed because the post sleep hook did something - * that took too long. */ - if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) { ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); } @@ -663,11 +672,30 @@ void xPortSysTickHandler( void ) } else { - /* Something other than the tick interrupt ended the sleep. - * Work out how long the sleep lasted rounded to complete tick + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick * periods (not the ulReload value which accounted for part * ticks). */ - ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; /* How many complete tick periods passed while the processor * was waiting? */ @@ -678,13 +706,39 @@ 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; + 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; + } + #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; + + if( ( portNVIC_SYSTICK_CTRL_REG & 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; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; /* Exit with interrupts enabled. */ __enable_irq(); @@ -705,11 +759,11 @@ void xPortSysTickHandler( void ) { /* Calculate the constants required to configure the tick interrupt. */ #if ( configUSE_TICKLESS_IDLE == 1 ) - { - ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); - xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; - ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); - } + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } #endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */ @@ -718,7 +772,7 @@ void xPortSysTickHandler( 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_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); } #endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */