CPUGetSysTimerCountElapsed Routine

OEMIdle calls CPUGetSysTimerCountElapsed to determine how much time has elapsed since the last timer tick. In some situations, CPUGetSysTimerCountElapsed is called with interrupts off, making it possible for a timer interrupt to be pending when it is called. This routine must compensate for the interrupt service routine (ISR), which will also update CurMSec when its interrupt is unmasked. Systems that use a PIT should note the return value from CPUClearSysTimerIRQ as it indicates whether the PIT interrupt was serviced in time to avoid losing timer information.

This routine is prototyped as shown in the following code example.

DWORD CPUGetSysTimerCountElapsed(
   DWORD dwTimerCountdownMSec,
   volatile DWORD *pCurMSec,
   DWORD *pPartialCurMSec,
   volatile ULARGE_INTEGER *pCurTicks
)

The following table shows the parameters used in this routine:

Parameter Description
dwTimerCountdownMSec The number of milliseconds that the system expected to sleep before the next timer interrupt.
pCurMSec Points to a millisecond counter, often but not always the global variable CurMSec.
pPartialCurMSec Some number of timer counts, not timer ticks, not accounted for in the current value of *pCurMSec.
pCurTicks Pointer to a 64-bit tick counter.

CPUGetSysTimerCountElapsed determines the number of counter increments or decrements that have elapsed and adds it to *pPartialCurMSec. It then calculates the number of whole milliseconds the sum represents and updates *pCurMSec with that value. Finally, it updates *pPartialCurMSec with any counts left over from this calculation.

CPUGetSysTimerCountElapsed treats *pCurTicks as a running number of timer counts, reflecting the number of times the counter has been incremented or decremented, not the number of interrupts it has generated or the number of milliseconds elapsed.

The following code example shows an implementation of CPUGetSysTimerCountElapsed for an ARM platform, using a PIT.

DWORD CPUGetSysTimerCountElapsed(
   DWORD dwTimerCountdownMSec,
   volatile DWORD *pCurMSec,
   DWORD *pPartialCurMSec,
   volatile ULARGE_INTEGER *pCurTicks
)
{
    TimerStruct_t  *pTimer = gSysTimers[OS_TIMER].pt;
    DWORD dwTick = dwTimerCountdownMSec * OEMCount1ms;
    DWORD dwCount;

    // If timer IRQ is pending, a full rescheduled period elapsed.
    if (CPUClearSysTimerIRQ( )) {
       *pCurMSec += dwTimerCountdownMSec;
        pCurTicks->QuadPart += dwTick;
        return dwTimerCountdownMSec;
    }
    // If no timer IRQ is pending, calculate how much time has elapsed.
    dwCount = pTimer->TimerValue;
    if (dwCount > dwTick) {
       // This is an error case. Recover gracefully.
       dwCount = dwTick;
    } 
    else {
       dwCount = dwTick - dwCount;
    }
    pCurTicks->QuadPart += dwCount;
    dwCount += *pPartialCurMSec;
    *pPartialCurMSec = dwCount % OEMCount1ms;
    *pCurMSec += (dwCount /= OEMCount1ms);

    return dwCount;
}

The following code example shows an implementation for the DDB5476 platform using a free-running timer with value and compare registers.

DWORD CPUGetSysTimerCountElapsed(
   DWORD dwTimerCountdownMSec,
   volatile DWORD *pCurMSec,
   DWORD *pPartialCurMSec,
   volatile ULARGE_INTEGER *pCurTicks
)
{
  DWORD dwTick = dwTimerCountdownMSec * OEMCount1ms;
  DWORD dwCount = R4000Compare( ) - R4000Count( );

  // Note: If dwCount is negative, the counter went past the compare 
  // point. The math still works because it accounts 
  // for the dwTick time plus the time past the compare point.
  dwCount = dwTick - dwCount;
  pCurTicks->QuadPart += dwCount;
  dwCount += *pPartialCurMSec;
  *pPartialCurMSec = dwCount % OEMCount1ms;
  dwCount /= OEMCount1ms;
  *pCurMSec += dwCount;

  return dwCount;
}

See Also

Boilerplate Interface Routines

Last updated on Wednesday, April 13, 2005

© 2005 Microsoft Corporation. All rights reserved.