📄 timer.c
字号:
//
// 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
//
#include <windows.h>
#include <nkintr.h>
#include <ceddk.h>
#include <oal.h>
#include <oal_intr_mips.h>
#include <au1.h>
//------------------------------------------------------------------------------
//
// Globals: g_pIC0Regs/g_pSysRegs
//
// The global variables are storing virual address for IC0 and SYS units
// for use in interrupt handling to avoid possible time consumig call to
// OALPAtoVA function.
//
static AU1_IC_REGS *g_pIC0Regs;
static AU1_SYS_REGS *g_pSysRegs;
//------------------------------------------------------------------------------
//
// Function: OALTimerInit
//
// Initialize timer oriented constants and start system timer. Usually system
// timer triggers each 1 ms but it can be changed if required to optimize
// platform and set beat period to multiple of ms.
//
// Implementation bellow uses TOY timer with input frequency 32768 Hz and
// it ignores countsPerMSec and countsMargin parameters. The timer uses
// high priority interrupt assignment for TOY interrupt. It changes
// assignment on interrupt controller and unhook default interrupt handler
// from MIPS interrupt 1.
//
BOOL OALTimerInit(
UINT32 sysTickMSec, UINT32 countsPerMSec, UINT32 countsMargin
) {
BOOL rc = FALSE;
UINT32 irq;
OALMSG(OAL_TIMER&&OAL_FUNC, (
L"+OALTimerInit(%d, %d, %d)\r\n", sysTickMSec, countsPerMSec,
countsMargin
));
// We can change period
g_oalTimer.maxPeriodMSec = 1;
g_oalTimer.countsPerMSec = 1;
g_oalTimer.countsMargin = 0;
g_oalTimer.msecPerSysTick = sysTickMSec;
g_oalTimer.countsPerSysTick = g_oalTimer.countsPerMSec * sysTickMSec;
g_oalTimer.actualMSecPerSysTick = g_oalTimer.msecPerSysTick;
g_oalTimer.actualCountsPerSysTick = g_oalTimer.countsPerSysTick;
// Set idle conversion constant and counters
idleconv = 2;
curridlehigh = curridlelow = 0;
g_oalTimer.curCounts = 0;
// Initialize high resolution timer function pointers
pQueryPerformanceFrequency = OALTimerQueryPerformanceFrequency;
pQueryPerformanceCounter = OALTimerQueryPerformanceCounter;
// Map IC0 & SYS registers
g_pSysRegs = (AU1_SYS_REGS*)OALPAtoUA(AU1_SYS_REGS_PA);
g_pIC0Regs = (AU1_IC_REGS*)OALPAtoUA(AU1_IC0_REGS_PA);
// Start 32.768kHz Oscillator
if ((INREG32(&g_pSysRegs->CNTRCTRL) & SYS_CNTRCTRL_32S) == 0) {
SETREG32(&g_pSysRegs->CNTRCTRL, SYS_CNTRCTRL_EO);
while ((INREG32(&g_pSysRegs->CNTRCTRL) & SYS_CNTRCTRL_32S) == 0);
}
// Disable TOY if it is set
if ((INREG32(&g_pSysRegs->CNTRCTRL) & SYS_CNTRCTRL_TEN) != 0) {
// Wait until TOY can be disabled/enabled
while ((INREG32(&g_pSysRegs->CNTRCTRL) & SYS_CNTRCTRL_ETS) != 0);
// Disable it
CLRREG32(&g_pSysRegs->CNTRCTRL, SYS_CNTRCTRL_TEN);
while ((INREG32(&g_pSysRegs->CNTRCTRL) & SYS_CNTRCTRL_ETS) != 0);
}
while ((INREG32(&g_pSysRegs->CNTRCTRL) & SYS_CNTRCTRL_TS) != 0);
// Set initial value to zero and -1
OUTREG32(&g_pSysRegs->TOYWRITE, 0);
OUTREG32(&g_pSysRegs->TOYMATCH0, -1);
OUTREG32(&g_pSysRegs->TOYMATCH1, -1);
OUTREG32(&g_pSysRegs->TOYMATCH2, -1);
// Use prescale it should results in approximatelly sysTickMSec ms tick
// NOTE: The TOYTRIM register will prescale the clock by its value + 1.
// Please account for the +1 when setting this register.
while ((INREG32(&g_pSysRegs->CNTRCTRL) & SYS_CNTRCTRL_TTS) != 0);
OUTREG32(&g_pSysRegs->TOYTRIM, (33 * sysTickMSec) - 1);
// Enable TOY
while ((INREG32(&g_pSysRegs->CNTRCTRL) & SYS_CNTRCTRL_ETS) != 0);
SETREG32(&g_pSysRegs->CNTRCTRL, SYS_CNTRCTRL_TEN);
while ((INREG32(&g_pSysRegs->CNTRCTRL) & SYS_CNTRCTRL_ETS) != 0);
// Assign it to request 1
OUTREG32(&g_pIC0Regs->ASSIGNCLR, 1 << IRQ_TOY);
// Unhook default interrupt handler
if (!UnhookInterrupt(1, OALIntr1Handler)) {
OALMSG(OAL_ERROR, (
L"ERROR: OALTimerInit: UnhookInterrupt for interrupt 1 failed\r\n"
));
goto cleanUp;
}
// Hook timer interrupt handler
if (!HookInterrupt(1, OALTimerIntrHandler)) {
OALMSG(OAL_ERROR, (
L"ERROR: OALTimerInit: HookInterrupt for interrupt 1 failed\r\n"
));
goto cleanUp;
}
// Enable TOY interrupt
irq = IRQ_TOY;
OALIntrEnableIrqs(1, &irq);
// We are ok
rc = TRUE;
cleanUp:
OALMSG(OAL_TIMER&&OAL_FUNC, (L"-OALTimerInit(rc = %d)\r\n", rc));
return rc;
}
//------------------------------------------------------------------------------
//
// Function: OALTimerIntrHandler
//
// This function implements timer interrupt handler.
//
UINT32 OALTimerIntrHandler()
{
UINT32 sysIntr = SYSINTR_NOP;
// Clear TOY rising edges
OUTREG32(&g_pIC0Regs->RISINGCLR, 1 << IRQ_TOY);
// Update the millisecond and high resolution counters
CurMSec += g_oalTimer.actualMSecPerSysTick;
g_oalTimer.curCounts += g_oalTimer.actualCountsPerSysTick;
// Reschedule?
if ((INT32)(CurMSec - dwReschedTime) >= 0) sysIntr = SYSINTR_RESCHED;
// Reload actual period values to start new period
g_oalTimer.actualMSecPerSysTick = g_oalTimer.msecPerSysTick;
g_oalTimer.actualCountsPerSysTick = g_oalTimer.countsPerSysTick;
#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
return sysIntr;
}
//------------------------------------------------------------------------------
//
// 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 exists and
// halted. It is important to be able to resume execution quickly upon
// receiving an interrupt.
//
// Actual implementaion on Au1 SoC uses IDLE0 mode. TOY timer hardware
// doesn't allow extend/change actual period. So there isn't way how to idle
// for longer than system tick period without possibility of CurMSec shift.
//
void OEMIdle(DWORD idleParam)
{
ULONG idlelow;
// If we should reschedule do it now
if (CurMSec >= dwReschedTime) return;
// Go to idle
OALCPUIdle();
// Add idle (we have 1 ms resolution, so do some estimate)
idlelow = curridlelow;
curridlelow += 1;
if (curridlelow < idlelow) curridlehigh++;
}
//------------------------------------------------------------------------------
//
// Function: OALTimerCountsSinceSysTick
//
// With TOY timer there isn't way how to get more accurate info about time.
//
INT32 OALTimerCountsSinceSysTick()
{
return 0;
}
//------------------------------------------------------------------------------
//
// Function: OALTimerGetCount
//
// Implementation is simple becuase TOY timer ticks in system tick period.
//
UINT32 OALGetTickCount()
{
return INREG32(&g_pSysRegs->TOYREAD) * g_oalTimer.msecPerSysTick;
}
//------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -