timer.c

来自「该BSP是基于PXA270+WINCE的BSP」· C语言 代码 · 共 329 行

C
329
字号
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
//  File:  timer.c
//
//  Intel Mainstone board initialization code.
//

#include <windows.h>
#include <nkintr.h>
#include <bulverde.h>
#include <oal.h>

//------------------------------------------------------------------------------
// Defines 
 
//------------------------------------------------------------------------------
// External Variables 

//------------------------------------------------------------------------------
// Global Variables 
 
//------------------------------------------------------------------------------
// Local Variables 
static XLLP_OST_HANDLE_T    g_XllpOSTHandle;
static volatile UINT32      LastTimerMatch;
static UINT32 g_TotalPartialCounts = 0;
static volatile UINT32 g_LastPartialCounts = 0;

//------------------------------------------------------------------------------
// Local Functions 

__inline void RechargeTimer(DWORD reschedTime, BOOL isTimerISR);
VOID OALTimerUpdateRescheduleTime(DWORD time);


BOOL OALTimerInit (UINT32 msecPerSysTick, 
                   UINT32 countsPerMSec, 
                   UINT32 countsMargin)
{
    UINT32 TimerMatch;
    UINT32 TimerCount;

    // Initialize timer state global variable.
    //
    g_oalTimer.countsPerMSec          = countsPerMSec;
    g_oalTimer.msecPerSysTick         = msecPerSysTick;
    g_oalTimer.actualMSecPerSysTick   = msecPerSysTick;
    g_oalTimer.countsMargin           = countsMargin;

    g_oalTimer.countsPerSysTick       = (countsPerMSec * msecPerSysTick);
    g_oalTimer.actualCountsPerSysTick = (countsPerMSec * msecPerSysTick);
    g_oalTimer.curCounts              = 0;
    g_oalTimer.maxPeriodMSec          = (UINT32)0x7FFFFFFF/g_oalTimer.countsPerMSec;

    // Initialize kernel-exported values.
    //
    idleconv     = countsPerMSec;
    curridlehigh = 0;
    curridlehigh = 0;

    // Initialize update reschedule time function pointer
    pOEMUpdateRescheduleTime = OALTimerUpdateRescheduleTime;
    
    // Initialize high resolution timer function pointers
    pQueryPerformanceFrequency = OALTimerQueryPerformanceFrequency;
    pQueryPerformanceCounter   = OALTimerQueryPerformanceCounter;

    // Obtain pointers to OST and INTC registers.
    //
    g_XllpOSTHandle.pOSTRegs  = (XLLP_OST_T *) OALPAtoVA(BULVERDE_BASE_REG_PA_OST, FALSE);
    g_XllpOSTHandle.pINTCRegs = (XLLP_INTC_T *)OALPAtoVA(BULVERDE_BASE_REG_PA_INTC, FALSE);

    // XLLI initializes oier and rtsr to zeroes, so no further
    // initialization needs to be done.  Match timers and
    // alarms are disabled.
    //
    // Current usage of Match registers:
    //  M0 - Scheduler
    //  M1 - Touch Panel
    //  M2 - Profiler

    // Configure and arm the timer interrupt  to interrupt starting at current count + 1 system tick interval.
    //
    TimerCount = g_XllpOSTHandle.pOSTRegs->oscr0;
    TimerMatch = TimerCount + g_oalTimer.countsPerSysTick;
    XllpOstConfigureTimer (&g_XllpOSTHandle, MatchReg0, TimerMatch);

    // set LastTimerMatch to TimerCount
    //
    LastTimerMatch = TimerCount;
    
    return(TRUE);
}


//------------------------------------------------------------------------------
//
//  Function: OALTimerCountsSinceSysTick
//
//  This function return count of hi res ticks since system tick.
//   (How many ticks since the last timer interrupt?)
//

INT32 OALTimerCountsSinceSysTick()
{
    return (g_XllpOSTHandle.pOSTRegs->oscr0 - LastTimerMatch);
}


//------------------------------------------------------------------------------
//
//  Function: OALTimerIntrHandler
//
//  This function implement timer interrupt handler. It is called from common
//  ARM interrupt handler.
//
UINT32 OALTimerIntrHandler()
{
    UINT32 sysIntr = SYSINTR_NOP;
    
    LastTimerMatch = g_XllpOSTHandle.pOSTRegs->osmr0;
    
    // Update the millisecond and high resolution counters
    CurMSec += g_oalTimer.actualMSecPerSysTick;
    g_oalTimer.curCounts += g_oalTimer.actualCountsPerSysTick;

    // g_LastPartialCounts represents fractional milliseconds which will
    // be added to CurMSec once they add up to one MSec.
    g_TotalPartialCounts += g_LastPartialCounts;
    g_LastPartialCounts = 0;

    //Handle Wrap-around
    if ((INT32)(CurMSec - dwReschedTime) >= 0)  
        sysIntr = SYSINTR_RESCHED;
    else {
        if ((g_TotalPartialCounts > g_oalTimer.countsPerMSec) && (dwReschedTime > (CurMSec + 1))) {
            CurMSec++;
            g_TotalPartialCounts -= g_oalTimer.countsPerMSec;
        }
    }
    
    RechargeTimer(dwReschedTime, TRUE);
    
    // Update LEDs.
    //
    OEMWriteDebugLED(0, (CurMSec/1000));

#ifdef OAL_ILTIMING
    if (g_oalILT.active) {
        if (--g_oalILT.counter == 0) {
            sysIntr = SYSINTR_TIMING;
            g_oalILT.counter = g_oalILT.counterSet;
            g_oalILT.isrTime2 = OALTimerCountsSinceSysTick();
        }
    }
#endif

    return (sysIntr);
}

//------------------------------------------------------------------------------
//
//  Function:  OALTimerUpdateRescheduleTime
//
//  This function is called by kernel to set next reschedule time.
//
VOID OALTimerUpdateRescheduleTime(DWORD time)
{
    UINT32 baseMSec;
    INT32 counts;

    // Get current system timer counter
    baseMSec = CurMSec;

    // Return if we are already setup correctly
    if (time == (baseMSec + g_oalTimer.actualMSecPerSysTick)) goto cleanUp;

    // How far we are from next tick
    counts = g_oalTimer.actualCountsPerSysTick - OALTimerCountsSinceSysTick();

    // If timer interrupts occurs, or we are within 1 ms of the scheduled
    // interrupt, just return - timer ISR will take care of it.
    if (baseMSec != CurMSec || counts < (INT32)g_oalTimer.countsPerMSec) {
        goto cleanUp;
    }        

    //Note: We are going to assume that RechargeTimer will not take more than 1 ms and since we have already
    // checked above that there is at least 1 ms before the next timer interrupt, no timer interrupts
    // can occur during RechargeTimer execution (thus we satisfy the condition imposed by RechargeTimer)
    RechargeTimer(time, 0);

cleanUp:
    return;    
}

//------------------------------------------------------------------------------
//
//  Function:  RechargeTimer
//
//
//  Assumes: a timer interrupt cannot occur inside this function (caller must ensure this condition)
//
__inline void  RechargeTimer(DWORD reschedMSec, BOOL isTimerISR)
{
    UINT32 baseMSec;
    UINT32 deltaMSec = 0;
    UINT32 deltaCounts;
    UINT32 prevDeltaCounts;
    UINT32 TimerCount;
    UINT32 newTimerMatch;
    UINT32 deltaMSec1;

    baseMSec = CurMSec;

    // reschedule time already passed?
    if ((INT32)(baseMSec - reschedMSec) >= 0) {
        // called by timerisr?
        // if yes, schedule next tick to maxsystick (rescheduler will run when SYSINTR_RESCHED is returned by ISR)
        if (isTimerISR)     
            deltaMSec = g_oalTimer.maxPeriodMSec;
        // if no,  need to schedule earlier than 1 systick (as rescheduler needs to run ASAP)
        else
            deltaMSec = 0;
    }
    else { 
        deltaMSec = reschedMSec - baseMSec;

        // If next tick is longer than maximal tick period,
        // then schedule next tick for max allowed MSec by the timer counter
        if (deltaMSec > g_oalTimer.maxPeriodMSec)
            deltaMSec = g_oalTimer.maxPeriodMSec;
    }
    
    // at this point we know the deltaMSec from CurMSec when next systick has to be scheduled

    // since timers work off of counts calculate deltaCounts
    deltaCounts = deltaMSec * g_oalTimer.countsPerMSec;

    // Recharge timer to start new period

    // ideal case: increment matchreg (osmr0) by deltaCounts
    // but can't do simply this because while this routine is being executed oscr0 is incrementing.
    // Hence, for these two conditions need to increment deltaCounts so that new osmr0 will be ahead of oscr0.
    // 1) if deltaCounts is too small and incrementing osmr0 by deltaCounts puts it before oscr0
    // 2) if incremeting osmr0 by deltaCounts puts it ahead of oscr0 but so close that by the time osmr0 is set oscr0 
    //     will be ahead of it
    // For both these conditions it is suffice to use this condition if (osmr0 + deltaCounts) < (oscr0 + margin) then increment deltaCounts

    newTimerMatch = LastTimerMatch + deltaCounts;
 
    /*** TIMER SPECIFIC CODE ***/
    TimerCount = g_XllpOSTHandle.pOSTRegs->oscr0;
    /*** TIMER SPECIFIC CODE END***/

    // setting osmr0 to newTimerMatch puts it before oscr0?
    if (((INT32)(TimerCount + g_oalTimer.countsMargin - LastTimerMatch - deltaCounts)  >  0)) {
        newTimerMatch =   TimerCount +  g_oalTimer.countsMargin;

        prevDeltaCounts = deltaCounts;

        //since timermatch value changed we need to recalculate deltaCounts and deltaMSec.
        //These values are used to set actualMSecPerSysTick and actualCountsPerSysTick.
        deltaCounts = (INT32)(newTimerMatch - LastTimerMatch);

        if (deltaCounts < g_oalTimer.countsPerMSec) {
            deltaMSec = 0;
            g_LastPartialCounts = deltaCounts;
        }
        else if (deltaCounts < (g_oalTimer.countsPerMSec << 1)) {  // deltaCounts < 2MSec
            deltaMSec = 1;
            g_LastPartialCounts = deltaCounts - g_oalTimer.countsPerMSec;
        }
        else {
            deltaMSec1 = deltaCounts / g_oalTimer.countsPerMSec;
            // deltaMSec1 above denotes the time that CurMSec should be incremented by next time when timer ISR runs.
            // But if if oscr0 ran way ahead of osmr0 due to interrupts being turned off deltaMSec1 could be greater than
            // deltaMSec which is the max value we can set for actualMSecPerSysTick at this point.
            // Note: in this case we will lose some time off of CurMSec compared to the wallclock.
            if (deltaMSec1 > deltaMSec) {
                // keep the old deltaMSec (i.e. do not reset it) but set it to 1 if it is 0
                if (!deltaMSec) {
                    deltaMSec = 1;
                    deltaCounts = g_oalTimer.countsPerMSec;
                }
                else {
                    deltaCounts = prevDeltaCounts;
                }
            }
            else {
                // reset deltaMSec
                deltaMSec = deltaMSec1;
                // deltaCounts is already calculated
                g_LastPartialCounts = deltaCounts - deltaMSec * g_oalTimer.countsPerMSec;
            }
        }
    }


    /*** TIMER SPECIFIC CODE ***/
     XllpOstConfigureTimer(&g_XllpOSTHandle, MatchReg0, newTimerMatch);
    /*** TIMER SPECIFIC CODE END***/

    g_oalTimer.actualMSecPerSysTick = deltaMSec;
    g_oalTimer.actualCountsPerSysTick = deltaCounts;

#ifdef DEBUG
    //code to check assumption that timer interrupt should not occur during this routine
    //
    if (baseMSec != CurMSec)
        {
            OEMWriteDebugLED(0, (0xEbbb | 0x1));     //code for error 
            while(1) {};
        }
    
#endif

}
//------------------------------------------------------------------------------

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?