Important:
This is retired content. This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
A version of this page is also available for
4/8/2010

You can minimize the impact of accessing the real-time clock (RTC) in any of the following ways:

  • Replace the hardware RTC with a software RTC.

  • Eliminate calls to the RTC.

  • Access the clock at optimal times.

In standard x86 bus architecture, where the complementary metal oxide semiconductor (CMOS) system clock houses the RTC and the system clock resides on the bus, the RTC accesses the bus several times. In doing so, the bus can be locked for several microseconds, blocking it from other activities. This translates into higher latencies for interrup service routines (ISRs) and longer times to fully service an interrupt request (IRQ). To avoid this, consider replacing the hardware RTC with a software RTC. On a CEPC hardware platform, you must also eliminate the calls to the RTC when GetSystemTimeis called, and access the clock at optimal times to ensure that a large drift is not introduced.

The following code example shows how to replace the hardware RTC with a software RTC:

Copy Code
#include <windows.h>
#include <nkintr.h>
#include "pc.h"
#include "timer.h"
// NOTE: A problem has been found with some chipsets such that
setting the time to 23:59:59 on the 29th or 30th day of a month
that has less than 31 days causes the clock to roll over
incorrectly. Uncomment the following line to fix this problem.
However, be aware that the fix consists of responding to calls that
set the time to HH:MM:59 by instead setting the time to HH:MM:58.
#define HARDWARE_TIME_SET_PROBLEM 1 __int64 RealTimeBias;
unsigned long AlarmTime[2];
SYSTEMTIME RTC_AlarmTime;
CRITICAL_SECTION RTC_critsect;
BOOL fRTCInit;
unsigned long volatile CurMSecHigh;
// RTC without CMOS Global Variables
BOOL rtInited = FALSE;
FILETIME rtStartFileTime;
FILETIME rtDeltaTime;
FILETIME rtCurFileTime;
FILETIME rtLastFileTime;
DWORD rtLastCurMSec;
extern DWORD CurMSec;
// Prototypes for RTC without CMOS
BOOL RT_Init_RealTime ( void );
BOOL RT_Bare_GetRealTime (LPSYSTEMTIME lpst);
BOOL RT_Bare_SetRealTime (LPSYSTEMTIME lpst);
void add64_64_64 (const FILETIME *lpnum1, 
						LPFILETIME lpnum2, 
						LPFILETIME lpres);
void CMOS_Write( BYTE offset, BYTE value )
{
   BYTE cAddr;
   // Remember, only the low-order 5 bits in the address register
are changed.
   cAddr = _inp( CMOS_ADDR );
   _outp( CMOS_ADDR, (cAddr & RTC_ADDR_MASK) | offset );
   _outp( CMOS_DATA, value );
   // NOTE : If bytes 16-45 are ever updated, the checksum should
be recalculated and stored. However, because none of those bytes
are currently used, it will not be addressed here.
   DEBUGCHK(offset < 16);
}
BYTE CMOS_Read( BYTE offset )
   {
	BYTE cAddr, cResult;
	// Remember, only the low-order 5 bits in the address
register are changed.
	cAddr = _inp( CMOS_ADDR );
	_outp( CMOS_ADDR, (cAddr & RTC_ADDR_MASK) | offset ); 
	cResult = _inp( CMOS_DATA );
	return (cResult);
   }
BOOL IsTimeEqual(LPSYSTEMTIME lpst1, LPSYSTEMTIME lpst2)
   {
	if (lpst1->wYear != lpst2->wYear)
		 return(FALSE);
	if (lpst1->wMonth != lpst2->wMonth)
		 return(FALSE);
	if (lpst1->wDayOfWeek != lpst2->wDayOfWeek)
		 return(FALSE);
	if (lpst1->wDay != lpst2->wDay)
		 return(FALSE);
	if (lpst1->wHour != lpst2->wHour)
		 return(FALSE);
	if (lpst1->wMinute != lpst2->wMinute)
		 return(FALSE);
	if (lpst1->wSecond != lpst2->wSecond)
		 return(FALSE);
	return (TRUE);
   }
   // NOTE : The RTC routines are not thread safe. But the ether
debug functions do not want me to change interrupts, and so on. So
the Bare_ functions is provided for callers who run with interrupts
off, and so on. Then the real functions call into the bare
functions after turning off interrupts.
BOOL Bare_GetRealTime(LPSYSTEMTIME lpst)
   {
	SYSTEMTIME st;
	LPSYSTEMTIME lpst1 = &st, lpst2 = lpst, lptmp;
	lpst1->wSecond = 61; // initialize to an invalid value 
	lpst2->wSecond = 62; // initialize to an invalid value 
	do
		 {
			// exchange lpst1 and lpst2
			lptmp = lpst1;
			lpst1 = lpst2;
			lpst2 = lptmp;
			// Wait until not updating.
			while (CMOS_Read(RTC_STATUS_A) & RTC_SRA_UIP);
			// Read all values.
			lpst1->wYear = CMOS_Read(RTC_YEAR);
			lpst1->wMonth = CMOS_Read(RTC_MONTH); 
			lpst1->wDayOfWeek = CMOS_Read(RTC_DO_WEEK);
			lpst1->wDay = CMOS_Read(RTC_DO_MONTH);
			lpst1->wHour = CMOS_Read(RTC_HOUR); 
			lpst1->wMinute = CMOS_Read(RTC_MINUTE); 
			lpst1->wSecond = CMOS_Read(RTC_SECOND); 
		 }
	while (!IsTimeEqual (lpst1, lpst2));
		 lpst->wMilliseconds = 0; // Not sure how you would get
this
	if (!(CMOS_Read (RTC_STATUS_B) & RTC_SRB_DM)) {
		 // Values returned in BCD.
		 lpst->wSecond = DECODE_BCD(lpst->wSecond);
		 lpst->wMinute = DECODE_BCD(lpst->wMinute);
		 lpst->wHour   = DECODE_BCD(lpst->wHour);
		 lpst->wDay	= DECODE_BCD(lpst->wDay);
		 lpst->wDayOfWeek = DECODE_BCD(lpst->wDayOfWeek);
		 lpst->wMonth  = DECODE_BCD(lpst->wMonth);
		 lpst->wYear   = DECODE_BCD(lpst->wYear);
}
   // OK - PC RTC returns 1998 as 98.
   lpst->wYear += (lpst->wYear > 70)? 1900 : 2000;
   #ifdef NOTDEF NKDbgPrintfW(TEXT("\r\nReal Time %d, %d, %d, %d,
%d, %d, %d, %d\r\n"),
				 lpst->wYear,
				 lpst->wMonth,
				 lpst->wDayOfWeek,
				 lpst->wDay,
				 lpst->wHour,
				 lpst->wMinute,
				 lpst->wSecond,
				 lpst->wMilliseconds );
   #endif
	return (TRUE);
   }
   BOOL Bare_SetRealTime(LPSYSTEMTIME lpst)
	{
		 BYTE cStatusRegA, cStatusRegB, Year;
		 #ifdef NOTDEF NKDbgPrintfW(TEXT("\r\nSet Real Time %d, %d,
%d, %d, %d, %d, %d, %d\r\n"),
						 pst->wYear,
						 lpst->wMonth,
						 lpst->wDayOfWeek,
						 lpst->wDay,
						 lpst->wHour,
						 lpst->wMinute,
						 lpst->wSecond,
						 lpst->wMilliseconds );
		 #endif
	Year = lpst->wYear % 100;
   #ifdef HARDWARE_TIME_SET_PROBLEM
	if (lpst->wSecond == 59)
		 {
			lpst->wSecond = 58;
		 }
   #endif
   // Read the update in progress bit, wait for it to be clear.
This bit will be set once per second for about 2 us. (Undoc. PC,
page 897)
   do
	{
		 cStatusRegA = CMOS_Read( RTC_STATUS_A);
}
   while ( cStatusRegA & RTC_SRA_UIP );
	// Disable updates while the values are changed
	cStatusRegB = CMOS_Read( RTC_STATUS_B );
	cStatusRegB |= RTC_SRB_UPDT;
	CMOS_Write( RTC_STATUS_B, cStatusRegB );
   if ( !(cStatusRegB & RTC_SRB_DM) )
	{
		 // BCD Mode
		 CMOS_Write( RTC_YEAR, (BYTE)(CREATE_BCD(Year))); 
		 CMOS_Write( RTC_MONTH,
(BYTE)(CREATE_BCD(lpst->wMonth))); 
		 CMOS_Write( RTC_DO_WEEK,
(BYTE)(CREATE_BCD(lpst->wDayOfWeek))); 
		 CMOS_Write( RTC_DO_MONTH,
(BYTE)(CREATE_BCD(lpst->wDay))); 
		 CMOS_Write( RTC_HOUR, 
(BYTE)(CREATE_BCD(lpst->wHour))); 
		 CMOS_Write( RTC_MINUTE,  
(BYTE)(CREATE_BCD(lpst->wMinute))); 
		 CMOS_Write( RTC_SECOND,  
(BYTE)(CREATE_BCD(lpst->wSecond))); 
		 // Not sure how you can do lpst->wMilliseconds;
}
   else
	{
		 // Binary mode
		 CMOS_Write( RTC_YEAR, (UCHAR)Year); 
		 CMOS_Write( RTC_MONTH, (UCHAR)lpst->wMonth); 
		 CMOS_Write( RTC_DO_WEEK, (UCHAR)lpst->wDayOfWeek); 
		 CMOS_Write( RTC_DO_MONTH, (UCHAR)lpst->wDay); 
		 CMOS_Write( RTC_HOUR, (UCHAR)lpst->wHour); 
		 CMOS_Write( RTC_MINUTE, (UCHAR)lpst->wMinute); 
		 CMOS_Write( RTC_SECOND, (UCHAR)lpst->wSecond); 
		 // Not sure how you can do lpst->wMilliseconds;
}
   // Reenable updates
   cStatusRegB &= ~RTC_SRB_UPDT;
   CMOS_Write( RTC_STATUS_B, cStatusRegB );
   return (TRUE);
   }
BOOL Bare_SetAlarmTime(LPSYSTEMTIME lpst)
   {
	BYTE cStatusRegA, cStatusRegB, Year;
	#ifdef NOTDEF
		 NKDbgPrintfW(TEXT("\r\nSet Alarm Time %d, %d, %d, %d, %d,
%d, %d, %d\r\n"),
			 lpst->wYear,
			 lpst->wMonth,
			 lpst->wDayOfWeek,
			 lpst->wDay,
			 lpst->wHour,
			 lpst->wMinute,
			 lpst->wSecond,
			 lpst->wMilliseconds );
	#endif
   // NOTE : Because our alarm only has a 1-day rollover, the full
alarm time needs to be stored and compared on alarm interrupts to
see if it is at the correct day.
   RTC_AlarmTime = *lpst;
   Year = lpst->wYear % 100;
   // Read the update in progress bit, wait for it to be clear.
This bit  will be set once per second for about 2 us. (Undoc. PC,
page 897)
   do
	{
		 cStatusRegA = CMOS_Read( RTC_STATUS_A);
}
   while ( cStatusRegA & RTC_SRA_UIP );
	// Disable updates while the values are changed
	cStatusRegB = CMOS_Read( RTC_STATUS_B );
	cStatusRegB |= RTC_SRB_UPDT;
	CMOS_Write( RTC_STATUS_B, cStatusRegB );
	if ( !(cStatusRegB & RTC_SRB_DM) )
		 {
			// BCD Mode
			CMOS_Write( RTC_ALRM_HOUR,
(BYTE)(CREATE_BCD(lpst->wHour)));
			CMOS_Write( RTC_ALRM_MINUTE,
(BYTE)(CREATE_BCD(lpst->wMinute)));
			CMOS_Write( RTC_ALRM_SECOND,
(BYTE)(CREATE_BCD(lpst->wSecond)));
		 }
	else
		 {
			// Binary mode
			CMOS_Write( RTC_ALRM_HOUR, (UCHAR)lpst->wHour);
			CMOS_Write( RTC_ALRM_MINUTE, (UCHAR)lpst->wMinute);
			CMOS_Write( RTC_ALRM_SECOND, (UCHAR)lpst->wSecond);
		 }
	// Enable alarm interrupt and reenable updates.
	cStatusRegB = (cStatusRegB | RTC_SRB_AI) & ~RTC_SRB_UPDT;
	CMOS_Write( RTC_STATUS_B, cStatusRegB );
	return( TRUE );
   }
BOOL OEMGetRealTime(LPSYSTEMTIME lpst)
   {
	BOOL RetVal;
	if (!fRTCInit)
		 {
			InitializeCriticalSection(&RTC_critsect);
			// RTC without CMOS
			if( !rtInited )RT_Init_RealTime();
			 fRTCInit = TRUE;
		 }
	EnterCriticalSection(&RTC_critsect);
	if( rtInited )
		 {
			//RTC without CMOS
			RetVal = RT_Bare_GetRealTime( lpst );
		 }
	else
		 {
			RetVal = Bare_GetRealTime(lpst);
		 }
		 LeaveCriticalSection(&RTC_critsect);
	return RetVal;
   }
BOOL OEMSetRealTime(LPSYSTEMTIME lpst)
   {
	BOOL RetVal;
	if (!fRTCInit)
		 {
			InitializeCriticalSection(&RTC_critsect);
			if( !rtInited )RT_Init_RealTime();
			 fRTCInit = TRUE;
		 }
	EnterCriticalSection(&RTC_critsect);
	if( rtInited )
		 {
			RetVal = RT_Bare_SetRealTime(lpst);
		 }
	else
		 {
			RetVal = Bare_SetRealTime(lpst);
		 }
	LeaveCriticalSection(&RTC_critsect);
	return RetVal;
   }
BOOL OEMSetAlarmTime(LPSYSTEMTIME lpst)
   {
	BOOL RetVal;
	if (!fRTCInit)
		 {
			InitializeCriticalSection(&RTC_critsect);
			fRTCInit = TRUE;
		 }
	EnterCriticalSection(&RTC_critsect);
	RetVal = Bare_SetAlarmTime(lpst);
	LeaveCriticalSection(&RTC_critsect);
	return RetVal;
   }
// The following function attempts to eliminate the dependency on
the CMOS for the clock times. A startup value is taken from the
CMOS with all later times being returned as the Tick Count plus
this initial value. Roll over is accounted for. If the RT system
has not been initialized, the legacy Bare_GetRealTime is called.
BOOL RT_Bare_GetRealTime(LPSYSTEMTIME lpst)
   {
	DWORD delta;
	// Calculate the current diference
	if( CurMSec >= rtLastCurMSec )
		 {
			delta = CurMSec - rtLastCurMSec;
		 }
	else
		 {
			// The counter had to be wrapped
			delta = CurMSec + (ULONG_MAX - rtLastCurMSec);
		 }
	rtDeltaTime.dwHighDateTime = 0;
	rtDeltaTime.dwLowDateTime = delta * 10000;
	// Add the delta to the last.
	add64_64_64(  &rtLastFileTime , & rtDeltaTime, &
rtCurFileTime );
	// Add the current time difference in & record the
values.
	rtLastFileTime.dwLowDateTime = rtCurFileTime.dwLowDateTime;
	rtLastFileTime.dwHighDateTime = rtCurFileTime.dwHighDateTime;
	rtLastCurMSec = CurMSec;
	// Get the answer in a system-time format.
	if(KFileTimeToSystemTime( &rtCurFileTime, lpst ) )
		 {
			return TRUE;
		 }
	else
		 {
			// Failed and what time is it?
			return FALSE;
		 }
}
   // The following functions Converts the system time to the
rtLastFileTime which stores the last time called. Zero the MS delta
counter by setting the rtLastCurMSec to the CurMSec.
   BOOL RT_Bare_SetRealTime(LPSYSTEMTIME lpst)
	{
		 if( KSystemTimeToFileTime(lpst, &rtLastFileTime ) )
			{
			 rtLastCurMSec = CurMSec;
			 return TRUE;
		}
		 return FALSE;
}
   // The following function initializes the RT area in an attempt
to eliminate the run time dependency of the OS on the CMOS. The X86
CMOS is very slow and can take up to 1ms to get the full CMOS
clock. This function get the start time from the CMOS and
initializes the local variables that the RT functions need.
   BOOL RT_Init_RealTime( void )
	{
		 SYSTEMTIME sysTime;
		 RETAILMSG(1,(TEXT("\r\n\n******Called
RT_Init_GetRealTime\r\n\n")));
		 // Get the curren time and store it.
		 if( !Bare_GetRealTime( &sysTime ))
			{
			 // Are not able to get the time?
			 return FALSE;
		}
		 // Convert the file time structure.
		 if( !KSystemTimeToFileTime( &sysTime,
&rtCurFileTime ) )
			{
			 return FALSE;
		}
	// Store Start and Current Versions of the File Time and
Large Ints.
	rtLastCurMSec = (DWORD)CurMSec;
	rtStartFileTime.dwLowDateTime = rtCurFileTime.dwLowDateTime;
	rtStartFileTime.dwHighDateTime =
rtCurFileTime.dwHighDateTime;
	rtLastFileTime.dwLowDateTime = rtCurFileTime.dwLowDateTime;
	rtLastFileTime.dwHighDateTime = rtCurFileTime.dwHighDateTime;
	rtInited = TRUE;
	return TRUE;
   }
void add64_64_64(const FILETIME *lpnum1, LPFILETIME lpnum2,
LPFILETIME, lpres)
   {
	__int64 num1, num2;
	num1 = (((__int64)lpnum1->dwHighDateTime)<<32)+
(__int64)lpnum1->dwLowDateTime;
	num2 = (((__int64)lpnum2->dwHighDateTime)<<32)+
(__int64)lpnum2->dwLowDateTime;
	num1 += num2;
	lpres->dwHighDateTime = (DWORD)(num1>>32);
	lpres->dwLowDateTime = (DWORD)(num1&0xffffffff);
   }
}

See Also