kerneltest/e32test/realtime/d_latncy.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 4 56f325a607ea
child 22 2f92ad2dc5db
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 1999-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\realtime\d_latncy.cpp
// 
//

#include "platform.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>
//#define FREE_RUNNING_MODE			// runs the millisecond timer in free running mode
#elif defined(__IS_OMAP1610__)
#include <omap_timer.h>
#include <omap_plat.h>
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
#include <omap_hw.h>
#include <shared_instrtimer.h>
#elif defined(__EPOC32__) && defined(__CPU_X86)
#include <x86.h>
#include <x86pc.h>
#elif defined(__RVEMUBOARD__)
#include <rvemuboard.h>
#elif defined(__NE1_TB__)
#include <upd35001_timer.h>
#endif

#ifdef __CPU_ARM
#include <arm.h>
#endif

#include <kernel/kern_priv.h>		//temporary
#include "d_latncy.h"

_LIT(KLddName,"Latency");
_LIT(KThreadName,"LatencyThreadK");

#if defined(__MEIG__)
const TInt KTickPeriodMs=2;
const TInt KTicksPerMillisecond=512;
#elif defined(__MAWD__)
const TInt KTickPeriodMs=1;
const TInt KTicksPerMillisecond=512;
#elif defined(__MISA__) || defined(__MCOT__)
const TInt KTicksPerMillisecond=3686;
const TInt KOstTicks=3685;	// not quite 1ms, so it goes in and out of phase with ms timer
TUint TriggerTime;
#elif defined(__MI920__) || defined(__NI1136__)
const TInt KTickPeriodMs=1;
#if defined(__MI920__) || defined(__NI1136__)
#ifdef FREE_RUNNING_MODE	
const TInt KTicksPerMillisecond=1500;
#else
const TInt KTicksPerMillisecond=24000;
#endif
#endif
#elif defined(__IS_OMAP1610__)
const TInt KTickPeriodMs=1;
TInt KTicksPerMillisecond = TOmapPlat::GetInputClk()/32000;
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
const TInt KTickPeriodMs=1;					// defined for compatibility but not used (ignored)
const TInt KTicksPerMillisecond = 12000; 	// Hard coded (12Mhz)
#elif defined(__EPOC32__) && defined(__CPU_X86)
const TInt KTickPeriodMs=1;
const TInt KTicksPerMillisecond=1193;
#elif defined(__RVEMUBOARD__)
const TInt KTickPeriodMs=1;
const TInt KTicksPerMillisecond=1000;
#elif defined(__NE1_TB__)
const TInt KTickPeriodMs=1;
const TInt KTicksPerMillisecond=66667;
#endif

#ifdef _DEBUG
const TInt KFudgeFactor=1;
#else
const TInt KFudgeFactor=1;
#endif

class DDeviceLatency : public DLogicalDevice
	{
public:
	DDeviceLatency();
	virtual TInt Install();
	virtual void GetCaps(TDes8& aDes) const;
	virtual TInt Create(DLogicalChannelBase*& aChannel);
	};

class DLatencyPowerHandler : public DPowerHandler
	{
public: // from DPOwerHandler
	void PowerUp();
	void PowerDown(TPowerState);
public:
	DLatencyPowerHandler(DLatency* aChannel);
public:
	DLatency* iChannel;
	};



inline TUint DLatency::Ticks()
	{
#if defined(__MEIG__)
	return KTicksPerMillisecond-(*(volatile TUint*)(KEigerTimer2Data16+KEigerBaseAddress)&0xffff);
#elif defined(__MAWD__)
	return KTicksPerMillisecond-(*(volatile TUint*)(KWindTimer2Value16+KWindBaseAddress)&0xffff);
#elif defined(__MISA__) || defined(__MCOT__)
	return *(volatile TUint*)KHwRwOstOscr-iTriggerTime;
#elif defined(__MI920__) || defined(__NI1136__)
	return KTicksPerMillisecond-(*(volatile TUint*)(KHwCounterTimer2+KHoTimerValue)&0xffff);
#elif defined(__IS_OMAP1610__)
	return KTicksPerMillisecond - *(volatile TUint*)(KHwBaseOSTimer1Reg+KHoOSTimer_READ_TIM);
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
	return (*(volatile TUint*)(iTimerInfo.iAddress + KHoGpTimer_TCRR)) - iTimerLoadValue;
#elif defined(__X86PC__)
	return 1194 - __HwTimer();
#elif defined(__RVEMUBOARD__)
	return KTicksPerMillisecond-(*(volatile TUint*)(KHwCounterTimer1+KHoTimerValue)&0xffff);
#elif defined(__NE1_TB__)
	return NETimer::Timer(0).iTimerCount;	// counts up, reset timer + interrupt on match
#endif
	}

#if !defined(__SMP__)
#if !defined(__EPOC32__) || !defined(__CPU_X86)
extern TUint IntStackPtr();
#endif
#endif

DECLARE_STANDARD_LDD()
	{
	return new DDeviceLatency;
	}

DDeviceLatency::DDeviceLatency()
//
// Constructor
//
	{
	//iParseMask=0;
	//iUnitsMask=0;
	iVersion=TVersion(1,0,1);
	}

TInt DDeviceLatency::Install()
//
// Install the device driver.
//
	{
	TInt r=SetName(&KLddName);
	return r;
	}

void DDeviceLatency::GetCaps(TDes8& aDes) const
//
// Return the Comm capabilities.
//
	{
	}

TInt DDeviceLatency::Create(DLogicalChannelBase*& aChannel)
//
// Create a channel on the device.
//
	{
	aChannel=new DLatency;
	return aChannel?KErrNone:KErrNoMemory;
	}

DLatency::DLatency()
	:	iMsCallBack(MsCallBack,this),
		iMsDfc(MsDfc,this,NULL,1)
//
// Constructor
//
	{
#if !defined(__SMP__)
#if !defined(__EPOC32__) || !defined(__CPU_X86)
	iIntStackTop=(TUint*)IntStackPtr();
#endif
#endif
#if defined(__MISA__) || defined(__MCOT__)
	iTickIncrement=KOstTicks*KFudgeFactor;
#endif
#if defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
	iTimerInfo.iAddress = 0;
#endif
	}

DLatency::~DLatency()
//
// Destructor
//
	{
	iOff = (TUint8)ETrue;
	StopTimer();
	iMsDfc.Cancel();

	if (iRtDfcQ)
		iRtDfcQ->Destroy();

	if (iPowerHandler)
		{
		iPowerHandler->Remove();
		delete iPowerHandler;
		}

	Kern::SafeClose((DObject*&)iClient, NULL);
	}

TInt DLatency::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer)
//
// Create the channel from the passed info.
//
	{
	if (!Kern::QueryVersionSupported(TVersion(1,0,1),aVer))
		return KErrNotSupported;

	// create the power handler
	iPowerHandler = new DLatencyPowerHandler(this);
	if (!iPowerHandler)
		return KErrNoMemory;
	iPowerHandler->Add();

	// Allocate a kernel thread to run the DFC 
	TInt r = Kern::DynamicDfcQCreate(iRtDfcQ, KNumPriorities-1,KThreadName);

	if (r != KErrNone)
		return r;
	
#ifdef CPU_AFFINITY_ANY
	NKern::ThreadSetCpuAffinity((NThread*)(iRtDfcQ->iThread), KCpuAffinityAny);			
#endif

	iMsDfc.SetDfcQ(iRtDfcQ);
	iClient=&Kern::CurrentThread();
	iClient->Open();
	Kern::SetThreadPriority(KNumPriorities-2);
	return KErrNone;
	}

