diff -r 73ea206103e6 -r 43365a9b78a3 kerneltest/e32test/power/t_frqchg.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/power/t_frqchg.cpp Tue Jul 06 15:50:07 2010 +0300 @@ -0,0 +1,775 @@ +// Copyright (c) 2010-2010 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\power\t_frqchg.cpp +// +// + +#define __E32TEST_EXTENSION__ +#include +#include +#include +#include +#include "d_frqchg.h" +#include +#include "u32std.h" + +RFrqChg Driver; +RTest test(_L("T_FRQCHG")); + +// test will fail if slice is > (expected+KSliceDeltaPercent%of expexted) +// or < (expected-KSliceDeltaPercent%expected) +const TInt KSliceDeltaPercent = 5; +// test will fail for global timer based timestamps if interval measured +// is > (expected+KTimeStampDeltaPercent%of expexted) +// or < (expected-KTimeStampDeltaPercent%expected) +const TInt KTimeStampDeltaPercent = 5; + +TInt RealToRatio(SRatio& aRatio, const TRealX& aReal) + { + aRatio.iSpare1 = 0; + aRatio.iSpare2 = 0; + if (aReal.iSign || aReal.IsZero() || aReal.IsNaN()) + { + aRatio.iM = 0; + aRatio.iX = 0; + return (aReal.IsZero()) ? KErrNone : KErrNotSupported; + } + TRealX rx(aReal); + TRealX rr(rx); + rr.iExp -= 32; + rr.iMantLo = 0; + rr.iMantHi = 0x80000000u; + rx += rr; // rounding + TInt exp = rx.iExp - 32767 - 31; + if (exp < -32768) + { + aRatio.iM = 0; + aRatio.iX = 0; + return KErrUnderflow; + } + if (exp > 32767) + { + aRatio.iM = 0xffffffffu; + aRatio.iX = 32767; + return KErrOverflow; + } + aRatio.iM = rx.iMantHi; + aRatio.iX = (TInt16)exp; + return KErrNone; + } + +TInt RatioToReal(TRealX& a, const SRatio& aRatio) + { + a.iSign = 0; + a.iFlag = 0; + a.iMantLo = 0; + a.iMantHi = aRatio.iM; + if (!aRatio.iM) + { + a.SetZero(); + return KErrNone; + } + TInt exp = aRatio.iX + 31 + 32767; + if (exp > 65534) + { + a.SetInfinite(EFalse); + } + else + { + a.iExp = (TUint16)exp; + } + return KErrNone; + } + +TInt RatioSetValue(TRealX& a, TUint32 aInt, TInt aDivisorExp) + { + a.Set(TUint(aInt)); + TInt exp = a.iExp; + exp -= aDivisorExp; + if (exp<1) + { + a.SetZero(); + return KErrUnderflow; + } + if (exp>65534) + { + a.SetInfinite(EFalse); + return KErrOverflow; + } + a.iExp = (TInt16)exp; + return KErrNone; + } + +TInt RatioReciprocal(SRatio& aRatio) + { + TRealX rx; + TInt r = RatioToReal(rx, aRatio); + if (r != KErrNone) + return r; + rx = TRealX(1) / rx; + return RealToRatio(aRatio, rx); + } + +TInt RatioMult(const SRatio& aRatio, TUint32& aInt32) + { + TRealX rx; + TInt r = RatioToReal(rx, aRatio); + if (r != KErrNone) + return r; + r = rx.MultEq(TRealX((TUint)aInt32)); + if (r != KErrNone) + return r; + if (rx.IsZero()) + { + aInt32 = 0; + return KErrNone; + } + rx.AddEq(TRealX(0.5)); + if (rx 32767+31) + { + aInt32 = ~0u; + return KErrOverflow; + } + aInt32 = rx.operator TUint(); + return KErrNone; + } + +void RatioPrint(const char* aTitle, const SRatio& aRatio) + { + TPtrC8 t8((const TUint8*)aTitle); + TBuf<256> t16; + t16.Copy(t8); + test.Printf(_L("%S: %08x %04x\n"), &t16, aRatio.iM, TUint16(aRatio.iX)); + } + +void RatioPrint2(const char* aTitle, const SRatio& aR1, const SRatio& aR2) + { + TPtrC8 t8((const TUint8*)aTitle); + TBuf<256> t16; + t16.Copy(t8); + test.Printf(_L("%S: %08x %04x %08x %04x\n"), &t16, aR1.iM, TUint16(aR1.iX), aR2.iM, TUint16(aR2.iX)); + } + +void TestEqual(const SRatio& aActual, const SRatio& aExpected) + { + if (aActual.iM==aExpected.iM && aActual.iX==aExpected.iX) + return; + RatioPrint("Actual", aActual); + RatioPrint("Expected", aExpected); + test(0); + } + +const TUint32 MultTestIntegers[] = + { + 0u, 1u, 2u, 3u, 5u, 7u, 11u, 13u, 17u, 19u, 23u, 29u, 31u, 37u, 41u, 43u, 47u, + 50u, 51u, 53u, 59u, 61u, 63u, 67u, 71u, 72u, 81u, 100u, 127u, 133u, 187u, 200u, + 4u, 8u, 16u, 32u, 64u, 128u, 256u, 512u, 1024u, 2048u, 4096u, 8192u, 16384u, + 32768u, 65536u, 131072u, 262144u, 524288u, 1048576u, 2097152u, 4194304u, 8388608u, + 16777216u, 33554432u, 67108864u, 134217728u, 268435456u, 536870912u, 1073741824u, + 2147483648u, 4294967295u, + 9u, 27u, 243u, 729u, 2187u, 6561u, 19683u, 59049u, 177147u, 531441u, 1594323u, + 4782969u, 14348907u, 43046721u, 129140163u, 387420489u, 1162261467u, 3486784401u, + 25u, 125u, 625u, 3125u, 15625u, 78125u, 390625u, 1953125u, 9765625u, + 48828125u, 244140625u, 1220703125u, + 49u, 343u, 2401u, 16807u, 117649u, 823543u, 5764801u, 40353607u, 282475249u, 1977326743u + }; + +void Test1M(const SRatio& aRatio) + { + SRatio ratio = aRatio; + const TInt N = sizeof(MultTestIntegers)/sizeof(MultTestIntegers[0]); + test.Printf(_L("Testing %d integers\n"), N); + TInt i; + for (i=0; iiSlotCount = aSlots; + p->iPutIndex = 0; + p->iBufBase = (TUint32*)User::Alloc(aSlots*sizeof(TUint32)); + if (!p->iBufBase) + { + delete p; + p = 0; + } + __e32_memory_barrier(); + return p; + } + +CircBuf::CircBuf() + { + iBufBase = 0; + } + +CircBuf::~CircBuf() + { + User::Free((TAny*)iBufBase); + } + +TInt CircBuf::TryPut(TUint32 aIn) + { + TUint32 orig = __e32_atomic_tau_rlx32(&iPutIndex, iSlotCount, 0, 1); + if (orig == iSlotCount) + return KErrOverflow; + iBufBase[orig] = aIn; + return KErrNone; + } + +void CircBuf::Reset() + { + __e32_atomic_store_ord32(&iPutIndex, 0); + } + + + +class CTimesliceTestThread : public CBase + { +public: + CTimesliceTestThread(); + ~CTimesliceTestThread(); + static CTimesliceTestThread* New(TUint32 aId, TInt aCpu, TInt aSlice, CircBuf* aBuf); + void Start(); + void Wait(); + TBool Finished(); + TInt Construct(TUint32 aId, TInt aCpu, TInt aSlice, CircBuf* aBuf); + static TInt ThreadFunc(TAny*); +public: + RThread iThread; + TRequestStatus iExitStatus; + TUint32 iId; + CircBuf* iBuf; + TUint32 iFreq; + TUint32 iThresh; + TUint32 iThresh2; + TInt iCpu; + TInt iSlice; + }; + +CTimesliceTestThread::CTimesliceTestThread() + { + iThread.SetHandle(0); + } + +CTimesliceTestThread::~CTimesliceTestThread() + { + if (iThread.Handle()) + { + if (iThread.ExitType() == EExitPending) + { + iThread.Kill(0); + Wait(); + } + CLOSE_AND_WAIT(iThread); + } + } + +TInt CTimesliceTestThread::Construct(TUint32 aId, TInt aCpu, TInt aSlice, CircBuf* aBuf) + { + iId = aId; + iCpu = aCpu; + iSlice = aSlice; + iBuf = aBuf; + + TInt r = HAL::Get(HAL::EFastCounterFrequency, (TInt&)iFreq); + if (r!=KErrNone) + return r; + iThresh = iFreq / 3000; + if (iThresh < 10) + iThresh = 10; + iThresh2 = iFreq; + TBuf<16> name = _L("TSThrd"); + name.AppendNum(iId); + r = iThread.Create(name, &ThreadFunc, 0x1000, NULL, this); + if (r!=KErrNone) + return r; + iThread.Logon(iExitStatus); + if (iExitStatus != KRequestPending) + { + iThread.Kill(0); + iThread.Close(); + iThread.SetHandle(0); + return iExitStatus.Int(); + } + return KErrNone; + } + +CTimesliceTestThread* CTimesliceTestThread::New(TUint32 aId, TInt aCpu, TInt aSlice, CircBuf* aBuf) + { + CTimesliceTestThread* p = new CTimesliceTestThread; + if (p) + { + TInt r = p->Construct(aId, aCpu, aSlice, aBuf); + if (r != KErrNone) + { + delete p; + p = 0; + } + } + return p; + } + +void CTimesliceTestThread::Start() + { + iThread.Resume(); + } + +TBool CTimesliceTestThread::Finished() + { + return (KRequestPending!=iExitStatus.Int()); + } + +void CTimesliceTestThread::Wait() + { + User::WaitForRequest(iExitStatus); + } + +TInt CTimesliceTestThread::ThreadFunc(TAny* aPtr) + { + CTimesliceTestThread& a = *(CTimesliceTestThread*)aPtr; + Driver.SetCurrentThreadCpu(a.iCpu); + Driver.SetCurrentThreadPriority(63); + Driver.SetCurrentThreadTimeslice(a.iSlice); + User::AfterHighRes(100000); + TUint id = a.iId; + TUint32 last_interval_begin = User::FastCounter(); + TUint32 last_seen_time = User::FastCounter(); + FOREVER + { + TUint32 nfc = User::FastCounter(); + TUint32 delta = nfc - last_seen_time; + TUint32 interval_length = last_seen_time - last_interval_begin; + if (delta > a.iThresh || interval_length > a.iThresh2) + { + last_interval_begin = nfc; + TUint32 x = (id<<30) | (interval_length&0x3fffffffu); + TInt r = a.iBuf->TryPut(x); + if (r != KErrNone) + break; + } + last_seen_time = nfc; + } + return KErrNone; + } + +CircBuf* RunTimesliceTest(TInt aCpu, TInt aSlice, TInt aCount, TInt aInterfere = 0) + { + TUint32 oldaff = 0; + TUint32 interfereAffinity = 0; + TUint tellKernel = 0x80000000u; + + CircBuf* buf = CircBuf::New(aCount); + test(buf != 0); + CTimesliceTestThread* t0 = CTimesliceTestThread::New(0, aCpu, aSlice, buf); + test(t0 != 0); + CTimesliceTestThread* t1 = CTimesliceTestThread::New(1, aCpu, aSlice, buf); + test(t1 != 0); + + if (aInterfere) + { + if (aInterfere < 0) + { + tellKernel = 0; + } + TInt r = UserSvr::HalFunction(EHalGroupKernel, EKernelHalNumLogicalCpus, 0, 0); + test(r>0); + interfereAffinity = (0x80000000 | ((0x1<Start(); + t1->Start(); + if (aInterfere) + { + TInt prescale = 1; + while (!t0->Finished() || !t1->Finished()) + { + User::AfterHighRes(23000); + Driver.SetLocalTimerPrescaler((1u<<1)|tellKernel, prescale); + prescale++; + if (prescale > 4) + { + prescale = 0; + } + } + } + + t0->Wait(); + t1->Wait(); + + delete t0; + delete t1; + if (aInterfere) + { + TUint32 aff; + Driver.SetLocalTimerPrescaler((1u<<1)|0x80000000u, -1); + RThread().SetPriority(EPriorityNormal); + Driver.SetCurrentThreadCpu(oldaff,&aff); + test_Equal(aff,interfereAffinity); + } + return buf; + } + +TUint32 ticks_to_us(TUint32 aTicks, TUint32 aF) + { + TUint64 x = TUint64(aTicks) * TUint64(1000000); + TUint64 f64 = aF; + x += (f64>>1); + x /= f64; + return I64LOW(x); + } + +void DisplayBuffer(CircBuf* aBuf, TUint32 aSlice ) + { + TUint32 f; + TInt r = HAL::Get(HAL::EFastCounterFrequency, (TInt&)f); + test_KErrNone(r); + TUint32* p = (TUint32*)aBuf->iBufBase; + TInt c = aBuf->iSlotCount; + TInt i; + TInt lid = -1; + TUint32 min = ~0u; + TUint32 max = 0; + TUint32 totivus = 0; + TBool firstchg = ETrue; + for (i=0; i>30; + TUint32 iv = (x<<2)>>2; + TUint32 ivus = ticks_to_us(iv,f); + if (lid >= 0) + { + if (lid == (TInt)id) + totivus += ivus; + else + { + if (!firstchg) + { + if (totivus < min) + min = totivus; + if (totivus > max) + max = totivus; + } + else + firstchg = EFalse; + totivus = ivus; + } + } + lid = (TInt)id; + test.Printf(_L("ID: %1d IV: %10d (=%10dus) TIV %10dus\n"), id, iv, ivus, totivus); + } + + if (aSlice > 0) + { + // check timeslices where within acceptable ranges + TUint32 sliceError = KSliceDeltaPercent*aSlice/100; + test_Compare(max,<,aSlice+sliceError); + test_Compare(min,>,aSlice-sliceError); + } + test.Printf(_L("RANGE %d-%dus (%dus)\n"), min, max, max-min); + } + +void TT() + { + test.Printf(_L("Timeslicing test ...\n")); + CircBuf* b = RunTimesliceTest(1, 50000, 100); + test.Next(_L("Baseline - expecting normal")); + DisplayBuffer(b,50000u); + delete b; + + Driver.SetLocalTimerPrescaler(1u<<1, 1); + b = RunTimesliceTest(1, 50000, 100); + test.Next(_L("expecting double")); + DisplayBuffer(b,100000u); + delete b; + + Driver.SetLocalTimerPrescaler(1u<<1|0x80000000u, 1); + test.Next(_L("expecting normal again")); + b = RunTimesliceTest(1, 50000, 100); + DisplayBuffer(b,50000u); + delete b; + + test.Next(_L("expecting half")); + Driver.SetLocalTimerPrescaler(1u<<1, -1); + b = RunTimesliceTest(1, 50000, 100); + DisplayBuffer(b,25000u); + delete b; + + Driver.SetLocalTimerPrescaler(1u<<1|0x80000000u, -1); + test.Next(_L("expecting normal again")); + b = RunTimesliceTest(1, 50000, 100); + DisplayBuffer(b,50000u); + delete b; + + b = RunTimesliceTest(1, 50000, 200 ,-1); + test.Next(_L("expecting random")); + DisplayBuffer(b,0u); // timeslices should be fairly random on this run + + b = RunTimesliceTest(1, 50000, 200 ,1); + test.Next(_L("expecting normal again")); + DisplayBuffer(b,50000u); + delete b; + } + +struct SGTRecord + { + TUint64 iTSInterval; + TUint64 iGTInterval; + }; + + +SGTRecord* RunGTTest(TInt aCount, TInt aWait) + { + TUint64 lastgt,lastts,gt,ts; + + SGTRecord* res = new SGTRecord[aCount]; + test(res!=0); + + + TInt r = Driver.ReadGlobalTimerAndTimestamp(lastgt,lastts); + test_Equal(r,KErrNone); + + for (TInt i = 0; i < aCount; i++) + { + User::AfterHighRes(aWait); + + TInt r = Driver.ReadGlobalTimerAndTimestamp(gt,ts); + test_Equal(r,KErrNone); + res[i].iGTInterval = gt-lastgt; + lastgt = gt; + res[i].iTSInterval = ts-lastts; + lastts = ts; + } + + return res; + } + +void DisplayGTResults(SGTRecord* aRec, TInt aCount, TUint32 aFreq, TUint64 aExpectedTSInterval, TUint64 aExpectedGTInterval) + { + SGTRecord max = { 0ul , 0ul }; + SGTRecord min = { KMaxTUint64 , KMaxTUint64 }; + + TUint64 errgt = (aExpectedGTInterval*KTimeStampDeltaPercent)/100; + TUint64 errts = (aExpectedTSInterval*KTimeStampDeltaPercent)/100; + + + for (TInt i = 0 ; i < aCount; i++) + { + test.Printf(_L("gt interval : %Lu (gtticks) %Lu (us)\n"), + aRec[i].iGTInterval, + aRec[i].iTSInterval*1000000u/TUint64(aFreq)); + + if (max.iTSInterval < aRec[i].iTSInterval) + { + max.iTSInterval = aRec[i].iTSInterval; + } + if (max.iGTInterval < aRec[i].iGTInterval) + { + max.iGTInterval = aRec[i].iGTInterval; + } + + if (min.iTSInterval > aRec[i].iTSInterval) + { + min.iTSInterval = aRec[i].iTSInterval; + } + if (min.iGTInterval > aRec[i].iGTInterval) + { + min.iGTInterval = aRec[i].iGTInterval; + } + } + + test.Printf(_L("RANGE Global Timer %Lu-%Lu ticks (%Lu ticks)\n"), + min.iGTInterval, max.iGTInterval, max.iGTInterval-min.iGTInterval); + + test.Printf(_L("RANGE Timestamp %Lu-%Lu us (%Lu us)\n"), + (1000000u*min.iGTInterval)/TUint64(aFreq), (1000000u*max.iGTInterval)/TUint64(aFreq), + (1000000u*max.iGTInterval)/TUint64(aFreq) - (1000000u*min.iGTInterval)/TUint64(aFreq)); + + if (errts) + { + test_Compare(max.iTSInterval,<,aExpectedTSInterval+errts); + test_Compare(min.iTSInterval,>,aExpectedTSInterval); + } + + if (errgt) + { + test_Compare(max.iGTInterval,<,aExpectedGTInterval+errgt); + test_Compare(min.iGTInterval,>,aExpectedGTInterval); + } + + } + +void GTT() + { + test.Printf(_L("Global timer tests ...\n")); + TUint64 gt,ts; + + TInt r = Driver.ReadGlobalTimerAndTimestamp(gt,ts); + if (KErrNotSupported == r ) + { + test.Printf(_L("Global timer not supported in this plaform, skipping GT tests\n")); + return; + } + + TUint32 f; + r = HAL::Get(HAL::EFastCounterFrequency, (TInt&)f); + test_KErrNone(r); + TInt wait = 100000; // 100ms + TInt count = 10; + + TUint64 expectedTs = (TUint64(f)*TUint64(wait))/1000000u; + TUint64 expectedGtOrig = expectedTs; + + SGTRecord* rec; + for (TInt i = 0; i < 10; i++) + { + TUint64 expectedGt = expectedGtOrig/(i+1); + r = Driver.SetGlobalTimerPrescaler(i); + test_KErrNone(r); + rec = RunGTTest(count, wait); + test.Printf(_L("expectedTS %Lu expectedGT %Lu\n"),expectedTs,expectedGt); + DisplayGTResults(rec,count, f, expectedTs , expectedGt); + delete rec; + } + + r = Driver.SetGlobalTimerPrescaler(-1); // back to default + test_KErrNone(r); + } + +void RunTests() + { + TestRatios(); + if (Driver.FrqChgTestPresent()!=KErrNone) + { + test.Printf(_L("Frequency Change not supported on this platform\n")); + return; + } + TT(); + GTT(); + } + +GLDEF_C TInt E32Main() + { + test.Title(); + test.Start(_L("Testing")); + TInt r = User::LoadLogicalDevice(KLddName); + if (r==KErrNotFound) + { + test.Printf(_L("Test not supported on this platform\n")); + } + else + { + if (r!=KErrNone) + { + test_Equal(KErrAlreadyExists, r); + } + r = Driver.Open(); + test_KErrNone(r); + RunTests(); + Driver.Close(); + } + + test.End(); + r = User::FreeLogicalDevice(KLddName); + test_KErrNone(r); + return KErrNone; + }