| /* | |
| * FreeRTOS Kernel V10.3.0 | |
| * Copyright (C) 2020 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! | |
| */ | |
| /* Standard includes. */ | |
| #include <limits.h> | |
| /* FreeRTOS includes. */ | |
| #include "FreeRTOS.h" | |
| #include "task.h" | |
| /* Library includes. */ | |
| #include "common_lib.h" | |
| #include "peripheral_library/interrupt/interrupt.h" | |
| #include "peripheral_library/basic_timer/btimer.h" | |
| /* This file contains functions that will override the default implementations | |
| in the RTOS port layer. Therefore only build this file if the low power demo | |
| is being built. */ | |
| #if( configCREATE_LOW_POWER_DEMO == 1 ) | |
| /* ID of the hibernation timer used to generate the tick. */ | |
| #define mainTICK_HTIMER_ID 0 | |
| /* Written to the hibernation timer control register to configure the timer for | |
| its higher resolution. */ | |
| #define mainHTIMER_HIGH_RESOLUTION 0 | |
| /* The frequency of the hibernation timer when it is running at its higher | |
| resolution and low resolution respectively. */ | |
| #define mainHIGHER_RESOLUTION_TIMER_HZ ( 32787UL ) /* (1000000us / 30.5us) as each LSB is 30.5us. */ | |
| #define mainLOW_RESOLUTION_TIMER_HZ ( 8UL ) /* ( 1000ms / 125ms ) as each LSB is 0.125s. */ | |
| /* When lpINCLUDE_TEST_TIMER is set to 1 a basic timer is used to generate | |
| interrupts at a low frequency. The purpose being to bring the CPU out of its | |
| sleep mode by an interrupt other than the tick interrupt, and therefore | |
| allowing an additional paths through the code to be tested. */ | |
| #define lpINCLUDE_TEST_TIMER 0 | |
| /* Registers and bits required to use the htimer in aggregated mode. */ | |
| #define lpHTIMER_PRELOAD_REGISTER ( * ( volatile uint16_t * ) 0x40009800 ) | |
| #define lpHTIMER_CONTROL_REGISTER ( * ( volatile uint16_t * ) 0x40009804 ) | |
| #define lpHTIMER_COUNT_REGISTER ( * ( volatile uint16_t * ) 0x40009808 ) | |
| #define lpEC_GIRQ17_ENABLE_SET ( * ( volatile uint32_t * ) 0x4000C0B8 ) | |
| #define lpEC_GIRQ17_SOURCE ( * ( volatile uint32_t * ) 0x4000C0B4 ) | |
| #define lpEC_GIRQ17_ENABLE_CLEAR ( * ( volatile uint32_t * ) 0x4000C0C0 ) | |
| #define lpBLOCK_ENABLE_SET ( * ( volatile uint32_t * ) 0x4000c200 ) | |
| #define lpGIRQ17_BIT_HTIMER ( 1UL << 20UL ) | |
| #define lpHTIMER_GIRQ_BLOCK ( 1Ul << 17UL ) | |
| /* Registers and bits required to use btimer 0 in aggregated mode. */ | |
| #define lpGIRQ23_ENABLE_SET ( * ( volatile uint32_t * ) 0x4000C130 ) | |
| #define lpEC_GIRQ23_SOURCE ( * ( volatile uint32_t * ) 0x4000C12C ) | |
| #define lpEC_GIRQ23_ENABLE_CLEAR ( * ( volatile uint32_t * ) 0x4000C138 ) | |
| #define lpGIRQ23_BIT_TIMER0 ( 1UL << 0UL ) | |
| #define lpBTIMER_GIRQ_BLOCK ( 1UL << 23UL ) | |
| /* | |
| * The low power demo does not use the SysTick, so override the | |
| * vPortSetupTickInterrupt() function with an implementation that configures | |
| * the low power clock. NOTE: This function name must not be changed as it | |
| * is called from the RTOS portable layer. | |
| */ | |
| void vPortSetupTimerInterrupt( void ); | |
| /* | |
| * To fully test the low power tick processing it is necessary to sometimes | |
| * bring the MCU out of its sleep state by a method other than the tick | |
| * interrupt. Interrupts generated from a basic timer are used for this | |
| * purpose. | |
| */ | |
| #if( lpINCLUDE_TEST_TIMER == 1 ) | |
| static void prvSetupBasicTimer( void ); | |
| #endif | |
| /*-----------------------------------------------------------*/ | |
| /* The reload value to use in the timer to generate the tick interrupt - | |
| assumes the timer is running at its higher resolution. */ | |
| static const uint32_t ulHighResolutionReloadValue = ( mainHIGHER_RESOLUTION_TIMER_HZ / configTICK_RATE_HZ ); | |
| /* Calculate how many clock increments make up a single tick period. */ | |
| static const uint32_t ulReloadValueForOneHighResolutionTick = ( mainHIGHER_RESOLUTION_TIMER_HZ / configTICK_RATE_HZ ); | |
| /* Calculate the maximum number of ticks that can be suppressed when using the | |
| high resolution clock and low resolution clock respectively. */ | |
| static uint32_t ulMaximumPossibleSuppressedHighResolutionTicks = 0; | |
| /* As the clock is only 2KHz, it is likely a value of 1 will be too much, so | |
| use zero - but leave the value here to assist porting to different clock | |
| speeds. */ | |
| static const uint32_t ulStoppedTimerCompensation = 0UL; | |
| /* Flag set from the tick interrupt to allow the sleep processing to know if | |
| sleep mode was exited because of an timer interrupt or a different interrupt. */ | |
| static volatile uint32_t ulTickFlag = pdFALSE; | |
| /*-----------------------------------------------------------*/ | |
| void NVIC_Handler_GIRQ17( void ) | |
| { | |
| /* The low power demo is using aggregated interrupts, so although in the | |
| demo the htimer is the only peripheral that will generate interrupts on | |
| this vector, in a real application it would be necessary to first check the | |
| interrupt source. */ | |
| if( ( lpEC_GIRQ17_SOURCE & lpGIRQ17_BIT_HTIMER ) != 0 ) | |
| { | |
| /* The htimer interrupted. Clear the interrupt. */ | |
| lpEC_GIRQ17_SOURCE = lpGIRQ17_BIT_HTIMER; | |
| lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulHighResolutionReloadValue; | |
| /* Increment the RTOS tick. */ | |
| if( xTaskIncrementTick() != pdFALSE ) | |
| { | |
| /* A context switch is required. Context switching is performed in | |
| the PendSV interrupt. Pend the PendSV interrupt. */ | |
| portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; | |
| } | |
| /* The CPU woke because of a tick. */ | |
| ulTickFlag = pdTRUE; | |
| } | |
| else | |
| { | |
| /* Don't expect any other interrupts to use this vector in this | |
| demo. Force an assert. */ | |
| configASSERT( lpEC_GIRQ17_SOURCE == 0 ); | |
| } | |
| } | |
| /*-----------------------------------------------------------*/ | |
| #if( lpINCLUDE_TEST_TIMER == 1 ) | |
| static void prvSetupBasicTimer( void ) | |
| { | |
| const uint8_t ucTimerChannel = 0; | |
| const uint32_t ulTimer0Count = configCPU_CLOCK_HZ / 10; | |
| /* Enable btimer 0 interrupt in the aggregated GIRQ23 block. */ | |
| lpEC_GIRQ23_SOURCE = lpGIRQ23_BIT_TIMER0; | |
| lpEC_GIRQ23_ENABLE_CLEAR = lpGIRQ23_BIT_TIMER0; | |
| lpBLOCK_ENABLE_SET = lpBTIMER_GIRQ_BLOCK; | |
| lpGIRQ23_ENABLE_SET = lpGIRQ23_BIT_TIMER0; | |
| /* To fully test the low power tick processing it is necessary to sometimes | |
| bring the MCU out of its sleep state by a method other than the tick | |
| interrupt. Interrupts generated from a basic timer are used for this | |
| purpose. */ | |
| btimer_init( ucTimerChannel, BTIMER_AUTO_RESTART | BTIMER_COUNT_DOWN | BTIMER_INT_EN, 0, ulTimer0Count, ulTimer0Count ); | |
| btimer_interrupt_status_get_clr( ucTimerChannel ); | |
| NVIC_SetPriority( GIRQ23_IRQn, ucTimerChannel ); | |
| NVIC_ClearPendingIRQ( GIRQ23_IRQn ); | |
| NVIC_EnableIRQ( GIRQ23_IRQn ); | |
| btimer_start( ucTimerChannel ); | |
| } | |
| #endif /* lpINCLUDE_TEST_TIMER */ | |
| /*-----------------------------------------------------------*/ | |
| void vPortSetupTimerInterrupt( void ) | |
| { | |
| ulMaximumPossibleSuppressedHighResolutionTicks = ( ( uint32_t ) USHRT_MAX ) / ulReloadValueForOneHighResolutionTick; | |
| /* Set up the hibernation timer to start at the value required by the | |
| tick interrupt. */ | |
| lpHTIMER_PRELOAD_REGISTER = ulHighResolutionReloadValue; | |
| lpHTIMER_CONTROL_REGISTER = mainHTIMER_HIGH_RESOLUTION; | |
| /* Enable the HTIMER interrupt in the aggregated GIR17 block. */ | |
| lpEC_GIRQ17_SOURCE = lpGIRQ17_BIT_HTIMER; | |
| lpEC_GIRQ17_ENABLE_CLEAR = lpGIRQ17_BIT_HTIMER; | |
| lpBLOCK_ENABLE_SET = lpHTIMER_GIRQ_BLOCK; | |
| lpEC_GIRQ17_ENABLE_SET = lpGIRQ17_BIT_HTIMER; | |
| /* The hibernation timer is not an auto-reload timer, so gets reset | |
| from within the ISR itself. For that reason it's interrupt is set | |
| to the highest possible priority to ensure clock slippage is minimised. */ | |
| NVIC_SetPriority( GIRQ17_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); | |
| NVIC_ClearPendingIRQ( GIRQ17_IRQn ); | |
| NVIC_EnableIRQ( GIRQ17_IRQn ); | |
| /* A basic timer is also started, purely for test purposes. Its only | |
| purpose is to bring the CPU out of its sleep mode by an interrupt other | |
| than the tick interrupt in order to get more code test coverage. */ | |
| #if( lpINCLUDE_TEST_TIMER == 1 ) | |
| { | |
| prvSetupBasicTimer(); | |
| } | |
| #endif | |
| } | |
| /*-----------------------------------------------------------*/ | |
| /* Override the default definition of vPortSuppressTicksAndSleep() that is | |
| weakly defined in the FreeRTOS Cortex-M port layer with a version that manages | |
| the hibernation timer, as the tick is generated from the low power hibernation | |
| timer and not the SysTick as would normally be the case on a Cortex-M. */ | |
| void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) | |
| { | |
| uint32_t ulCompleteTickPeriods, ulReloadValue, ulCompletedTimerDecrements, ulCountAfterSleep, ulCountBeforeSleep; | |
| eSleepModeStatus eSleepAction; | |
| TickType_t xModifiableIdleTime; | |
| /* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */ | |
| /* Make sure the hibernation timer reload value does not overflow the | |
| counter. */ | |
| if( xExpectedIdleTime > ( TickType_t ) ulMaximumPossibleSuppressedHighResolutionTicks ) | |
| { | |
| xExpectedIdleTime = ( TickType_t ) ulMaximumPossibleSuppressedHighResolutionTicks; | |
| } | |
| /* Stop the timer momentarily. The time the timer 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. Take the count value first as clearing | |
| the preload value also seems to clear the count. */ | |
| ulCountBeforeSleep = ( uint32_t ) lpHTIMER_COUNT_REGISTER; | |
| lpHTIMER_PRELOAD_REGISTER = 0; | |
| /* Calculate the reload value required to wait xExpectedIdleTime tick | |
| periods. -1 is used as the current time slice will already be part way | |
| through, the part value coming from the current timer count value. */ | |
| ulReloadValue = ulCountBeforeSleep + ( ulReloadValueForOneHighResolutionTick * ( xExpectedIdleTime - 1UL ) ); | |
| if( ulReloadValue > ulStoppedTimerCompensation ) | |
| { | |
| /* Compensate for the fact that the timer is going to be stopped | |
| momentarily. */ | |
| 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" ); | |
| __asm volatile( "dsb" ); | |
| __asm volatile( "isb" ); | |
| /* The tick flag is set to false before sleeping. If it is true when sleep | |
| mode is exited then sleep mode was probably exited because the tick was | |
| suppressed for the entire xExpectedIdleTime period. */ | |
| ulTickFlag = pdFALSE; | |
| /* If a context switch is pending then abandon the low power entry as | |
| the context switch might have been pended by an external interrupt that | |
| requires processing. */ | |
| eSleepAction = eTaskConfirmSleepModeStatus(); | |
| if( eSleepAction == eAbortSleep ) | |
| { | |
| /* Restart the timer from whatever remains in the counter register, | |
| but 0 is not a valid value. */ | |
| ulReloadValue = ulCountBeforeSleep - ulStoppedTimerCompensation; | |
| if( ulReloadValue == 0 ) | |
| { | |
| ulReloadValue = ulReloadValueForOneHighResolutionTick; | |
| ulCompleteTickPeriods = 1UL; | |
| } | |
| else | |
| { | |
| ulCompleteTickPeriods = 0UL; | |
| } | |
| lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulReloadValue; | |
| /* Re-enable interrupts - see comments above the cpsid instruction() | |
| above. */ | |
| __asm volatile( "cpsie i" ); | |
| __asm volatile( "dsb" ); | |
| __asm volatile( "isb" ); | |
| } | |
| else | |
| { | |
| /* Write the calculated reload value, which will also start the | |
| timer. */ | |
| lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulReloadValue; | |
| /* Allow the application to define some pre-sleep processing. */ | |
| xModifiableIdleTime = xExpectedIdleTime; | |
| configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); | |
| /* xExpectedIdleTime being set to 0 by configPRE_SLEEP_PROCESSING() | |
| means the application defined code has already executed the sleep | |
| instructions. */ | |
| if( xModifiableIdleTime > 0 ) | |
| { | |
| __asm volatile( "dsb" ); | |
| __asm volatile( "wfi" ); | |
| __asm volatile( "isb" ); | |
| } | |
| /* Allow the application to define some post sleep processing. */ | |
| configPOST_SLEEP_PROCESSING( xModifiableIdleTime ); | |
| /* Stop the hibernation timer. Again, the time the timer 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. Take the count value first as | |
| setting the preload to zero also seems to clear the count. */ | |
| ulCountAfterSleep = lpHTIMER_COUNT_REGISTER; | |
| lpHTIMER_PRELOAD_REGISTER = 0; | |
| /* Re-enable interrupts - see comments above the cpsid instruction() | |
| above. */ | |
| __asm volatile( "cpsie i" ); | |
| __asm volatile( "dsb" ); | |
| __asm volatile( "isb" ); | |
| if( ulTickFlag != pdFALSE ) | |
| { | |
| /* The tick interrupt has already executed, although because this | |
| function is called with the scheduler suspended the actual tick | |
| processing will not occur until after this function has exited. | |
| The timer has already been reloaded to count in ticks, and can just | |
| continue counting down from its current value. */ | |
| ulReloadValue = ulCountAfterSleep; | |
| /* Sanity check that the timer's reload value has indeed been | |
| reset. */ | |
| configASSERT( ( uint32_t ) lpHTIMER_PRELOAD_REGISTER == ulReloadValueForOneHighResolutionTick ); | |
| /* The tick interrupt handler will already have pended the tick | |
| processing in the kernel. 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 sleeping. The | |
| actual stepping of the tick appears later in this function. */ | |
| ulCompleteTickPeriods = xExpectedIdleTime - 1UL; | |
| } | |
| else | |
| { | |
| /* Something other than the tick interrupt ended the sleep. How | |
| many complete tick periods passed while the processor was | |
| sleeping? */ | |
| ulCompletedTimerDecrements = ulReloadValue - ulCountAfterSleep; | |
| /* Undo the adjustment that was made to the reload value to account | |
| for the fact that a time slice was part way through when this | |
| function was called before working out how many complete tick | |
| periods this represents. (could have used [ulExpectedIdleTime * | |
| ulReloadValueForOneHighResolutionTick] instead of ulReloadValue on | |
| the previous line, but this way avoids the multiplication). */ | |
| ulCompletedTimerDecrements += ( ulReloadValueForOneHighResolutionTick - ulCountBeforeSleep ); | |
| ulCompleteTickPeriods = ulCompletedTimerDecrements / ulReloadValueForOneHighResolutionTick; | |
| /* The reload value is set to whatever fraction of a single tick | |
| period remains. */ | |
| ulReloadValue = ( ( ulCompleteTickPeriods + 1UL ) * ulReloadValueForOneHighResolutionTick ) - ulCompletedTimerDecrements; | |
| } | |
| /* Cannot use a reload value of 0 - it will not start the timer. */ | |
| if( ulReloadValue == 0 ) | |
| { | |
| /* There is no fraction remaining. */ | |
| ulReloadValue = ulReloadValueForOneHighResolutionTick; | |
| ulCompleteTickPeriods++; | |
| } | |
| /* Restart the timer so it runs down from the reload value. The reload | |
| value will get set to the value required to generate exactly one tick | |
| period the next time the tick interrupt executes. */ | |
| lpHTIMER_PRELOAD_REGISTER = ( uint16_t ) ulReloadValue; | |
| } | |
| /* Wind the tick forward by the number of tick periods that the CPU | |
| remained in a low power state. */ | |
| vTaskStepTick( ulCompleteTickPeriods ); | |
| } | |
| /*-----------------------------------------------------------*/ | |
| void NVIC_Handler_GIRQ23( void ) | |
| { | |
| static volatile uint32_t ulTimerCounts = 0; | |
| /* The low power demo is using aggregated interrupts, so although in the | |
| demo btimer 0 is the only peripheral that will generate interrupts on | |
| this vector, in a real application it would be necessary to first check the | |
| interrupt source. */ | |
| if( ( lpEC_GIRQ23_SOURCE & lpGIRQ23_BIT_TIMER0 ) != 0 ) | |
| { | |
| /* Btimer0 interrupted. Clear the interrupt. */ | |
| lpEC_GIRQ23_SOURCE = lpGIRQ23_BIT_TIMER0; | |
| /* This timer is used for test purposes. Its only function is to | |
| generate interrupts while the MCU is sleeping, so the MCU is sometimes | |
| brought out of sleep by a means other than the tick interrupt. */ | |
| ulTimerCounts++; | |
| } | |
| else | |
| { | |
| /* Don't expect any other interrupts to use this vector in this | |
| demo. Force an assert. */ | |
| configASSERT( lpEC_GIRQ23_SOURCE == 0 ); | |
| } | |
| } | |
| /*-----------------------------------------------------------*/ | |
| #endif /* configCREATE_LOW_POWER_DEMO == 1 */ |