Hi all,
I use FreeRTOS v 10.0.1. and I have some issues when xTickCount wraps around.
It seemd that pxCallbackFunction from xTimer stopped to be called on timer expiration.
It happens sometimes when xTickCount wraps around. I didn’t manage to find exact conditions at which it would reproduce every time.
I’ve been tracking down this problem and when I’ve turned on RTOS assertions I got cought by assert in tasks.c: 2611.
It seems that it is
configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ) from taskSWITCH_DELAYED_LISTS() which is called in xTaskIncrementTick().
I’ve looked for some fixes for such issue in newer freeRTOS versions but I couldn’t find anything matching in a changlog (https://www.freertos.org/History.txt).
Maybe someone will know if it is known issue, or if any fix for that was applied since 10.0.1 (maybe I’ve overlooked something).
Yes, I use tickless idle.
I have configUSE_TICKLESS_IDLE set to 1 to use built in implementation of tickless idle for nrf52 port (delivered by Nordic’s nrf5 sdk). It looks like:
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{
/*
* Implementation note:
*
* To help debugging the option configUSE_TICKLESS_IDLE_SIMPLE_DEBUG was presented.
* This option would make sure that even if program execution was stopped inside
* this function no more than expected number of ticks would be skipped.
*
* Normally RTC works all the time even if firmware execution was stopped
* and that may lead to skipping too much of ticks.
*/
TickType_t enterTime;
/* Make sure the SysTick reload value does not overflow the counter. */
if ( xExpectedIdleTime > portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
xExpectedIdleTime = portNRF_RTC_MAXTICKS - configEXPECTED_IDLE_TIME_BEFORE_SLEEP;
}
/* Block all the interrupts globally */
#ifdef SOFTDEVICE_PRESENT
do{
uint8_t dummy = 0;
uint32_t err_code = sd_nvic_critical_region_enter(&dummy);
APP_ERROR_CHECK(err_code);
}while (0);
#else
__disable_irq();
#endif
enterTime = nrf_rtc_counter_get(portNRF_RTC_REG);
if ( eTaskConfirmSleepModeStatus() != eAbortSleep )
{
TickType_t xModifiableIdleTime;
TickType_t wakeupTime = (enterTime + xExpectedIdleTime) & portNRF_RTC_MAXTICKS;
/* Stop tick events */
nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
/* Configure CTC interrupt */
nrf_rtc_cc_set(portNRF_RTC_REG, 0, wakeupTime);
nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0);
nrf_rtc_int_enable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK);
__DSB();
/*
* Clear FPU exceptions.
* Without this step, the FPU interrupt is marked as pending,
* preventing system from sleeping. Exceptions cleared:
* - IOC - Invalid Operation cumulative exception bit.
* - DZC - Division by Zero cumulative exception bit.
* - OFC - Overflow cumulative exception bit.
* - UFC - Underflow cumulative exception bit.
* - IXC - Inexact cumulative exception bit.
* - IDC - Input Denormal cumulative exception bit.
*/
uint32_t fpscr;
fpscr = __get_FPSCR();
__set_FPSCR(fpscr & ~0x9Fu);
__DMB();
NVIC_ClearPendingIRQ(FPU_IRQn);
/* 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 )
{
#ifdef SOFTDEVICE_PRESENT
if (nrf_sdh_is_enabled())
{
uint32_t err_code = sd_app_evt_wait();
APP_ERROR_CHECK(err_code);
}
else
#endif
{
/* No SD - we would just block interrupts globally.
* BASEPRI cannot be used for that because it would prevent WFE from wake up.
*/
do{
__WFE();
} while (0 == (NVIC->ISPR[0] | NVIC->ISPR[1]));
}
}
configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
nrf_rtc_int_disable(portNRF_RTC_REG, NRF_RTC_INT_COMPARE0_MASK);
nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_COMPARE_0);
/* Correct the system ticks */
{
TickType_t diff;
TickType_t exitTime;
nrf_rtc_event_clear(portNRF_RTC_REG, NRF_RTC_EVENT_TICK);
nrf_rtc_int_enable (portNRF_RTC_REG, NRF_RTC_INT_TICK_MASK);
exitTime = nrf_rtc_counter_get(portNRF_RTC_REG);
diff = (exitTime - enterTime) & portNRF_RTC_MAXTICKS;
/* It is important that we clear pending here so that our corrections are latest and in sync with tick_interrupt handler */
NVIC_ClearPendingIRQ(portNRF_RTC_IRQn);
if ((configUSE_TICKLESS_IDLE_SIMPLE_DEBUG) && (diff > xExpectedIdleTime))
{
diff = xExpectedIdleTime;
}
if (diff > 0)
{
vTaskStepTick(diff);
}
}
}
#ifdef SOFTDEVICE_PRESENT
uint32_t err_code = sd_nvic_critical_region_exit(0);
APP_ERROR_CHECK(err_code);
#else
__enable_irq();
#endif
There does appear to be a small error in the Nordic code, and I think it would cause the assertion failure you are experiencing under specific circumstances. Function vTaskStepTick() doesn’t process ticks normally, so it should approach but never reachxNextTaskUnblockTime.
You could try a small fix like this:
Current Code:
if (diff > 0)
{
vTaskStepTick(diff);
}
Proposed Code:
if (diff > 0)
{
vTaskStepTick(diff - 1);
NVIC_SetPendingIRQ(portNRF_RTC_IRQn);
}
But I don’t know anything about Nordic’s use of the RTC or its ISR, so for all I know this code could totally blow up the system tick.