#if defined(__MISA__) 
// For SA1100/SA1110 use a separate timer on a FIQ interrupt (OST match 0)
TInt DLatency::StartTimer()
	{
	TInt r=Interrupt::Bind(KIntIdOstMatchGeneral,MsCallBack,this);
	if (r==KErrNone)
		{
		TSa1100::ModifyIntLevels(0,KHtIntsOstMatchGeneral);	// route new timer interrupt to FIQ
		TSa1100::SetOstMatchEOI(KHwOstMatchGeneral);
		TUint oscr=TSa1100::OstData();
		iTriggerTime=oscr+KOstTicks*KFudgeFactor;
		TSa1100::SetOstMatch(KHwOstMatchGeneral,iTriggerTime);
		TSa1100::EnableOstInterrupt(KHwOstMatchGeneral);
		Interrupt::Enable(KIntIdOstMatchGeneral);
		}
	return r;
	}
#elif defined(__MCOT__)
// For Cotulla use a separate timer on a FIQ interrupt (OST match 0)
TInt DLatency::StartTimer()
	{
	TInt r=Interrupt::Bind(KIntIdOstMatchGeneral,MsCallBack,this);
	if (r==KErrNone)
		{
		TCotulla::ModifyIntLevels(0,KHtIntsOstMatchGeneral);	// route new timer interrupt to FIQ
		TCotulla::SetOstMatchEOI(KHwOstMatchGeneral);
		TUint oscr=TCotulla::OstData();
		iTriggerTime=oscr+KOstTicks*KFudgeFactor;
		TCotulla::SetOstMatch(iTriggerTime,KHwOstMatchGeneral);
		TCotulla::EnableOstInterrupt(KHwOstMatchGeneral);
		Interrupt::Enable(KIntIdOstMatchGeneral);
		}
	return r;
	}
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
TInt DLatency::StartTimer()
/* 
 *  For OMAP2420 initialise a new timer to generate an interrupt every 1ms
 */
	{
	__ASSERT_ALWAYS(!iTimerInfo.iAddress, Kern::Fault("D_Latncy: timer allocated twice.",
													 iTimerInfo.iAddress));

	// Get an available Timer from the system
    TInt r = OmapTimerMgr::GetTimer(iGPTimerId, iTimerInfo);
    if (KErrNone != r)
    	{
    	return r;
    	}
    	
    // Configure the timer
    r = ConfigureTimer();
    if (KErrNone != r)
    	{
    	DisableTimer();
    	return r;
    	}
    
    // Bind to timer interrupt
    r = Interrupt::Bind(iTimerInfo.iInterruptId, MsCallBack, this);
    if (KErrNone != r)
        {
        DisableTimer();
        return r;
        }
              
    // Unmask timer IT in interrupt controller
    r = Interrupt::Enable(iTimerInfo.iInterruptId);
    if (KErrNone != r)
		{
		Interrupt::Unbind(iTimerInfo.iInterruptId);
		DisableTimer();
    	return r;
    	}
    
    // Start timer
    TOmap::ModifyRegister32(iTimerInfo.iAddress + KHoGpTimer_TCLR, KClear32,
                        KHtGpTimer_TCLR_St);
        
    return KErrNone;
	}

void DLatency::DisableTimer()
/*
 *	Disable the interface and functional clock and mark the timer as available 
 */
	{
	  // Stop timer
    TOmap::ModifyRegister32(iTimerInfo.iAddress + KHoGpTimer_TCLR,
                        KHtGpTimer_TCLR_St, KClear32);

#if defined(__WAKEUP_3430__)
    // Disable Timer clocks using Timer framework instead of using TPRcm direct calls for 3430
    TInt r = OmapTimerMgr::DisableClocks(iGPTimerId);
    if (r != KErrNone)
        __ASSERT_ALWAYS(r, Kern::Fault("Timer clocks disable failed", 0)) ;
#else
	// Disable timer interface clock in PRCM
	TPrcm::InterfaceClkCtrl(iTimerInfo.iPrcmDeviceId, EFalse);
	
	// Disable timer functional clock in PRCM
	TPrcm::FunctionalClkCtrl(iTimerInfo.iPrcmDeviceId, EFalse);
#endif

	// Release the timer
	OmapTimerMgr::ReleaseTimer(iGPTimerId);
	
	iTimerInfo.iAddress = 0;
	}


TInt DLatency::ConfigureTimer()
/*
 *	This method will configure a timer to:
 *		-	run at the system clock (12Mhz)
 *		-	no prescaler (disable TCLR[PRE])
 *		-   autoreload and overflow interrupt enabled (TLDR will contain a
 *			value to generate an interrupt every 1000microsec)
 */
	{

#if defined(__WAKEUP_3430__)
	// Enable Timer clocks using timer framework instead of TPrcm direct calls for 3430
    TInt r = OmapTimerMgr::EnableClocks(iGPTimerId);
    if (r != KErrNone)
        __ASSERT_ALWAYS(r, Kern::Fault("Timer Clocks enable failed", 0)) ;

	// Select the input clock to be system clock 
    r = OmapTimerMgr::SetTimerClkSrc(iGPTimerId, ESysClk);
#else
	// Enable timer interface clock in PRCM  
	TPrcm::InterfaceClkCtrl(iTimerInfo.iPrcmDeviceId, ETrue, ETrue);
	// Enable timer functional clock in PRCM
	TPrcm::FunctionalClkCtrl(iTimerInfo.iPrcmDeviceId, ETrue, ETrue);

	// Select the input clock to be system clock 
    TInt r = OmapTimerMgr::SetTimerClkSrc(iGPTimerId, ESysClk);
#endif

    if (KErrNone != r)	
    	return r;

    // Timer OCP configuration: - software reset
    TOmap::SetRegister32( iTimerInfo.iAddress + KHoGpTimerTIOCP_CFG,
                          KHtGpTimer_TIOCP_CFG_SoftReset);

    // Wait for reset to be complete
    TUint16 timeOut = 1000;
    while ( !(TOmap::Register32(iTimerInfo.iAddress + KHoGpTimer_TISTAT) & 
    			KHtGpTimer_TISTAT_ResetComplete)
    			&& --timeOut);
    
   // Check if the timer has been reset or we hit the timeout
   __ASSERT_ALWAYS((TOmap::Register32(iTimerInfo.iAddress + KHoGpTimer_TISTAT) & 
    			KHtGpTimer_TISTAT_ResetComplete), Kern::Fault("D_Latncy: failed to reset timer.",
													 iGPTimerId));
	
    // Set PRE to be 0, PTV value is ignored, AutoReload is enabled
    TOmap::SetRegister32(iTimerInfo.iAddress + KHoGpTimer_TCLR, KHtGpTimer_TCLR_AR );

	//PTV argument is 0 because of TCLR[PRE] = 0 (prescaling disabled)
	TInt timerPTV = 0;
	
	// Calculate clock frequence from the ticks per ms
	TInt timerClkSrcFreq = KTicksPerMillisecond * 1000;
    
    iTimerLoadValue = OmapTimerMgr::TimerLoadValue(/*microsecs*/1000, timerClkSrcFreq, timerPTV);                          

	// First, load value in TCRR and TLDR registers
    TOmap::SetRegister32(iTimerInfo.iAddress + KHoGpTimer_TCRR, iTimerLoadValue);
    TOmap::SetRegister32(iTimerInfo.iAddress + KHoGpTimer_TLDR, iTimerLoadValue);

    // Enable overflow interrupt
    TOmap::SetRegister32(iTimerInfo.iAddress + KHoGpTimer_TIER,
                         KHtGpTimer_TIER_OverFlow);

    return KErrNone;
	}
#else
TInt DLatency::StartTimer()
	{
	iMsCallBack.OneShot(KTickPeriodMs*KFudgeFactor);
	return KErrNone;
	}
#endif

#if defined(__MISA__) 
// For SA1100/SA1110 use a separate timer on a FIQ interrupt (OST match 0)
void DLatency::StopTimer()
	{
	TSa1100::ModifyIntLevels(KHtIntsOstMatchGeneral,0);
	TSa1100::DisableOstInterrupt(KHwOstMatchGeneral);
	Interrupt::Disable(KIntIdOstMatchGeneral);
	Interrupt::Unbind(KIntIdOstMatchGeneral);
	TSa1100::SetOstMatchEOI(KHwOstMatchGeneral);
	}
#elif defined(__MCOT__)
// For Cotulla use a separate timer on a FIQ interrupt (OST match 0)
void DLatency::StopTimer()
	{
	TCotulla::ModifyIntLevels(KHtIntsOstMatchGeneral,0);
	TCotulla::DisableOstInterrupt(KHwOstMatchGeneral);
	Interrupt::Disable(KIntIdOstMatchGeneral);
	Interrupt::Unbind(KIntIdOstMatchGeneral);
	TCotulla::SetOstMatchEOI(KHwOstMatchGeneral);
	}
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
void DLatency::StopTimer()
	{
	Interrupt::Disable(iTimerInfo.iInterruptId);
	Interrupt::Unbind(iTimerInfo.iInterruptId);
	DisableTimer();
	}
#else
void DLatency::StopTimer()
	{
	iMsCallBack.Cancel();
	}
#endif

TInt DLatency::Request(TInt aFunction, TAny* a1, TAny* a2)
//
// Client requests
//
	{
	// Kern::Printf("DLatency::Request() 0x%x)\n", aFunction);
	TInt r=KErrNone;
	switch (aFunction)
		{
		case RLatency::EControlStart:
			iStarted = (TUint8)ETrue;
			StartTimer();
			break;
		case RLatency::EControlTicksPerMs:
			r=KTicksPerMillisecond;
			break;
		case RLatency::EControlGetResults:
			iResults.iUserThreadTicks = Ticks();
			kumemput32(a1, &iResults, sizeof(SLatencyResults));
			break;
		default:
			r = KErrNotSupported;
			break;
		}
	return(r);
	}

#ifdef __CAPTURE_EXTRAS
extern void CaptureExtras(SLatencyResults&);
#endif

#if !defined(__MISA__) && !defined(__MCOT__)
void DLatency::MsCallBack(TAny* aPtr)
	{
	DLatency* pL = (DLatency*)aPtr;
#if defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
	pL->iResults.iIntTicks = pL->Ticks();
	TOmap::SetRegister32(pL->iTimerInfo.iAddress + KHoGpTimer_TISR, KHtGpTimer_TISR_OverFlow);
#else
	pL->iResults.iIntTicks = Ticks();
#endif
#ifdef __CAPTURE_EXTRAS
	CaptureExtras(pL->iResults);
#endif
#if defined(__EPOC32__) && defined(__CPU_X86)
	pL->iResults.iIntRetAddr = X86::IrqReturnAddress();
#elif defined(__CPU_ARM) && defined(__SMP__)
	pL->iResults.iIntRetAddr = Arm::IrqReturnAddress();
#else
	pL->iResults.iIntRetAddr=(pL->iIntStackTop)[-1];
#endif
	if (!pL->iOff)
		{
		pL->iMsCallBack.Again(KTickPeriodMs*KFudgeFactor);
		pL->iMsDfc.Add();
		}
	}
#endif

void DLatency::MsDfc(TAny* aPtr)
	{
	DLatency* pL = (DLatency*)aPtr;
	pL->iResults.iKernThreadTicks=pL->Ticks();
	NKern::ThreadRequestSignal(&pL->iClient->iNThread);
	}

DLatencyPowerHandler::DLatencyPowerHandler(DLatency* aChannel)
	:	DPowerHandler(KLddName), 
		iChannel(aChannel)
	{
	}

void DLatencyPowerHandler::PowerUp()
	{
	iChannel->iOff = (TUint8)EFalse;
	if (iChannel->iStarted)
		iChannel->StartTimer();
	PowerUpDone();
	}

void DLatencyPowerHandler::PowerDown(TPowerState)
	{
	iChannel->iOff = (TUint8)ETrue;
	iChannel->StopTimer();
	PowerDownDone();
	}