/*++ Copyright (c) 1994 Digital Equipment Corporation Module Name: ev5prof.c Abstract: This module implements the Profile Counter using the performance counters within the EV5 core. This module is appropriate for all machines based on microprocessors using the EV5 core. N.B. - This module assumes that all processors in a multiprocessor system are running the microprocessor at the same clock speed. Author: Steve Brooks 14-Feb-1995 (adapted from ev4prof.c) Environment: Kernel mode Revision History: --*/ #include "halp.h" #include "axp21164.h" // // Default Number of Profile Clock Ticks per sample // ULONG Halp21164NumberOfTicks = 1; ULONG Halp21164NumberOfTicksReload; // // Define space in the HAL-reserved part of the PCR structure for each // performance counter's interval count // // Note that ev5ints.s depends on these positions in the PCR. // #define PCRProfileCount ((PULONG)(HAL_21164_PCR->ProfileCount.ProfileCount)) #define PCRProfileCountReload ((PULONG)(&HAL_21164_PCR->ProfileCount.ProfileCountReload)) // // Define the currently selected profile source for each counter // KPROFILE_SOURCE Halp21164ProfileSource0; KPROFILE_SOURCE Halp21164ProfileSource1; KPROFILE_SOURCE Halp21164ProfileSource2; #define INTERVAL_DELTA (10) // // Define the mapping between possible profile sources and the // CPU-specific settings. // typedef struct _HALP_PROFILE_MAPPING { BOOLEAN Supported; ULONG MuxControl; ULONG Counter; ULONG EventCount; ULONG NumberOfTicks; } HALP_PROFILE_MAPPING, *PHALP_PROFILE_MAPPING; HALP_PROFILE_MAPPING Halp21164ProfileMapping[ProfileMaximum] = { {TRUE, Ev5Cycles, Ev5PerformanceCounter0, Ev5CountEvents2xx16, 10}, {FALSE, 0,0,0,0}, {TRUE, Ev5Instructions, Ev5PerformanceCounter0, Ev5CountEvents2xx16, 10}, {TRUE, Ev5PipeDry, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5LoadsIssued, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {FALSE, 0,0,0,0}, {TRUE, Ev5AllFlowIssued, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5NonIssue, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5DcacheLDMisses, Ev5PerformanceCounter2, Ev5CountEvents2xx14, 10}, {TRUE, Ev5IcacheRFBMisses, Ev5PerformanceCounter2, Ev5CountEvents2xx14, 10}, {FALSE, 0,0,0,0}, {TRUE, Ev5BRMispredicts, Ev5PerformanceCounter2, Ev5CountEvents2xx14, 10}, {TRUE, Ev5StoresIssued, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5FPOpsIssued, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5IntOpsIssued, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5DualIssue, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5TripleIssue, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5QuadIssue, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {FALSE, 0,0,0,0}, {TRUE, Ev5Cycles, Ev5PerformanceCounter0, Ev5CountEvents2xx16, 10}, {TRUE, Ev5IcacheIssued, Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5DcacheAccesses,Ev5PerformanceCounter1, Ev5CountEvents2xx16, 10}, {TRUE, Ev5MBStallCycles, Ev5PerformanceCounter2, Ev5CountEvents2xx14, 10}, {TRUE, Ev5LDxLInstIssued,Ev5PerformanceCounter2, Ev5CountEvents2xx14, 10} }; BOOLEAN HalQueryProfileInterval( IN KPROFILE_SOURCE Source ); NTSTATUS HalSetProfileSourceInterval( IN KPROFILE_SOURCE ProfileSource, IN OUT ULONG *Interval ); VOID Halp21164UpdatePerformanceCounter( IN ULONG PerformanceCounter, IN ULONG MuxControl, IN ULONG EventCount ); NTSTATUS Halp21164ProfileSourceInformation ( OUT PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnedLength ) /*++ Routine Description: Returns the HAL_PROFILE_SOURCE_INFORMATION for this processor. Arguments: Buffer - output buffer BufferLength - length of buffer on input ReturnedLength - The length of data returned Return Value: STATUS_SUCCESS STATUS_BUFFER_TOO_SMALL - The ReturnedLength contains the buffersize currently needed. --*/ { PHAL_PROFILE_SOURCE_INFORMATION SourceInfo; NTSTATUS Status; if (BufferLength != sizeof(HAL_PROFILE_SOURCE_INFORMATION)) { Status = STATUS_INFO_LENGTH_MISMATCH; return Status; } SourceInfo = (PHAL_PROFILE_SOURCE_INFORMATION)Buffer; SourceInfo->Supported = HalQueryProfileInterval(SourceInfo->Source); if (SourceInfo->Supported) { SourceInfo->Interval = Halp21164ProfileMapping[SourceInfo->Source].EventCount * Halp21164ProfileMapping[SourceInfo->Source].NumberOfTicks; } Status = STATUS_SUCCESS; return Status; } NTSTATUS Halp21164ProfileSourceInterval ( OUT PVOID Buffer, IN ULONG BufferLength ) /*++ Routine Description: Returns the HAL_PROFILE_SOURCE_INTERVAL for this processor. Arguments: Buffer - output buffer BufferLength - length of buffer on input Return Value: STATUS_SUCCESS STATUS_BUFFER_TOO_SMALL - The ReturnedLength contains the buffersize currently needed. --*/ { PHAL_PROFILE_SOURCE_INTERVAL Interval; NTSTATUS Status; if (BufferLength != sizeof(HAL_PROFILE_SOURCE_INTERVAL)) { Status = STATUS_INFO_LENGTH_MISMATCH; return Status; } Interval = (PHAL_PROFILE_SOURCE_INTERVAL)Buffer; Status = HalSetProfileSourceInterval(Interval->Source, &Interval->Interval); return Status; } VOID Halp21164InitializeProfiler( VOID ) /*++ Routine Description: This routine is called during initialization to initialize profiling for each processor in the system. Arguments: None. Return Value: None. --*/ { // // Establish the profile interrupt as the interrupt handler for // all performance counter interrupts. // PCR->InterruptRoutine[PC0_VECTOR] = Halp21164PerformanceCounter0Interrupt; PCR->InterruptRoutine[PC1_VECTOR] = Halp21164PerformanceCounter1Interrupt; PCR->InterruptRoutine[PC2_VECTOR] = Halp21164PerformanceCounter2Interrupt; return; } BOOLEAN Hal21164QueryProfileInterval( IN KPROFILE_SOURCE Source ) /*++ Routine Description: Given a profile source, returns whether or not that source is supported. Arguments: Source - Supplies the profile source Return Value: TRUE - Profile source is supported FALSE - Profile source is not supported --*/ { if (Source > (sizeof(Halp21164ProfileMapping)/sizeof(HALP_PROFILE_MAPPING))) { return(FALSE); } return(Halp21164ProfileMapping[Source].Supported); } NTSTATUS Hal21164SetProfileSourceInterval( IN KPROFILE_SOURCE ProfileSource, IN OUT ULONG *Interval ) /*++ Routine Description: Sets the profile interval for a specified profile source Arguments: ProfileSource - Supplies the profile source Interval - Supplies the specified profile interval Returns the actual profile interval Return Value: NTSTATUS --*/ { ULONG FastTickPeriod; ULONG SlowTickPeriod; ULONG TickPeriod; ULONG FastCountEvents; ULONG SlowCountEvents; ULONGLONG CountEvents; ULONGLONG TempInterval; if (!HalQueryProfileInterval(ProfileSource)) { return(STATUS_NOT_IMPLEMENTED); } if (ProfileSource == ProfileTime) { // // Convert the clock tick period (in 100ns units ) into // a cycle count period // CountEvents = ((ULONGLONG)(*Interval) * 100000) / PCR->CycleClockPeriod; } else { CountEvents = (ULONGLONG)*Interval; } FastCountEvents = Ev5CountEvents2xx8; SlowCountEvents = Ev5CountEvents2xx16; if (Halp21164ProfileMapping[ProfileSource].Counter == Ev5PerformanceCounter0) { FastCountEvents = Ev5CountEvents2xx16; } else if (Halp21164ProfileMapping[ProfileSource].Counter == Ev5PerformanceCounter2) { SlowCountEvents = Ev5CountEvents2xx14; } // // Limit the interval to the smallest interval we can time. // if (CountEvents < FastCountEvents) { CountEvents = (ULONGLONG)FastCountEvents; } // // Assume we will use the fast event count // Halp21164ProfileMapping[ProfileSource].EventCount = FastCountEvents; Halp21164ProfileMapping[ProfileSource].NumberOfTicks = (ULONG)((CountEvents + FastCountEvents - 1) / FastCountEvents); // // See if we can successfully use the slower period. If the requested // interval is greater than the slower tick period and the difference // between the requested interval and the interval that we can deliver // with the slower clock is acceptable, then use the slower clock. // We define an acceptable difference as a difference of less than // INTERVAL_DELTA of the requested interval. // if (CountEvents > SlowCountEvents) { ULONG NewInterval; NewInterval = (ULONG)(((CountEvents + SlowCountEvents-1) / SlowCountEvents) * SlowCountEvents); if (((NewInterval - CountEvents) * 100 / CountEvents) < INTERVAL_DELTA) { Halp21164ProfileMapping[ProfileSource].EventCount = SlowCountEvents; Halp21164ProfileMapping[ProfileSource].NumberOfTicks = NewInterval / SlowCountEvents; } } *Interval = Halp21164ProfileMapping[ProfileSource].EventCount * Halp21164ProfileMapping[ProfileSource].NumberOfTicks; if (ProfileSource == ProfileTime) { // // Convert cycle count back into 100ns clock ticks // // Use 64-bit integer to prevent overflow. // TempInterval = (ULONGLONG)(*Interval) * (ULONGLONG)(PCR->CycleClockPeriod); *Interval = (ULONG)(TempInterval / 100000); } return(STATUS_SUCCESS); } ULONG Hal21164SetProfileInterval ( IN ULONG Interval ) /*++ Routine Description: This routine sets the profile interrupt interval. Arguments: Interval - Supplies the desired profile interval in 100ns units. Return Value: The actual profile interval. --*/ { ULONG NewInterval; NewInterval = Interval; HalSetProfileSourceInterval(ProfileTime, &NewInterval); return(NewInterval); } VOID Hal21164StartProfileInterrupt ( KPROFILE_SOURCE ProfileSource ) /*++ Routine Description: This routine turns on the profile interrupt. N.B. This routine must be called at PROCLK_LEVEL while holding the profile lock. Arguments: None. Return Value: None. --*/ { ULONG PerformanceCounter; ULONG MuxControl; ULONG EventCount; // // Check input to see if we are turning on a source that is // supported. If it is unsupported, just return. // if ((ProfileSource > (sizeof(Halp21164ProfileMapping)/sizeof(HALP_PROFILE_MAPPING))) || (!Halp21164ProfileMapping[ProfileSource].Supported)) { return; } // // Set the performance counter within the processor to begin // counting total cycles. // PerformanceCounter = Halp21164ProfileMapping[ProfileSource].Counter; MuxControl = Halp21164ProfileMapping[ProfileSource].MuxControl; if (PerformanceCounter == Ev5PerformanceCounter0) { EventCount = (Halp21164ProfileMapping[ProfileSource].EventCount == Ev5CountEvents2xx16) ? Ev5EventCountLow : Ev5EventCountHigh; Halp21164ProfileSource0 = ProfileSource; Halp21164UpdatePerformanceCounter( PerformanceCounter, MuxControl, EventCount ); PCRProfileCountReload[0] = Halp21164ProfileMapping[ProfileSource].NumberOfTicks; PCRProfileCount[0] = Halp21164ProfileMapping[ProfileSource].NumberOfTicks; // // Enable the performance counter interrupt. // HalEnableSystemInterrupt ( PC0_VECTOR, PROFILE_LEVEL, LevelSensitive ); } else if (PerformanceCounter == Ev5PerformanceCounter1) { EventCount = (Halp21164ProfileMapping[ProfileSource].EventCount == Ev5CountEvents2xx16) ? Ev5EventCountLow : Ev5EventCountHigh; Halp21164ProfileSource1 = ProfileSource; Halp21164UpdatePerformanceCounter( PerformanceCounter, MuxControl, EventCount ); PCRProfileCountReload[1] = Halp21164ProfileMapping[ProfileSource].NumberOfTicks; PCRProfileCount[1] = Halp21164ProfileMapping[ProfileSource].NumberOfTicks; // // Enable the performance counter interrupt. // HalEnableSystemInterrupt ( PC1_VECTOR, PROFILE_LEVEL, LevelSensitive ); } else if (PerformanceCounter == Ev5PerformanceCounter2) { EventCount = (Halp21164ProfileMapping[ProfileSource].EventCount == Ev5CountEvents2xx14) ? Ev5EventCountLow : Ev5EventCountHigh; Halp21164ProfileSource2 = ProfileSource; Halp21164UpdatePerformanceCounter( PerformanceCounter, MuxControl, EventCount ); PCRProfileCountReload[2] = Halp21164ProfileMapping[ProfileSource].NumberOfTicks; PCRProfileCount[2] = Halp21164ProfileMapping[ProfileSource].NumberOfTicks; // // Enable the performance counter interrupt. // HalEnableSystemInterrupt ( PC2_VECTOR, PROFILE_LEVEL, LevelSensitive ); } return; } VOID Hal21164StopProfileInterrupt ( KPROFILE_SOURCE ProfileSource ) /*++ Routine Description: This routine turns off the profile interrupt. N.B. This routine must be called at PROCLK_LEVEL while holding the profile lock. Arguments: None. Return Value: None. --*/ { ULONG PerformanceCounter; ULONG Vector; // // Check input to see if we are turning off a source that is // supported. If it is unsupported, just return. // if ((ProfileSource > (sizeof(Halp21164ProfileMapping)/sizeof(HALP_PROFILE_MAPPING))) || (!Halp21164ProfileMapping[ProfileSource].Supported)) { return; } // // Stop the performance counter from interrupting. // PerformanceCounter = Halp21164ProfileMapping[ProfileSource].Counter; Halp21164UpdatePerformanceCounter( PerformanceCounter, 0, Ev5CounterDisable ); // // Disable the performance counter interrupt. // if (PerformanceCounter == Ev5PerformanceCounter0) { HalDisableSystemInterrupt( PC0_VECTOR, PROFILE_LEVEL ); // // Clear the current profile count. Can't clear value in PCR // since a profile interrupt could be pending or in progress // so clear the reload counter. // PCRProfileCountReload[0] = 0; } else if (PerformanceCounter == Ev5PerformanceCounter1) { HalDisableSystemInterrupt( PC1_VECTOR, PROFILE_LEVEL ); // // Clear the current profile count. Can't clear value in PCR // since a profile interrupt could be pending or in progress // so clear the reload counter. // PCRProfileCountReload[1] = 0; } else if (PerformanceCounter == Ev5PerformanceCounter2) { HalDisableSystemInterrupt( PC2_VECTOR, PROFILE_LEVEL ); // // Clear the current profile count. Can't clear value in PCR // since a profile interrupt could be pending or in progress // so clear the reload counter. // PCRProfileCountReload[2] = 0; } return; } VOID Halp21164UpdatePerformanceCounter( IN ULONG PerformanceCounter, IN ULONG MuxControl, IN ULONG EventCount ) //++ // // Routine Description: // // Write the specified microprocessor internal performance counter. // // Arguments: // // PerformanceCounter(a0) - Supplies the number of the performance counter // to write. // // MuxControl(a2) - Supplies the mux control value which selects which // type of event to count when the counter is enabled. // // EventCount(a3) - Supplies the event interval when the counter is // enabled. // // Return Value: // // None. // //-- { PMCTR_21164 PmCtr; // the performance counter register ULONG CboxMux1 = 0; // CBOX select 1 mux value ULONG CboxMux2 = 0; // CBOX select 2 mux value PmCtr.all = HalpRead21164PerformanceCounter(); // // Check for special values first: // if ( MuxControl >= Ev5PcSpecial ) { switch( MuxControl ) { // // Count JsrRet Issued // case Ev5JsrRetIssued: PmCtr.Ctl1 = EventCount; PmCtr.Sel1 = Ev5FlowChangeInst; PmCtr.Sel2 = Ev5PCMispredicts; break; // // Count CondBr Issued // case Ev5CondBrIssued: PmCtr.Ctl1 = EventCount; PmCtr.Sel1 = Ev5FlowChangeInst; PmCtr.Sel2 = Ev5BRMispredicts; break; // // Count all flow change inst Issued // case Ev5AllFlowIssued: PmCtr.Ctl1 = EventCount; PmCtr.Sel1 = Ev5FlowChangeInst; if ( (PmCtr.Sel2 == Ev5PCMispredicts) || (PmCtr.Sel2 == Ev5BRMispredicts)) { PmCtr.Sel2 = Ev5LongStalls; } break; // // Must be an Scache counter. Select the appropriate counter // in Sel1 or Sel2, and pass the CBOX mux value to WritePerfCounter // default: if ( MuxControl <= Ev5ScSystemCmdReq ) { PmCtr.Ctl1 = EventCount; PmCtr.Sel1 = Ev5CBOXInput1; CboxMux1 = MuxControl - Ev5ScMux1; } else if ( MuxControl <= Ev5ScSysReadReq ) { PmCtr.Ctl2 = EventCount; PmCtr.Sel2 = Ev5CBOXInput2; CboxMux2 = MuxControl - Ev5ScMux2; } } // switch } else if ( PerformanceCounter == Ev5PerformanceCounter0 ) { PmCtr.Ctl0 = EventCount; PmCtr.Sel0 = MuxControl; } else if ( PerformanceCounter == Ev5PerformanceCounter1 ) { PmCtr.Ctl1 = EventCount; PmCtr.Sel1 = MuxControl; } else if ( PerformanceCounter == Ev5PerformanceCounter2 ) { PmCtr.Ctl2 = EventCount; PmCtr.Sel2 = MuxControl; } HalpWrite21164PerformanceCounter(PmCtr.all, CboxMux1, CboxMux2); }