Tickless Idle Sleep on SAML21 Xplained Pro - vTaskDelay time error

alvaromuro wrote on Thursday, November 09, 2017:

I am configuring the Atmel SAM L21 Xplained pro A board (ATSAML21J18A), with Atmel Studio 7 and FreeRTOS v8.0.1 to work with Tickless Idle sleep, based on the files of an ASF example called [FreeRTOS tickless demo using OLED1 Xplained Pro]http://asf.atmel.no/docs/3.21.0/common.services.freertos.oled1_tickless_xpro_example.saml21_xplained_pro/html/index.html).

I have created a simple task which just blinks a LED and after calls vTaskDelay and blocks for 3000 ms. I run it first without activating tickless mode, and the program works perfectly. The task blinks, then blocks for 3000 ms, and starts again.

When I activate tickless mode, the led Blinks at the same right rate, the system then enters the sleep mode correctly, and wakes up again to blink, but the delay is not what it should be, it’s shorter, around 1000 ms instead of 3000 ms.

In FreeRTOSConfig.h I have:

#define configUSE_TICKLESS_IDLE                 2
#define configCPU_CLOCK_HZ                      ( system_gclk_gen_get_hz(GCLK_GENERATOR_0) )
#define configTICK_RATE_HZ                      ( ( portTickType ) 100 )

My main Clock is set up at 12MHz from OSC16M:

/* Configure GCLK generator 0 (Main Clock) */
#  define CONF_CLOCK_GCLK_0_ENABLE                true
#  define CONF_CLOCK_GCLK_0_RUN_IN_STANDBY        false
#  define CONF_CLOCK_GCLK_0_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_OSC16M
#  define CONF_CLOCK_GCLK_0_PRESCALER             1
#  define CONF_CLOCK_GCLK_0_OUTPUT_ENABLE         false

/* SYSTEM_CLOCK_SOURCE_OSC16M configuration - Internal 16MHz oscillator */
#  define CONF_CLOCK_OSC16M_FREQ_SEL              SYSTEM_OSC16M_12M
#  define CONF_CLOCK_OSC16M_ON_DEMAND             true
#  define CONF_CLOCK_OSC16M_RUN_IN_STANDBY        false

I have clocked the tick with the ultra low power oscillator ULPOSC32k:
in conf_clocks.h

/* Configure GCLK generator 5 */
#  define CONF_CLOCK_GCLK_5_ENABLE                true
#  define CONF_CLOCK_GCLK_5_RUN_IN_STANDBY        false
#  define CONF_CLOCK_GCLK_5_CLOCK_SOURCE          SYSTEM_CLOCK_SOURCE_ULP32K
#  define CONF_CLOCK_GCLK_5_PRESCALER             1
#  define CONF_CLOCK_GCLK_5_OUTPUT_ENABLE         false

and modified the vPortSetupTimerInterrupt as follows:

void vPortSetupTimerInterrupt(void)
{
	// Struct for configuring TC
	struct tc_config tcconf;
	// Set up configuration values
	tc_get_config_defaults(&tcconf);
	tcconf.clock_source    = GCLK_GENERATOR_5;   
	tcconf.counter_size    = TC_COUNTER_SIZE_32BIT;
	tcconf.run_in_standby  = true;
	tcconf.clock_prescaler = TC_CLOCK_PRESCALER_DIV1;
	tcconf.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ;

	// Initialize the TC
	tc_init(&tc, TICK_TC, &tcconf);

	// Register and enable callback for freeRTOS tick handler
	tc_register_callback(&tc, (tc_callback_t) xPortSysTickHandler, TC_CALLBACK_CC_CHANNEL0);
	tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0);

	// Set top value equal to one os tick
	tc_set_top_value(&tc, TIMER_RELOAD_VALUE_ONE_TICK);

	// Enable the timer
	tc_enable(&tc);
}

and in FreeRTOSConfig.h the example has the following definitions which I asssume are correct:

/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names - or at least those used in the unmodified vector table. */
#define vPortSVCHandler                         SVC_Handler
#define xPortPendSVHandler                      PendSV_Handler

#if defined (__GNUC__) || defined (__ICCARM__)
#include <portmacro.h>
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );
#endif
#define portSUPPRESS_TICKS_AND_SLEEP             vPortSuppressTicksAndSleep

I also have the following timer configuration:

//! Frequency of timer
//#define TIMER_HZ                            ( configCPU_CLOCK_HZ )
#define TIMER_HZ                            ( system_gclk_gen_get_hz(GCLK_GENERATOR_5 ))

//! Value per os tick of timer
#define TIMER_RELOAD_VALUE_ONE_TICK         ( TIMER_HZ / configTICK_RATE_HZ )

//!  Maximum value of timer
#define TIMER_MAX_COUNT                     ( 0xffffffff )

//! Maximum possible suppressed ticks with timer
#define TIMER_MAX_POSSIBLE_SUPPRESSED_TICKS ( TIMER_MAX_COUNT / TIMER_RELOAD_VALUE_ONE_TICK )

And finally the call to delay the task is:

/* Block until 3000ms */
		vTaskDelay ( pdMS_TO_TICKS (3000) );

/* Definition of pdMS_TO_TICKS function*/
#ifndef pdMS_TO_TICKS
#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000 ) )
#endif

I would very much appreciate any insight on why the vTaskDelay doesn’t respect the given 3000 ms and resumes the task faster than it should.

rtel wrote on Thursday, November 09, 2017:

I’m not sure that we have M0 code with tickless idle? Maybe I am wrong.

You say you run the code first without tickless idle and the blink rate
is correct, then with tickless idle, and the blink rate is wrong. Is
the clock setup the same in both cases - so in both cases you are using
the low power oscillator to generate the tick interrupt rather than the
SysTick?

alvaromuro wrote on Friday, November 10, 2017:

Thanks for your prompt reply. In Atmel Studio 7 there are tickless mode working examples for SAML21, SAMD20 and D21 and SAM R21 and R30, which use M0. I suppose they are rigth.

No, with tickless idle the LED blink rate is also right, but the delay of the task generated with vTaskDelay is shorter than it should. That is, the time the system is in idle task sleep, should be 3000 ms but instead is just 1000 ms.

In relation of using the low power oscillator in tickless idle, I’m not sure how to check that. This is what I found:

At the beginning of the tickless.c demo file, where vPortSetupTimerInterrupt and vPortSuppressTicksAndSleep are defined, it says:

The tickless feature is implemented using a timer, configured with the desired timeout value, to wake the device. The same timer is also used to generate the system tick, so that time is kept in the optimal way, eliminating drift in most cases.

And rigth before the definition of vPortSetupTimerInterrupt, it says:

Function that sets up a timer to use for os tick. The same timer is also used as the sleep timer.

Here is the code of vPortSuppressTicksAndSleep, is there something here that can cause that?:

* Function to configure timer for sleep, and calculate time slept.
 */ 
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{
	
	// Reconfigure the timer to act as sleep timer
	tc_disable_callback(&tc, TC_CALLBACK_CC_CHANNEL0);
	tc_unregister_callback(&tc, TC_CALLBACK_CC_CHANNEL0);
	tc_register_callback(&tc, empty_callback, TC_CALLBACK_CC_CHANNEL0);
	tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0);

	// Check that the offset is not greater than the range of the timer
	if (xExpectedIdleTime > TIMER_MAX_POSSIBLE_SUPPRESSED_TICKS)
	{
		xExpectedIdleTime = TIMER_MAX_POSSIBLE_SUPPRESSED_TICKS;
	}

	// Set sleep time, -1 because we want to wake up before the last tick
	tc_set_top_value(&tc, (xExpectedIdleTime - 1) * TIMER_RELOAD_VALUE_ONE_TICK);

	// Clear overflow interrupt flag
	tc.hw->COUNT32.INTFLAG.reg = TC_INTFLAG_OVF;

	// Check if we still should sleep
	if (eTaskConfirmSleepModeStatus() == eAbortSleep)
	{
		// Reset the timer to act as SysTick
		tc_disable_callback(&tc, TC_CALLBACK_CC_CHANNEL0);
		tc_unregister_callback(&tc, TC_CALLBACK_CC_CHANNEL0);
		tc_register_callback(&tc, (tc_callback_t) xPortSysTickHandler, TC_CALLBACK_CC_CHANNEL0);
		tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0);
		tc_set_top_value(&tc, TIMER_RELOAD_VALUE_ONE_TICK);
	}
	else
	{
		if (xExpectedIdleTime > 0)
		{
			// Data sync barrier before sleep
			__asm volatile ("dsb");
			// Go to sleep
			__asm volatile ("wfi");

			// If OVF interrupt flag is set, we know the timer has wrapped
			if (tc.hw->COUNT32.INTFLAG.reg & TC_INTFLAG_OVF)
			{
				vTaskStepTick(xExpectedIdleTime - 1);
			}
			// We do not know how long we've slept
			else
			{
				// Calculate from Counter how long we've slept
				// Reset counter to less than one os tick
				// This might result in a tiny drift in time.
				uint32_t count_val = tc_get_count_value(&tc);
				vTaskStepTick(count_val / TIMER_RELOAD_VALUE_ONE_TICK);
				tc_set_count_value(&tc, count_val % TIMER_RELOAD_VALUE_ONE_TICK);
			}
		}
		// Reset the timer to act as SysTick
		tc_disable_callback(&tc, TC_CALLBACK_CC_CHANNEL0);
		tc_unregister_callback(&tc, TC_CALLBACK_CC_CHANNEL0);
		tc_register_callback(&tc, (tc_callback_t) xPortSysTickHandler, TC_CALLBACK_CC_CHANNEL0);
		tc_enable_callback(&tc, TC_CALLBACK_CC_CHANNEL0);
		tc_set_top_value(&tc, TIMER_RELOAD_VALUE_ONE_TICK);

		// Make sure that the counter hasn't passed the CC before callback was registered
		if ( tc_get_count_value(&tc) > TIMER_RELOAD_VALUE_ONE_TICK )
		{
			// If so, reload count value, and step one tick */
			tc_set_count_value(&tc, tc_get_count_value(&tc) % TIMER_RELOAD_VALUE_ONE_TICK);
			vTaskStepTick(1);
		}
	}
}

Also, I see another possible cause related with Tickless Low power ARM RTOS Cortex-M Microcontrollers, in the following instruction for the cases when you use a clock other than Systick:

Provide an implementation of vPortSetupTimerInterrupt() that generates an interrupt at the frequency specified by the configTICK_RATE_HZ FreeRTOSConfig.h constant.

I’m not sure how to get the interrupt frequency properly configured. My configTICK_RATE_HZ is 100. Tracing back the interrupt timer, in vPortSetupTimerInterrupt (at tickless.c), I have the GLCK_GENERATOR_5 with DIV 1:

    tcconf.clock_source    = GCLK_GENERATOR_5;   
    tcconf.clock_prescaler = TC_CLOCK_PRESCALER_DIV1;
    //Wavegen function Match Frequency is chosen to reload the count register on every CC0 match.//
    tcconf.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ; 

and in conf_clocks.h, the GCLK_GENERATOR_5 has SYSTEM_CLOCK_SOURCE_ULP32K also with prescaler at 1. Thus it looks that the input frequency of the interrupt is 32k, am I right?

Also there is a definition of TIMER_HZ at tickless.c which I equaled to system_gclk_gen_get_hz(GCLK_GENERATOR_5 ). I’m not sure if this is correct.

So, if the difference between the configTICK_RATE_HZ and the interrupt frequency at vPortSetupTimerInterrupt is the cause, how could I configure it? I tried changing the values of both prescalers but with no luck.

Please see the attached files for clarification, I feel I’m getting closer to the solution thanks to your support.

rtel wrote on Friday, November 10, 2017:

and in conf_clocks.h, the GCLK_GENERATOR_5 has
SYSTEM_CLOCK_SOURCE_ULP32K also with prescaler at 1. Thus it looks that
the input frequency of the interrupt is 32k, am I right?

No idea - that is way outside the scope of RTOS support.

I’m afraid this is very hard to support that kind of question directly,
especially as I don’t think this is code we provided, and we can’t get
into device specific register settings (we run on LOTS of different
devices and would have to get the data sheets out and experiment with
settings on real hardware to get into clock settings on individual
peripherals on individual parts - just not practical).

My advise would be to start at the bottom of the system, ensuring each
part is working correctly and as expected before working your way up.
For example, ensure the input frequency of the clock is what you expect
(probably with an oscilloscope), then set up the clock and check the
output frequency is what you expect. Then check the variables in the
software have the frequency that is actually output from the
clock…and keep working up until you get into the ‘suppress ticks
and sleep’ function - and see that, given the inputs, a hand calculation
of the time at which the clock should be programmed to generate an
interrupt next matches the time actually calculated in the code.

alvaromuro wrote on Monday, November 13, 2017:

Yes, I was also thinking this is more related with the atmel code than to the FreeRTOS. I will follow your advise to go from bottom to top, your support has been very helpful, thanks!

alvaromuro wrote on Friday, November 17, 2017:

Solved. Just in case it helps somebody: It was a problem related with the configuration of the timer that generates the OS tick in vPortSuppressTicksAndSleep: changing the Timer instance from TC4 to TC2 solves the issue.

// Initialize the TC
   tc_init(&tc, TICK_TC, &tcconf);

 //! Timer/Counter instance to use as tick timer
        //#define TICK_TC TC4  - Commented out as it's not working
 #define TICK_TC TC2

Now it generates a correct tick frequency independently of the CPU clock frequency configured, and thus the vTaskDelay works properly.

However, I will still have to see the implications this will have when I activate the low power modes, as it seems the TC4 is the only timer active in Power Domain 0 (PD0)(the lowest power domain).

Thanks for your help.