📄 vartick.c
字号:
}
// update the counter register with the new counter
// NOTE that nDiffTick can be negative.
SHxIncrCounterRegister (nDiffTick);
// OEMMinTickDistance is too small for variable-tick scheduler if the debugchk failed
DEBUGCHK (LastINTMSec == dwPrev);
}
//
// TimerISR for SHx
//
DWORD SHxTimerISR (void)
{
int nDiffMSec = dwReschedTime - NextINTMSec;
int nDiffTick = 0;
DWORD dwCounterElapsed = SHxMaxTicks - pTmuTimer0->tcnt;
DWORD dwRetVal = SYSINTR_RESCHED;
/*
#ifdef DEBUG
char string[8];
static int ii = 0;
void PrintLED(char *);
// Output to LED
string[0] = 'T';
string[1] = 'I';
string[2] = 'M';
string[3] = '0';
string[4] = '0' + (UCHAR)ii / 100;
string[5] = '0' + (UCHAR)(ii %100) / 10;
string[6] = '0' + (UCHAR)(ii % 10);
string[7] = '\0';
PrintLED(string);
if( ++ii > 1000 ) ii = 0;
#endif
*/
wNumInterrupts ++;
// Take care of the case if we're interrupted way late
// Perf isn't an issue here since this should only occure when debugger disabling the interrupts.
if (dwCounterElapsed >= (DWORD) SHxCount1MS) {
SHxSetCounterRegister (SHxMaxTicks);
// we can either try to catch up with the time lost, or simply forget about the lost ticks.
// the following code will try to catch up with the time, but the problem is that it'll
// result in "time jump" from drivers' point of view.
// CurTicks.QuadPart += dwCounterElapsed;
// NextINTMSec += dwCounterElapsed / SHxCount1MS;
// nDiffMSec = dwReschedTime - NextINTMSec;
}
// auto reload of max
CurTicks.QuadPart += dwCounterBase;
dwCounterBase = SHxMaxTicks;
//
// clear underflow flag
//
do {
pTmuTimer0->tcr &= ~TMU_TCR_UNF;
} while (pTmuTimer0->tcr & TMU_TCR_UNF);
if (nDiffMSec <= 0) {
// time to reschedule, update next reschedule time to the max we can set
nDiffMSec = nMaxSchedMSec;
} else {
// don't need to reschedule yet (most likely due to timer's limit of setting
// interrupt tick too far away, or iltiming/profiler running)
if (nDiffMSec > nMaxSchedMSec) {
nDiffMSec = nMaxSchedMSec;
}
dwRetVal = SYSINTR_NOP;
}
// update LastINTMSec and NextINTMSec
LastINTMSec = NextINTMSec;
NextINTMSec += nDiffMSec;
// counter had already been reloaded with nMaxSchedMSec * SHxCount1MS.
// change it if we need to (tick shifted, or need to wake up earlier)
if ((nDiffMSec -= nMaxSchedMSec) != 0) {
nDiffTick = nDiffMSec * SHxCount1MS;
}
if (nDiffTick || shifted_tick) {
// actully decrementing the counter here as nDiffMSec is always < nMaxSchedMSec
SHxIncrCounterRegister (nDiffTick - shifted_tick);
dwCounterBase += nDiffTick;
shifted_tick = 0;
}
if (pOEMUpdateTimerLED) {
pOEMUpdateTimerLED (LastINTMSec);
}
return dwRetVal;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL SHxInitClock(void)
{
// derive constants from OEMTimerFreq;
SHxCount1MS = OEMTimerFreq / 1000; // 1 MS
nMaxSchedMSec = (0x7FFFFFFF / SHxCount1MS) - 1; // Max MS representable by 32 integer - 1 (so we don't overflow)
NextINTMSec = LastINTMSec + nMaxSchedMSec;
SHxMaxTicks = (DWORD) nMaxSchedMSec * SHxCount1MS;
idleconv = SHxCount1MS;
dwCounterBase = SHxMaxTicks;
// derive timer from OEMTimerBaseAddr
pTmuComReg = (PTMU_COMREG) (OEMTimerBaseAddr + TMU_TSTR0_OFFSET);
pTmuTimer0 = (PTMU_CLOCK_REG) (OEMTimerBaseAddr + TMU_TCOR0_OFFSET);
pTmuTimer1 = pTmuTimer0 + 1;
pTmuTimer2 = pTmuTimer0 + 2;
// Use the count/compare timer as our system tick.
pQueryPerformanceCounter = OEMQueryPerformanceCounter;
pQueryPerformanceFrequency = OEMQueryPerformanceFrequency;
pOEMUpdateRescheduleTime = SHxUpdateReschedTime;
DEBUGMSG (1, (L"OEMTimerFreq = %8.8lx, SHxCount1MS = %8.8lx, nMaxSchedMSec = %8.8lx, SHxMaxTicks = %8.8lx\r\n",
OEMTimerFreq, SHxCount1MS, nMaxSchedMSec, SHxMaxTicks));
SHxInitTimer (SHxMaxTicks);
return(TRUE);
}
//------------------------------------------------------------------------------
//
// This routine is called by the kernel when there are no threads ready to
// run. The CPU should be put into a reduced power mode and halted. It is
// important to be able to resume execution quickly upon receiving an interrupt.
// Note: It is assumed that interrupts are off when OEMIdle is called. Interrrupts
// are turned off when OEMIdle returns.
//
//------------------------------------------------------------------------------
void
OEMIdle(
DWORD dwIdleParam
)
{
DWORD dwTick = pTmuTimer0->tcnt, dwTickLeft;
ULARGE_INTEGER currIdle = {
curridlelow,
curridlehigh
};
pCPUEnterIdle (dwIdleParam);
// wake up from Idle, calculate idle time
INTERRUPTS_OFF ();
dwTickLeft = pTmuTimer0->tcnt;
if (dwTick > dwTickLeft)
dwTick -= dwTickLeft;
currIdle.QuadPart += dwTick;
curridlelow = currIdle.LowPart;
curridlehigh = currIdle.HighPart;
}
//------------------------------------------------------------------------------
// This routine is called by the kernel to get the 'real' tick count.
//------------------------------------------------------------------------------
DWORD SC_GetTickCount(void)
{
volatile DWORD dwNext, dwShift, dwPrev; // volatile for comipler issue workaround.
int nTicks;
do {
dwPrev = LastINTMSec;
dwNext = NextINTMSec;
dwShift = shifted_tick;
nTicks = pTmuTimer0->tcnt - dwShift;
if (pTmuTimer0->tcr & TMU_TCR_UNF) {
return dwNext;
}
// return LastINTMSec if interrupted or within 1MS of last interrupt (avoid calling "divide" in Scheduler since this
// will usually occurs while handling Timer interrupt.)
if ((dwPrev != LastINTMSec) || ((int) (SHxMaxTicks - nTicks) < SHxCount1MS))
return LastINTMSec;
} while ((dwNext != NextINTMSec) || (dwShift != shifted_tick));
return dwNext - ((nTicks + SHxCount1MS - 1) / SHxCount1MS);
}
//------------------------------------------------------------------------------
// This routine returns the perf count since the last timer interrupt.
//------------------------------------------------------------------------------
DWORD PerfCountSinceTick (void)
{
volatile DWORD dwShift, dwPrev, dwPrevCnt; // volatile for comipler issue workaround.
int nTicks;
do {
dwPrev = LastINTMSec;
dwShift = shifted_tick;
dwPrevCnt = dwCounterBase;
nTicks = pTmuTimer0->tcnt - dwShift;
if ((pTmuTimer0->tcr & TMU_TCR_UNF) || (dwPrev != LastINTMSec))
return dwPrevCnt;
} while ((dwPrevCnt != dwCounterBase) || (dwShift != shifted_tick));
return dwPrevCnt - nTicks;
}
//------------------------------------------------------------------------------
//
// Timer2 - handle profiler and iltiming
//
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Start timer 2 (parameter - interrupt interval in micro-seconds)
//------------------------------------------------------------------------------
void SHxStartTimer2 (DWORD dwUSec)
{
#if (SH7770_REVISION == SH7770_1STCUT)
UCHAR *tstr;
#endif
DWORD dwCount;
if (!dwUSec) {
//
// Use default rate (1 MS)
//
dwUSec = 1000;
} else if (dwUSec < 20) {
//
// Rate specified: at least 20us
//
dwUSec = 20;
}
dwCount = (dwUSec * SHxCount1MS) / 1000;
//
// Init timer2 and enable timer2 interrupt
//
DEBUGMSG(1, (TEXT("TMU_TSTR0 0x%02X \r\n"), pTmuComReg->tstr0 ));
DEBUGMSG(1, (TEXT("TMU_TCR2 0x%08X \r\n"), pTmuTimer2->tcr ));
DEBUGMSG(1, (TEXT("TMU_TCOR2 0x%08X \r\n"), pTmuTimer2->tcor ));
DEBUGMSG(1, (TEXT("TMU_TCNT2 0x%08X \r\n"), pTmuTimer2->tcnt ));
// make sure timer2 is topped
#if (SH7770_REVISION == SH7770_1STCUT)
tstr = (UCHAR *)DRV_GLOBAL_BASE + TSTR0_GLOBAL_OFFSET;
*tstr &= ~TMU2_ENABLE;
pTmuComReg->tstr0 = *tstr;
#else
pTmuComReg->tstr0 &= ~TMU2_ENABLE;
#endif
// initialize timer constant and count register
pTmuTimer2->tcor = dwCount;
pTmuTimer2->tcnt = dwCount;
// start timer2
#if (SH7770_REVISION == SH7770_1STCUT)
tstr = (UCHAR *)DRV_GLOBAL_BASE + TSTR0_GLOBAL_OFFSET;
*tstr |= TMU2_ENABLE;
pTmuComReg->tstr0 = *tstr;
#else
pTmuComReg->tstr0 |= TMU2_ENABLE;
#endif
// enable timer2 interrupts
pTmuTimer2->tcr = TMU_TCR_UNIE | TMU_TCR_TPSC_D16;
DEBUGMSG(1, (TEXT("->TMU_TSTR0 0x%02X \r\n"), pTmuComReg->tstr0 ));
DEBUGMSG(1, (TEXT("->TMU_TCR0 0x%08X \r\n"), pTmuTimer0->tcr ));
DEBUGMSG(1, (TEXT("->TMU_TCOR0 0x%08X \r\n"), pTmuTimer0->tcor ));
DEBUGMSG(1, (TEXT("->TMU_TCNT0 0x%08X \r\n"), pTmuTimer0->tcnt ));
DEBUGMSG(1, (TEXT("->TMU_TCR1 0x%08X \r\n"), pTmuTimer1->tcr ));
DEBUGMSG(1, (TEXT("->TMU_TCOR1 0x%08X \r\n"), pTmuTimer1->tcor ));
DEBUGMSG(1, (TEXT("->TMU_TCNT1 0x%08X \r\n"), pTmuTimer1->tcnt ));
DEBUGMSG(1, (TEXT("->TMU_TCR2 0x%08X \r\n"), pTmuTimer2->tcr ));
DEBUGMSG(1, (TEXT("->TMU_TCOR2 0x%08X \r\n"), pTmuTimer2->tcor ));
DEBUGMSG(1, (TEXT("->TMU_TCNT2 0x%08X \r\n"), pTmuTimer2->tcnt ));
}
//------------------------------------------------------------------------------
// Stop Timer 2
//------------------------------------------------------------------------------
void SHxStopTimer2 (void)
{
DEBUGMSG(1, (TEXT("TMU_TCR2 0x%08X \r\n"), pTmuTimer2->tcr ));
//
// Diable timer2 interrupt
//
pTmuTimer2->tcr = TMU_TCR_TPSC_D16;
DEBUGMSG(1, (TEXT("->TMU_TCR2 0x%08X \r\n"), pTmuTimer2->tcr ));
}
//------------------------------------------------------------------------------
// Timer 2 perf count since tick. Since we don't have to worry about interrupt here
// it's a lot simplier.
//------------------------------------------------------------------------------
DWORD SHxTimer2CountSinceTick (void)
{
return pTmuTimer2->tcor - pTmuTimer2->tcnt;
}
//------------------------------------------------------------------------------
// ISR for Timer2
//------------------------------------------------------------------------------
DWORD SHxTimer2ISRWrapped (DWORD ra)
{
extern void ProfilerHit (DWORD ra);
/*
#ifdef DEBUG
char string[8];
static int ii = 0;
void PrintLED(char *);
// Output to LED
string[0] = 'T';
string[1] = 'I';
string[2] = 'M';
string[3] = '2';
string[4] = '0' + (UCHAR)ii / 100;
string[5] = '0' + (UCHAR)(ii %100) / 10;
string[6] = '0' + (UCHAR)(ii % 10);
string[7] = '\0';
PrintLED(string);
if( ++ii > 1000 ) ii = 0;
#endif
*/
if (fIntrTime) {
dwIsrTime1 = SHxTimer2CountSinceTick ();
dwSPC = ra;
wNumInterrupts ++;
}
//
// clear underflow flag
//
do {
pTmuTimer2->tcr &= ~TMU_TCR_UNF;
} while (pTmuTimer2->tcr & TMU_TCR_UNF);
if (pOEMUpdateProfLED)
pOEMUpdateProfLED (ra);
if (fIntrTime) {
wNumInterrupts = 0;
dwIsrTime2 = SHxTimer2CountSinceTick ();
if( dwIsrTime1 > dwIsrTime2 ){
dwIsrTime2 += pTmuTimer2->tcor;
}
return SYSINTR_TIMING;
}
if (fProfilerRunning) {
ProfilerHit (ra);
}
return SYSINTR_NOP;
}
//------------------------------------------------------------------------------
// disable profiler interrupts
//------------------------------------------------------------------------------
void OEMProfileTimerDisable(void)
{
//
// Stop Timer 2
//
if (fProfilerRunning) {
SHxStopTimer2 ();
fProfilerRunning = FALSE;
RETAILMSG(1, (TEXT("Profiler interrupt stopped\r\n")));
}
}
//------------------------------------------------------------------------------
// start profiler interrupts
//------------------------------------------------------------------------------
void OEMProfileTimerEnable(DWORD dwUSec)
{
if (fIntrTime) {
RETAILMSG(1, (TEXT("Cannot starting Monte Carlo Profiler while iltiming is running\r\n")));
} else if (!fProfilerRunning) {
RETAILMSG(1, (TEXT("Starting profile timer at %d uS rate\r\n"), dwUSec));
fProfilerRunning = TRUE;
SHxStartTimer2 (dwUSec);
}
}
#pragma optimize("", on)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -