Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem TCP & FAT




Loading

Implementing tickless idle on Cortex M4.

Posted by jabad06 on November 9, 2013

I am currently using atmel's atmsa4ls4a chip and using the AST instead of the SysTick for my rtos ticks. I was able to get this to run smoothly but am now having trouble with the vPortSuppressTicksAndSleep function. I use the freetos sample code as a baseline of my implementation. In my implementation I put the system into retention mode and then let the ast wake up the system. When I try to disable the ast and enable the ast in the PortSupressTicksandSleep function the system hangs for some reason. I have stopped trying that and just set the counter value while the ast is running. This causes the system to hang after working for a little bit.

Here is my implementation: /* The tick interrupt handler. This is always the same other than the part that clears the interrupt, which is specific to the clock being used to generate the tick. / void ASTALARMHandler(void) { / Protect incrementing the tick with an interrupt safe critical section. */ ( void ) portSETINTERRUPTMASKFROMISR(); { if( xTaskIncrementTick() != pdFALSE ) { portNVICINTCTRLREG = portNVICPENDSVSET_BIT; }

	/* Just completely clear the interrupt mask on exit by passing 0 because
	it is known that this interrupt will only ever execute with the lowest
	possible interrupt priority. */
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 );

/* The CPU woke because of a tick. */
ulTickFlag = pdTRUE;

/* If this is the first tick since exiting tickless mode then the AST needs
to be reconfigured to generate interrupts at the defined tick frequency. */
ast_write_alarm0_value( AST, ulAlarmValueForOneTick );

/* Ensure the interrupt is clear before exiting. */
ast_clear_interrupt_flag( AST, AST_INTERRUPT_ALARM );

} /-----------------------------------------------------------/

/* Override the default definition of vPortSetupTimerInterrupt() that is weakly defined in the FreeRTOS Cortex-M3 port layer with a version that configures the asynchronous timer (AST) to generate the tick interrupt. */ void vPortSetupTimerInterrupt( void ) { struct astconfig astconf;

/* Ensure the AST can bring the CPU out of sleep mode. */
sleepmgr_lock_mode( SLEEPMGR_RET );

/* Ensure the 32KHz oscillator is enabled. */
if( osc_is_ready( OSC_ID_OSC32 ) == pdFALSE )
{
	osc_enable( OSC_ID_OSC32 );
	osc_wait_ready( OSC_ID_OSC32 );
}

/* Enable the AST itself. */
ast_enable( AST );

ast_conf.mode = AST_COUNTER_MODE;  /* Simple up counter. */
ast_conf.osc_type = AST_OSC_32KHZ;
ast_conf.psel = 0; /* No prescale so the actual frequency is 32KHz/2. */
ast_conf.counter = 0;
ast_set_config( AST, &ast_conf );

/* The AST alarm interrupt is used as the tick interrupt.  Ensure the alarm
status starts clear. */
ast_clear_interrupt_flag( AST, AST_INTERRUPT_ALARM );

/* Enable wakeup from alarm 0 in the AST and power manager.  */
ast_enable_wakeup( AST, AST_WAKEUP_ALARM );
bpm_enable_wakeup_source( BPM, ( 1 << BPM_BKUPWEN_AST ) );

/* Tick interrupt MUST execute at the lowest interrupt priority. */
NVIC_SetPriority( AST_ALARM_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY);
ast_enable_interrupt( AST, AST_INTERRUPT_ALARM );
NVIC_ClearPendingIRQ( AST_ALARM_IRQn );
NVIC_EnableIRQ( AST_ALARM_IRQn );

/* Automatically clear the counter on interrupt. */
ast_enable_counter_clear_on_alarm( AST, portAST_ALARM_CHANNEL );

/* Start with the tick active and generating a tick with regular period. */
ast_write_alarm0_value( AST, ulAlarmValueForOneTick );
ast_write_counter_value( AST, 0 );

/* See the comments where xMaximumPossibleSuppressedTicks is declared. */
xMaximumPossibleSuppressedTicks = ULONG_MAX / ulAlarmValueForOneTick;

}

/-----------------------------------------------------------/

/* Override the default definition of vPortSuppressTicksAndSleep() that is weakly defined in the FreeRTOS Cortex-M3 port layet with a version that manages the asynchronous timer (AST), as the tick is generated from the low power AST and not the SysTick as would normally be the case on a Cortex-M. */ void vPortSuppressTicksAndSleep( portTickType xExpectedIdleTime ) { uint32t ulAlarmValue, ulCompleteTickPeriods; eSleepModeStatus eSleepAction; portTickType xModifiableIdleTime; enum sleepmgrmode xSleepMode;

/* THIS FUNCTION IS CALLED WITH THE SCHEDULER SUSPENDED. */

/* Make sure the AST reload value does not overflow the counter. */
if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
{
	xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
}

/* Calculate the reload value required to wait xExpectedIdleTime tick
periods. */
ulAlarmValue = ulAlarmValueForOneTick * xExpectedIdleTime;
if( ulAlarmValue > ulStoppedTimerCompensation )
{
	/* Compensate for the fact that the AST is going to be stopped
	momentarily. */
	ulAlarmValue -= ulStoppedTimerCompensation;
}

/* Stop the AST momentarily.  The time the AST 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. */
prvDisableAST();

/* 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		\n\t"
				"dsb			\n\t" );

/* 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 tick. */
	prvEnableAST();

	/* Re-enable interrupts - see comments above the cpsid instruction()
	above. */
	__asm volatile( "cpsie i" );
}
else
{
	/* Adjust the alarm value to take into account that the current time
	slice is already partially complete. */
	ulAlarmValue -= ast_read_counter_value( AST );
	ast_write_alarm0_value( AST, ulAlarmValue );

	/* Restart the AST. */
	prvEnableAST();

	/* 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 WAIT
	instruction. */
	if( xModifiableIdleTime > 0 )
	{
		/* Find the deepest allowable sleep mode. */
		xSleepMode = sleepmgr_get_sleep_mode();

		if( xSleepMode != SLEEPMGR_ACTIVE )
		{
			/* Sleep until something happens. */
			bpm_sleep( BPM, xSleepMode );
		}
	}

	/* Allow the application to define some post sleep processing. */
	configPOST_SLEEP_PROCESSING( xModifiableIdleTime );

	/* Stop AST.  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. */
	prvDisableAST();

	/* Re-enable interrupts - see comments above the cpsid instruction()
	above. */
	__asm volatile( "cpsie i" );

	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.
		Reset the alarm value with whatever remains of this tick period. */
		ulAlarmValue = ulAlarmValueForOneTick - ast_read_counter_value( AST );
		ast_write_alarm0_value( AST, ulAlarmValue );

		/* 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? */
		ulCompleteTickPeriods = ast_read_counter_value( AST ) / ulAlarmValueForOneTick;

		/* The alarm value is set to whatever fraction of a single tick
		period remains. */
		ulAlarmValue = ast_read_counter_value( AST ) - ( ulCompleteTickPeriods * ulAlarmValueForOneTick );
		ast_write_alarm0_value( AST, ulAlarmValue );
	}

	/* Restart the AST so it runs up to the alarm value.  The alarm value
	will get set to the value required to generate exactly one tick period
	the next time the AST interrupt executes. */
	prvEnableAST();

	/* Wind the tick forward by the number of tick periods that the CPU
	remained in a low power state. */
	vTaskStepTick( ulCompleteTickPeriods );
}

}


Implementing tickless idle on Cortex M4.

Posted by richardbarry on November 9, 2013

FreeRTOS runs on many, many different chips, so unfortunately we cannot provide support that requires us to start downloading and reading chip specific data sheets and manuals - it falls outside FreeRTOS support and is just not practical.

However, in this case, is there any reason you cannot use or adapt the code already provided in the FreeRTOS download for the SAM4L? It already uses the AST.

http://www.freertos.org/AtmelSAM4L-EKLowPowerTick-lessRTOSDemo.html

Regards.


Implementing tickless idle on Cortex M4.

Posted by jabad06 on November 10, 2013

I was having some issues with the provided demo. So I tried implementing my own. Maybe you can answer the problem with the issue I had with the demo.

I had one task in this demo that would process data and then suspend itself. The task would resume after a gpio interrupt goes off and calls taskresumefromisr. It looks like the task is missing some of these interrupts and at times the system would freeze. Could you provide any insight on why this could be happening?


Implementing tickless idle on Cortex M4.

Posted by jabad06 on November 10, 2013

I forgot to note that the gpio interrupt is an interrupt that can wake up the system from sleep. So instead of letting the system wake up with the timer and resume the rtos the gpio would go off before the timer goes off and resumes the system. I have attached the modified demo code. The main change is under vPortSetupTimerInterrupt where I declare another interrupt source besides the AST.

Attachments


Implementing tickless idle on Cortex M4.

Posted by richardbarry on November 10, 2013

I had one task in this demo that would process data and then suspend itself. The task would resume after a gpio interrupt goes off and calls taskresumefromisr.

See the documentation for the xTaskResumeFromISR(), noting in particular the paragraph that starts "xTaskResumeFromISR() should not be used to synchronise a task with an interrupt".

http://www.freertos.org/taskresumefromisr.html

Regards.


Implementing tickless idle on Cortex M4.

Posted by jabad06 on November 11, 2013

Thank you for the note. After some modifications I am still having some issues where the freertos is hanging. In my example before where i have two tasks I now simplified to just one task that blinks an led after every 5 seconds by using vTaskDelay.

When the the freertos calls vPortSuppressTicksAndSleep the demo shows that there are two ways that the system can wake up. One is through the AST and the other is some other interrupt. If it is some other interrupt it will calculate how many ticks are left and resume the rtos from there. The freertos freezes when I allow an external interrupt to end the sleep earlier than it was suppose to (specifically the EIC wakes it up).

In the attached file I enable eic before going to sleep and then disable it after it wakes up. If I dont enable the eic as an option to wake up the sleep prematurely the task works fine. It is when I enable it to wake up the sleep prematurely is when the rtos freezes.

Attachments


Implementing tickless idle on Cortex M4.

Posted by richardbarry on November 11, 2013

Are you sure this is related to tickless operation? What happens when you use the EIC when configUSETICKLESSIDLE is set to 0?

When you say "freezes", what is it executing? If you stop on the debugger, what is the CPU processing? (for example, maybe it is in a fault handler, or maybe it is continuously entering and exiting the same interrupt, etc.)

Regards.


Implementing tickless idle on Cortex M4.

Posted by hilt0n on November 11, 2013

Hi,

I have exactly the same problem with same configuration:

I have a task who wake up the system every 1 seconds (all is correct with just that). But when I use an external interrupt my system works only if the button is pressed when the system doesn't sleep. If the system is sleeping, it falls in the assert loop after a call to prvTaskExitError. This is the same behavior for synchronous interrupt (without wake-up)

Best regards


Implementing tickless idle on Cortex M4.

Posted by hilt0n on November 11, 2013

Visibly, the problem appears in the function vTaskStepTick, I finish always inside after an external interrupt in sleep mode :

~~~~~~ void vTaskStepTick( portTickType xTicksToJump ) { /* Correct the tick count value after a period during which the tick was suppressed. Note this does not call the tick hook function for each stepped tick. */ //configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime ); if((xTickCount + xTicksToJump) > xNextTaskUnblockTime){ while(1) { _asm volatile( "NOP" ); } }
xTickCount += xTicksToJump; traceINCREASETICK_COUNT( xTicksToJump ); } ~~~~~~

Visibly, the xTicksToJump value is too high, in my case, I have xTickCount = 44, xTicksToJump = 15000 and xNextTaskUnblockTime = 150..

I try to figure out, any idea ?

Regards,


Implementing tickless idle on Cortex M4.

Posted by richardbarry on November 11, 2013

The first thing I notice in the code you posted is your call to eic_enable() after interrupts have been disabled. I suspect this call will enable interrupts where they ought not to be.

Try enabling and disabling the interrupts using the Atmel library enable/disable functions Enableglobalinterrupt() Disableglobalinterrupt() in place of the __asm volatile( "cpsid i nt" and __asm volatile( "cpsie i" );. That way the interrupt enable/disable should nest.

Does that fix the problem?

Regards.


Implementing tickless idle on Cortex M4.

Posted by richardbarry on November 11, 2013

Oops - looking at the drivers, don't use the Enableglobalinterrupt() Disableglobalinterrupt() functions either. Use:

flag = cpuirqsave();

cpuirqrestore( flag );

Regards.


Implementing tickless idle on Cortex M4.

Posted by jabad06 on November 11, 2013

No it did not fix the problem. Even if eic_enable re enables the other interrupts besides the eic interrupt that would only wake the system prematurely instead of AST. Waking up the system prematurely should not freeze the freertos am I right?

I am sure this is because of the configUse_tickless idle. When I don't use tickless the system runs properly. I know the system is freezing since the leds do not blink. I have set several leds to blink at different parts of my code to debug. The task with the blinking led does not blink.


Implementing tickless idle on Cortex M4.

Posted by jabad06 on November 12, 2013

I forgot to mention the problem is the same as YanickL

In the following piece of code void vTaskStepTick( portTickType xTicksToJump ) { /* Correct the tick count value after a period during which the tick was suppressed. Note this does not call the tick hook function for each stepped tick. */ configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime );

	xTickCount += xTicksToJump;
	traceINCREASE_TICK_COUNT( xTicksToJump );
}

configASSERT goes off. vTaskStepTick gets called at the end of vPortSuppressTicksAndSleep.


Implementing tickless idle on Cortex M4.

Posted by richardbarry on November 12, 2013

When you enter tickless mode a timer must be set to bring the system out of sleep before the next task must execute - whenever that may be in the future. The assert() will trap an error if, on exiting the tickles mode, time has moved past that point. Therefore if the assert() is indicating an error then either you have spent too long inside the sleep function, or the time calculated as the time now is wrong.

I have tried running the standard demo with the code you posted as:

eicenable(EIC); eiclineenable(EIC, EXTINT1); /* Sleep until something happens. */ //bpmsleep( BPM, xSleepMode ); bpmsleep( BPM, BPMSMSLEEP0 ); eiclinedisable(EIC, EXTINT1); eic_disable(EIC);

and don't find any problem but I'm not actually generating the external interrupt. In your system does it assert() all the time or only when the external interrupt is generated. If the latter, how long is the interrupt handler taking to execute? I have tried to generate external interrupts off a button push but have not been able to do so (even the EIC demo provided by Atmel is not generating interrupts) so can't test it.

Do you need to enable and disable the EIC on each cycle? If you do, it would be better done from the pre and post sleep hook functions so you don't have to edit the code.

Regards.


Implementing tickless idle on Cortex M4.

Posted by hilt0n on November 12, 2013

I you change the constant mainTXDELAY in the file mainlowpower.c of the demo from ( 500UL / portTICKRATEMS ) to ( 10UL / portTICKRATE_MS ), the system freeze too. If the task cannot be executed in the right time, the system freeze.

I don't know if it's the normal comportment of FreeRtos but it seems to be strange?

(I have done that with the clean, unmodified source of the demo)


Implementing tickless idle on Cortex M4.

Posted by richardbarry on November 12, 2013

I tried setting mainTX_DELAY to 10 and find the code runs without any problem. With the demo in its default configuration a Tx delay of 10 is too short for the CPU to ever go into the tickless mode, and the "suppress ticks and sleep" function is never called.

Note that when mainTXDELAY is set to 10 it sends to the queue at a rate that is faster than the Rx task is receiving from the queue as the Rx task delays for mainLEDTOGGLEDELAY ticks with the LED on each time is receives a character - and mainLEDTOGGLEDELAY is greater than 10. It does not prevent the code from running though, it just means the execution pattern is not as intended. Reducing mainLEDTOGGLE_DELAY to 5 means it can keep up with the Tx task.

Regards.


Implementing tickless idle on Cortex M4.

Posted by hilt0n on November 12, 2013

Thank Richard, indeed its my mistake (wrong debugging). But I have finally found the problem (I hope).

For this implementation, the AST timer is configured for clearing its value on ALARM compare but when the uC wake up from EIC, there is no ALARM compare interrupt and then no reset of the AST counter value.

In case of wake up from other sources than AST ALARM, we need to clear the current counter value when we calcul the next ALARM value:

~~~~~~~~~ /* Something other than the tick interrupt ended the sleep. How many complete tick periods passed while the processor was sleeping? */ ulCompleteTickPeriods = astreadcounter_value( AST ) / ulAlarmValueForOneTick;

/* The alarm value is set to whatever fraction of a single tick period remains. */ ulAlarmValue = astreadcounter_value( AST ) - ( ulCompleteTickPeriods * ulAlarmValueForOneTick );

// Need to clear the counter astwritecountervalue( AST, 0 );
astwritealarm0value( AST, ulAlarmValue ); ~~~~~~~~~

May be some others changes are necessary but all is working since I have modified this.

Regards,


Implementing tickless idle on Cortex M4.

Posted by jabad06 on November 14, 2013

Yes it is working on my end also. Clearing the counter value back to 0 after an eic wakes up the system fixes the hanging.


Implementing tickless idle on Cortex M4.

Posted by hilt0n on September 15, 2014

Hi Richard,

It seems the problem isn't fixed. I can now tell you more about the problem. Firstly, if you want to try it, you can simply use the demo in the source for SAM4L (with SAM4K-EK) and mix it with a demo project of EIC (in ATMEL studio) named "EIC (External Interrupt Controller) Example 1 - SAM4L-EK"

To be short, we can simply add the EIC driver in ASF and replace the file mainlowpower.c with mine. When you press the button PB0, the interrupt is generated then the problem appears.

To debug the AST counter, I stop it in debug stop mode with the following code at the beginning of my program: uint32t *periphdbg = (void *)(0xE0042000UL); *periph_dbg |= 0x0007;

The solution of clearing the AST counter in case of external wake up works maybe 99 times on 100 but sometime I have problem with complex program and the elapsed time is bigger than the next unblock time.

As you proposed, I have tried to find the problem where the next counter value is defined but I don't find any problem.

Have you an idea ?

Attachments

main_low_power.c (5967 bytes)

Implementing tickless idle on Cortex M4.

Posted by rtel on September 15, 2014

I used the ASF wizard to add the EIC module into my SAM4L-EK project, then used your mainlowpower.c file - but when I push the button marked PB0 no interrupts are generated.

What else do I need to do?

Regards.


Implementing tickless idle on Cortex M4.

Posted by hilt0n on September 16, 2014

Ha yes sorry, you need to add:

define CONFBOARDEIC

in config/conf/board.h to unmask EIC gpio mux init in board init file.


Implementing tickless idle on Cortex M4.

Posted by rtel on September 16, 2014

Please try the following, then report back:

1) Find the function prvEnableAST() in SAM4Llowpowertickmanagement().

2) Add the following line to the top of the function: "astwritecounter_value( AST, 0 );"

Regards.


Implementing tickless idle on Cortex M4.

Posted by rtel on September 16, 2014

Actually - instead try the attached. Only one line different from the current release.

Attachments


Implementing tickless idle on Cortex M4.

Posted by hilt0n on September 16, 2014

Ok thank you, I have found the same solution than you (reset the AST counter when the uC is awakened by external interrupt) however, I have sometimes some problems with timing and fall into the assert of the function vTaskStepTick because the actual time is bigger than the next unblock time.

Have you an idea, a direction to find the problem ?

Best regards


Implementing tickless idle on Cortex M4.

Posted by jpt106 on September 18, 2014

I believe I found the issue. The AST counter was not being reset and updated correctly. I was able to reliable reproduce the problem and the attached file fixes the issue on my system.

Attachments


Implementing tickless idle on Cortex M4.

Posted by rtel on September 19, 2014

Are you saying the fix I already posted did not fix your problem?

Regards.


Implementing tickless idle on Cortex M4.

Posted by jpt106 on September 22, 2014

Sorry I was not clear. Yes the fix posted made the problem happen less often but I was to reproduce the issue still. I am using tickless configuration in my system. There is a task that runs periodically every 8 ticks (62.5 milliseconds). There is CLI interface through a UART. From the CLI I run different commands that cause other tasks to run processing the request for the CLI command. For testing, I have a script that continuously runs different CLI commands. This causes the other tasks to randomly run and generate other interrupts beside the AST interrupt.

Without your fix posted, running the test script my system fails the assert "configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime )" within a minute. With the posted fix, it takes about 10 minutes running the test script.

Originally I found when the system was failing it was calculating a negative alarm value and programming that negative value into the alarm for the AST. The code I posted tried to prevent the negative alarm value issue. After further investigation, I found the root cause of the issue. The AST clear counter on alarm does not clear the counter on the same AST clock cycle as when the counter equals the alarm. The counter gets reset to one on the next clock cycle when the counter exceeds the alarm value. This caused a race condition where during the time of single AST clock cycle (61 microseconds) the counter equals the alarm value. I saw during debugging in the code "if( ulTickFlag != pdFALSE)" where astreadcounter_value( AST ) returned the same value as the alarm value. This caused a negative value to be calculated for ulAlarmValue.

The newly posted code fixes the root cause directly by detected when the race condition happens and taking action.

Attachments


Implementing tickless idle on Cortex M4.

Posted by hilt0n on September 29, 2014

Thanks Joe for your big work, I didn't yet tested completely your solution but that sound promising. On my side, I have simply took the tickless implementation of the STM32L, it's very similar to the SAM4L but there is 3 missing asynchronous counter clear on the SAM4L. I actually wonder why this porting difference..

Attachments


[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]




Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

FreeRTOS kernel V10 is available for immediate download. Now MIT licensed.


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

OpenRTOS and SafeRTOS