// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// e32test\system\d_tick.cpp
// LDD for testing tick-based timers
//
//
#include "platform.h"
#include <assp.h>
#if defined(__MEIG__)
#include <cl7211.h>
#elif defined(__MAWD__)
#include <windermere.h>
#elif defined(__MISA__)
#include <sa1100.h>
#elif defined(__MCOT__)
#include <cotulla.h>
#elif defined(__MI920__) || defined(__NI1136__)
#include <integratorap.h>
#elif defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__)
#include <omap.h>
#include <omap_timer.h>
#elif defined(__WINS__)
#include "nk_priv.h"
#elif defined(__RVEMUBOARD__)
#include <rvemuboard.h>
#elif defined(__NE1_TB__)
#include <upd35001_timer.h>
#elif defined(__MRAP__)
#include <rap.h>
#endif
#include <kernel/kern_priv.h>
#include "d_tick.h"
#include "../misc/prbs.h"
#if defined(__WINS__)
typedef Int64 TCounter;
typedef Int64 TDelta;
#else
typedef TUint TCounter;
typedef TInt TDelta;
#endif
#if defined(__MISA__)|| defined(__MCOT__)
inline TCounter TIMER()
{ return *(volatile TUint*)KHwRwOstOscr; }
#endif
#if defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__)
inline TCounter TIMER()
{ return TOmapTimer::Timer3Value(); }
#endif
#ifdef __MAWD__
inline TCounter TIMER()
{ return *(volatile TUint*)(KWindBaseAddress+KWindTimer1Value16)&0xffff; }
#endif
#ifdef __MEIG__
inline TCounter TIMER()
{ return *(volatile TUint*)(KEigerBaseAddress+KEigerTimer1Data16)&0xffff; }
#endif
#if defined(__MI920__) || defined(__NI1136__)
inline TCounter TIMER()
{ return *(volatile TUint*)(KHwCounterTimer1+KHoTimerValue)&0xffff;}
#endif
#if defined(__RVEMUBOARD__)
inline TCounter TIMER()
{ return *(volatile TUint*)(KHwCounterTimer1+KHoTimerValue)&0xffff;}
#endif
#if defined(__NE1_TB__)
inline TCounter TIMER()
{ return NETimer::Timer(2).iTimerCount; }
#endif
#ifdef __MRAP__
inline TCounter TIMER()
{ TRap::SetRegister32(1, KRapRegRTC001_TRIGGER);
return TRap::Register32(KRapRegRTC001_LONGCOUNT); }
#endif
#if defined(__EPOC32__) && defined(__CPU_X86)
TCounter TIMER();
void SetUpTimerChannel2();
#endif
#ifdef __WINS__
inline TCounter TIMER()
{
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
return c.QuadPart;
}
#endif
#if defined(__MRAP__)
inline TDelta TimeDelta(TCounter initial, TCounter final)
{ return final-initial; } // RAP RTC timer counts up
inline TInt LongTimeDelta(TCounter initial, TCounter final, TUint, TUint)
{ return final-initial; } // RAP RTC timer counts up
#endif
#if defined(__MISA__) || defined(__MCOT__)
inline TDelta TimeDelta(TCounter initial, TCounter final)
{ return final-initial; } // SA1100 timer counts up
inline TInt LongTimeDelta(TCounter initial, TCounter final, TUint, TUint)
{ return final-initial; } // SA1100 timer counts up
#endif
#if defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__)
inline TDelta TimeDelta(TCounter initial, TCounter final)
{ return initial-final; } // OMAP timer counts down
inline TInt LongTimeDelta(TCounter initial, TCounter final, TUint, TUint)
{ return initial-final; }
#endif
#if defined(__MI920__) || defined(__NI1136__)
inline TDelta TimeDelta(TCounter initial, TCounter final)
{ return (initial-final)&0xffff; } // Integrator timer counts down
TInt LongTimeDelta(TCounter initial, TCounter final, TUint init_ms, TUint final_ms)
{
TUint r=(initial-final)&0xffff; // Integrator timer counts down
TUint ms=final_ms-init_ms;
ms=2*ms-r;
ms=(ms+32768)&~0xffff;
return r+ms;
}
#endif
#if defined(__RVEMUBOARD__)
inline TDelta TimeDelta(TCounter initial, TCounter final)
{ return (initial-final)&0xffff; } // Timer counts down
TInt LongTimeDelta(TCounter initial, TCounter final, TUint init_ms, TUint final_ms)
{
TUint r=(initial-final)&0xffff; // Timer counts down
TUint ms=final_ms-init_ms;
ms=2*ms-r;
ms=(ms+32768)&~0xffff;
return r+ms;
}
#endif
#if defined(__NE1_TB__)
inline TDelta TimeDelta(TCounter initial, TCounter final)
{ return final - initial; }
inline TDelta LongTimeDelta(TCounter initial, TCounter final, TUint, TUint)
{ return final - initial; }
#endif
#if defined(__MAWD__) || defined(__MEIG__)
inline TDelta TimeDelta(TCounter initial, TCounter final)
{ return (initial-final)&0xffff; } // Eiger/Windermere timer counts down
TInt LongTimeDelta(TCounter initial, TCounter final, TUint init_ms, TUint final_ms)
{
TUint r=(initial-final)&0xffff; // Eiger/Windermere timer counts down
TUint ms=final_ms-init_ms;
ms=2*ms-r;
ms=(ms+32768)&~0xffff;
return r+ms;
}
#endif
#if defined(__EPOC32__) && defined(__CPU_X86)
TDelta TimeDelta(TUint initial, TUint final)
{
TUint tickdiff=(initial-final)&0xffff;
TUint msdiff=((final>>16)-(initial>>16))&0xffff;
msdiff=1193*msdiff-tickdiff;
msdiff=(msdiff+32768)&~0xffff;
return msdiff+tickdiff;
}
TInt LongTimeDelta(TUint initial, TUint final, TUint init_ms, TUint final_ms)
{
TUint r=(initial-final)&0xffff; // PC timer counts down
TUint ms=final_ms-init_ms;
ms=1193*ms-r;
ms=(ms+32768)&~0xffff;
return r+ms;
}
#endif
#ifdef __WINS__
inline TDelta TimeDelta(TCounter initial, TCounter final)
{ return final-initial; } // counts up
inline TDelta LongTimeDelta(TCounter initial, TCounter final, TUint, TUint)
{ return final-initial; } // counts up
#endif
const TInt KMajorVersionNumber=0;
const TInt KMinorVersionNumber=1;
const TInt KBuildVersionNumber=1;
const TInt KDaysFrom0ADTo2000AD=730497; // See US_TIME.CPP to verify this
const TInt KSecondsPerDay=86400;
TUint TicksToMicroseconds(TDelta aTicks)
{
#if defined(__MISA__) || defined(__MCOT__)
Int64 ticks(aTicks);
ticks*=(1000000);
ticks+=KHwOscFreqHz/2; // 3.6864MHz tick
ticks/=KHwOscFreqHz;
return (TUint)ticks;
#endif
#if defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__)
// Timer runs at 12Mhz/32 = 375kHz. Each tick is 2.66...us which is 16/6us
aTicks<<=4; // * 16
aTicks+=3; // rounding to the closest number of us
return (TInt)(aTicks/6); // us = (ticks*16+3)/6
#endif
#if defined(__MI920__) || defined(__NI1136__)
aTicks<<=14; // 1 tick = 32/3 us
aTicks+=768; // round
return (TInt)(aTicks/1536);
#endif
#if defined(__RVEMUBOARD__)
return (TInt)(aTicks*256); // 1 tick = 256 us
#endif
#if defined(__NE1_TB__)
NETimer& T2 = NETimer::Timer(2);
TUint prescale = __e32_find_ms1_32(T2.iPrescaler & 0x3f);
TUint f = 66666667 >> prescale;
TUint64 x = I64LIT(1000000);
x *= TUint64(aTicks);
x += TUint64(f>>1);
x /= TUint64(f);
return (TUint)x;
#endif
#if defined(__MAWD__) || defined(__MEIG__)
return aTicks*500; // 2kHz tick
#endif
#if defined(__EPOC32__) && defined(__CPU_X86)
return (aTicks*8381+4190)/10000;
#endif
#ifdef __WINS__
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
aTicks*=1000000;
aTicks+=f.QuadPart/2;
aTicks/=f.QuadPart;
return (TUint)aTicks;
#endif
}
class DTick;
class TTickTimer
{
public:
enum TMode
{
EOneShot,
EPeriodic,
EAbsolute,
ETickDelay,
};
public:
TTickTimer();
TInt Start(TInt aMode, TUint aMin, TUint aRange, TInt aCount);
void Cancel();
void CompleteClient(TInt aValue);
static void TickCallBack(TAny* aPtr);
static void SecondCallBack(TAny* aPtr);
public:
TTickLink iTickLink;
TSecondLink iSecondLink;
TMode iMode;
TInt iInterval;
TTimeK iExpiryTime;
TUint iMin;
TUint iRange;
TInt iParam;
TCounter iStartTime0;
TUint iStartTime1;
TCounter iStartTime;
TInt iMinErr;
TInt iMaxErr;
Int64 iTotalErr;
TInt iCount;
TInt iRequestedCount;
TInt iIgnore;
DTick* iLdd;
TInt iId;
TClientRequest* iRequest;
};
class DTickFactory : public DLogicalDevice
//
// Tick timer LDD factory
//
{
public:
DTickFactory();
virtual TInt Install(); //overriding pure virtual
virtual void GetCaps(TDes8& aDes) const; //overriding pure virtual
virtual TInt Create(DLogicalChannelBase*& aChannel); //overriding pure virtual
};
class DTick : public DLogicalChannelBase
//
// Tick timer LDD channel
//
{
public:
DTick();
~DTick();
protected:
virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
public:
void TimerExpired(TInt anId);
inline DThread* Client() { return iThread; }
public:
DThread* iThread;
TInt iTickPeriodUs;
TTimeK iYear2000;
TTickTimer iTickTimer[KMaxTimers];
TUint iSeed[2];
};
TTickTimer::TTickTimer()
: iMode(EOneShot),
iInterval(0),
iExpiryTime(0),
iMin(0),
iRange(1),
iParam(0),
iStartTime0(0),
iStartTime1(0),
iStartTime(0),
iMinErr(KMaxTInt),
iMaxErr(KMinTInt),
iTotalErr(0),
iCount(0),
iRequestedCount(0),
iIgnore(0),
iLdd(NULL),
iId(0),
iRequest(NULL)
{
}
void TTickTimer::TickCallBack(TAny* aPtr)
{
TCounter timer_val=TIMER();
TTickTimer& m=*(TTickTimer*)aPtr;
TDelta time=TimeDelta(m.iStartTime, timer_val);
TInt time_us=TicksToMicroseconds(time);
TInt rounded_interval=((m.iInterval+500)/1000)*1000;
TInt error=time_us-rounded_interval;
if (!m.iIgnore)
{
if (error<m.iMinErr)
m.iMinErr=error;
if (error>m.iMaxErr)
m.iMaxErr=error;
m.iTotalErr+=error;
}
if (m.iIgnore==1 && m.iMode==EPeriodic)
{
m.iStartTime0=timer_val;
m.iStartTime1=NKern::TickCount();
}
if ((m.iIgnore && m.iIgnore--) || (++m.iCount<m.iRequestedCount))
{
if (m.iMode==EOneShot)
{
TUint rnd=Random(m.iLdd->iSeed);
TUint ticks=(rnd%m.iRange)+m.iMin;
m.iInterval=ticks*m.iLdd->iTickPeriodUs;
m.iStartTime=TIMER();
m.iTickLink.OneShot(m.iInterval, TickCallBack, &m);
}
else if (m.iMode==ETickDelay)
{
m.iStartTime=TIMER();
m.iTickLink.OneShot(m.iInterval, TickCallBack, &m);
NKern::Sleep(m.iRange);
}
else
m.iStartTime=timer_val;
return;
}
m.CompleteClient(KErrNone);
if (m.iMode==EPeriodic)
{
m.iStartTime0=LongTimeDelta(m.iStartTime0, timer_val, m.iStartTime1, NKern::TickCount());
m.iTickLink.Cancel();
}
}
void TTickTimer::SecondCallBack(TAny* aPtr)
{
TTickTimer& m=*(TTickTimer*)aPtr;
TTimeK now=Kern::SystemTime();
Int64 error=now-m.iExpiryTime;
if (error>KMaxTInt)
error=KMaxTInt;
if (error<KMinTInt)
error=KMinTInt;
if (error<m.iMinErr)
m.iMinErr=(TInt)error;
if (error>m.iMaxErr)
m.iMaxErr=(TInt)error;
m.iTotalErr+=error;
if (++m.iCount<m.iRequestedCount)
{
TUint rnd=Random(m.iLdd->iSeed);
TUint secs=(rnd%m.iRange)+m.iMin;
m.iExpiryTime=now+secs*1000000+999999;
m.iExpiryTime/=1000000;
m.iExpiryTime*=1000000;
m.iSecondLink.At(m.iExpiryTime, SecondCallBack, &m);
return;
}
m.CompleteClient(KErrNone);
}
TInt TTickTimer::Start(TInt aMode, TUint a1, TUint a2, TInt aCount)
{
TInt r=KErrGeneral;
iMode=(TMode)aMode;
iMin=a1;
iRange=a2;
iMinErr=KMaxTInt;
iMaxErr=KMinTInt;
iTotalErr=0;
iCount=0;
iRequestedCount=aCount;
switch (aMode)
{
case EOneShot:
{
TUint rnd=Random(iLdd->iSeed);
TUint ticks=(rnd%iRange)+iMin;
iInterval=ticks*iLdd->iTickPeriodUs;
iStartTime=TIMER();
iStartTime0=0;
iStartTime1=0;
iTickLink.OneShot(iInterval, TickCallBack, this);
iIgnore=Min(aCount-1,1);
r=KErrNone;
break;
}
case EPeriodic:
{
iInterval=iMin*iLdd->iTickPeriodUs;
iStartTime=TIMER();
iStartTime0=iStartTime;
iStartTime1=NKern::TickCount();
iTickLink.Periodic(iInterval, TickCallBack, this);
iIgnore=Min(aCount-1,1);
r=KErrNone;
break;
}
case EAbsolute:
{
TUint rnd=Random(iLdd->iSeed);
TUint secs=(rnd%iRange)+iMin;
TTimeK now=Kern::SystemTime();
iExpiryTime=now+secs*1000000+999999;
iExpiryTime/=1000000;
iExpiryTime*=1000000;
iSecondLink.At(iExpiryTime, SecondCallBack, this);
iIgnore=0;
iStartTime0=0;
iStartTime1=0;
r=KErrNone;
break;
}
case ETickDelay:
{
iInterval=iMin*iLdd->iTickPeriodUs;
iStartTime=TIMER();
iStartTime0=0;
iStartTime1=0;
iTickLink.OneShot(iInterval, TickCallBack, this);
iIgnore=Min(aCount-1,1);
r=KErrNone;
break;
}
default:
break;
}
return r;
}
void TTickTimer::CompleteClient(TInt aValue)
{
Kern::QueueRequestComplete(iLdd->Client(),iRequest,aValue);
}
void TTickTimer::Cancel()
{
iTickLink.Cancel();
iSecondLink.Cancel();
CompleteClient(KErrCancel);
}
DECLARE_STANDARD_LDD()
{
return new DTickFactory;
}
DTickFactory::DTickFactory()
{
iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
//iParseMask=0;//No units, no info, no PDD
//iUnitsMask=0;//Only one thing
}
TInt DTickFactory::Create(DLogicalChannelBase*& aChannel)
//
// Create a new DTick on this logical device
//
{
aChannel=new DTick;
return aChannel?KErrNone:KErrNoMemory;
}
TInt DTickFactory::Install()
//
// Install the LDD - overriding pure virtual
//
{
return SetName(&KTickTestLddName);
}
void DTickFactory::GetCaps(TDes8& aDes) const
//
// Get capabilities - overriding pure virtual
//
{
TCapsTickTestV01 b;
b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b));
}
DTick::DTick()
//
// Constructor
//
{
TInt i;
for (i=0; i<KMaxTimers; i++)
{
iTickTimer[i].iLdd=this;
iTickTimer[i].iId=i;
}
iSeed[0]=NKern::TickCount();
iSeed[1]=0;
iTickPeriodUs=Kern::TickPeriod();
// Careful with the constants here or GCC will get it wrong
iYear2000=Int64(KDaysFrom0ADTo2000AD)*Int64(KSecondsPerDay);
iYear2000*=1000000;
iThread=&Kern::CurrentThread();
iThread->Open();
}
TInt DTick::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer)
//
// Create channel
//
{
if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer))
return KErrNotSupported;
for (TInt i=0; i<KMaxTimers; i++)
{
TInt r = Kern::CreateClientRequest(iTickTimer[i].iRequest);
if (r != KErrNone)
return r;
}
#if defined(__IS_OMAP1510__) || defined(__IS_OMAP1610__)
// Set up Timer3 as a free-running timer at 12Mhz/32 = 375kHz
TOmapTimer::SetTimer3Ctrl( TOmapTimer::KHtOSTimer_Cntl_Ar
| TOmapTimer::KHtOSTimer_Cntl_Free
| TOmapTimer::KHtOSTimer_Cntl_ClkEnable );
TOmapTimer::SetTimer3Prescale( TOmapTimer::EPrescaleBy32 );
// Autoreload 0xFFFFFFFF to effectively wrap from zero back to 0xFFFFFFFF
TOmapTimer::SetTimer3LoadTim( 0xFFFFFFFF );
TOmapTimer::StartTimer3();
#endif
#if defined(__MI920__) || defined(__NI1136__)
// Set up timer 1 as free running 93.75KHz clock
TIntegratorAP::SetTimerLoad(TIntegratorAP::ECounterTimer1, 0); // start from 0xffff downwards
TIntegratorAP::SetTimerMode(TIntegratorAP::ECounterTimer1, TIntegratorAP::ETimerModeFreeRunning);
TIntegratorAP::SetTimerPreScale(TIntegratorAP::ECounterTimer1, TIntegratorAP::ETimerPreScaleDiv256); // 93.75kHz wrap 699ms
TIntegratorAP::EnableTimer(TIntegratorAP::ECounterTimer1, TIntegratorAP::EEnable);
TIntegratorAP::DisableIrq(TIntegratorAP::EIrqSet0,EIntIdTimer0); // make sure timer int is disabled
#endif
#if defined(__RVEMUBOARD__)
// Switch timer 1 to a 1MHz clock in the system controller Ctrl register
TRvEmuBoard::SetSCCtrl(KTimer1EnSel);
// Set up timer 1 as free running 3.90625kHz clock
TRvEmuBoard::SetTimerMode(KHwCounterTimer1, TRvEmuBoard::ETimerModeFreeRunning);
TRvEmuBoard::SetTimerPreScale(KHwCounterTimer1, TRvEmuBoard::ETimerPreScaleDiv256);// 3.90625kHz wrap 16.777s
TRvEmuBoard::EnableTimer(KHwCounterTimer1, TRvEmuBoard::EEnable);
#endif
#if defined(__NE1_TB__)
// nothing to do since we use fast counter
#endif
#ifdef __MAWD__
// Set up timer 1 as free running 2kHz clock
TWind::SetBuzzerControl(0); // disable buzzer
TWind::SetTimer1Control(KWindTimer1ControlTimerEnable);
TWind::SetTimer1Load(0);
#endif
#ifdef __MEIG__
// Set up timer 1 as free running 2kHz clock
TEiger::ModifyControl21(KEigerControlTimer1PreOrFree|KEigerControlTimer1K512OrK2|
KEigerControlBuzzerToggle|KEigerControlBuzzerTimer1OrToggle,0);
TEiger::SetTimer1Data(0);
#endif
#if defined(__EPOC32__) && defined(__CPU_X86)
// Set up timer channel 2 as free running counter at 14318180/12 Hz
SetUpTimerChannel2();
#endif
return KErrNone;
}
DTick::~DTick()
//
// Destructor
//
{
TInt i;
for (i=0; i<KMaxTimers; i++)
{
iTickTimer[i].Cancel();
Kern::DestroyClientRequest(iTickTimer[i].iRequest);
}
Kern::SafeClose((DObject*&)iThread, NULL);
}
TInt DTick::Request(TInt aFunction, TAny* a1, TAny* a2)
//
// Runs in context of client thread, system unlocked on entry and exit
//
{
NKern::ThreadEnterCS(); // stop thread kills
TInt r=KErrNone;
TInt id=(TInt)a1;
TTickTimer& m=iTickTimer[id];
switch (aFunction)
{
case RTickTest::EControlStart:
{
STimerStartInfo info;
kumemget(&info,a2,sizeof(info));
r=m.iRequest->SetStatus(info.iStatus);
if (r==KErrNone)
r=m.Start(info.iMode, info.iMin, info.iRange, info.iCount);
break;
}
case RTickTest::EControlStop:
{
m.Cancel();
break;
}
case RTickTest::EControlGetInfo:
{
STickTestInfo info;
info.iMinErr=m.iMinErr;
info.iMaxErr=m.iMaxErr;
info.iCount=m.iCount;
info.iRequestedCount=m.iRequestedCount;
Int64 avg=m.iTotalErr/m.iCount;
info.iAvgErr=(TInt)avg;
info.iTotalTime=TicksToMicroseconds(m.iStartTime0);
kumemput(a2,&info,sizeof(info));
break;
}
case RTickTest::EControlReadRtc:
{
TInt hwrtc;
Arch::TheAsic()->SystemTimeInSecondsFrom2000(hwrtc);
*(TTimeK*)a1=Kern::SystemTime();
TTimeK hwtimeK=(TTimeK)hwrtc;
hwtimeK*=1000000;
hwtimeK+=iYear2000;
kumemput(a2,&hwtimeK,sizeof(hwtimeK));
break;
}
case RTickTest::EControlGetTickPeriod:
{
r=iTickPeriodUs;
break;
}
default:
r=KErrNotSupported;
break;
}
NKern::ThreadLeaveCS();
return r;
}