📄 timer.c
字号:
}
//-----------------------------------------------------------------------------
//
// Function: OEMIdle
//
// This function is called by the kernel when there are no threads ready to
// run. The CPU should be put into a reduced power mode if possible and
// halted. It is important to be able to resume execution quickly upon
// receiving an interrupt.
//
// Interrupts are disabled when OEMIdle is called and when it returns.
//
// Note that system timer must be running when CPU/SoC is moved to reduced
// power mode.
//
// Parameters:
// dwIdleParam
// [in] Used by MIPS CPU only. Base value for program status
// register (PSR) to allow interrupts to be enabled.
//
// Returns:
// None.
//
//------------------------------------------------------------------------------
void OEMIdle(DWORD dwIdleParam)
{
UINT32 idleMSec, idleSysTicks;
INT32 countBeforeIdle, countAfterIdle, idleCounts;
ULARGE_INTEGER idle;
// Compute the remaining idle time
idleMSec = dwReschedTime - CurMSec;
// Idle time has expired - we need to return
if ((INT32)idleMSec <= 0) return;
// If we don't have enough margin to update the timer before the current
// system tick expires, just return
if ((OALTimerCountsSinceSysTick() + g_oalTimer.countsMargin) >=
g_oalTimer.countsPerSysTick) return;
// Limit the maximum idle time to what is supported.
// Counter size is the limiting parameter. When kernel
// profiler or interrupt latency timing is active, it is set
// to one system tick.
if (idleMSec > g_oalTimer.maxPeriodMSec)
{
idleMSec = g_oalTimer.maxPeriodMSec;
}
// We can wait only full systick
idleSysTicks = idleMSec / g_oalTimer.msecPerSysTick;
// This is idle time in hi-res ticks
idleCounts = idleSysTicks * g_oalTimer.countsPerSysTick;
// Prolong beat period to idle time -- don't do it idle time isn't
// longer than one system tick. Even if OALTimerExtendSysTick function
// should accept this value it can cause problems if kernel profiler
// or interrupt latency timing is active.
if (idleSysTicks > 1)
{
// Extend timer period.
OALTimerUpdate(idleCounts, g_oalTimer.countsMargin);
// Update value for timer interrupt which wakeup from idle
g_oalTimer.actualMSecPerSysTick = idleMSec;
g_oalTimer.actualCountsPerSysTick = idleCounts;
}
// Find how many hi-res ticks were are consumed in the current system tick
// before idle mode is entered
countBeforeIdle = OALTimerCountsSinceSysTick();
// Move SoC/CPU to idle mode
OALCPUIdle();
// Find how many hi-res ticks were are consumed in the current system tick
// after idle mode is exited
countAfterIdle = OALTimerCountsSinceSysTick();
// Return system tick period back to original. Don't call when idle
// time was one system tick. See comment above.
if (idleSysTicks > 1)
{
// Since interrupts were disabled upon entry to idle mode, this code
// should assume the timer interrupt may be pending, but not yet
// serviced. Execution resumes within OEMIdle before the timer interrupt
// is serviced. We will clear any pending timer interrupt and prepare
// for the next available system tick.
// Restore original system tick and expired ticks
idleSysTicks = OALTimerUpdate(g_oalTimer.countsPerSysTick,
g_oalTimer.countsMargin);
// Restore original values used for advancing timer globals
g_oalTimer.actualMSecPerSysTick = g_oalTimer.msecPerSysTick;
g_oalTimer.actualCountsPerSysTick = g_oalTimer.countsPerSysTick;
// Adjust system timer OAL variables. Note expired system ticks
// returned by OALTimerUpdate is rounded down to nearest system tick.
CurMSec += (idleSysTicks * g_oalTimer.msecPerSysTick);
g_oalTimer.curCounts += (idleSysTicks * g_oalTimer.countsPerSysTick);
}
// Get real idle value. If result is negative we didn't idle at all.
idleCounts = countAfterIdle - countBeforeIdle;
if (idleCounts < 0) idleCounts = 0;
// Update idle counters
idle.LowPart = curridlelow;
idle.HighPart = curridlehigh;
idle.QuadPart += idleCounts;
curridlelow = idle.LowPart;
curridlehigh = idle.HighPart;
}
//------------------------------------------------------------------------------
//
// Function: OALTimerSetCompare
//
// This function sets timer compare value. This function should be
// implemented only for systems with count/compare timer type. It is used
// for OALTimerRecharge/OALTimerCountsSinceSysTick/OALTimerReduceSysTick and
// OALTimerExtendSysTick implementation for systems with count/compare timer.
//
VOID OALTimerSetCompare(UINT32 compare)
{
// Clear timer compare interrupt flag (write-1-clear)
OUTREG32(&g_pEPIT->SR, CSP_BITFMASK(EPIT_SR_OCIF));
OUTREG32(&g_pEPIT->CMPR, compare);
#ifdef VPMX31
// Keep advancing the compare value until we have a valid timer event.
// On Virtio, the timer advances while the ARM simulator is inactive
// resulting in invalid timer compare settings.
while ((INT32) (INREG32(&g_pEPIT->CNT) - INREG32(&g_pEPIT->CMPR)) <= 0)
{
OUTREG32(&g_pEPIT->CMPR, INREG32(&g_pEPIT->CNT) - g_oalTimer.countsMargin);
// Clear timer compare interrupt flag (write-1-clear)
OUTREG32(&g_pEPIT->SR, CSP_BITFMASK(EPIT_SR_OCIF));
}
#endif
}
//------------------------------------------------------------------------------
//
// Function: OALTimerRecharge
//
// This function recharge count/compare timer. In case that we are late more
// than margin value we will use count as new counter base. Under
// normal conditions previous compare value stored in global variable is used.
// Using global variable instead reading compare register allows compensate
// offset when we reduce system tick to value which can cause hazard.
//
// Parameters:
// period
// [in] System tick period in raw timer ticks.
////
// margin
// [in] Safe time range in raw timer ticks where the timer can be
// modified without errors.
//
// Returns:
// None.
//
//-----------------------------------------------------------------------------
VOID OALTimerRecharge(UINT32 period, UINT32 margin)
{
UINT32 cnt, cmpr;
// Grab current timer counter
cnt = INREG32(&g_pEPIT->CNT);
// Advance base for current system tick to beginning of next system tick
g_BaseTimerCount -= period;
// Set timer compare to end of next system tick
cmpr = g_BaseTimerCount - period;
// If requested timer compare value is invalid
if ((INT32) (cnt - cmpr - margin) < 0)
{
// Allow the timer to catch up
g_BaseTimerCount = cnt;
// Set timer compare to end of next system tick
cmpr = cnt - period;
}
OALTimerSetCompare(cmpr);
}
//------------------------------------------------------------------------------
//
// Function: OALTimerUpdate
//
// This function is called to change length of actual system timer period.
// If end of actual period is closer than margin period isn't changed (so
// original period elapse). Function returns time which already expires
// in new period length units. If end of new period is closer to actual time
// than margin period end is shifted by margin (but next period should fix
// this shift - this is reason why OALTimerRecharge doesn't read back
// compare register and it uses saved value instead).
//
UINT32 OALTimerUpdate(UINT32 period, UINT32 margin)
{
UINT32 rc = 0, cmpr, cnt, expired;
// Grab current timer counter
cnt = INREG32(&g_pEPIT->CNT);
// If requested system tick is zero
if (period == 0)
{
// Let interrupt occur ASAP
cmpr = cnt - margin;
OALTimerSetCompare(cmpr);
}
// Else, requested system tick is non-zero
else
{
// Get number of expired high-res ticks in current system tick
expired = g_BaseTimerCount - cnt;
// If we want to reduce system tick (i.e. ticks expired in
// current system tick meet or exceed requested period)
if (expired >= period)
{
// Calculate expired time in new period length units
rc = expired / period;
// Advance base for current system tick to beginning of next system tick
g_BaseTimerCount -= (rc * period);
}
// New compare value can be determined by subtracting
// requested period current system tick base
cmpr = g_BaseTimerCount - period;
// If we have enough margin to update timer compare value
if ((INT32) (cnt - cmpr - margin) >= 0)
{
OALTimerSetCompare(cmpr);
}
}
return rc;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -