This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Date/Time reset after 49.7 days

Other Parts Discussed in Thread: DM3730, OMAP3530

All,

We have an interesting problem with one of our machines (It is running Windows CE 6.0, and DM3730 processor).  After 49.7 days of work, the system time/date was reset, but not to the initial one, just to time and date of the previous reset of the machine. Basically it was reverted by 49.7 days.

Since this is the time after which GetTickCount rolls over, we think that implementation of GetLocalTime or GetSystemTime is based on GetTickCount. Basically, OS reads the time only during startup, and after that it only uses GetTickCount to determine the current date and time based on the initial value. This is my guess.  I couldn't find the code for GetSystemTime nor GetLocalTime functions, so I'm not sure for 100%.

Please let me know it you've seen something similar, or if you  have a solution for this problem.

In our case resetting the machine helped - the date and time was correct after that, and customer didn't have to do anything additional at this time, but anyway,  the machine is designed to run continuously, and we don't want to reset it every 49.7 days.

Please, let me know if you have any ideas on how to solve it, and if this is a problem of the OS or BSP.

Thank you!

Zack

 

  • OK, I found the problem.  This is exactly what I've susspected.  In file "C:\WINCE600\PLATFORM\COMMON\SRC\SOC\COMMON_TI_V1\TPS659XX\OALRTC\rtc.c" there is a function "OEMGetRealTime".  In that function variable "delta" is 32 bit, and this is causing problems. I've created a wrapper function for "OEMGetTickCount", and now it's working fine.  This wrapper function returns a 64 bit value, and also detects the overrun situation of the "OEMGetTickCount" function.

    Here is the code (new/changed lines in bold):

    // ZZ: Added wrapper for OEMGetTickCount handling values larger than 32 bits
    DWORD  g_dwOldTickCount = 0;
    DWORD  g_dwNumberOfOverruns = 0;

    ULONGLONG CA_OEMGetTickCount()
    {
     DWORD dwTickCount = OEMGetTickCount();
     ULONGLONG uReturnValue = 0;

     uReturnValue = (ULONGLONG)dwTickCount;

     // Overrun situation
     if(dwTickCount < g_dwOldTickCount)
     {
      g_dwNumberOfOverruns++;
      uReturnValue += ((ULONGLONG)g_dwNumberOfOverruns * (ULONGLONG)0xFFFFFFFF);
     }
     else if(g_dwNumberOfOverruns)
     {
      uReturnValue += ((ULONGLONG)g_dwNumberOfOverruns * (ULONGLONG)0xFFFFFFFF);
     }

     g_dwOldTickCount = dwTickCount;

     return uReturnValue;
    }


    //------------------------------------------------------------------------------
    //
    //  Function:  OEMGetRealTime
    //
    //  This function is called by the kernel to retrieve the time from
    //  the real-time clock.
    //
    BOOL
    OEMGetRealTime(
        SYSTEMTIME *pSystemTime
        )
    {
        ULONGLONG   delta;
        ULONGLONG   time;

        OALMSG(OAL_TIMER && OAL_FUNC, (L"+OEMGetRealTime()\r\n"));

        if (!s_rtc.initialized)
            {
            // Return default time if RTC isn't initialized
            pSystemTime->wYear   = RTC_BASE_YEAR_MIN;
            pSystemTime->wMonth  = 1;
            pSystemTime->wDay    = 1;
            pSystemTime->wHour   = 0;
            pSystemTime->wMinute = 0;
            pSystemTime->wSecond = 0;
            pSystemTime->wDayOfWeek    = 0;
            pSystemTime->wMilliseconds = 0;
            }
        else
            {
            EnterCriticalSection(&s_rtc.cs);
            if (g_ResumeRTC)
          {
                // suspend/resume occured, sync RTC
                OALIoCtlHalRtcTime(0, NULL, 0, NULL, 0, NULL);
                g_ResumeRTC = FALSE;
          }
            delta = CA_OEMGetTickCount() - (ULONGLONG)s_rtc.baseTickCount;
    //        delta = OEMGetTickCount() - s_rtc.baseTickCount;
            time = s_rtc.baseFiletime + s_rtc.baseOffset + delta * 10000;
            NKFileTimeToSystemTime((FILETIME*)&time, pSystemTime);
            pSystemTime->wMilliseconds = 0;
            LeaveCriticalSection(&s_rtc.cs);
            }

        OALMSG(OAL_TIMER && OAL_FUNC, (L"-OEMGetRealTime() = %s\r\n", SystemTimeToString(pSystemTime)));

        return TRUE;
    }

    With this solution in place, the overrun will happen not after 49.7 days, just after 584 million years ;)

    Best regards,

    Zack

  • Zack,

    I really appreciate you taking the time to post the solution, thanks very much!

    Looking at OEMSetRealTime I suspect there's a similar issue there.

    Cheers,
    Carsten

  • I found this thread very interesting. A couple of comments ...I think another approach is to check the delta and if it is too big or rolled over call OALIoCtlHalRtcTime which resysncs system time with the RTC ...might be some coner case issues though. Also, if you have included the TPS659XX_RTC driver (not the OALRTC code) I believe there is a registry setting "SyncPeriod" that is defaulted to one hour. This should sync the RTC time to system time by calling OALIoCtlHalRtcTime also. It looks like OALIoCtlHalRtcTime will reset baseTickCount to OEMGetTickCount() therefor reseting the delta.

  • Carsten and David,

    Thank you for your posts.

    I've been investigating this issue a little farther too.  Yes, it looks like OEMSetRealTime has similar problem too, and also synchronization would be a better solution than keeping the time difference and adding it each time we get the time.  So, I've been trying to find a solution to synchronize with the real time in case of  rolling over, but... recently I found a different method, and it's very simple.

    Looks like the OS (not BSP) versions of these functions handle the 32 bit time rollover correctly, and we can activate it by adding the following line to the registry:

    [HKEY_LOCAL_MACHINE\Platform]
     "SoftRTC"=dword:00000001

    I've been testing it for some time and so far it's working fine...

    With this I could remove the code related to my previous solution.

    Please let me know if it works also for you.

    Best regards,

    Zack

  • i do not think it is even an issue if you include the TPS659XX_RTC driver ... which looks to be available in all the latest releases of the BSP.

    It will periodically sync the system time to the RTC anyway (by default every hour) and the delta will be reset so it will never grow that much. At least that is how I read it, let me know if you or anyone else sees if differently.

  • David,

    Thank you for your post.

    Do you mean the driver located under PLATFORM\COMMON\SRC\SOC\COMMON_TI_V1\TPS659XX?  This is the driver I've had problems with, and I had to modify the following file: PLATFORM\COMMON\SRC\SOC\COMMON_TI_V1\TPS659XX\OALRTC\rtc.c (or set the SoftRTS registry setting).  But maybe I have an old BSP code. I have to check that.

    Best regards,

    Zack

     

    For the

  • No that is the OAL RTC code and is not a driver. I am talking about the RTC "driver" which is located in the BSP's PLATFORM directory and not in the common code. Look, for example,  under:

    C:\<WINCEROOT>\PLATFORM\EVM_OMAP3530\SRC\DRIVERS\TPS659XX_RTC

    This is a driver that sets up an interrupt ~every hour (sync period) that will resync the system time to the RTC time.

     

  • Just a quick follow-up.

    Testing on our OMAP platform showed the same behaviour as described by Zack.

    I chose to implement a fix similar to Zack's initial suggestion, the main differences being OEMSetRealTime also works now and adding a critical section to protect the 'overrun count' and 'previous tick count'.

    So, for the benefit of future readers:

    • Add the code included below to rtc.c
    • Zero out the s_tick struct and initialise s_tick.cs in OALIoCtlHalInitRTC
    • Change the following variables to ULONGLONG: s_rtc.baseTickCount, local var 'delta' in OEMGetRealTime, local var 'tickDelta' in OEMSetRealTime
    • Replace all calls to OEMGetTickCount with OEMGetTickCount64

    static struct {

        CRITICAL_SECTION cs;

        ULONGLONG numberOfOverruns;
        DWORD previousTickCount32;

    } s_tick = { 0 };

    static ULONGLONG OEMGetTickCount64()
    {
        DWORD tickCount32;
        ULONGLONG tickCount64;

        EnterCriticalSection(&s_tick.cs);

        tickCount32 = OEMGetTickCount();

        // Check for 32-bit wrap, occurs every 49.7 days.
        if(tickCount32 < s_tick.previousTickCount32)
        {
            s_tick.numberOfOverruns++;
        }

        s_tick.previousTickCount32 = tickCount32;

        tickCount64 = tickCount32 + (s_tick.numberOfOverruns * 0x100000000ULL);

        LeaveCriticalSection(&s_tick.cs);

        return tickCount64;
    }

  • Or just include the driver and don’t modify anything (assuming you are using the 659xx companion with RTC). I would prefer that method as the RTC has a much better crystal controlled time base and would drift far less than a system tic only based system time, especially over a 50 day period. If I had a system that does not reset often I would certainly use the RTC resync feature of the RTC driver. The 1ms tic used for system time as derived from the system oscillator is not precisely 1ms and will surely gain (or loose) time over such a long period.