From 459dceb29c38f82afba9c6c5863910798ce97c3f Mon Sep 17 00:00:00 2001 From: RichardBarry Date: Mon, 16 Mar 2020 09:59:32 -0700 Subject: [PATCH 01/13] Fix Coverity warnings: In most cases the return value of xTaskResumeAll() is cast to void when it is not needed. This PR fixes a couple of instances in the heap_n.c implementations where that was not the case. --- portable/MemMang/heap_4.c | 2 +- portable/MemMang/heap_5.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/portable/MemMang/heap_4.c b/portable/MemMang/heap_4.c index a266144f9..ade9e4782 100644 --- a/portable/MemMang/heap_4.c +++ b/portable/MemMang/heap_4.c @@ -474,7 +474,7 @@ size_t xBlocks = 0, xMaxSize = 0, xMinSize = portMAX_DELAY; /* portMAX_DELAY use } while( pxBlock != pxEnd ); } } - xTaskResumeAll(); + ( void ) xTaskResumeAll(); pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize; pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize; diff --git a/portable/MemMang/heap_5.c b/portable/MemMang/heap_5.c index 9b1dbaf46..bd6b66ab2 100644 --- a/portable/MemMang/heap_5.c +++ b/portable/MemMang/heap_5.c @@ -529,7 +529,7 @@ size_t xBlocks = 0, xMaxSize = 0, xMinSize = portMAX_DELAY; /* portMAX_DELAY use } while( pxBlock != pxEnd ); } } - xTaskResumeAll(); + ( void ) xTaskResumeAll(); pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize; pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize; From 177e79fc79b62e8301f85ed19633c8aef5ef708d Mon Sep 17 00:00:00 2001 From: Gaurav-Aggarwal-AWS <33462878+aggarg@users.noreply.github.com> Date: Mon, 16 Mar 2020 10:50:49 -0700 Subject: [PATCH 02/13] Add "Tickless Idle" support for ARMv8M ports (#29) Signed-off-by: Gaurav Aggarwal --- portable/ARMv8M/non_secure/port.c | 260 ++++++++++++++++-- .../portable/GCC/ARM_CM23/portmacro.h | 9 + .../portable/GCC/ARM_CM23_NTZ/portmacro.h | 9 + .../portable/GCC/ARM_CM33/portmacro.h | 9 + .../portable/GCC/ARM_CM33_NTZ/portmacro.h | 9 + .../portable/IAR/ARM_CM23/portmacro.h | 9 + .../portable/IAR/ARM_CM23_NTZ/portmacro.h | 9 + .../portable/IAR/ARM_CM33/portmacro.h | 9 + .../portable/IAR/ARM_CM33_NTZ/portmacro.h | 9 + portable/GCC/ARM_CM23/non_secure/port.c | 260 ++++++++++++++++-- portable/GCC/ARM_CM23/non_secure/portmacro.h | 9 + portable/GCC/ARM_CM23_NTZ/non_secure/port.c | 260 ++++++++++++++++-- .../GCC/ARM_CM23_NTZ/non_secure/portmacro.h | 9 + portable/GCC/ARM_CM33/non_secure/port.c | 260 ++++++++++++++++-- portable/GCC/ARM_CM33/non_secure/portmacro.h | 9 + portable/GCC/ARM_CM33_NTZ/non_secure/port.c | 260 ++++++++++++++++-- .../GCC/ARM_CM33_NTZ/non_secure/portmacro.h | 9 + portable/IAR/ARM_CM23/non_secure/port.c | 260 ++++++++++++++++-- portable/IAR/ARM_CM23/non_secure/portmacro.h | 9 + portable/IAR/ARM_CM23_NTZ/non_secure/port.c | 260 ++++++++++++++++-- .../IAR/ARM_CM23_NTZ/non_secure/portmacro.h | 9 + portable/IAR/ARM_CM33/non_secure/port.c | 260 ++++++++++++++++-- portable/IAR/ARM_CM33/non_secure/portasm.s | 1 - portable/IAR/ARM_CM33/non_secure/portmacro.h | 9 + portable/IAR/ARM_CM33_NTZ/non_secure/port.c | 260 ++++++++++++++++-- .../IAR/ARM_CM33_NTZ/non_secure/portasm.s | 1 - .../IAR/ARM_CM33_NTZ/non_secure/portmacro.h | 9 + 27 files changed, 2286 insertions(+), 200 deletions(-) diff --git a/portable/ARMv8M/non_secure/port.c b/portable/ARMv8M/non_secure/port.c index a86da7246..ba6bd2602 100644 --- a/portable/ARMv8M/non_secure/port.c +++ b/portable/ARMv8M/non_secure/port.c @@ -71,25 +71,32 @@ /** * @brief Constants required to manipulate the NVIC. */ -#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) -#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) -#define portNVIC_SYSTICK_CURRENT_VALUE ( ( volatile uint32_t * ) 0xe000e018 ) -#define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) -#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) -#define portNVIC_SYSTICK_CLK ( 0x00000004 ) -#define portNVIC_SYSTICK_INT ( 0x00000002 ) -#define portNVIC_SYSTICK_ENABLE ( 0x00000001 ) -#define portNVIC_PENDSVSET ( 0x10000000 ) +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #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 /*-----------------------------------------------------------*/ /** * @brief Constants required to manipulate the SCB. */ #define portSCB_SYS_HANDLER_CTRL_STATE_REG ( * ( volatile uint32_t * ) 0xe000ed24 ) -#define portSCB_MEM_FAULT_ENABLE ( 1UL << 16UL ) +#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL ) /*-----------------------------------------------------------*/ /** @@ -169,15 +176,30 @@ #define portMPU_RLAR_REGION_ENABLE ( 1UL ) /* Enable privileged access to unmapped region. */ -#define portMPU_PRIV_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portMPU_PRIV_BACKGROUND_ENABLE_BIT ( 1UL << 2UL ) /* Enable MPU. */ -#define portMPU_ENABLE ( 1UL << 0UL ) +#define portMPU_ENABLE_BIT ( 1UL << 0UL ) /* Expected value of the portMPU_TYPE register. */ #define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ /*-----------------------------------------------------------*/ +/** + * @brief The maximum 24-bit number. + * + * It is needed because the systick is a 24-bit counter. + */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/** + * @brief 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 ) +/*-----------------------------------------------------------*/ + /** * @brief Constants required to set up the initial stack. */ @@ -332,17 +354,211 @@ static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; */ portDONT_DISCARD volatile SecureContextHandle_t xSecureContext = portNO_SECURE_CONTEXT; #endif /* configENABLE_TRUSTZONE */ + +#if( configUSE_TICKLESS_IDLE == 1 ) + /** + * @brief The number of SysTick increments that make up one tick period. + */ + static uint32_t ulTimerCountsForOneTick = 0; + + /** + * @brief The maximum number of tick periods that can be suppressed is + * limited by the 24 bit resolution of the SysTick timer. + */ + static uint32_t xMaximumPossibleSuppressedTicks = 0; + + /** + * @brief Compensate for the CPU cycles that pass while the SysTick is + * stopped (low power functionality only). + */ + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + __attribute__(( weak )) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + 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" ); + __asm volatile( "dsb" ); + __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. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + * this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + * periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + * above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* 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. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + 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 + * 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. */ + __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 ); + + /* 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). */ + 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. */ + 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 ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + 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. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + * 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; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + 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. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } +#endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FUNCTION */ { + /* 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 ); + } + #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and reset the SysTick. */ - *( portNVIC_SYSTICK_CTRL ) = 0UL; - *( portNVIC_SYSTICK_CURRENT_VALUE ) = 0UL; + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - *( portNVIC_SYSTICK_LOAD ) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - *( portNVIC_SYSTICK_CTRL ) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; + 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; } /*-----------------------------------------------------------*/ @@ -446,11 +662,11 @@ volatile uint32_t ulDummy = 0UL; ( portMPU_RLAR_REGION_ENABLE ); /* Enable mem fault. */ - portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE; + portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE_BIT; /* Enable MPU with privileged background access i.e. unmapped * regions have privileged access. */ - portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE | portMPU_ENABLE ); + portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE_BIT | portMPU_ENABLE_BIT ); } } #endif /* configENABLE_MPU */ @@ -484,7 +700,7 @@ volatile uint32_t ulDummy = 0UL; void vPortYield( void ) /* PRIVILEGED_FUNCTION */ { /* Set a PendSV to request a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; /* Barriers are normally not required but do ensure the code is * completely within the specified behaviour for the architecture. */ @@ -527,7 +743,7 @@ uint32_t ulPreviousMask; if( xTaskIncrementTick() != pdFALSE ) { /* Pend a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask ); @@ -772,8 +988,8 @@ uint8_t ucSVCNumber; BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ { /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ - *( portNVIC_SYSPRI2 ) |= portNVIC_PENDSV_PRI; - *( portNVIC_SYSPRI2 ) |= portNVIC_SYSTICK_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; #if( configENABLE_MPU == 1 ) { diff --git a/portable/ARMv8M/non_secure/portable/GCC/ARM_CM23/portmacro.h b/portable/ARMv8M/non_secure/portable/GCC/ARM_CM23/portmacro.h index 2b338f754..2e8bba256 100644 --- a/portable/ARMv8M/non_secure/portable/GCC/ARM_CM23/portmacro.h +++ b/portable/ARMv8M/non_secure/portable/GCC/ARM_CM23/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/ARMv8M/non_secure/portable/GCC/ARM_CM23_NTZ/portmacro.h b/portable/ARMv8M/non_secure/portable/GCC/ARM_CM23_NTZ/portmacro.h index 2b338f754..2e8bba256 100644 --- a/portable/ARMv8M/non_secure/portable/GCC/ARM_CM23_NTZ/portmacro.h +++ b/portable/ARMv8M/non_secure/portable/GCC/ARM_CM23_NTZ/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/ARMv8M/non_secure/portable/GCC/ARM_CM33/portmacro.h b/portable/ARMv8M/non_secure/portable/GCC/ARM_CM33/portmacro.h index 0b44140ec..64fea84e2 100644 --- a/portable/ARMv8M/non_secure/portable/GCC/ARM_CM33/portmacro.h +++ b/portable/ARMv8M/non_secure/portable/GCC/ARM_CM33/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/ARMv8M/non_secure/portable/GCC/ARM_CM33_NTZ/portmacro.h b/portable/ARMv8M/non_secure/portable/GCC/ARM_CM33_NTZ/portmacro.h index 03b792cd8..d2c849c77 100644 --- a/portable/ARMv8M/non_secure/portable/GCC/ARM_CM33_NTZ/portmacro.h +++ b/portable/ARMv8M/non_secure/portable/GCC/ARM_CM33_NTZ/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/ARMv8M/non_secure/portable/IAR/ARM_CM23/portmacro.h b/portable/ARMv8M/non_secure/portable/IAR/ARM_CM23/portmacro.h index 6607607bf..badf77062 100644 --- a/portable/ARMv8M/non_secure/portable/IAR/ARM_CM23/portmacro.h +++ b/portable/ARMv8M/non_secure/portable/IAR/ARM_CM23/portmacro.h @@ -216,6 +216,15 @@ typedef struct MPU_SETTINGS #define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Critical section management. */ diff --git a/portable/ARMv8M/non_secure/portable/IAR/ARM_CM23_NTZ/portmacro.h b/portable/ARMv8M/non_secure/portable/IAR/ARM_CM23_NTZ/portmacro.h index 6607607bf..b4598d526 100644 --- a/portable/ARMv8M/non_secure/portable/IAR/ARM_CM23_NTZ/portmacro.h +++ b/portable/ARMv8M/non_secure/portable/IAR/ARM_CM23_NTZ/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/ARMv8M/non_secure/portable/IAR/ARM_CM33/portmacro.h b/portable/ARMv8M/non_secure/portable/IAR/ARM_CM33/portmacro.h index adc4d3d23..316213bfc 100644 --- a/portable/ARMv8M/non_secure/portable/IAR/ARM_CM33/portmacro.h +++ b/portable/ARMv8M/non_secure/portable/IAR/ARM_CM33/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/ARMv8M/non_secure/portable/IAR/ARM_CM33_NTZ/portmacro.h b/portable/ARMv8M/non_secure/portable/IAR/ARM_CM33_NTZ/portmacro.h index adc4d3d23..316213bfc 100644 --- a/portable/ARMv8M/non_secure/portable/IAR/ARM_CM33_NTZ/portmacro.h +++ b/portable/ARMv8M/non_secure/portable/IAR/ARM_CM33_NTZ/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/GCC/ARM_CM23/non_secure/port.c b/portable/GCC/ARM_CM23/non_secure/port.c index a86da7246..ba6bd2602 100644 --- a/portable/GCC/ARM_CM23/non_secure/port.c +++ b/portable/GCC/ARM_CM23/non_secure/port.c @@ -71,25 +71,32 @@ /** * @brief Constants required to manipulate the NVIC. */ -#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) -#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) -#define portNVIC_SYSTICK_CURRENT_VALUE ( ( volatile uint32_t * ) 0xe000e018 ) -#define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) -#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) -#define portNVIC_SYSTICK_CLK ( 0x00000004 ) -#define portNVIC_SYSTICK_INT ( 0x00000002 ) -#define portNVIC_SYSTICK_ENABLE ( 0x00000001 ) -#define portNVIC_PENDSVSET ( 0x10000000 ) +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #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 /*-----------------------------------------------------------*/ /** * @brief Constants required to manipulate the SCB. */ #define portSCB_SYS_HANDLER_CTRL_STATE_REG ( * ( volatile uint32_t * ) 0xe000ed24 ) -#define portSCB_MEM_FAULT_ENABLE ( 1UL << 16UL ) +#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL ) /*-----------------------------------------------------------*/ /** @@ -169,15 +176,30 @@ #define portMPU_RLAR_REGION_ENABLE ( 1UL ) /* Enable privileged access to unmapped region. */ -#define portMPU_PRIV_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portMPU_PRIV_BACKGROUND_ENABLE_BIT ( 1UL << 2UL ) /* Enable MPU. */ -#define portMPU_ENABLE ( 1UL << 0UL ) +#define portMPU_ENABLE_BIT ( 1UL << 0UL ) /* Expected value of the portMPU_TYPE register. */ #define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ /*-----------------------------------------------------------*/ +/** + * @brief The maximum 24-bit number. + * + * It is needed because the systick is a 24-bit counter. + */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/** + * @brief 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 ) +/*-----------------------------------------------------------*/ + /** * @brief Constants required to set up the initial stack. */ @@ -332,17 +354,211 @@ static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; */ portDONT_DISCARD volatile SecureContextHandle_t xSecureContext = portNO_SECURE_CONTEXT; #endif /* configENABLE_TRUSTZONE */ + +#if( configUSE_TICKLESS_IDLE == 1 ) + /** + * @brief The number of SysTick increments that make up one tick period. + */ + static uint32_t ulTimerCountsForOneTick = 0; + + /** + * @brief The maximum number of tick periods that can be suppressed is + * limited by the 24 bit resolution of the SysTick timer. + */ + static uint32_t xMaximumPossibleSuppressedTicks = 0; + + /** + * @brief Compensate for the CPU cycles that pass while the SysTick is + * stopped (low power functionality only). + */ + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + __attribute__(( weak )) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + 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" ); + __asm volatile( "dsb" ); + __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. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + * this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + * periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + * above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* 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. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + 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 + * 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. */ + __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 ); + + /* 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). */ + 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. */ + 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 ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + 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. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + * 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; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + 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. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } +#endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FUNCTION */ { + /* 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 ); + } + #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and reset the SysTick. */ - *( portNVIC_SYSTICK_CTRL ) = 0UL; - *( portNVIC_SYSTICK_CURRENT_VALUE ) = 0UL; + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - *( portNVIC_SYSTICK_LOAD ) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - *( portNVIC_SYSTICK_CTRL ) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; + 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; } /*-----------------------------------------------------------*/ @@ -446,11 +662,11 @@ volatile uint32_t ulDummy = 0UL; ( portMPU_RLAR_REGION_ENABLE ); /* Enable mem fault. */ - portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE; + portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE_BIT; /* Enable MPU with privileged background access i.e. unmapped * regions have privileged access. */ - portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE | portMPU_ENABLE ); + portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE_BIT | portMPU_ENABLE_BIT ); } } #endif /* configENABLE_MPU */ @@ -484,7 +700,7 @@ volatile uint32_t ulDummy = 0UL; void vPortYield( void ) /* PRIVILEGED_FUNCTION */ { /* Set a PendSV to request a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; /* Barriers are normally not required but do ensure the code is * completely within the specified behaviour for the architecture. */ @@ -527,7 +743,7 @@ uint32_t ulPreviousMask; if( xTaskIncrementTick() != pdFALSE ) { /* Pend a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask ); @@ -772,8 +988,8 @@ uint8_t ucSVCNumber; BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ { /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ - *( portNVIC_SYSPRI2 ) |= portNVIC_PENDSV_PRI; - *( portNVIC_SYSPRI2 ) |= portNVIC_SYSTICK_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; #if( configENABLE_MPU == 1 ) { diff --git a/portable/GCC/ARM_CM23/non_secure/portmacro.h b/portable/GCC/ARM_CM23/non_secure/portmacro.h index 2b338f754..2e8bba256 100644 --- a/portable/GCC/ARM_CM23/non_secure/portmacro.h +++ b/portable/GCC/ARM_CM23/non_secure/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/GCC/ARM_CM23_NTZ/non_secure/port.c b/portable/GCC/ARM_CM23_NTZ/non_secure/port.c index a86da7246..ba6bd2602 100644 --- a/portable/GCC/ARM_CM23_NTZ/non_secure/port.c +++ b/portable/GCC/ARM_CM23_NTZ/non_secure/port.c @@ -71,25 +71,32 @@ /** * @brief Constants required to manipulate the NVIC. */ -#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) -#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) -#define portNVIC_SYSTICK_CURRENT_VALUE ( ( volatile uint32_t * ) 0xe000e018 ) -#define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) -#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) -#define portNVIC_SYSTICK_CLK ( 0x00000004 ) -#define portNVIC_SYSTICK_INT ( 0x00000002 ) -#define portNVIC_SYSTICK_ENABLE ( 0x00000001 ) -#define portNVIC_PENDSVSET ( 0x10000000 ) +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #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 /*-----------------------------------------------------------*/ /** * @brief Constants required to manipulate the SCB. */ #define portSCB_SYS_HANDLER_CTRL_STATE_REG ( * ( volatile uint32_t * ) 0xe000ed24 ) -#define portSCB_MEM_FAULT_ENABLE ( 1UL << 16UL ) +#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL ) /*-----------------------------------------------------------*/ /** @@ -169,15 +176,30 @@ #define portMPU_RLAR_REGION_ENABLE ( 1UL ) /* Enable privileged access to unmapped region. */ -#define portMPU_PRIV_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portMPU_PRIV_BACKGROUND_ENABLE_BIT ( 1UL << 2UL ) /* Enable MPU. */ -#define portMPU_ENABLE ( 1UL << 0UL ) +#define portMPU_ENABLE_BIT ( 1UL << 0UL ) /* Expected value of the portMPU_TYPE register. */ #define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ /*-----------------------------------------------------------*/ +/** + * @brief The maximum 24-bit number. + * + * It is needed because the systick is a 24-bit counter. + */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/** + * @brief 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 ) +/*-----------------------------------------------------------*/ + /** * @brief Constants required to set up the initial stack. */ @@ -332,17 +354,211 @@ static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; */ portDONT_DISCARD volatile SecureContextHandle_t xSecureContext = portNO_SECURE_CONTEXT; #endif /* configENABLE_TRUSTZONE */ + +#if( configUSE_TICKLESS_IDLE == 1 ) + /** + * @brief The number of SysTick increments that make up one tick period. + */ + static uint32_t ulTimerCountsForOneTick = 0; + + /** + * @brief The maximum number of tick periods that can be suppressed is + * limited by the 24 bit resolution of the SysTick timer. + */ + static uint32_t xMaximumPossibleSuppressedTicks = 0; + + /** + * @brief Compensate for the CPU cycles that pass while the SysTick is + * stopped (low power functionality only). + */ + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + __attribute__(( weak )) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + 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" ); + __asm volatile( "dsb" ); + __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. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + * this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + * periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + * above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* 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. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + 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 + * 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. */ + __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 ); + + /* 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). */ + 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. */ + 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 ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + 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. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + * 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; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + 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. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } +#endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FUNCTION */ { + /* 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 ); + } + #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and reset the SysTick. */ - *( portNVIC_SYSTICK_CTRL ) = 0UL; - *( portNVIC_SYSTICK_CURRENT_VALUE ) = 0UL; + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - *( portNVIC_SYSTICK_LOAD ) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - *( portNVIC_SYSTICK_CTRL ) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; + 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; } /*-----------------------------------------------------------*/ @@ -446,11 +662,11 @@ volatile uint32_t ulDummy = 0UL; ( portMPU_RLAR_REGION_ENABLE ); /* Enable mem fault. */ - portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE; + portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE_BIT; /* Enable MPU with privileged background access i.e. unmapped * regions have privileged access. */ - portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE | portMPU_ENABLE ); + portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE_BIT | portMPU_ENABLE_BIT ); } } #endif /* configENABLE_MPU */ @@ -484,7 +700,7 @@ volatile uint32_t ulDummy = 0UL; void vPortYield( void ) /* PRIVILEGED_FUNCTION */ { /* Set a PendSV to request a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; /* Barriers are normally not required but do ensure the code is * completely within the specified behaviour for the architecture. */ @@ -527,7 +743,7 @@ uint32_t ulPreviousMask; if( xTaskIncrementTick() != pdFALSE ) { /* Pend a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask ); @@ -772,8 +988,8 @@ uint8_t ucSVCNumber; BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ { /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ - *( portNVIC_SYSPRI2 ) |= portNVIC_PENDSV_PRI; - *( portNVIC_SYSPRI2 ) |= portNVIC_SYSTICK_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; #if( configENABLE_MPU == 1 ) { diff --git a/portable/GCC/ARM_CM23_NTZ/non_secure/portmacro.h b/portable/GCC/ARM_CM23_NTZ/non_secure/portmacro.h index 2b338f754..2e8bba256 100644 --- a/portable/GCC/ARM_CM23_NTZ/non_secure/portmacro.h +++ b/portable/GCC/ARM_CM23_NTZ/non_secure/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/GCC/ARM_CM33/non_secure/port.c b/portable/GCC/ARM_CM33/non_secure/port.c index a86da7246..ba6bd2602 100644 --- a/portable/GCC/ARM_CM33/non_secure/port.c +++ b/portable/GCC/ARM_CM33/non_secure/port.c @@ -71,25 +71,32 @@ /** * @brief Constants required to manipulate the NVIC. */ -#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) -#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) -#define portNVIC_SYSTICK_CURRENT_VALUE ( ( volatile uint32_t * ) 0xe000e018 ) -#define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) -#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) -#define portNVIC_SYSTICK_CLK ( 0x00000004 ) -#define portNVIC_SYSTICK_INT ( 0x00000002 ) -#define portNVIC_SYSTICK_ENABLE ( 0x00000001 ) -#define portNVIC_PENDSVSET ( 0x10000000 ) +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #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 /*-----------------------------------------------------------*/ /** * @brief Constants required to manipulate the SCB. */ #define portSCB_SYS_HANDLER_CTRL_STATE_REG ( * ( volatile uint32_t * ) 0xe000ed24 ) -#define portSCB_MEM_FAULT_ENABLE ( 1UL << 16UL ) +#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL ) /*-----------------------------------------------------------*/ /** @@ -169,15 +176,30 @@ #define portMPU_RLAR_REGION_ENABLE ( 1UL ) /* Enable privileged access to unmapped region. */ -#define portMPU_PRIV_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portMPU_PRIV_BACKGROUND_ENABLE_BIT ( 1UL << 2UL ) /* Enable MPU. */ -#define portMPU_ENABLE ( 1UL << 0UL ) +#define portMPU_ENABLE_BIT ( 1UL << 0UL ) /* Expected value of the portMPU_TYPE register. */ #define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ /*-----------------------------------------------------------*/ +/** + * @brief The maximum 24-bit number. + * + * It is needed because the systick is a 24-bit counter. + */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/** + * @brief 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 ) +/*-----------------------------------------------------------*/ + /** * @brief Constants required to set up the initial stack. */ @@ -332,17 +354,211 @@ static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; */ portDONT_DISCARD volatile SecureContextHandle_t xSecureContext = portNO_SECURE_CONTEXT; #endif /* configENABLE_TRUSTZONE */ + +#if( configUSE_TICKLESS_IDLE == 1 ) + /** + * @brief The number of SysTick increments that make up one tick period. + */ + static uint32_t ulTimerCountsForOneTick = 0; + + /** + * @brief The maximum number of tick periods that can be suppressed is + * limited by the 24 bit resolution of the SysTick timer. + */ + static uint32_t xMaximumPossibleSuppressedTicks = 0; + + /** + * @brief Compensate for the CPU cycles that pass while the SysTick is + * stopped (low power functionality only). + */ + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + __attribute__(( weak )) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + 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" ); + __asm volatile( "dsb" ); + __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. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + * this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + * periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + * above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* 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. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + 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 + * 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. */ + __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 ); + + /* 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). */ + 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. */ + 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 ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + 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. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + * 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; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + 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. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } +#endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FUNCTION */ { + /* 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 ); + } + #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and reset the SysTick. */ - *( portNVIC_SYSTICK_CTRL ) = 0UL; - *( portNVIC_SYSTICK_CURRENT_VALUE ) = 0UL; + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - *( portNVIC_SYSTICK_LOAD ) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - *( portNVIC_SYSTICK_CTRL ) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; + 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; } /*-----------------------------------------------------------*/ @@ -446,11 +662,11 @@ volatile uint32_t ulDummy = 0UL; ( portMPU_RLAR_REGION_ENABLE ); /* Enable mem fault. */ - portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE; + portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE_BIT; /* Enable MPU with privileged background access i.e. unmapped * regions have privileged access. */ - portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE | portMPU_ENABLE ); + portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE_BIT | portMPU_ENABLE_BIT ); } } #endif /* configENABLE_MPU */ @@ -484,7 +700,7 @@ volatile uint32_t ulDummy = 0UL; void vPortYield( void ) /* PRIVILEGED_FUNCTION */ { /* Set a PendSV to request a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; /* Barriers are normally not required but do ensure the code is * completely within the specified behaviour for the architecture. */ @@ -527,7 +743,7 @@ uint32_t ulPreviousMask; if( xTaskIncrementTick() != pdFALSE ) { /* Pend a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask ); @@ -772,8 +988,8 @@ uint8_t ucSVCNumber; BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ { /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ - *( portNVIC_SYSPRI2 ) |= portNVIC_PENDSV_PRI; - *( portNVIC_SYSPRI2 ) |= portNVIC_SYSTICK_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; #if( configENABLE_MPU == 1 ) { diff --git a/portable/GCC/ARM_CM33/non_secure/portmacro.h b/portable/GCC/ARM_CM33/non_secure/portmacro.h index 0b44140ec..64fea84e2 100644 --- a/portable/GCC/ARM_CM33/non_secure/portmacro.h +++ b/portable/GCC/ARM_CM33/non_secure/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/GCC/ARM_CM33_NTZ/non_secure/port.c b/portable/GCC/ARM_CM33_NTZ/non_secure/port.c index a86da7246..ba6bd2602 100644 --- a/portable/GCC/ARM_CM33_NTZ/non_secure/port.c +++ b/portable/GCC/ARM_CM33_NTZ/non_secure/port.c @@ -71,25 +71,32 @@ /** * @brief Constants required to manipulate the NVIC. */ -#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) -#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) -#define portNVIC_SYSTICK_CURRENT_VALUE ( ( volatile uint32_t * ) 0xe000e018 ) -#define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) -#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) -#define portNVIC_SYSTICK_CLK ( 0x00000004 ) -#define portNVIC_SYSTICK_INT ( 0x00000002 ) -#define portNVIC_SYSTICK_ENABLE ( 0x00000001 ) -#define portNVIC_PENDSVSET ( 0x10000000 ) +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #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 /*-----------------------------------------------------------*/ /** * @brief Constants required to manipulate the SCB. */ #define portSCB_SYS_HANDLER_CTRL_STATE_REG ( * ( volatile uint32_t * ) 0xe000ed24 ) -#define portSCB_MEM_FAULT_ENABLE ( 1UL << 16UL ) +#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL ) /*-----------------------------------------------------------*/ /** @@ -169,15 +176,30 @@ #define portMPU_RLAR_REGION_ENABLE ( 1UL ) /* Enable privileged access to unmapped region. */ -#define portMPU_PRIV_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portMPU_PRIV_BACKGROUND_ENABLE_BIT ( 1UL << 2UL ) /* Enable MPU. */ -#define portMPU_ENABLE ( 1UL << 0UL ) +#define portMPU_ENABLE_BIT ( 1UL << 0UL ) /* Expected value of the portMPU_TYPE register. */ #define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ /*-----------------------------------------------------------*/ +/** + * @brief The maximum 24-bit number. + * + * It is needed because the systick is a 24-bit counter. + */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/** + * @brief 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 ) +/*-----------------------------------------------------------*/ + /** * @brief Constants required to set up the initial stack. */ @@ -332,17 +354,211 @@ static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; */ portDONT_DISCARD volatile SecureContextHandle_t xSecureContext = portNO_SECURE_CONTEXT; #endif /* configENABLE_TRUSTZONE */ + +#if( configUSE_TICKLESS_IDLE == 1 ) + /** + * @brief The number of SysTick increments that make up one tick period. + */ + static uint32_t ulTimerCountsForOneTick = 0; + + /** + * @brief The maximum number of tick periods that can be suppressed is + * limited by the 24 bit resolution of the SysTick timer. + */ + static uint32_t xMaximumPossibleSuppressedTicks = 0; + + /** + * @brief Compensate for the CPU cycles that pass while the SysTick is + * stopped (low power functionality only). + */ + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + __attribute__(( weak )) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + 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" ); + __asm volatile( "dsb" ); + __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. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + * this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + * periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + * above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* 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. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + 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 + * 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. */ + __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 ); + + /* 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). */ + 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. */ + 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 ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + 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. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + * 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; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + 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. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } +#endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FUNCTION */ { + /* 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 ); + } + #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and reset the SysTick. */ - *( portNVIC_SYSTICK_CTRL ) = 0UL; - *( portNVIC_SYSTICK_CURRENT_VALUE ) = 0UL; + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - *( portNVIC_SYSTICK_LOAD ) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - *( portNVIC_SYSTICK_CTRL ) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; + 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; } /*-----------------------------------------------------------*/ @@ -446,11 +662,11 @@ volatile uint32_t ulDummy = 0UL; ( portMPU_RLAR_REGION_ENABLE ); /* Enable mem fault. */ - portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE; + portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE_BIT; /* Enable MPU with privileged background access i.e. unmapped * regions have privileged access. */ - portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE | portMPU_ENABLE ); + portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE_BIT | portMPU_ENABLE_BIT ); } } #endif /* configENABLE_MPU */ @@ -484,7 +700,7 @@ volatile uint32_t ulDummy = 0UL; void vPortYield( void ) /* PRIVILEGED_FUNCTION */ { /* Set a PendSV to request a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; /* Barriers are normally not required but do ensure the code is * completely within the specified behaviour for the architecture. */ @@ -527,7 +743,7 @@ uint32_t ulPreviousMask; if( xTaskIncrementTick() != pdFALSE ) { /* Pend a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask ); @@ -772,8 +988,8 @@ uint8_t ucSVCNumber; BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ { /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ - *( portNVIC_SYSPRI2 ) |= portNVIC_PENDSV_PRI; - *( portNVIC_SYSPRI2 ) |= portNVIC_SYSTICK_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; #if( configENABLE_MPU == 1 ) { diff --git a/portable/GCC/ARM_CM33_NTZ/non_secure/portmacro.h b/portable/GCC/ARM_CM33_NTZ/non_secure/portmacro.h index 03b792cd8..d2c849c77 100644 --- a/portable/GCC/ARM_CM33_NTZ/non_secure/portmacro.h +++ b/portable/GCC/ARM_CM33_NTZ/non_secure/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/IAR/ARM_CM23/non_secure/port.c b/portable/IAR/ARM_CM23/non_secure/port.c index a86da7246..ba6bd2602 100644 --- a/portable/IAR/ARM_CM23/non_secure/port.c +++ b/portable/IAR/ARM_CM23/non_secure/port.c @@ -71,25 +71,32 @@ /** * @brief Constants required to manipulate the NVIC. */ -#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) -#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) -#define portNVIC_SYSTICK_CURRENT_VALUE ( ( volatile uint32_t * ) 0xe000e018 ) -#define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) -#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) -#define portNVIC_SYSTICK_CLK ( 0x00000004 ) -#define portNVIC_SYSTICK_INT ( 0x00000002 ) -#define portNVIC_SYSTICK_ENABLE ( 0x00000001 ) -#define portNVIC_PENDSVSET ( 0x10000000 ) +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #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 /*-----------------------------------------------------------*/ /** * @brief Constants required to manipulate the SCB. */ #define portSCB_SYS_HANDLER_CTRL_STATE_REG ( * ( volatile uint32_t * ) 0xe000ed24 ) -#define portSCB_MEM_FAULT_ENABLE ( 1UL << 16UL ) +#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL ) /*-----------------------------------------------------------*/ /** @@ -169,15 +176,30 @@ #define portMPU_RLAR_REGION_ENABLE ( 1UL ) /* Enable privileged access to unmapped region. */ -#define portMPU_PRIV_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portMPU_PRIV_BACKGROUND_ENABLE_BIT ( 1UL << 2UL ) /* Enable MPU. */ -#define portMPU_ENABLE ( 1UL << 0UL ) +#define portMPU_ENABLE_BIT ( 1UL << 0UL ) /* Expected value of the portMPU_TYPE register. */ #define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ /*-----------------------------------------------------------*/ +/** + * @brief The maximum 24-bit number. + * + * It is needed because the systick is a 24-bit counter. + */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/** + * @brief 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 ) +/*-----------------------------------------------------------*/ + /** * @brief Constants required to set up the initial stack. */ @@ -332,17 +354,211 @@ static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; */ portDONT_DISCARD volatile SecureContextHandle_t xSecureContext = portNO_SECURE_CONTEXT; #endif /* configENABLE_TRUSTZONE */ + +#if( configUSE_TICKLESS_IDLE == 1 ) + /** + * @brief The number of SysTick increments that make up one tick period. + */ + static uint32_t ulTimerCountsForOneTick = 0; + + /** + * @brief The maximum number of tick periods that can be suppressed is + * limited by the 24 bit resolution of the SysTick timer. + */ + static uint32_t xMaximumPossibleSuppressedTicks = 0; + + /** + * @brief Compensate for the CPU cycles that pass while the SysTick is + * stopped (low power functionality only). + */ + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + __attribute__(( weak )) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + 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" ); + __asm volatile( "dsb" ); + __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. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + * this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + * periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + * above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* 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. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + 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 + * 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. */ + __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 ); + + /* 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). */ + 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. */ + 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 ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + 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. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + * 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; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + 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. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } +#endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FUNCTION */ { + /* 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 ); + } + #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and reset the SysTick. */ - *( portNVIC_SYSTICK_CTRL ) = 0UL; - *( portNVIC_SYSTICK_CURRENT_VALUE ) = 0UL; + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - *( portNVIC_SYSTICK_LOAD ) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - *( portNVIC_SYSTICK_CTRL ) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; + 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; } /*-----------------------------------------------------------*/ @@ -446,11 +662,11 @@ volatile uint32_t ulDummy = 0UL; ( portMPU_RLAR_REGION_ENABLE ); /* Enable mem fault. */ - portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE; + portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE_BIT; /* Enable MPU with privileged background access i.e. unmapped * regions have privileged access. */ - portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE | portMPU_ENABLE ); + portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE_BIT | portMPU_ENABLE_BIT ); } } #endif /* configENABLE_MPU */ @@ -484,7 +700,7 @@ volatile uint32_t ulDummy = 0UL; void vPortYield( void ) /* PRIVILEGED_FUNCTION */ { /* Set a PendSV to request a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; /* Barriers are normally not required but do ensure the code is * completely within the specified behaviour for the architecture. */ @@ -527,7 +743,7 @@ uint32_t ulPreviousMask; if( xTaskIncrementTick() != pdFALSE ) { /* Pend a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask ); @@ -772,8 +988,8 @@ uint8_t ucSVCNumber; BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ { /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ - *( portNVIC_SYSPRI2 ) |= portNVIC_PENDSV_PRI; - *( portNVIC_SYSPRI2 ) |= portNVIC_SYSTICK_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; #if( configENABLE_MPU == 1 ) { diff --git a/portable/IAR/ARM_CM23/non_secure/portmacro.h b/portable/IAR/ARM_CM23/non_secure/portmacro.h index 6607607bf..badf77062 100644 --- a/portable/IAR/ARM_CM23/non_secure/portmacro.h +++ b/portable/IAR/ARM_CM23/non_secure/portmacro.h @@ -216,6 +216,15 @@ typedef struct MPU_SETTINGS #define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Critical section management. */ diff --git a/portable/IAR/ARM_CM23_NTZ/non_secure/port.c b/portable/IAR/ARM_CM23_NTZ/non_secure/port.c index a86da7246..ba6bd2602 100644 --- a/portable/IAR/ARM_CM23_NTZ/non_secure/port.c +++ b/portable/IAR/ARM_CM23_NTZ/non_secure/port.c @@ -71,25 +71,32 @@ /** * @brief Constants required to manipulate the NVIC. */ -#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) -#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) -#define portNVIC_SYSTICK_CURRENT_VALUE ( ( volatile uint32_t * ) 0xe000e018 ) -#define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) -#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) -#define portNVIC_SYSTICK_CLK ( 0x00000004 ) -#define portNVIC_SYSTICK_INT ( 0x00000002 ) -#define portNVIC_SYSTICK_ENABLE ( 0x00000001 ) -#define portNVIC_PENDSVSET ( 0x10000000 ) +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #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 /*-----------------------------------------------------------*/ /** * @brief Constants required to manipulate the SCB. */ #define portSCB_SYS_HANDLER_CTRL_STATE_REG ( * ( volatile uint32_t * ) 0xe000ed24 ) -#define portSCB_MEM_FAULT_ENABLE ( 1UL << 16UL ) +#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL ) /*-----------------------------------------------------------*/ /** @@ -169,15 +176,30 @@ #define portMPU_RLAR_REGION_ENABLE ( 1UL ) /* Enable privileged access to unmapped region. */ -#define portMPU_PRIV_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portMPU_PRIV_BACKGROUND_ENABLE_BIT ( 1UL << 2UL ) /* Enable MPU. */ -#define portMPU_ENABLE ( 1UL << 0UL ) +#define portMPU_ENABLE_BIT ( 1UL << 0UL ) /* Expected value of the portMPU_TYPE register. */ #define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ /*-----------------------------------------------------------*/ +/** + * @brief The maximum 24-bit number. + * + * It is needed because the systick is a 24-bit counter. + */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/** + * @brief 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 ) +/*-----------------------------------------------------------*/ + /** * @brief Constants required to set up the initial stack. */ @@ -332,17 +354,211 @@ static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; */ portDONT_DISCARD volatile SecureContextHandle_t xSecureContext = portNO_SECURE_CONTEXT; #endif /* configENABLE_TRUSTZONE */ + +#if( configUSE_TICKLESS_IDLE == 1 ) + /** + * @brief The number of SysTick increments that make up one tick period. + */ + static uint32_t ulTimerCountsForOneTick = 0; + + /** + * @brief The maximum number of tick periods that can be suppressed is + * limited by the 24 bit resolution of the SysTick timer. + */ + static uint32_t xMaximumPossibleSuppressedTicks = 0; + + /** + * @brief Compensate for the CPU cycles that pass while the SysTick is + * stopped (low power functionality only). + */ + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + __attribute__(( weak )) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + 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" ); + __asm volatile( "dsb" ); + __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. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + * this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + * periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + * above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* 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. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + 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 + * 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. */ + __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 ); + + /* 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). */ + 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. */ + 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 ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + 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. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + * 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; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + 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. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } +#endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FUNCTION */ { + /* 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 ); + } + #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and reset the SysTick. */ - *( portNVIC_SYSTICK_CTRL ) = 0UL; - *( portNVIC_SYSTICK_CURRENT_VALUE ) = 0UL; + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - *( portNVIC_SYSTICK_LOAD ) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - *( portNVIC_SYSTICK_CTRL ) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; + 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; } /*-----------------------------------------------------------*/ @@ -446,11 +662,11 @@ volatile uint32_t ulDummy = 0UL; ( portMPU_RLAR_REGION_ENABLE ); /* Enable mem fault. */ - portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE; + portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE_BIT; /* Enable MPU with privileged background access i.e. unmapped * regions have privileged access. */ - portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE | portMPU_ENABLE ); + portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE_BIT | portMPU_ENABLE_BIT ); } } #endif /* configENABLE_MPU */ @@ -484,7 +700,7 @@ volatile uint32_t ulDummy = 0UL; void vPortYield( void ) /* PRIVILEGED_FUNCTION */ { /* Set a PendSV to request a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; /* Barriers are normally not required but do ensure the code is * completely within the specified behaviour for the architecture. */ @@ -527,7 +743,7 @@ uint32_t ulPreviousMask; if( xTaskIncrementTick() != pdFALSE ) { /* Pend a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask ); @@ -772,8 +988,8 @@ uint8_t ucSVCNumber; BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ { /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ - *( portNVIC_SYSPRI2 ) |= portNVIC_PENDSV_PRI; - *( portNVIC_SYSPRI2 ) |= portNVIC_SYSTICK_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; #if( configENABLE_MPU == 1 ) { diff --git a/portable/IAR/ARM_CM23_NTZ/non_secure/portmacro.h b/portable/IAR/ARM_CM23_NTZ/non_secure/portmacro.h index 6607607bf..b4598d526 100644 --- a/portable/IAR/ARM_CM23_NTZ/non_secure/portmacro.h +++ b/portable/IAR/ARM_CM23_NTZ/non_secure/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/IAR/ARM_CM33/non_secure/port.c b/portable/IAR/ARM_CM33/non_secure/port.c index a86da7246..ba6bd2602 100644 --- a/portable/IAR/ARM_CM33/non_secure/port.c +++ b/portable/IAR/ARM_CM33/non_secure/port.c @@ -71,25 +71,32 @@ /** * @brief Constants required to manipulate the NVIC. */ -#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) -#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) -#define portNVIC_SYSTICK_CURRENT_VALUE ( ( volatile uint32_t * ) 0xe000e018 ) -#define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) -#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) -#define portNVIC_SYSTICK_CLK ( 0x00000004 ) -#define portNVIC_SYSTICK_INT ( 0x00000002 ) -#define portNVIC_SYSTICK_ENABLE ( 0x00000001 ) -#define portNVIC_PENDSVSET ( 0x10000000 ) +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #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 /*-----------------------------------------------------------*/ /** * @brief Constants required to manipulate the SCB. */ #define portSCB_SYS_HANDLER_CTRL_STATE_REG ( * ( volatile uint32_t * ) 0xe000ed24 ) -#define portSCB_MEM_FAULT_ENABLE ( 1UL << 16UL ) +#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL ) /*-----------------------------------------------------------*/ /** @@ -169,15 +176,30 @@ #define portMPU_RLAR_REGION_ENABLE ( 1UL ) /* Enable privileged access to unmapped region. */ -#define portMPU_PRIV_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portMPU_PRIV_BACKGROUND_ENABLE_BIT ( 1UL << 2UL ) /* Enable MPU. */ -#define portMPU_ENABLE ( 1UL << 0UL ) +#define portMPU_ENABLE_BIT ( 1UL << 0UL ) /* Expected value of the portMPU_TYPE register. */ #define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ /*-----------------------------------------------------------*/ +/** + * @brief The maximum 24-bit number. + * + * It is needed because the systick is a 24-bit counter. + */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/** + * @brief 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 ) +/*-----------------------------------------------------------*/ + /** * @brief Constants required to set up the initial stack. */ @@ -332,17 +354,211 @@ static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; */ portDONT_DISCARD volatile SecureContextHandle_t xSecureContext = portNO_SECURE_CONTEXT; #endif /* configENABLE_TRUSTZONE */ + +#if( configUSE_TICKLESS_IDLE == 1 ) + /** + * @brief The number of SysTick increments that make up one tick period. + */ + static uint32_t ulTimerCountsForOneTick = 0; + + /** + * @brief The maximum number of tick periods that can be suppressed is + * limited by the 24 bit resolution of the SysTick timer. + */ + static uint32_t xMaximumPossibleSuppressedTicks = 0; + + /** + * @brief Compensate for the CPU cycles that pass while the SysTick is + * stopped (low power functionality only). + */ + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + __attribute__(( weak )) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + 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" ); + __asm volatile( "dsb" ); + __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. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + * this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + * periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + * above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* 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. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + 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 + * 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. */ + __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 ); + + /* 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). */ + 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. */ + 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 ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + 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. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + * 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; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + 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. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } +#endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FUNCTION */ { + /* 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 ); + } + #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and reset the SysTick. */ - *( portNVIC_SYSTICK_CTRL ) = 0UL; - *( portNVIC_SYSTICK_CURRENT_VALUE ) = 0UL; + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - *( portNVIC_SYSTICK_LOAD ) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - *( portNVIC_SYSTICK_CTRL ) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; + 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; } /*-----------------------------------------------------------*/ @@ -446,11 +662,11 @@ volatile uint32_t ulDummy = 0UL; ( portMPU_RLAR_REGION_ENABLE ); /* Enable mem fault. */ - portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE; + portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE_BIT; /* Enable MPU with privileged background access i.e. unmapped * regions have privileged access. */ - portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE | portMPU_ENABLE ); + portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE_BIT | portMPU_ENABLE_BIT ); } } #endif /* configENABLE_MPU */ @@ -484,7 +700,7 @@ volatile uint32_t ulDummy = 0UL; void vPortYield( void ) /* PRIVILEGED_FUNCTION */ { /* Set a PendSV to request a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; /* Barriers are normally not required but do ensure the code is * completely within the specified behaviour for the architecture. */ @@ -527,7 +743,7 @@ uint32_t ulPreviousMask; if( xTaskIncrementTick() != pdFALSE ) { /* Pend a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask ); @@ -772,8 +988,8 @@ uint8_t ucSVCNumber; BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ { /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ - *( portNVIC_SYSPRI2 ) |= portNVIC_PENDSV_PRI; - *( portNVIC_SYSPRI2 ) |= portNVIC_SYSTICK_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; #if( configENABLE_MPU == 1 ) { diff --git a/portable/IAR/ARM_CM33/non_secure/portasm.s b/portable/IAR/ARM_CM33/non_secure/portasm.s index 7cb585b5a..639e2a8e1 100644 --- a/portable/IAR/ARM_CM33/non_secure/portasm.s +++ b/portable/IAR/ARM_CM33/non_secure/portasm.s @@ -24,7 +24,6 @@ * * 1 tab == 4 spaces! */ - /* Including FreeRTOSConfig.h here will cause build errors if the header file contains code not understood by the assembler - for example the 'extern' keyword. To avoid errors place any such code inside a #ifdef __ICCARM__/#endif block so diff --git a/portable/IAR/ARM_CM33/non_secure/portmacro.h b/portable/IAR/ARM_CM33/non_secure/portmacro.h index adc4d3d23..316213bfc 100644 --- a/portable/IAR/ARM_CM33/non_secure/portmacro.h +++ b/portable/IAR/ARM_CM33/non_secure/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ diff --git a/portable/IAR/ARM_CM33_NTZ/non_secure/port.c b/portable/IAR/ARM_CM33_NTZ/non_secure/port.c index a86da7246..ba6bd2602 100644 --- a/portable/IAR/ARM_CM33_NTZ/non_secure/port.c +++ b/portable/IAR/ARM_CM33_NTZ/non_secure/port.c @@ -71,25 +71,32 @@ /** * @brief Constants required to manipulate the NVIC. */ -#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) -#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) -#define portNVIC_SYSTICK_CURRENT_VALUE ( ( volatile uint32_t * ) 0xe000e018 ) -#define portNVIC_INT_CTRL ( ( volatile uint32_t * ) 0xe000ed04 ) -#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) -#define portNVIC_SYSTICK_CLK ( 0x00000004 ) -#define portNVIC_SYSTICK_INT ( 0x00000002 ) -#define portNVIC_SYSTICK_ENABLE ( 0x00000001 ) -#define portNVIC_PENDSVSET ( 0x10000000 ) +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) #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 /*-----------------------------------------------------------*/ /** * @brief Constants required to manipulate the SCB. */ #define portSCB_SYS_HANDLER_CTRL_STATE_REG ( * ( volatile uint32_t * ) 0xe000ed24 ) -#define portSCB_MEM_FAULT_ENABLE ( 1UL << 16UL ) +#define portSCB_MEM_FAULT_ENABLE_BIT ( 1UL << 16UL ) /*-----------------------------------------------------------*/ /** @@ -169,15 +176,30 @@ #define portMPU_RLAR_REGION_ENABLE ( 1UL ) /* Enable privileged access to unmapped region. */ -#define portMPU_PRIV_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portMPU_PRIV_BACKGROUND_ENABLE_BIT ( 1UL << 2UL ) /* Enable MPU. */ -#define portMPU_ENABLE ( 1UL << 0UL ) +#define portMPU_ENABLE_BIT ( 1UL << 0UL ) /* Expected value of the portMPU_TYPE register. */ #define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ /*-----------------------------------------------------------*/ +/** + * @brief The maximum 24-bit number. + * + * It is needed because the systick is a 24-bit counter. + */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/** + * @brief 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 ) +/*-----------------------------------------------------------*/ + /** * @brief Constants required to set up the initial stack. */ @@ -332,17 +354,211 @@ static volatile uint32_t ulCriticalNesting = 0xaaaaaaaaUL; */ portDONT_DISCARD volatile SecureContextHandle_t xSecureContext = portNO_SECURE_CONTEXT; #endif /* configENABLE_TRUSTZONE */ + +#if( configUSE_TICKLESS_IDLE == 1 ) + /** + * @brief The number of SysTick increments that make up one tick period. + */ + static uint32_t ulTimerCountsForOneTick = 0; + + /** + * @brief The maximum number of tick periods that can be suppressed is + * limited by the 24 bit resolution of the SysTick timer. + */ + static uint32_t xMaximumPossibleSuppressedTicks = 0; + + /** + * @brief Compensate for the CPU cycles that pass while the SysTick is + * stopped (low power functionality only). + */ + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + __attribute__(( weak )) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + 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" ); + __asm volatile( "dsb" ); + __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. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + * this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + * periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + * above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* 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. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + 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 + * 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. */ + __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 ); + + /* 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). */ + 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. */ + 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 ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + 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. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + * 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; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + 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. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } +#endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) /* PRIVILEGED_FUNCTION */ { + /* 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 ); + } + #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and reset the SysTick. */ - *( portNVIC_SYSTICK_CTRL ) = 0UL; - *( portNVIC_SYSTICK_CURRENT_VALUE ) = 0UL; + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* Configure SysTick to interrupt at the requested rate. */ - *( portNVIC_SYSTICK_LOAD ) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; - *( portNVIC_SYSTICK_CTRL ) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; + 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; } /*-----------------------------------------------------------*/ @@ -446,11 +662,11 @@ volatile uint32_t ulDummy = 0UL; ( portMPU_RLAR_REGION_ENABLE ); /* Enable mem fault. */ - portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE; + portSCB_SYS_HANDLER_CTRL_STATE_REG |= portSCB_MEM_FAULT_ENABLE_BIT; /* Enable MPU with privileged background access i.e. unmapped * regions have privileged access. */ - portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE | portMPU_ENABLE ); + portMPU_CTRL_REG |= ( portMPU_PRIV_BACKGROUND_ENABLE_BIT | portMPU_ENABLE_BIT ); } } #endif /* configENABLE_MPU */ @@ -484,7 +700,7 @@ volatile uint32_t ulDummy = 0UL; void vPortYield( void ) /* PRIVILEGED_FUNCTION */ { /* Set a PendSV to request a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; /* Barriers are normally not required but do ensure the code is * completely within the specified behaviour for the architecture. */ @@ -527,7 +743,7 @@ uint32_t ulPreviousMask; if( xTaskIncrementTick() != pdFALSE ) { /* Pend a context switch. */ - *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET; + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask ); @@ -772,8 +988,8 @@ uint8_t ucSVCNumber; BaseType_t xPortStartScheduler( void ) /* PRIVILEGED_FUNCTION */ { /* Make PendSV, CallSV and SysTick the same priority as the kernel. */ - *( portNVIC_SYSPRI2 ) |= portNVIC_PENDSV_PRI; - *( portNVIC_SYSPRI2 ) |= portNVIC_SYSTICK_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; #if( configENABLE_MPU == 1 ) { diff --git a/portable/IAR/ARM_CM33_NTZ/non_secure/portasm.s b/portable/IAR/ARM_CM33_NTZ/non_secure/portasm.s index 3585141e5..89ecbb7b0 100644 --- a/portable/IAR/ARM_CM33_NTZ/non_secure/portasm.s +++ b/portable/IAR/ARM_CM33_NTZ/non_secure/portasm.s @@ -24,7 +24,6 @@ * * 1 tab == 4 spaces! */ - /* Including FreeRTOSConfig.h here will cause build errors if the header file contains code not understood by the assembler - for example the 'extern' keyword. To avoid errors place any such code inside a #ifdef __ICCARM__/#endif block so diff --git a/portable/IAR/ARM_CM33_NTZ/non_secure/portmacro.h b/portable/IAR/ARM_CM33_NTZ/non_secure/portmacro.h index adc4d3d23..316213bfc 100644 --- a/portable/IAR/ARM_CM33_NTZ/non_secure/portmacro.h +++ b/portable/IAR/ARM_CM33_NTZ/non_secure/portmacro.h @@ -227,6 +227,15 @@ typedef struct MPU_SETTINGS #define portEXIT_CRITICAL() vPortExitCritical() /*-----------------------------------------------------------*/ +/** + * @brief Tickless idle/low power functionality. + */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + /** * @brief Task function macros as described on the FreeRTOS.org WEB site. */ From 3f62dfdb81965a6cf42ab905f64ccc01587cbb8d Mon Sep 17 00:00:00 2001 From: Yuhui Zheng <10982575+yuhui-zheng@users.noreply.github.com> Date: Tue, 17 Mar 2020 13:06:09 -0700 Subject: [PATCH 03/13] Update issue templates Adding bug report, documentation, and general inquiry templates. There's no easy way to redirect "general inquiry" directly to forum. If better option is available later, will update. --- .github/ISSUE_TEMPLATE/bug-report.md | 36 +++++++++++++++++++ .github/ISSUE_TEMPLATE/documentation-issue.md | 21 +++++++++++ .github/ISSUE_TEMPLATE/general-inquiry.md | 12 +++++++ 3 files changed, 69 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/documentation-issue.md create mode 100644 .github/ISSUE_TEMPLATE/general-inquiry.md diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 000000000..9e0f24291 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Create a report to help us improve our code. +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A concise description of what the bug is. + +**Target** +- Development board: [e.g. HiFive11 RevB] +- Instruction Set Architecture: [e.g. RV32IMAC] +- IDE and version: [e.g. Freedom Studio 4.12.0.2019-08-2] +- Toolchain and version: [e.g. riscv64-unknown-elf-gcc-8.3.0-2019.08.0] + +**Host** +- Host OS: [e.g. MacOS] +- Version: [e.g. Mojave 10.14.6] + +**To Reproduce** +- Use project ... and configure with ... +- Run on ... and could observe ... + +**Expected behavior** +A concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. +e.g. code snippet to reproduce the issue. +e.g. stack trace, memory dump, debugger log, and many etc. diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.md b/.github/ISSUE_TEMPLATE/documentation-issue.md new file mode 100644 index 000000000..062a678b5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation-issue.md @@ -0,0 +1,21 @@ +--- +name: Documentation issue +about: Create a report to help us improve our documentation. +title: "[DOC]" +labels: documentation +assignees: '' + +--- + +**Describe the issue** +Please describe the issue and expected clarification in concise language. + +**Reference** +Please attach the URL at which you are experiencing the issue. + +**Screenshot** +If applicable, please attach screenshot. + +**Browser** +- Browser: [e.g. Chrome] +- Version: [e.g. 80.0.3987.132] diff --git a/.github/ISSUE_TEMPLATE/general-inquiry.md b/.github/ISSUE_TEMPLATE/general-inquiry.md new file mode 100644 index 000000000..d3b8baf70 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general-inquiry.md @@ -0,0 +1,12 @@ +--- +name: General inquiry +about: Free form communication. +title: "[Inquiry]" +labels: question +assignees: '' + +--- + +We do encourage you to take a look at [FreeRTOS official site](https://www.freertos.org) for general information and [FreeRTOS forum](https://forums.freertos.org) to access our community. + +If still needed, could create a general inquiry report here. From 3f6cd683cbc350d949161a9bc27435e04424d18d Mon Sep 17 00:00:00 2001 From: Yuhui Zheng <10982575+yuhui-zheng@users.noreply.github.com> Date: Tue, 17 Mar 2020 13:08:50 -0700 Subject: [PATCH 04/13] Create SECURITY.md Bring in SECURITY.md from aws/.github. --- SECURITY.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..d1a40a4db --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +## Reporting a Vulnerability + +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security +via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to aws-security@amazon.com. +Please do **not** create a public github issue. From 1ebcac3c9f8c8fc91cbd348677c9398f0b993f4c Mon Sep 17 00:00:00 2001 From: Yuhui Zheng <10982575+yuhui-zheng@users.noreply.github.com> Date: Tue, 17 Mar 2020 13:11:11 -0700 Subject: [PATCH 05/13] Create pull_request_template.md Using the same pull request template as what we have in FreeRTOS/FreeRTOS. --- .github/pull_request_template.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..c3c8607fb --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,16 @@ + + +Description +----------- + + +Test Steps +----------- + + +Related Issue +----------- + + + +By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. From 62f615f662ec87281648b272edb2d23bd42367d8 Mon Sep 17 00:00:00 2001 From: Yuhui Zheng <10982575+yuhui-zheng@users.noreply.github.com> Date: Tue, 17 Mar 2020 19:30:19 -0700 Subject: [PATCH 06/13] Adding url to direct users to FreeRTOS-Kernel github page. (#32) --- GitHub-FreeRTOS-Kernel-Home.url | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 GitHub-FreeRTOS-Kernel-Home.url diff --git a/GitHub-FreeRTOS-Kernel-Home.url b/GitHub-FreeRTOS-Kernel-Home.url new file mode 100644 index 000000000..ee675d615 --- /dev/null +++ b/GitHub-FreeRTOS-Kernel-Home.url @@ -0,0 +1,8 @@ +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,2 +[InternetShortcut] +URL=https://github.com/FreeRTOS/FreeRTOS-Kernel +IconIndex=0 +IDList= +HotKey=0 + From eff07c040adfec9513e20d474cc53371d0f09983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?WineQ=E5=9C=889?= Date: Fri, 20 Mar 2020 00:00:05 +0800 Subject: [PATCH 07/13] Update tasks.c (#26) Reopen this PR. --- tasks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks.c b/tasks.c index 49d28c63e..8299498c0 100644 --- a/tasks.c +++ b/tasks.c @@ -4133,10 +4133,10 @@ TCB_t *pxTCB; the mutex. If the mutex is held by a task then it cannot be given from an interrupt, and if a mutex is given by the holding task then it must be the running state task. Remove - the holding task from the ready/delayed list. */ + the holding task from the ready list. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { - taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); } else { From 90a35847498bb68ec1dbd0403c577ea1b7895d0f Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Wed, 4 Mar 2020 18:00:41 +0000 Subject: [PATCH 08/13] portable/GCC/Posix: add new port for Posix (Linux) applications This is similar to the Windows port, allowing FreeRTOS kernel applications to run as regular applications on Posix (Linux) systems. You can use this in a 32-bit or 64-bit application (although there are dynamic memory allocation trace points that do not support 64-bit addresses). Many of the same caveats of running an RTOS on a non-real-time system apply, but this is still very useful for easy debugging/testing applications in a simulated environment. In particular, it allows easy use of tools such as valgrind. You can call standard library functions from tasks but care must be taken with any that internally take mutexes or block. This includes malloc()/free() and many stdio functions (e.g., printf()). Replacement malloc(), free(), realloc(), and calloc() functions are provided which are safe. printf() needs to be called with a FreeRTOS mutex help (or called from only a single task). Each task is run in its own pthread, which makes debugging with standard tools (such as GDB) easier backtraces for individual tasks are available. Threads for non-running tasks are blocked in sigwait(). The stack for each task (thread) is allocated when the thread is created, and the stack provided during task creation is not used. This is so the stack has guard pages, to help with detecting stack overflows. Task switch is done by resuming the thread for the next task by sending it the resume signal (SIGUSR1) and then suspending the current thread. The timer interrupt uses SIGALRM and care is taken to ensure that the signal handler runs only on the thread for the current task. The additional data needed per-thread is stored at the top on the task's stack. When a running task is being deleted, its thread is marked it as dying so when we switch away from it it exits instead of suspending. This ensures that even if the idle task doesn't run, threads are deleted which allows for more threads to be created (if many tasks are being created and deleted in rapid succession). To further aid debugging, SIGINT (^C) is not blocked inside critical sections. This allows it to be used break into GDB while in a critical section. This means that care must be taken with any custom SIGINT handlers as these are like NMIs. This is somewhat inspired by an existing port by William Davy (https://www.freertos.org/FreeRTOS-simulator-for-Linux.html) but it takes a number of different approaches to make it switch tasks reliableand there's little similarly with the original implementation. - Critical sections block scheduling/"interrupts" by blocking signals using pthread_sigmask(). This is more expensive than attempting to use flags but works reliably and is analogous to the interrupt enable/disable on real hardware. - Care is take to ensure that the SIGALRM handler (for the timer tick) is runnable only on the pthread for the running task. This makes tasks switches more straight-forward and reliable as we can suspend the thread while in the signal handler. - Task switches save/restore the critical nesting on the stack. - Only uses a single (SIGUSR1) signal which is ignored and thus GDB's default signal handling options won't trap/print on this signal. - Extra per-thread data is stored on the task's stack, making it accessible in O(1) instead of performing a O(n) lookup of the array. - Uses the task create/delete hooks in a similar way to the Windows port, rather than overloading trace points. --- portable/ThirdParty/GCC/Posix/port.c | 601 ++++++++++++++++++++++ portable/ThirdParty/GCC/Posix/portmacro.h | 134 +++++ 2 files changed, 735 insertions(+) create mode 100644 portable/ThirdParty/GCC/Posix/port.c create mode 100644 portable/ThirdParty/GCC/Posix/portmacro.h diff --git a/portable/ThirdParty/GCC/Posix/port.c b/portable/ThirdParty/GCC/Posix/port.c new file mode 100644 index 000000000..bae6e2f84 --- /dev/null +++ b/portable/ThirdParty/GCC/Posix/port.c @@ -0,0 +1,601 @@ +/* + * FreeRTOS Kernel V10.3.0 + * Copyright (C) 2020 Cambridge Consultants Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the Posix port. + * + * Each task has a pthread which eases use of standard debuggers + * (allowing backtraces of tasks etc). Threads for tasks that are not + * running are blocked in sigwait(). + * + * Task switch is done by resuming the thread for the next task by + * sending it the resume signal (SIGUSR1) and then suspending the + * current thread. + * + * The timer interrupt uses SIGALRM and care is taken to ensure that + * the signal handler runs only on the thread for the current task. + * + * Use of part of the standard C library requires care as some + * functions can take pthread mutexes internally which can result in + * deadlocks as the FreeRTOS kernel can switch tasks while they're + * holding a pthread mutex. + * + * Replacement malloc(), free(), calloc(), and realloc() are provided + * for glibc (see below for more information). + * + * stdio (printf() and friends) should be called from a single task + * only or serialized with a FreeRTOS primitive such as a binary + * semaphore or mutex. + *----------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" +/*-----------------------------------------------------------*/ + +#define SIG_RESUME SIGUSR1 + +typedef struct THREAD +{ + pthread_t pthread; + pdTASK_CODE pxCode; + void *pvParams; + BaseType_t xDying; +} Thread_t; + +/* + * The additional per-thread data is stored at the beginning of the + * task's stack. + */ +static inline Thread_t *prvGetThreadFromTask(TaskHandle_t xTask) +{ +StackType_t *pxTopOfStack = *(StackType_t **)xTask; + + return (Thread_t *)(pxTopOfStack + 1); +} + +/*-----------------------------------------------------------*/ + +static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT; +static sigset_t xResumeSignals; +static sigset_t xAllSignals; +static sigset_t xSchedulerOriginalSignalMask; +static pthread_t hMainThread = ( pthread_t )NULL; +static volatile portBASE_TYPE uxCriticalNesting; +/*-----------------------------------------------------------*/ + +static portBASE_TYPE xSchedulerEnd = pdFALSE; +/*-----------------------------------------------------------*/ + +static void prvSetupSignalsAndSchedulerPolicy( void ); +static void prvSetupTimerInterrupt( void ); +static void *prvWaitForStart( void * pvParams ); +static void prvSwitchThread( Thread_t *xThreadToResume, Thread_t *xThreadToSuspend ); +static void prvSuspendSelf( void ); +static void prvResumeThread( pthread_t xThreadId ); +static void vPortSystemTickHandler( int sig ); +static void vPortStartFirstTask( void ); +/*-----------------------------------------------------------*/ + +/* + * The standard glibc malloc(), free() etc. take an internal lock so + * it is not safe to switch tasks while calling them. + * + * Requiring the application use the safe xPortMalloc() and + * vPortFree() is not sufficient as malloc() is used internally by + * glibc (e.g., by strdup() and the pthread library.) + * + * To further complicate things malloc() and free() may be called + * outside of task context during pthread destruction so using + * vTaskSuspend() and xTaskResumeAll() cannot be used. + * vPortEnterCritical() and vPortExitCritical() cannot be used either + * as they use global state for the critical section nesting (this + * cannot be fixed by using TLS as pthread destruction needs to free + * the TLS). + * + * Explicitly save/disable and restore the signal mask to block the + * timer (SIGALRM) and other signals. + */ + +extern void *__libc_malloc(size_t); +extern void __libc_free(void *); +extern void *__libc_calloc(size_t, size_t); +extern void *__libc_realloc(void *ptr, size_t); + +void *malloc(size_t size) +{ +sigset_t xSavedSignals; +void *ptr; + + pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals ); + ptr = __libc_malloc( size ); + pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL ); + + return ptr; +} + +void free(void *ptr) +{ +sigset_t xSavedSignals; + + pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals ); + __libc_free( ptr ); + pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL ); +} + +void *calloc(size_t nmemb, size_t size) +{ +sigset_t xSavedSignals; +void *ptr; + + pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals ); + ptr = __libc_calloc( nmemb, size ); + pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL ); + + return ptr; +} + +void *realloc(void *ptr, size_t size) +{ +sigset_t xSavedSignals; + + pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals ); + ptr = __libc_realloc( ptr, size ); + pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL ); + + return ptr; +} + +static void prvFatalError( const char *pcCall, int iErrno ) +{ + fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) ); + abort(); +} + +/* + * See header file for description. + */ +portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, + portSTACK_TYPE *pxEndOfStack, + pdTASK_CODE pxCode, void *pvParameters ) +{ +Thread_t *thread; +pthread_attr_t xThreadAttributes; +size_t ulStackSize; +int iRet; + + (void)pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy ); + + /* + * Store the additional thread data at the start of the stack. + */ + thread = (Thread_t *)(pxTopOfStack + 1) - 1; + pxTopOfStack = (portSTACK_TYPE *)thread - 1; + ulStackSize = (pxTopOfStack + 1 - pxEndOfStack) * sizeof(*pxTopOfStack); + + thread->pxCode = pxCode; + thread->pvParams = pvParameters; + thread->xDying = pdFALSE; + + pthread_attr_init( &xThreadAttributes ); + pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize ); + + vPortEnterCritical(); + + iRet = pthread_create( &thread->pthread, &xThreadAttributes, + prvWaitForStart, thread ); + if ( iRet ) + { + prvFatalError( "pthread_create", iRet ); + } + + vPortExitCritical(); + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +void vPortStartFirstTask( void ) +{ +Thread_t *pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + /* Start the first task. */ + prvResumeThread( pxFirstThread->pthread ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +portBASE_TYPE xPortStartScheduler( void ) +{ +int iSignal; +sigset_t xSignals; + + hMainThread = pthread_self(); + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + prvSetupTimerInterrupt(); + + /* Start the first task. */ + vPortStartFirstTask(); + + /* Wait until signaled by vPortEndScheduler(). */ + sigemptyset( &xSignals ); + sigaddset( &xSignals, SIG_RESUME ); + + while ( !xSchedulerEnd ) + { + sigwait( &xSignals, &iSignal ); + } + + /* Restore original signal mask. */ + (void)pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL ); + + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ +struct itimerval itimer; +struct sigaction sigtick; + + /* Stop the timer and ignore any pending SIGALRMs that would end + * up running on the main thread when it is resumed. */ + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = 0; + (void)setitimer( ITIMER_REAL, &itimer, NULL ); + + sigtick.sa_flags = 0; + sigtick.sa_handler = SIG_IGN; + sigaction( SIGALRM, &sigtick, NULL ); + + /* Signal the scheduler to exit its loop. */ + xSchedulerEnd = pdTRUE; + (void)pthread_kill( hMainThread, SIG_RESUME ); + + prvSuspendSelf(); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + if ( uxCriticalNesting == 0 ) + { + vPortDisableInterrupts(); + } + uxCriticalNesting++; +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + uxCriticalNesting--; + + /* If we have reached 0 then re-enable the interrupts. */ + if( uxCriticalNesting == 0 ) + { + vPortEnableInterrupts(); + } +} +/*-----------------------------------------------------------*/ + +void vPortYieldFromISR( void ) +{ +Thread_t *xThreadToSuspend; +Thread_t *xThreadToResume; + + xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + vTaskSwitchContext(); + + xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + prvSwitchThread( xThreadToResume, xThreadToSuspend ); +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + vPortEnterCritical(); + + vPortYieldFromISR(); + + vPortExitCritical(); +} +/*-----------------------------------------------------------*/ + +void vPortDisableInterrupts( void ) +{ + pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL ); +} +/*-----------------------------------------------------------*/ + +void vPortEnableInterrupts( void ) +{ + pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL ); +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xPortSetInterruptMask( void ) +{ + /* Interrupts are always disabled inside ISRs (signals + handlers). */ + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +void vPortClearInterruptMask( portBASE_TYPE xMask ) +{ +} +/*-----------------------------------------------------------*/ + +static uint64_t prvGetTimeNs(void) +{ +struct timespec t; + + clock_gettime(CLOCK_MONOTONIC, &t); + + return t.tv_sec * 1000000000ull + t.tv_nsec; +} + +static uint64_t prvStartTimeNs; +static uint64_t prvTickCount; + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +void prvSetupTimerInterrupt( void ) +{ +struct itimerval itimer; +int iRet; + + /* Initialise the structure with the current timer information. */ + iRet = getitimer( ITIMER_REAL, &itimer ); + if ( iRet ) + { + prvFatalError( "getitimer", errno ); + } + + /* Set the interval between timer events. */ + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = portTICK_RATE_MICROSECONDS; + + /* Set the current count-down. */ + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = portTICK_RATE_MICROSECONDS; + + /* Set-up the timer interrupt. */ + iRet = setitimer( ITIMER_REAL, &itimer, NULL ); + if ( iRet ) + { + prvFatalError( "setitimer", errno ); + } + + prvStartTimeNs = prvGetTimeNs(); +} +/*-----------------------------------------------------------*/ + +static void vPortSystemTickHandler( int sig ) +{ +Thread_t *pxThreadToSuspend; +Thread_t *pxThreadToResume; +uint64_t xExpectedTicks; + + uxCriticalNesting++; /* Signals are blocked in this signal handler. */ + + pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + /* Tick Increment, accounting for any lost signals or drift in + * the timer. */ + xExpectedTicks = (prvGetTimeNs() - prvStartTimeNs) + / (portTICK_RATE_MICROSECONDS * 1000); + do { + xTaskIncrementTick(); + prvTickCount++; + } while (prvTickCount < xExpectedTicks); + +#if ( configUSE_PREEMPTION == 1 ) + /* Select Next Task. */ + vTaskSwitchContext(); + + pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + prvSwitchThread(pxThreadToResume, pxThreadToSuspend); +#endif + + uxCriticalNesting--; +} +/*-----------------------------------------------------------*/ + +void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield ) +{ +Thread_t *pxThread = prvGetThreadFromTask( pxTaskToDelete ); + + pxThread->xDying = pdTRUE; +} + +void vPortCancelThread( void *pxTaskToDelete ) +{ +Thread_t *pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete ); + + /* + * The thread has already been suspended so it can be safely + * cancelled. + */ + pthread_cancel( pxThreadToCancel->pthread ); + pthread_join( pxThreadToCancel->pthread, NULL ); +} +/*-----------------------------------------------------------*/ + +static void *prvWaitForStart( void * pvParams ) +{ +Thread_t *pxThread = pvParams; + + prvSuspendSelf(); + + /* Resumed for the first time, unblocks all signals. */ + uxCriticalNesting = 0; + vPortEnableInterrupts(); + + /* Call the task's entry point. */ + pxThread->pxCode( pxThread->pvParams ); + + return NULL; +} +/*-----------------------------------------------------------*/ + +static void prvSwitchThread( Thread_t *pxThreadToResume, + Thread_t *pxThreadToSuspend ) +{ +BaseType_t uxSavedCriticalNesting; + + if ( pxThreadToSuspend != pxThreadToResume ) + { + /* + * Switch tasks. + * + * The critical section nesting is per-task, so save it on the + * stack of the current (suspending thread), restoring it when + * we switch back to this task. + */ + uxSavedCriticalNesting = uxCriticalNesting; + + prvResumeThread( pxThreadToResume->pthread ); + if ( pxThreadToSuspend->xDying ) + { + pthread_exit( NULL ); + } + prvSuspendSelf(); + + uxCriticalNesting = uxSavedCriticalNesting; + } +} +/*-----------------------------------------------------------*/ + +static void prvSuspendSelf( void ) +{ +int iSig; + + /* + * Suspend this thread by waiting for a SIG_RESUME signal. + * + * A suspended thread must not handle signals (interrupts) so + * all signals must be blocked by calling this from: + * + * - Inside a critical section (vPortEnterCritical() / + * vPortExitCritical()). + * + * - From a signal handler that has all signals masked. + * + * - A thread with all signals blocked with pthread_sigmask(). + */ + sigwait( &xResumeSignals, &iSig ); +} + +/*-----------------------------------------------------------*/ + +static void prvResumeThread( pthread_t xThreadId ) +{ + if ( pthread_self() != xThreadId ) + { + pthread_kill( xThreadId, SIG_RESUME ); + } +} +/*-----------------------------------------------------------*/ + +static void prvSetupSignalsAndSchedulerPolicy( void ) +{ +struct sigaction sigresume, sigtick; +int iRet; + + hMainThread = pthread_self(); + + /* Initialise common signal masks. */ + sigemptyset( &xResumeSignals ); + sigaddset( &xResumeSignals, SIG_RESUME ); + sigfillset( &xAllSignals ); + /* Don't block SIGINT so this can be used to break into GDB while + * in a critical section. */ + sigdelset( &xAllSignals, SIGINT ); + + /* + * Block all signals in this thread so all new threads + * inherits this mask. + * + * When a thread is resumed for the first time, all signals + * will be unblocked. + */ + (void)pthread_sigmask( SIG_SETMASK, &xAllSignals, + &xSchedulerOriginalSignalMask ); + + /* SIG_RESUME is only used with sigwait() so doesn't need a + handler. */ + sigresume.sa_flags = 0; + sigresume.sa_handler = SIG_IGN; + sigfillset( &sigresume.sa_mask ); + + sigtick.sa_flags = 0; + sigtick.sa_handler = vPortSystemTickHandler; + sigfillset( &sigtick.sa_mask ); + + iRet = sigaction( SIG_RESUME, &sigresume, NULL ); + if ( iRet ) + { + prvFatalError( "sigaction", errno ); + } + + iRet = sigaction( SIGALRM, &sigtick, NULL ); + if ( iRet ) + { + prvFatalError( "sigaction", errno ); + } +} +/*-----------------------------------------------------------*/ + +unsigned long ulPortGetRunTime( void ) +{ +struct tms xTimes; + + times( &xTimes ); + + return ( unsigned long ) xTimes.tms_utime; +} +/*-----------------------------------------------------------*/ diff --git a/portable/ThirdParty/GCC/Posix/portmacro.h b/portable/ThirdParty/GCC/Posix/portmacro.h new file mode 100644 index 000000000..8046cbc8f --- /dev/null +++ b/portable/ThirdParty/GCC/Posix/portmacro.h @@ -0,0 +1,134 @@ +/* + * FreeRTOS Kernel V10.3.0 + * Copyright 2020 Cambridge Consultants Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE unsigned long +#define portBASE_TYPE long +#define portPOINTER_SIZE_TYPE intptr_t + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +typedef unsigned long TickType_t; +#define portMAX_DELAY ( TickType_t ) ULONG_MAX + +#define portTICK_TYPE_IS_ATOMIC 1 + +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portHAS_STACK_OVERFLOW_CHECKING ( 1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portTICK_RATE_MICROSECONDS ( ( portTickType ) 1000000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ +extern void vPortYield( void ); + +#define portYIELD() vPortYield() + +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) vPortYield() +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern void vPortDisableInterrupts( void ); +extern void vPortEnableInterrupts( void ); +#define portSET_INTERRUPT_MASK() ( vPortDisableInterrupts() ) +#define portCLEAR_INTERRUPT_MASK() ( vPortEnableInterrupts() ) + +extern portBASE_TYPE xPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( portBASE_TYPE xMask ); + +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); +#define portSET_INTERRUPT_MASK_FROM_ISR() xPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask(x) +#define portDISABLE_INTERRUPTS() portSET_INTERRUPT_MASK() +#define portENABLE_INTERRUPTS() portCLEAR_INTERRUPT_MASK() +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() + +/*-----------------------------------------------------------*/ + +extern void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield ); +extern void vPortCancelThread( void *pxTaskToDelete ); +#define portPRE_TASK_DELETE_HOOK( pvTaskToDelete, pxPendYield ) vPortThreadDying( ( pvTaskToDelete ), ( pxPendYield ) ) +#define portCLEAN_UP_TCB( pxTCB ) vPortCancelThread( pxTCB ) +/*-----------------------------------------------------------*/ + +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +/* + * Tasks run in their own pthreads and context switches between them + * are always a full memory barrier. ISRs are emulated as signals + * which also imply a full memory barrier. + * + * Thus, only a compilier barrier is needed to prevent the compiler + * reordering. + */ +#define portMEMORY_BARRIER() __asm volatile( "" ::: "memory" ) + +extern unsigned long ulPortGetRunTime( void ); +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() /* no-op */ +#define portGET_RUN_TIME_COUNTER_VALUE() ulPortGetRunTime() + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ From 3b8c72c66920bd6513b5a2f5143ced866376ad92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?WineQ=E5=9C=889?= Date: Mon, 23 Mar 2020 10:18:48 +0800 Subject: [PATCH 09/13] Update tasks.c (#22) An optimization for prvResetNextTaskUnblockTime(). --- tasks.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tasks.c b/tasks.c index 8299498c0..835d7692b 100644 --- a/tasks.c +++ b/tasks.c @@ -3964,8 +3964,7 @@ TCB_t *pxTCB; the item at the head of the delayed list. This is the time at which the task at the head of the delayed list should be removed from the Blocked state. */ - ( pxTCB ) = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) ); + xNextTaskUnblockTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxDelayedTaskList ); } } /*-----------------------------------------------------------*/ From a5531aade68238009c938e3ced6aadb1dafefddc Mon Sep 17 00:00:00 2001 From: Yuhui Zheng <10982575+yuhui-zheng@users.noreply.github.com> Date: Sun, 22 Mar 2020 18:58:21 -0700 Subject: [PATCH 10/13] pxTCB is no longer needed in this local function. --- tasks.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tasks.c b/tasks.c index 835d7692b..d63f6ccfd 100644 --- a/tasks.c +++ b/tasks.c @@ -3948,8 +3948,6 @@ static void prvCheckTasksWaitingTermination( void ) static void prvResetNextTaskUnblockTime( void ) { -TCB_t *pxTCB; - if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) { /* The new current delayed list is empty. Set xNextTaskUnblockTime to From 464695a4f2655476c15b8d3d988361fb237d5bf6 Mon Sep 17 00:00:00 2001 From: Yuhui Zheng <10982575+yuhui-zheng@users.noreply.github.com> Date: Tue, 24 Mar 2020 11:54:03 -0700 Subject: [PATCH 11/13] Synopsys ARC port, adding support for ARC EM and HS cores -- continued from PR #8. (#28) Synopsys ARC port, adding support for ARC EM and HS cores. --- .../GCC/ARC_EM_HS/arc_freertos_exceptions.c | 52 ++ .../GCC/ARC_EM_HS/arc_freertos_exceptions.h | 45 ++ .../ThirdParty/GCC/ARC_EM_HS/arc_support.s | 521 ++++++++++++++++++ .../ThirdParty/GCC/ARC_EM_HS/freertos_tls.c | 205 +++++++ portable/ThirdParty/GCC/ARC_EM_HS/port.c | 283 ++++++++++ portable/ThirdParty/GCC/ARC_EM_HS/portmacro.h | 158 ++++++ 6 files changed, 1264 insertions(+) create mode 100644 portable/ThirdParty/GCC/ARC_EM_HS/arc_freertos_exceptions.c create mode 100644 portable/ThirdParty/GCC/ARC_EM_HS/arc_freertos_exceptions.h create mode 100644 portable/ThirdParty/GCC/ARC_EM_HS/arc_support.s create mode 100644 portable/ThirdParty/GCC/ARC_EM_HS/freertos_tls.c create mode 100644 portable/ThirdParty/GCC/ARC_EM_HS/port.c create mode 100644 portable/ThirdParty/GCC/ARC_EM_HS/portmacro.h diff --git a/portable/ThirdParty/GCC/ARC_EM_HS/arc_freertos_exceptions.c b/portable/ThirdParty/GCC/ARC_EM_HS/arc_freertos_exceptions.c new file mode 100644 index 000000000..cefd2aa3e --- /dev/null +++ b/portable/ThirdParty/GCC/ARC_EM_HS/arc_freertos_exceptions.c @@ -0,0 +1,52 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/** + * \file + * \brief exception processing for freertos + */ + +// #include "embARC.h" + +#include "arc_freertos_exceptions.h" + +#ifdef __GNU__ +extern void gnu_printf_setup(void); +#endif +/** + * \brief freertos related cpu exception initialization, all the interrupts handled by freertos must be not + * fast irqs. If fiq is needed, please install the default firq_exc_entry or your own fast irq entry into + * the specific interrupt exception. + */ +void freertos_exc_init(void) +{ + +#ifdef __GNU__ + gnu_printf_setup(); +#endif + +} diff --git a/portable/ThirdParty/GCC/ARC_EM_HS/arc_freertos_exceptions.h b/portable/ThirdParty/GCC/ARC_EM_HS/arc_freertos_exceptions.h new file mode 100644 index 000000000..ae398f4f6 --- /dev/null +++ b/portable/ThirdParty/GCC/ARC_EM_HS/arc_freertos_exceptions.h @@ -0,0 +1,45 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef ARC_FREERTOS_EXCEPTIONS_H +#define ARC_FREERTOS_EXCEPTIONS_H + +/* + * here, all arc cpu exceptions share the same entry, also for all interrupt + * exceptions + */ +extern void exc_entry_cpu(void); /* cpu exception entry for freertos */ +extern void exc_entry_int(void); /* int exception entry for freertos */ + +/* task dispatch functions in .s */ +extern void start_r(void); +extern void start_dispatch(); +extern void dispatch(); + +extern void freertos_exc_init(void); + +#endif /* ARC_FREERTOS_EXCEPTIONS_H */ diff --git a/portable/ThirdParty/GCC/ARC_EM_HS/arc_support.s b/portable/ThirdParty/GCC/ARC_EM_HS/arc_support.s new file mode 100644 index 000000000..27aa5fef7 --- /dev/null +++ b/portable/ThirdParty/GCC/ARC_EM_HS/arc_support.s @@ -0,0 +1,521 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/** + * \file + * \ingroup OS_FREERTOS + * \brief freertos support for arc processor + * like task dispatcher, interrupt handler + */ +/** @cond OS_FREERTOS_ASM_ARC_SUPPORT */ + +/* + * core-dependent part in assemble language (for arc) + */ +#define __ASSEMBLY__ +#include "arc/arc.h" +#include "arc/arc_asm_common.h" + +/* + * task dispatcher + * + */ + .text + .align 4 + .global dispatch +dispatch: +/* + * the pre-conditions of this routine are task context, CPU is + * locked, dispatch is enabled. + */ + SAVE_NONSCRATCH_REGS /* save callee save registers */ + mov r1, dispatch_r + PUSH r1 /* save return address */ + ld r0, [pxCurrentTCB] + bl dispatcher + +/* return routine when task dispatch happened in task context */ +dispatch_r: + RESTORE_NONSCRATCH_REGS /* recover registers */ + j [blink] + +/* + * start dispatch + */ + .global start_dispatch + .align 4 +start_dispatch: +/* + * this routine is called in the non-task context during the startup of the kernel + * , and all the interrupts are locked. + * + * when the dispatcher is called, the cpu is locked, no nest exception (CPU exception/interrupt). + * In target_initialize, all interrupt priority mask should be cleared, cpu should be + * locked, the interrupts outside the kernel such as fiq can be + * enabled. + */ + clri + mov r0, 0 + st r0, [exc_nest_count] + b dispatcher_0 +/* + * dispatcher + */ +dispatcher: + ld r1, [ulCriticalNesting] + PUSH r1 /* save critical nesting */ + st sp, [r0] /* save stack pointer of current task, r0->pxCurrentTCB */ + jl vTaskSwitchContext /* change the value of pxCurrentTCB */ +/* + * before dispatcher is called, task context | cpu locked | dispatch enabled + * should be satisfied. In this routine, the processor will jump + * into the entry of next to run task + * + * i.e. kernel mode, IRQ disabled, dispatch enabled + */ +dispatcher_0: + ld r1, [pxCurrentTCB] + ld sp, [r1] /* recover task stack */ +#if ARC_FEATURE_STACK_CHECK +#if ARC_FEATURE_SEC_PRESENT + lr r0, [AUX_SEC_STAT] + bclr r0, r0, AUX_SEC_STAT_BIT_SSC + sflag r0 +#else + lr r0, [AUX_STATUS32] + bclr r0, r0, AUX_STATUS_BIT_SC + kflag r0 +#endif + jl vPortSetStackCheck +#if ARC_FEATURE_SEC_PRESENT + lr r0, [AUX_SEC_STAT] + bset r0, r0, AUX_SEC_STAT_BIT_SSC + sflag r0 +#else + lr r0, [AUX_STATUS32] + bset r0, r0, AUX_STATUS_BIT_SC + kflag r0 +#endif +#endif + POP r0 /* get critical nesting */ + st r0, [ulCriticalNesting] + POP r0 /* get return address */ + j [r0] + +/* + * task startup routine + * + */ + .text + .global start_r + .align 4 +start_r: + seti /* unlock cpu */ + mov blink, vPortEndTask /* set return address */ + POP r1 /* get task function body */ + POP r0 /* get task parameters */ + j [r1] + +/****** exceptions and interrupts handing ******/ +/****** entry for exception handling ******/ + .global exc_entry_cpu + .align 4 +exc_entry_cpu: + + EXCEPTION_PROLOGUE + + mov blink, sp + mov r3, sp /* as exception handler's para(p_excinfo) */ + + ld r0, [exc_nest_count] + add r1, r0, 1 + st r1, [exc_nest_count] + brne r0, 0, exc_handler_1 +/* change to exception stack if interrupt happened in task context */ + mov sp, _e_stack +exc_handler_1: + PUSH blink + + lr r0, [AUX_ECR] + lsr r0, r0, 16 + mov r1, exc_int_handler_table + ld.as r2, [r1, r0] + + mov r0, r3 + jl [r2] /* !!!!jump to exception handler where interrupts are not allowed! */ + +/* interrupts are not allowed */ +ret_exc: + POP sp + mov r1, exc_nest_count + ld r0, [r1] + sub r0, r0, 1 + st r0, [r1] + brne r0, 0, ret_exc_1 /* nest exception case */ + lr r1, [AUX_IRQ_ACT] /* nest interrupt case */ + brne r1, 0, ret_exc_1 + + ld r0, [context_switch_reqflg] + brne r0, 0, ret_exc_2 +ret_exc_1: /* return from non-task context, interrupts or exceptions are nested */ + + EXCEPTION_EPILOGUE + rtie + +/* there is a dispatch request */ +ret_exc_2: + /* clear dispatch request */ + mov r0, 0 + st r0, [context_switch_reqflg] + + ld r0, [pxCurrentTCB] + breq r0, 0, ret_exc_1 + + SAVE_CALLEE_REGS /* save callee save registers */ + + lr r0, [AUX_STATUS32] + bclr r0, r0, AUX_STATUS_BIT_AE /* clear exception bit */ + kflag r0 + + mov r1, ret_exc_r /* save return address */ + PUSH r1 + + bl dispatcher /* r0->pxCurrentTCB */ + +ret_exc_r: + /* recover exception status */ + lr r0, [AUX_STATUS32] + bset r0, r0, AUX_STATUS_BIT_AE + kflag r0 + + RESTORE_CALLEE_REGS /* recover registers */ + EXCEPTION_EPILOGUE + rtie + +/****** entry for normal interrupt exception handling ******/ + .global exc_entry_int /* entry for interrupt handling */ + .align 4 +exc_entry_int: +#if ARC_FEATURE_FIRQ == 1 +#if ARC_FEATURE_RGF_NUM_BANKS > 1 + lr r0, [AUX_IRQ_ACT] /* check whether it is P0 interrupt */ + btst r0, 0 + jnz exc_entry_firq +#else + PUSH r10 + lr r10, [AUX_IRQ_ACT] + btst r10, 0 + POP r10 + jnz exc_entry_firq +#endif +#endif + INTERRUPT_PROLOGUE + + mov blink, sp + + clri /* disable interrupt */ + ld r3, [exc_nest_count] + add r2, r3, 1 + st r2, [exc_nest_count] + seti /* enable higher priority interrupt */ + + brne r3, 0, irq_handler_1 +/* change to exception stack if interrupt happened in task context */ + mov sp, _e_stack +#if ARC_FEATURE_STACK_CHECK +#if ARC_FEATURE_SEC_PRESENT + lr r0, [AUX_SEC_STAT] + bclr r0, r0, AUX_SEC_STAT_BIT_SSC + sflag r0 +#else + lr r0, [AUX_STATUS32] + bclr r0, r0, AUX_STATUS_BIT_SC + kflag r0 +#endif +#endif +irq_handler_1: + PUSH blink + + lr r0, [AUX_IRQ_CAUSE] + mov r1, exc_int_handler_table + ld.as r2, [r1, r0] /* r2 = exc_int_handler_table + irqno *4 */ +/* handle software triggered interrupt */ + lr r3, [AUX_IRQ_HINT] + cmp r3, r0 + bne.d irq_hint_handled + xor r3, r3, r3 + sr r3, [AUX_IRQ_HINT] +irq_hint_handled: + + jl [r2] /* jump to interrupt handler */ +/* no interrupts are allowed from here */ +ret_int: + clri /* disable interrupt */ + + POP sp + mov r1, exc_nest_count + ld r0, [r1] + sub r0, r0, 1 + st r0, [r1] +/* if there are multi-bits set in IRQ_ACT, it's still in nest interrupt */ + lr r0, [AUX_IRQ_CAUSE] + sr r0, [AUX_IRQ_SELECT] + lr r3, [AUX_IRQ_PRIORITY] + lr r1, [AUX_IRQ_ACT] + bclr r2, r1, r3 + brne r2, 0, ret_int_1 + + ld r0, [context_switch_reqflg] + brne r0, 0, ret_int_2 +ret_int_1: /* return from non-task context */ + INTERRUPT_EPILOGUE + rtie +/* there is a dispatch request */ +ret_int_2: + /* clear dispatch request */ + mov r0, 0 + st r0, [context_switch_reqflg] + + ld r0, [pxCurrentTCB] + breq r0, 0, ret_int_1 + +/* r1 has old AUX_IRQ_ACT */ + PUSH r1 +/* clear related bits in IRQ_ACT manually to simulate a irq return */ + sr r2, [AUX_IRQ_ACT] + + SAVE_CALLEE_REGS /* save callee save registers */ + mov r1, ret_int_r /* save return address */ + PUSH r1 + + bl dispatcher /* r0->pxCurrentTCB */ + +ret_int_r: + RESTORE_CALLEE_REGS /* recover registers */ + POPAX AUX_IRQ_ACT + INTERRUPT_EPILOGUE + rtie + +#if ARC_FEATURE_FIRQ == 1 + .global exc_entry_firq + .align 4 +exc_entry_firq: +#if ARC_FEATURE_STACK_CHECK && ARC_FEATURE_RGF_NUM_BANKS > 1 +#if ARC_FEATURE_SEC_PRESENT + lr r0, [AUX_SEC_STAT] + bclr r0, r0, AUX_SEC_STAT_BIT_SSC + sflag r0 +#else + lr r0, [AUX_STATUS32] + bclr r0, r0, AUX_STATUS_BIT_SC + kflag r0 +#endif +#endif + SAVE_FIQ_EXC_REGS + + mov blink, sp + + ld r3, [exc_nest_count] + add r2, r3, 1 + st r2, [exc_nest_count] + + brne r3, 0, firq_handler_1 +#if ARC_FEATURE_STACK_CHECK && ARC_FEATURE_RGF_NUM_BANKS == 1 +#if ARC_FEATURE_SEC_PRESENT + lr r0, [AUX_SEC_STAT] + bclr r0, r0, AUX_SEC_STAT_BIT_SSC + sflag r0 +#else + lr r0, [AUX_STATUS32] + bclr r0, r0, AUX_STATUS_BIT_SC + kflag r0 +#endif +#endif +/* change to exception stack if interrupt happened in task context */ + mov sp, _e_stack +firq_handler_1: + PUSH blink + + lr r0, [AUX_IRQ_CAUSE] + mov r1, exc_int_handler_table + ld.as r2, [r1, r0] /* r2 = exc_int_handler_table + irqno *4 */ +/* handle software triggered interrupt */ + lr r3, [AUX_IRQ_HINT] + brne r3, r0, firq_hint_handled + xor r3, r3, r3 + sr r3, [AUX_IRQ_HINT] +firq_hint_handled: + + jl [r2] /* jump to interrupt handler */ +/* no interrupts are allowed from here */ +ret_firq: + clri + POP sp + + mov r1, exc_nest_count + ld r0, [r1] + sub r0, r0, 1 + st r0, [r1] +/* if there are multi-bits set in IRQ_ACT, it's still in nest interrupt */ + lr r1, [AUX_IRQ_ACT] + bclr r1, r1, 0 + brne r1, 0, ret_firq_1 + + ld r0, [context_switch_reqflg] + brne r0, 0, ret_firq_2 +ret_firq_1: /* return from non-task context */ + RESTORE_FIQ_EXC_REGS + rtie +/* there is a dispatch request */ +ret_firq_2: + /* clear dispatch request */ + mov r0, 0 + st r0, [context_switch_reqflg] + + ld r0, [pxCurrentTCB] + breq r0, 0, ret_firq_1 + +/* reconstruct the interruptted context + * When ARC_FEATURE_RGF_BANKED_REGS >= 16 (16, 32), sp is banked + * so need to restore the fast irq stack. + */ +#if ARC_FEATURE_RGF_BANKED_REGS >= 16 + RESTORE_LP_REGS +#if ARC_FEATURE_CODE_DENSITY + RESTORE_CODE_DENSITY +#endif + RESTORE_R58_R59 +#endif + +/* when BANKED_REGS == 16, r4-r9 wiil be also saved in fast irq stack + * so pop them out + */ +#if ARC_FEATURE_RGF_BANKED_REGS == 16 && !defined(ARC_FEATURE_RF16) + POP r9 + POP r8 + POP r7 + POP r6 + POP r5 + POP r4 +#endif + +/* for other cases, unbanked regs are already in interrupted context's stack, + * so just need to save and pop the banked regs + */ + +/* save the interruptted context */ +#if ARC_FEATURE_RGF_BANKED_REGS > 0 +/* switch back to bank0 */ + lr r0, [AUX_STATUS32] + bic r0, r0, 0x70000 + kflag r0 +#endif + +#if ARC_FEATURE_RGF_BANKED_REGS == 4 +/* r4 - r12, gp, fp, r30, blink already saved */ + PUSH r0 + PUSH r1 + PUSH r2 + PUSH r3 +#elif ARC_FEATURE_RGF_BANKED_REGS == 8 +/* r4 - r9, r0, r11 gp, fp, r30, blink already saved */ + PUSH r0 + PUSH r1 + PUSH r2 + PUSH r3 + PUSH r12 +#elif ARC_FEATURE_RGF_BANKED_REGS >= 16 +/* nothing is saved, */ + SAVE_R0_TO_R12 + + SAVE_R58_R59 + PUSH gp + PUSH fp + PUSH r30 /* general purpose */ + PUSH blink + +#if ARC_FEATURE_CODE_DENSITY + SAVE_CODE_DENSITY +#endif + SAVE_LP_REGS +#endif + PUSH ilink + lr r0, [AUX_STATUS32_P0] + PUSH r0 + lr r0, [AUX_IRQ_ACT] + PUSH r0 + bclr r0, r0, 0 + sr r0, [AUX_IRQ_ACT] + + SAVE_CALLEE_REGS /* save callee save registers */ + + mov r1, ret_firq_r /* save return address */ + PUSH r1 + ld r0, [pxCurrentTCB] + bl dispatcher /* r0->pxCurrentTCB */ + +ret_firq_r: + RESTORE_CALLEE_REGS /* recover registers */ + POPAX AUX_IRQ_ACT + POPAX AUX_STATUS32_P0 + POP ilink + +#if ARC_FEATURE_RGF_NUM_BANKS > 1 +#if ARC_FEATURE_RGF_BANKED_REGS == 4 +/* r4 - r12, gp, fp, r30, blink already saved */ + POP r3 + POP r2 + POP r1 + POP r0 + RESTORE_FIQ_EXC_REGS +#elif ARC_FEATURE_RGF_BANKED_REGS == 8 +/* r4 - r9, gp, fp, r30, blink already saved */ + POP r12 + POP r3 + POP r2 + POP r1 + POP r0 + RESTORE_FIQ_EXC_REGS +#elif ARC_FEATURE_RGF_BANKED_REGS >= 16 + RESTORE_LP_REGS +#if ARC_FEATURE_CODE_DENSITY + RESTORE_CODE_DENSITY +#endif + POP blink + POP r30 + POP fp + POP gp + + RESTORE_R58_R59 + RESTORE_R0_TO_R12 +#endif /* ARC_FEATURE_RGF_BANKED_REGS */ +#else + RESTORE_FIQ_EXC_REGS +#endif /* ARC_FEATURE_RGF_NUM_BANKS */ + rtie +#endif +/** @endcond */ diff --git a/portable/ThirdParty/GCC/ARC_EM_HS/freertos_tls.c b/portable/ThirdParty/GCC/ARC_EM_HS/freertos_tls.c new file mode 100644 index 000000000..0d24c3c17 --- /dev/null +++ b/portable/ThirdParty/GCC/ARC_EM_HS/freertos_tls.c @@ -0,0 +1,205 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#if defined(__MW__) + +#include +#include +#include + +#include "FreeRTOS.h" + +#include "queue.h" +#include "semphr.h" +#include "task.h" + +#include "arc/arc_exception.h" +#include "embARC_toolchain.h" +#include "embARC_debug.h" + +#ifdef ENABLE_FREERTOS_TLS_DEBUG +#define TLS_DEBUG(fmt, ...) EMBARC_PRINTF(fmt, ##__VA_ARGS__) +#else +#define TLS_DEBUG(fmt, ...) +#endif + +/* + * Runtime routines to execute constructors and + * destructors for task local storage. + */ +extern void __mw_run_tls_dtor(); +extern void __mw_run_tls_ctor(); + +/* + * Linker generated symbols to mark .tls section addresses + * first byte .. last byte + */ +extern char _ftls[], _etls[]; +#pragma weak _ftls +#pragma weak _etls + +void executable_requires_tls_section(void) +{ +#if _ARC + for (;;) { + _flag(1); + _nop(); + _nop(); + _nop(); + _nop(); + _nop(); + } +#endif +} +#pragma off_inline(executable_requires_tls_section); +#pragma alias(executable_requires_tls_section, "executable_requires_.tls_section"); + +static void* init_task_tls(void) +{ + uint32_t len = (uint32_t)(_etls - _ftls); + void *tls = NULL; + +#if FREERTOS_HEAP_SEL == 3 + #warning "FreeRTOS TLS support is not compatible with heap 3 solution(FREERTOS_HEAP_SEL=3)!" + #warning "You can change FREERTOS_HEAP_SEL in freertos.mk to select other heap solution." +#else + tls = pvPortMalloc(len); +#endif + if (tls) { + TLS_DEBUG("Malloc task tls:%dbytes\r\n", len); + memcpy(tls, _ftls, len); + __mw_run_tls_ctor(); // Run constructors + } + return tls; +} + +static void free_task_tls(void *pxTCB) +{ + TaskHandle_t task2free = (TaskHandle_t)pxTCB; + + if (task2free != NULL) { + void *tls = pvTaskGetThreadLocalStoragePointer(task2free, 0); + if (tls) { + TLS_DEBUG("Free task tls\r\n"); + __mw_run_tls_dtor(); + vPortFree(tls); + vTaskSetThreadLocalStoragePointer(task2free, 0, NULL); + } + } +} + +void task_end_hook(void *pxTCB) +{ + free_task_tls(pxTCB); +} + +static void* get_isr_tls(void) +{ + // In an ISR + static int first = 1; + if (_Rarely(first)) { + first = 0; + __mw_run_tls_ctor(); // Run constructors + } + return (void *)_ftls; +} +#pragma off_inline(get_isr_tls) + +static void* get_task_tls(void) +{ + TaskHandle_t cur_task; + + cur_task = xTaskGetCurrentTaskHandle(); + if (cur_task == NULL) return get_isr_tls(); + void *tls = pvTaskGetThreadLocalStoragePointer(cur_task, 0); + if (tls == NULL) { + tls = init_task_tls(); + if (tls) { + vTaskSetThreadLocalStoragePointer(cur_task, 0, tls); + } else { + tls = get_isr_tls(); + } + } + return tls; +} +#pragma off_inline(get_task_tls) + +#if _ARC /* for ARC XCALLs need to preserve flags */ + extern void * _Preserve_flags _mwget_tls(void); +#endif + +/* + * Back end gens calls to find local data for this task + */ +void* _mwget_tls(void) +{ + if (_ftls == (char *)0) { + executable_requires_tls_section(); + } + if (exc_sense()) { /* In ISR */ + return get_isr_tls(); + } else { /* In Task */ + return get_task_tls(); + } +} + + +// simple interface of thread safe +typedef xSemaphoreHandle _lock_t; +#if configUSE_RECURSIVE_MUTEXES != 1 +#error "configUSE_RECURSIVE_MUTEXES in FreeRTOSConfig.h need to 1" +#endif + +void _mwmutex_create(_lock_t *mutex_ptr) +{ + *mutex_ptr = xSemaphoreCreateRecursiveMutex(); +} + +void _mwmutex_delete(_lock_t *mutex_ptr) +{ + if ((*mutex_ptr) != NULL) { + vSemaphoreDelete(*mutex_ptr); + } +} + +void _mwmutex_lock(_lock_t mutex) +{ + if ((mutex) != NULL) { + while (xSemaphoreTakeRecursive(mutex, portMAX_DELAY) != pdTRUE); + } +} + +void _mwmutex_unlock(_lock_t mutex) +{ + if ((mutex) != NULL) { + xSemaphoreGiveRecursive(mutex); + } +} + +#else + +#endif /* __MW__ */ diff --git a/portable/ThirdParty/GCC/ARC_EM_HS/port.c b/portable/ThirdParty/GCC/ARC_EM_HS/port.c new file mode 100644 index 000000000..06f65ccef --- /dev/null +++ b/portable/ThirdParty/GCC/ARC_EM_HS/port.c @@ -0,0 +1,283 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * Implementation of functions defined in portable.h + */ + +#include "FreeRTOS.h" +#include "task.h" +#include "FreeRTOSConfig.h" + +#include "arc/arc_exception.h" +#include "arc/arc_timer.h" +#include "board.h" + +#include "arc_freertos_exceptions.h" + +volatile unsigned int ulCriticalNesting = 999UL; +volatile unsigned int context_switch_reqflg; /* task context switch request flag in exceptions and interrupts handling */ + +/* --------------------------------------------------------------------------*/ +/** + * @brief kernel tick interrupt handler of freertos + */ +/* ----------------------------------------------------------------------------*/ +static void vKernelTick( void *ptr ) +{ + /* clear timer interrupt */ + timer_int_clear(BOARD_OS_TIMER_ID); + board_timer_update(configTICK_RATE_HZ); + + if (xTaskIncrementTick()) { + portYIELD_FROM_ISR(); /* need to make task switch */ + } +} + +/* --------------------------------------------------------------------------*/ +/** + * @brief setup freertos kernel tick + */ +/* ----------------------------------------------------------------------------*/ +static void prvSetupTimerInterrupt(void) +{ + unsigned int cyc = configCPU_CLOCK_HZ / configTICK_RATE_HZ; + + int_disable(BOARD_OS_TIMER_INTNO); /* disable os timer interrupt */ + timer_stop(BOARD_OS_TIMER_ID); + timer_start(BOARD_OS_TIMER_ID, TIMER_CTRL_IE | TIMER_CTRL_NH, cyc); + + int_handler_install(BOARD_OS_TIMER_INTNO, (INT_HANDLER_T)vKernelTick); + int_pri_set(BOARD_OS_TIMER_INTNO, INT_PRI_MIN); + int_enable(BOARD_OS_TIMER_INTNO); +} + +/* + * Setup the stack of a new task so it is ready to be placed under the + * scheduler control. The registers have to be placed on the stack in + * the order that the port expects to find them. + * + * For ARC, task context switch is implemented with the help of SWI exception + * It's not efficient but simple. + * + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) +{ + /* To ensure asserts in tasks.c don't fail, although in this case the assert + is not really required. */ + pxTopOfStack--; + + /* Setup the initial stack of the task. The stack is set exactly as + expected by the portRESTORE_CONTEXT() macro. */ + + /* When the task starts is will expect to find the function parameter in + R0. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* function body */ + + /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) start_r; /* dispatch return address */ + + pxTopOfStack--; + *pxTopOfStack = (StackType_t) portNO_CRITICAL_NESTING; + return pxTopOfStack; +} + +/* --------------------------------------------------------------------------*/ +/** + * @brief start the freertos scheduler, go to the first task + * + * @returns + */ +/* ----------------------------------------------------------------------------*/ +BaseType_t xPortStartScheduler( void ) +{ + /* Start the timer that generates the tick ISR. */ + prvSetupTimerInterrupt(); + start_dispatch(); + + /* Should not get here! */ + return 0; +} + +/* --------------------------------------------------------------------------*/ +/** + * @brief + */ +/* ----------------------------------------------------------------------------*/ +void vPortEndScheduler( void ) +{ + +} + +/* --------------------------------------------------------------------------*/ +/** + * @brief generate a task switch request in ISR + */ +/* ----------------------------------------------------------------------------*/ +void vPortYieldFromIsr(void) +{ + unsigned int status32; + + status32 = cpu_lock_save(); + context_switch_reqflg = true; + cpu_unlock_restore(status32); +} + +/* --------------------------------------------------------------------------*/ +/** + * @brief + */ +/* ----------------------------------------------------------------------------*/ +void vPortYield(void) +{ + unsigned int status32; + + status32 = cpu_lock_save(); + dispatch(); + cpu_unlock_restore(status32); +} + +/* --------------------------------------------------------------------------*/ +/** + * @brief + */ +/* ----------------------------------------------------------------------------*/ +void vPortEndTask(void) +{ + +#if ( INCLUDE_vTaskDelete == 1 ) + vTaskDelete(NULL); /* Delete task itself */ +#endif + + while(1) { /* Yield to other task */ + vPortYield(); + } +} + +#if ARC_FEATURE_STACK_CHECK + +/* + * !!! Note !!! + * This a trick!!! + * It's a copy from task.c. We need to konw the definition of TCB for the purpose of hardware + * stack check. Pls don't forget to update it when FreeRTOS is updated. + */ +typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +{ + volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ + #endif + + ListItem_t xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ + ListItem_t xEventListItem; /*< Used to reference a task from an event list. */ + UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */ + StackType_t *pxStack; /*< Points to the start of the stack. */ + char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + StackType_t *pxEndOfStack; /*< Points to the highest valid address for the stack. */ + #endif + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */ + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTCBNumber; /*< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */ + UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */ + #endif + + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */ + UBaseType_t uxMutexesHeld; + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + TaskHookFunction_t pxTaskTag; + #endif + + #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + + #if( configGENERATE_RUN_TIME_STATS == 1 ) + uint32_t ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */ + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + /* Allocate a Newlib reent structure that is specific to this task. + Note Newlib support has been included by popular demand, but is not + used by the FreeRTOS maintainers themselves. FreeRTOS is not + responsible for resulting newlib operation. User must be familiar with + newlib and must provide system-wide implementations of the necessary + stubs. Be warned that (at the time of writing) the current newlib design + implements a system-wide malloc() that must be provided with locks. */ + struct _reent xNewLib_reent; + #endif + + #if( configUSE_TASK_NOTIFICATIONS == 1 ) + volatile uint32_t ulNotifiedValue; + volatile uint8_t ucNotifyState; + #endif + + /* See the comments above the definition of + tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ + #endif + + #if( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDelayAborted; + #endif + + #if( configUSE_POSIX_ERRNO == 1 ) + int iTaskErrno; + #endif + +} tskTCB; + + +void vPortSetStackCheck(TaskHandle_t old, TaskHandle_t new) +{ + + if (new != NULL) { +#if ARC_FEATURE_SEC_PRESENT + arc_aux_write(AUX_S_KSTACK_BASE, (uint32_t)(new->pxEndOfStack)); + arc_aux_write(AUX_S_KSTACK_TOP, (uint32_t)(new->pxStack)); +#else + arc_aux_write(AUX_KSTACK_BASE, (uint32_t)(new->pxEndOfStack)); + arc_aux_write(AUX_KSTACK_TOP, (uint32_t)(new->pxStack)); +#endif + } +} +#endif diff --git a/portable/ThirdParty/GCC/ARC_EM_HS/portmacro.h b/portable/ThirdParty/GCC/ARC_EM_HS/portmacro.h new file mode 100644 index 000000000..dc950369c --- /dev/null +++ b/portable/ThirdParty/GCC/ARC_EM_HS/portmacro.h @@ -0,0 +1,158 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* record stack high address for stack check */ +#ifndef configRECORD_STACK_HIGH_ADDRESS + #define configRECORD_STACK_HIGH_ADDRESS 1 +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE unsigned int +#define portBASE_TYPE portLONG + +#ifndef Inline +#define Inline static __inline__ +#endif +#ifndef Asm +#define Asm __asm__ volatile +#endif + +/* + * normal constants + */ +#ifndef NULL +#define NULL 0 /* invalid pointer */ +#endif /* NULL */ + +#ifndef true +#define true 1 /* true */ +#endif /* true */ + +#ifndef false +#define false 0 /* false */ +#endif /* false */ + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff +#else + typedef unsigned int TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +#endif + +#define portNO_CRITICAL_NESTING 0x0 +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +#define portNOP() Asm( "nop_s" ); +#define IPM_ENABLE_ALL 1 + +#define portYIELD_FROM_ISR() vPortYieldFromIsr() +#define portYIELD() vPortYield() + +/* Critical section management. */ +#define portDISABLE_INTERRUPTS() \ +{ \ + Asm("clri"); \ + Asm("":::"memory"); \ +} \ + +#define portENABLE_INTERRUPTS() \ +{ \ + Asm("":::"memory"); \ + Asm("seti"); \ +} \ + +extern volatile unsigned int ulCriticalNesting; + +#define portENTER_CRITICAL() \ +{ \ + portDISABLE_INTERRUPTS() \ + ulCriticalNesting++; \ +} + + +#define portEXIT_CRITICAL() \ +{ \ + if (ulCriticalNesting > portNO_CRITICAL_NESTING) \ + { \ + ulCriticalNesting--; \ + if (ulCriticalNesting == portNO_CRITICAL_NESTING) \ + { \ + portENABLE_INTERRUPTS() \ + } \ + } \ +} + + +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) + +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() do {} while (0) /* we use the timer */ +#define portALT_GET_RUN_TIME_COUNTER_VALUE(dest) (dest = xTickCount) + +#if defined(__MW__) +extern void task_end_hook(void *pxTCB); +#define portCLEAN_UP_TCB( pxTCB ) task_end_hook((void *)pxTCB) +#else +#define portCLEAN_UP_TCB( pxTCB ) ( void ) pxTCB +#endif + +void vPortYield(void); +void vPortYieldFromIsr(void); + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ From 334de5d8ab557940b9420b00838fdc32eb4a1598 Mon Sep 17 00:00:00 2001 From: Gaurav-Aggarwal-AWS <33462878+aggarg@users.noreply.github.com> Date: Mon, 6 Apr 2020 15:51:40 -0700 Subject: [PATCH 12/13] Enable ARMv7-M MPU ports to place FreeRTOS kernel code outside of flash (#46) Problem Description ------------------- The current flash organization in ARMv7-M MPU ports looks as follows: __FLASH_segment_start__ ------->+-----------+<----- __FLASH_segment_start__ | Vector | | Table | | + | | Kernel | | Code | +-----------+<----- __privileged_functions_end__ | | | | | | | Other | | Code | | | | | | | __FLASH_segment_end__ ------>+-----------+ The FreeRTOS kernel sets up the following MPU regions: * Unprivileged Code - __FLASH_segment_start__ to __FLASH_segment_end__. * Privileged Code - __FLASH_segment_start__ to __privileged_functions_end__. The above setup assumes that the FreeRTOS kernel code (i.e. privileged_functions) is placed at the beginning of the flash and, therefore, uses __FLASH_segment_start__ as the starting location of the privileged code. This prevents a user from placing the FreeRTOS kernel code outside of flash (say to an external RAM) and still have vector table at the beginning of flash (which is many times a hardware requirement). Solution -------- This commit addresses the above limitation by using a new variable __privileged_functions_start__ as the starting location of the privileged code. This enables users to place the FreeRTOS kernel code wherever they choose. The FreeRTOS kernel now sets up the following MPU regions: * Unprivileged Code - __FLASH_segment_start__ to __FLASH_segment_end__. * Privileged Code - __privileged_functions_start__ to __privileged_functions_end__. As a result, a user can now place the kernel code to an external RAM. A possible organization is: Flash External RAM +------------+ +-----------+<------ __privileged_functions_start__ | Vector | | | | Table | | | | | | | __FLASH_segment_start__ ----->+------------+ | Kernel | | | | Code | | | | | | | | | | | | | | Other | | | | Code | +-----------+<------ __privileged_functions_end__ | | | | | | __FLASH_segment_end__ ----->+------------+ Note that the above configuration places the vector table in an unmapped region. This is okay because we enable the background region, and so the vector table will still be accessible to the privileged code and not accessible to the unprivileged code (vector table is only needed by the privileged code). Backward Compatibility ---------------------- The FreeRTOS kernel code now uses a new variable, namely __privileged_functions_start__, which needs to be exported from linker script to indicate the starting location of the privileged code. All of our existing demos already export this variable and therefore, they will continue to work. If a user has created a project which does not export this variable, they will get a linker error for unresolved symbol __privileged_functions_start__. They need to export a variable __privileged_functions_start__ with the value equal to __FLASH_segment_start__. Issue ----- https://sourceforge.net/p/freertos/feature-requests/56/ Signed-off-by: Gaurav Aggarwal --- portable/GCC/ARM_CM3_MPU/port.c | 12 ++++++------ portable/GCC/ARM_CM4_MPU/port.c | 13 +++++++------ portable/IAR/ARM_CM4F_MPU/port.c | 12 ++++++------ portable/RVDS/ARM_CM4_MPU/port.c | 12 ++++++------ 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/portable/GCC/ARM_CM3_MPU/port.c b/portable/GCC/ARM_CM3_MPU/port.c index 2233f8db9..5693a2ff7 100644 --- a/portable/GCC/ARM_CM3_MPU/port.c +++ b/portable/GCC/ARM_CM3_MPU/port.c @@ -584,6 +584,7 @@ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) static void prvSetupMPU( void ) { +extern uint32_t __privileged_functions_start__[]; extern uint32_t __privileged_functions_end__[]; extern uint32_t __FLASH_segment_start__[]; extern uint32_t __FLASH_segment_end__[]; @@ -593,7 +594,7 @@ extern uint32_t __privileged_data_end__[]; /* Check the expected MPU is present. */ if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE ) { - /* First setup the entire flash for unprivileged read only access. */ + /* First setup the unprivileged flash for unprivileged read only access. */ portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portUNPRIVILEGED_FLASH_REGION ); @@ -603,16 +604,15 @@ extern uint32_t __privileged_data_end__[]; ( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | ( portMPU_REGION_ENABLE ); - /* Setup the first 16K for privileged only access (even though less - * than 10K is actually being used). This is where the kernel code is - * placed. */ - portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ + /* Setup the privileged flash for privileged only access. This is where + * the kernel code is * placed. */ + portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portPRIVILEGED_FLASH_REGION ); portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) | ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | - ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | + ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) | ( portMPU_REGION_ENABLE ); /* Setup the privileged data RAM region. This is where the kernel data diff --git a/portable/GCC/ARM_CM4_MPU/port.c b/portable/GCC/ARM_CM4_MPU/port.c index 63026d85c..5f9697cea 100644 --- a/portable/GCC/ARM_CM4_MPU/port.c +++ b/portable/GCC/ARM_CM4_MPU/port.c @@ -640,6 +640,7 @@ static void prvSetupMPU( void ) #if defined( __ARMCC_VERSION ) /* Declaration when these variable are defined in code instead of being * exported from linker scripts. */ + extern uint32_t * __privileged_functions_start__; extern uint32_t * __privileged_functions_end__; extern uint32_t * __FLASH_segment_start__; extern uint32_t * __FLASH_segment_end__; @@ -647,6 +648,7 @@ static void prvSetupMPU( void ) extern uint32_t * __privileged_data_end__; #else /* Declaration when these variable are exported from linker scripts. */ + extern uint32_t __privileged_functions_start__[]; extern uint32_t __privileged_functions_end__[]; extern uint32_t __FLASH_segment_start__[]; extern uint32_t __FLASH_segment_end__[]; @@ -656,7 +658,7 @@ static void prvSetupMPU( void ) /* Check the expected MPU is present. */ if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE ) { - /* First setup the entire flash for unprivileged read only access. */ + /* First setup the unprivileged flash for unprivileged read only access. */ portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portUNPRIVILEGED_FLASH_REGION ); @@ -666,16 +668,15 @@ static void prvSetupMPU( void ) ( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | ( portMPU_REGION_ENABLE ); - /* Setup the first nK for privileged only access (even though less - than 10K is actually being used). This is where the kernel code is - placed. */ - portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ + /* Setup the privileged flash for privileged only access. This is where + the kernel code is placed. */ + portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portPRIVILEGED_FLASH_REGION ); portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) | ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | - ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | + ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) | ( portMPU_REGION_ENABLE ); /* Setup the privileged data RAM region. This is where the kernel data diff --git a/portable/IAR/ARM_CM4F_MPU/port.c b/portable/IAR/ARM_CM4F_MPU/port.c index 39e7e4015..6b4dd0a78 100644 --- a/portable/IAR/ARM_CM4F_MPU/port.c +++ b/portable/IAR/ARM_CM4F_MPU/port.c @@ -506,6 +506,7 @@ __weak void vPortSetupTimerInterrupt( void ) static void prvSetupMPU( void ) { +extern uint32_t __privileged_functions_start__[]; extern uint32_t __privileged_functions_end__[]; extern uint32_t __FLASH_segment_start__[]; extern uint32_t __FLASH_segment_end__[]; @@ -515,7 +516,7 @@ extern uint32_t __privileged_data_end__[]; /* Check the expected MPU is present. */ if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE ) { - /* First setup the entire flash for unprivileged read only access. */ + /* First setup the unprivileged flash for unprivileged read only access. */ portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portUNPRIVILEGED_FLASH_REGION ); @@ -525,16 +526,15 @@ extern uint32_t __privileged_data_end__[]; ( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | ( portMPU_REGION_ENABLE ); - /* Setup the first 16K for privileged only access (even though less - * than 10K is actually being used). This is where the kernel code is - * placed. */ - portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ + /* Setup the privileged flash for privileged only access. This is where + * the kernel code is placed. */ + portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portPRIVILEGED_FLASH_REGION ); portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) | ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | - ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | + ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) | ( portMPU_REGION_ENABLE ); /* Setup the privileged data RAM region. This is where the kernel data diff --git a/portable/RVDS/ARM_CM4_MPU/port.c b/portable/RVDS/ARM_CM4_MPU/port.c index ab0b2ba39..59ae308d3 100644 --- a/portable/RVDS/ARM_CM4_MPU/port.c +++ b/portable/RVDS/ARM_CM4_MPU/port.c @@ -638,6 +638,7 @@ __asm void vPortEnableVFP( void ) static void prvSetupMPU( void ) { +extern uint32_t __privileged_functions_start__; extern uint32_t __privileged_functions_end__; extern uint32_t __FLASH_segment_start__; extern uint32_t __FLASH_segment_end__; @@ -647,7 +648,7 @@ extern uint32_t __privileged_data_end__; /* Check the expected MPU is present. */ if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE ) { - /* First setup the entire flash for unprivileged read only access. */ + /* First setup the unprivileged flash for unprivileged read only access. */ portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portUNPRIVILEGED_FLASH_REGION ); @@ -657,16 +658,15 @@ extern uint32_t __privileged_data_end__; ( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | ( portMPU_REGION_ENABLE ); - /* Setup the first 16K for privileged only access (even though less - * than 10K is actually being used). This is where the kernel code is - * placed. */ - portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ + /* Setup the privileged flash for privileged only access. This is where + * the kernel code is placed. */ + portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */ ( portMPU_REGION_VALID ) | ( portPRIVILEGED_FLASH_REGION ); portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) | ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | - ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | + ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) | ( portMPU_REGION_ENABLE ); /* Setup the privileged data RAM region. This is where the kernel data From 07e672c448e2a4ea56ae793f1c6dae26d908b16e Mon Sep 17 00:00:00 2001 From: Gaurav-Aggarwal-AWS <33462878+aggarg@users.noreply.github.com> Date: Tue, 14 Apr 2020 09:03:11 -0700 Subject: [PATCH 13/13] Add definition of portDONT_DISCARD to ARMv7-M ports (#50) Enabling Link Time Optimization (LTO) causes some of the functions used in assembly to be incorrectly stripped off, resulting in linker error. To avoid this, these functions are marked with portDONT_DISCARD macro, definition of which is port specific. This commit adds the definition of portDONT_DISCARD for ARMv7-M ports. Signed-off-by: Gaurav Aggarwal --- portable/GCC/ARM_CM0/portmacro.h | 1 + portable/GCC/ARM_CM3/portmacro.h | 1 + portable/GCC/ARM_CM3_MPU/portmacro.h | 1 + portable/GCC/ARM_CM4F/portmacro.h | 1 + portable/GCC/ARM_CM4_MPU/portmacro.h | 1 + portable/GCC/ARM_CM7/r0p1/portmacro.h | 1 + 6 files changed, 6 insertions(+) diff --git a/portable/GCC/ARM_CM0/portmacro.h b/portable/GCC/ARM_CM0/portmacro.h index 3b402b428..2e59a83dc 100644 --- a/portable/GCC/ARM_CM0/portmacro.h +++ b/portable/GCC/ARM_CM0/portmacro.h @@ -73,6 +73,7 @@ typedef unsigned long UBaseType_t; #define portSTACK_GROWTH ( -1 ) #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 8 +#define portDONT_DISCARD __attribute__(( used )) /*-----------------------------------------------------------*/ diff --git a/portable/GCC/ARM_CM3/portmacro.h b/portable/GCC/ARM_CM3/portmacro.h index 29bccbb2e..673d93757 100644 --- a/portable/GCC/ARM_CM3/portmacro.h +++ b/portable/GCC/ARM_CM3/portmacro.h @@ -73,6 +73,7 @@ typedef unsigned long UBaseType_t; #define portSTACK_GROWTH ( -1 ) #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 8 +#define portDONT_DISCARD __attribute__(( used )) /*-----------------------------------------------------------*/ /* Scheduler utilities. */ diff --git a/portable/GCC/ARM_CM3_MPU/portmacro.h b/portable/GCC/ARM_CM3_MPU/portmacro.h index eef435801..61da7413a 100644 --- a/portable/GCC/ARM_CM3_MPU/portmacro.h +++ b/portable/GCC/ARM_CM3_MPU/portmacro.h @@ -109,6 +109,7 @@ typedef struct MPU_SETTINGS #define portSTACK_GROWTH ( -1 ) #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 8 +#define portDONT_DISCARD __attribute__(( used )) /*-----------------------------------------------------------*/ /* SVC numbers for various services. */ diff --git a/portable/GCC/ARM_CM4F/portmacro.h b/portable/GCC/ARM_CM4F/portmacro.h index e1e7fadf3..b94366dfd 100644 --- a/portable/GCC/ARM_CM4F/portmacro.h +++ b/portable/GCC/ARM_CM4F/portmacro.h @@ -73,6 +73,7 @@ typedef unsigned long UBaseType_t; #define portSTACK_GROWTH ( -1 ) #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 8 +#define portDONT_DISCARD __attribute__(( used )) /*-----------------------------------------------------------*/ /* Scheduler utilities. */ diff --git a/portable/GCC/ARM_CM4_MPU/portmacro.h b/portable/GCC/ARM_CM4_MPU/portmacro.h index eef435801..61da7413a 100644 --- a/portable/GCC/ARM_CM4_MPU/portmacro.h +++ b/portable/GCC/ARM_CM4_MPU/portmacro.h @@ -109,6 +109,7 @@ typedef struct MPU_SETTINGS #define portSTACK_GROWTH ( -1 ) #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 8 +#define portDONT_DISCARD __attribute__(( used )) /*-----------------------------------------------------------*/ /* SVC numbers for various services. */ diff --git a/portable/GCC/ARM_CM7/r0p1/portmacro.h b/portable/GCC/ARM_CM7/r0p1/portmacro.h index c01d305dc..124e8073a 100644 --- a/portable/GCC/ARM_CM7/r0p1/portmacro.h +++ b/portable/GCC/ARM_CM7/r0p1/portmacro.h @@ -73,6 +73,7 @@ typedef unsigned long UBaseType_t; #define portSTACK_GROWTH ( -1 ) #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 8 +#define portDONT_DISCARD __attribute__(( used )) /*-----------------------------------------------------------*/ /* Scheduler utilities. */