Hello all,
I’m running V10.3 of the Kernel on my SAMD51.
I currently have no tasks at all (bar the idle) and have defined configUSE_TICKLESS_IDLE. My code is calculating that it can sleep for max of around 5 seconds. I toggle a pin on entering vPortSuppressTicksAndSleep.
Interestingly I see two different behaviours - If I run the code in DEBUG then I see the pin toggle with a period of 5 seconds, all correct, but the power draw is still large. 10s of mA.
If the code is ran in RELEASE (with -O3) then I see a draw of around 3mA (I believe I may have leaky hardware) but the pin does not toggle at all and 3mA looks to be regular draw, not a sleep mode.
The bulk of interesting stuff in suppress is given below:
/* 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
* __disable_interrupt() call 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;
}
I should add that the FreeRTOS tick is using MCLK rather than any external. And the following M4 registers are used:
/* Constants required to manipulate the core. Registers first… */
#define portNVIC_SYSTICK_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000e010 ) )
#define portNVIC_SYSTICK_LOAD_REG ( *( ( volatile uint32_t * ) 0xe000e014 ) )
#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) )
#define portNVIC_SHPR3_REG ( ( ( volatile uint32_t * ) 0xe000ed20 ) )
/ …then bits in the registers. */
#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL )
#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL )
#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL )