Win32 Tick precision improvement?

Hi
I’ve found a possibly easy solution to improve the tick precision under Win32.

Function added to port.c

static TickType_t GetTimeToSleep(TickType_t xMinimumWindowsBlockTime)
{
static BOOL b_firstTime = TRUE;
static DWORD lastSysTimeMs;

static unsigned int counter = 0;
static TickType_t timeToSleep;
TickType_t nominalTimeToSleep = max(portTICK_PERIOD_MS, xMinimumWindowsBlockTime);
static TickType_t lastfreeRTOSTick;

if (b_firstTime)
{
	b_firstTime = FALSE;
	lastfreeRTOSTick = xTaskGetTickCountFromISR();
	lastSysTimeMs = timeGetTime();
	timeToSleep = nominalTimeToSleep;
}

counter++;
if ((counter & 0xf) == 0) // avoid too much noise on the corrections
{		
	TickType_t currentFreeRTOSTick = xTaskGetTickCountFromISR();
	TickType_t elapsedFreeRTOSMs = (currentFreeRTOSTick - lastfreeRTOSTick) * portTICK_PERIOD_MS;
	//lastfreeRTOSTick = currentFreeRTOSTick;

	DWORD currentSystemTimeMs = timeGetTime();
	DWORD elapsedSystemMs = currentSystemTimeMs - lastSysTimeMs;
	//lastSysTimeMs = currentSystemTimeMs;

	if (elapsedFreeRTOSMs < elapsedSystemMs)
	{
		// freertos tick slower then system tick
		if (timeToSleep != 0)
		{
			timeToSleep--;
		}
	}
	else if(elapsedFreeRTOSMs > elapsedSystemMs)
	{
        // freertos tick faster then system tick
		timeToSleep++;
	}
	else
	{
	}
}
return timeToSleep;

}

and instead of
if( portTICK_PERIOD_MS < xMinimumWindowsBlockTime )
{
Sleep( xMinimumWindowsBlockTime );
}
else
{
Sleep( portTICK_PERIOD_MS );
}

I just call
Sleep(GetTimeToSleep(xMinimumWindowsBlockTime));

I know the solution is quite naive but the first tests are promising.
I simulate a watch in our application basing only on the FreeRTOS tick and after 10 minutes the time difference between a stopwatch and my application watch is almost 0 seconds, which is a HUGE improvement compared to the original port.c

My FreeRTOS tick is configured for 1ms and xMinimumWindowsBlockTime is also reported to be 1 ms.

Any suggestions are welcome.

Thanks for your valuable input. We actually have a pull request that improves the Windows tick count by using a block that is relative to the time maintained by Windows rather than the time maintained by the kernel which is running under Windows. Strangely I can’t find it right now otherwise I would link to it. The only reason we have not merged it yet is because it will require the timings in our tests to be updated too.

Yes the PR was https://github.com/FreeRTOS/FreeRTOS/pull/5

The failures in the tests pointed out all kinds of side-effects with adjusting the time, the most serious of which was that because of the simulator’s high CPU usage we ended up on some machines spending almost 100% of the time time context switching and not getting any CPU cycles out of the simulator.

Of course on the other hand if we do not fix this the simulator fails to behave as needed when interactions with other devices requires timing, e.g. timeouts or sending keep-alives.

Lets consider all of these inputs and see how we can get the best of both worlds.

If I look at this PR it dynamically adjusts essentially a correction factor for the timer, but the flaw with this is that the time it takes is not due to a ratiometric difference, it is actually caused by the scheduler in Windows not scheduling this task for the same amount of CPU time. This can cause wild swings, which is why you needed to add the counter and the basic filter to slow down the adjustments.

I think this approach is clever, it is low cost and it will definitely improve the timing accuracy, the approach in PR# 5 which uses a Windows timer also works. So the question is which one is going to serve us best here?

Lets discuss it a bit, I will ask Lee to also join in.

Thanks @davidefer for your valuable suggestion and thanks @cobusve for adding me to this interesting discussion.

The root cause of the incorrect ticks in Windows Simulator is that the thread calling sleep is not guaranteed to be sleeping for the exact time provided as argument to sleep. Quoting from Windows sleep documentation,

After the sleep interval has passed, the thread is ready to run. If you specify 0 milliseconds, the thread will relinquish the remainder of its time slice but remain ready. Note that a ready thread is not guaranteed to run immediately. Consequently, the thread may not run until some time after the sleep interval elapses.

PR #5 addresses that issue by avoiding the call to sleep and waiting for a timer to expire. However, we have found the issues that @cobusve already mentioned with the fix. We will need to fix those before this PR can be merged.

The approach proposed by @davidefer is a clever trick to fix the issue. The drawback I could think of is the same as what was already mentioned. It may cause a swing in ticks. However, this could already be happening without this fix as well.

I think that PR #5 has a benefit about having minimal to no swings for ticks. However, the issues introduced by it need to be addressed before it can be merged.

Hi thanx for the replies.

Unfortunately it seems that my modification causes some deadlocks. Once out of ten my application gets stuck during the startup procedure, most probably the scheduler gets locked.
If I disable my modification, everything is fine.

I guess the Sleep(0) could be a reason for this but I’m not sure.

Besides that, I’ve introduced some watermarks for the timeToSleep value and I don’t see big changes in that (min=0, max =2). The cpu usage is quite low too, no problems in this area.

If you have some suggestions regarding the deadlock they will be very welcome. I have to investigate further. It is very difficult to understand exactly what is going on.

The variable uxSchedulerSuspended gets stuck to 1.
I suspect the port is not prepared for a Sleep(0). I’ve tried to synchronize the prvSimulatedPeripheralTimer with prvProcessTickInterrupt but no success.
Perhaps the port is working only when the sleep actually gives time for the context switches and the related tasks to be executed. If a tick increment comes too early then something goes wrong.

Hi
I don’t know whether you have done steps forward concerning the tick precision under Win32.
Here I have another proposal which seems working on my PC:

#define portSIMULATED_TIMER_THREAD_PRIORITY		 THREAD_PRIORITY_ABOVE_NORMAL

/**
\brief         Sleep time adjustment
\param[in]     xMinimumWindowsBlockTime: reported by timeGetDevCaps
\return        the time to sleep in ms
Adjust the sleep time depending on the difference between the system time and the FreeRTOS tick
*/
static TickType_t prvGetTimeToSleep(TickType_t xMinimumWindowsBlockTime)
{
    static BOOL b_firstTime = TRUE;
    static DWORD lastSysTimeMs;
    static TickType_t lastfreeRTOSTick;

    TickType_t nominalTimeToSleep = max(portTICK_PERIOD_MS, xMinimumWindowsBlockTime);
    TickType_t timeToSleep = (TickType_t)1;

    if (b_firstTime)
    {
        b_firstTime = FALSE;
        lastfreeRTOSTick = xTaskGetTickCountFromISR();  // and never sampled again (24 days @ 1ms tick until overflow)
        lastSysTimeMs = timeGetTime();                  // and never sampled again (24 days until overflow)
        timeToSleep = nominalTimeToSleep;
    }
    else
    {
        TickType_t currentFreeRTOSTick = xTaskGetTickCountFromISR();
        LONG elapsedFreeRTOSMs = (currentFreeRTOSTick - lastfreeRTOSTick) * portTICK_PERIOD_MS;

        DWORD currentSystemTimeMs = timeGetTime();
        LONG elapsedSystemTimeMs = currentSystemTimeMs - lastSysTimeMs;

        LONG timeDiff_FRT_Win = elapsedFreeRTOSMs - elapsedSystemTimeMs;

        if (timeDiff_FRT_Win > 10)
        {
            // freertos faster than windows
            timeToSleep = nominalTimeToSleep + 1;
        }
        else if ((timeDiff_FRT_Win <= 10) && (timeDiff_FRT_Win >= -10))
        {
            // ok to sleep nominal
            timeToSleep = nominalTimeToSleep;
        }
        else
        {
            // windows faster than freertos 
            timeToSleep = 0;
        }
        static LONG highWM = 0L;
        static LONG lowWM = 0x7FFFFFFFL;
        // debug infos
        highWM = max(timeDiff_FRT_Win, highWM);
        lowWM = min(timeDiff_FRT_Win, lowWM);
    }

    return timeToSleep;
}

    /* Wait until the timer expires and we can access the simulated interrupt
    variables.  *NOTE* this is not a 'real time' way of generating tick
    events as the next wake time should be relative to the previous wake
    time, not the time that Sleep() is called.  It is done this way to
    prevent overruns in this very non real time simulated/emulated
    environment. */

    TickType_t toSleep = prvGetTimeToSleep(xMinimumWindowsBlockTime);
    Sleep(toSleep);

I had to change the priority of the timer task because otherwise the sleep(0) wouldn’t actually release control, thus not allowing the FreeRTOS tick processing.

After this modification I actually ask myself why one should set the Win32 threads priorities at all. If the tick is too slow or too fast, my function would correct it.

After some preliminary tests it seems that the port works.
Please let me know your comments.

I attach the whole port, modifications are marked with #PORTCHANGE
port.c (31.5 KB) portmacro.h (6.4 KB)