Hardware/CPU/AMD10CPU.cs
changeset 201 958e9fe8afdf
parent 197 fb66f749b7ff
child 203 ca487ba88c24
     1.1 --- a/Hardware/CPU/AMD10CPU.cs	Mon Sep 27 23:48:41 2010 +0000
     1.2 +++ b/Hardware/CPU/AMD10CPU.cs	Thu Sep 30 16:51:09 2010 +0000
     1.3 @@ -36,7 +36,10 @@
     1.4  */
     1.5  
     1.6  using System;
     1.7 +using System.Collections.Generic;
     1.8 +using System.Diagnostics;
     1.9  using System.Globalization;
    1.10 +using System.Runtime.InteropServices;
    1.11  using System.Text;
    1.12  using System.Threading;
    1.13  
    1.14 @@ -47,16 +50,22 @@
    1.15      private readonly Sensor coreTemperature;
    1.16      private readonly Sensor[] coreClocks;
    1.17      private readonly Sensor busClock;
    1.18 -
    1.19 +      
    1.20 +    private const uint PERF_CTL_0 = 0xC0010000;
    1.21 +    private const uint PERF_CTR_0 = 0xC0010004;
    1.22 +    private const uint P_STATE_0 = 0xC0010064;
    1.23      private const uint COFVID_STATUS = 0xC0010071;
    1.24 -    private const uint P_STATE_0 = 0xC0010064;
    1.25  
    1.26      private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
    1.27      private const ushort MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
    1.28      private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
    1.29 -    
    1.30 +
    1.31      private readonly uint miscellaneousControlAddress;
    1.32  
    1.33 +    private double timeStampCounterMultiplier;
    1.34 +
    1.35 +    private StringBuilder debug = new StringBuilder();
    1.36 +
    1.37      public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
    1.38        : base(processorIndex, cpuid, settings) 
    1.39      {            
    1.40 @@ -76,15 +85,87 @@
    1.41        for (int i = 0; i < coreClocks.Length; i++) {
    1.42          coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
    1.43            this, settings);
    1.44 -        if (hasTSC)
    1.45 +        if (HasTimeStampCounter)
    1.46            ActivateSensor(coreClocks[i]);
    1.47        }
    1.48  
    1.49 +      // set affinity to the first thread for all frequency estimations
    1.50 +      IntPtr thread = NativeMethods.GetCurrentThread();
    1.51 +      UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
    1.52 +        (UIntPtr)(1L << cpuid[0][0].Thread));
    1.53 +
    1.54 +      uint ctlEax, ctlEdx;
    1.55 +      WinRing0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
    1.56 +      uint ctrEax, ctrEdx;
    1.57 +      WinRing0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
    1.58 +
    1.59 +      timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
    1.60 +
    1.61 +      // restore the performance counter registers
    1.62 +      WinRing0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
    1.63 +      WinRing0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
    1.64 +
    1.65 +      // restore the thread affinity.
    1.66 +      NativeMethods.SetThreadAffinityMask(thread, mask);
    1.67 +
    1.68        Update();                   
    1.69      }
    1.70  
    1.71 +    private double estimateTimeStampCounterMultiplier() {
    1.72 +      // preload the function
    1.73 +      estimateTimeStampCounterMultiplier(0);
    1.74 +      estimateTimeStampCounterMultiplier(0);
    1.75 +
    1.76 +      // estimate the multiplier
    1.77 +      List<double> estimate = new List<double>(3);
    1.78 +      for (int i = 0; i < 3; i++)
    1.79 +        estimate.Add(estimateTimeStampCounterMultiplier(0.025));
    1.80 +      estimate.Sort();
    1.81 +      return estimate[1];
    1.82 +    }
    1.83 +
    1.84 +    private double estimateTimeStampCounterMultiplier(double timeWindow) {
    1.85 +      uint eax, edx;
    1.86 +     
    1.87 +      // select event "076h CPU Clocks not Halted" and enable the counter
    1.88 +      WinRing0.Wrmsr(PERF_CTL_0,
    1.89 +        (1 << 22) | // enable performance counter
    1.90 +        (1 << 17) | // count events in user mode
    1.91 +        (1 << 16) | // count events in operating-system mode
    1.92 +        0x76, 0x00000000);
    1.93 +
    1.94 +      // set the counter to 0
    1.95 +      WinRing0.Wrmsr(PERF_CTR_0, 0, 0);
    1.96 +
    1.97 +      long ticks = (long)(timeWindow * Stopwatch.Frequency);
    1.98 +      uint lsbBegin, msbBegin, lsbEnd, msbEnd;
    1.99 +
   1.100 +      long timeBegin = Stopwatch.GetTimestamp() +
   1.101 +        (long)Math.Ceiling(0.001 * ticks);
   1.102 +      long timeEnd = timeBegin + ticks;
   1.103 +      while (Stopwatch.GetTimestamp() < timeBegin) { }
   1.104 +      WinRing0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
   1.105 +      while (Stopwatch.GetTimestamp() < timeEnd) { }
   1.106 +      WinRing0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
   1.107 +
   1.108 +      WinRing0.Rdmsr(COFVID_STATUS, out eax, out edx);
   1.109 +      uint cpuDid = (eax >> 6) & 7;
   1.110 +      uint cpuFid = eax & 0x1F;
   1.111 +      double coreMultiplier = MultiplierFromIDs(cpuDid, cpuFid);
   1.112 +
   1.113 +      ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   1.114 +      ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   1.115 +
   1.116 +      double coreFrequency = 1e-6 * 
   1.117 +        (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
   1.118 +        (timeEnd - timeBegin);
   1.119 +
   1.120 +      double busFrequency = coreFrequency / coreMultiplier;
   1.121 +      return 0.5 * Math.Round(2 * TimeStampCounterFrequency / busFrequency);
   1.122 +    }
   1.123 +
   1.124      protected override uint[] GetMSRs() {
   1.125 -      return new uint[] { P_STATE_0, COFVID_STATUS };
   1.126 +      return new uint[] { PERF_CTL_0, PERF_CTR_0, P_STATE_0, COFVID_STATUS };
   1.127      }
   1.128  
   1.129      public override string GetReport() {
   1.130 @@ -94,12 +175,18 @@
   1.131        r.Append("Miscellaneous Control Address: 0x");
   1.132        r.AppendLine((miscellaneousControlAddress).ToString("X",
   1.133          CultureInfo.InvariantCulture));
   1.134 +      r.Append("Time Stamp Counter Multiplier: ");
   1.135 +      r.AppendLine(timeStampCounterMultiplier.ToString(
   1.136 +        CultureInfo.InvariantCulture));
   1.137 +      r.AppendLine();
   1.138 +
   1.139 +      r.Append(debug);
   1.140        r.AppendLine();
   1.141  
   1.142        return r.ToString();
   1.143      }
   1.144  
   1.145 -    private double MultiplierFromIDs(uint divisorID, uint frequencyID) {
   1.146 +    private static double MultiplierFromIDs(uint divisorID, uint frequencyID) {
   1.147        return 0.5 * (frequencyID + 0x10) / (1 << (int)divisorID);
   1.148      }
   1.149  
   1.150 @@ -118,35 +205,29 @@
   1.151          }
   1.152        }
   1.153  
   1.154 -      if (hasTSC) {
   1.155 +      if (HasTimeStampCounter) {
   1.156          double newBusClock = 0;
   1.157  
   1.158          for (int i = 0; i < coreClocks.Length; i++) {
   1.159            Thread.Sleep(1);
   1.160  
   1.161            uint curEax, curEdx;
   1.162 -          uint maxEax, maxEdx;
   1.163            if (WinRing0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
   1.164 -            (UIntPtr)(1L << cpuid[i][0].Thread)) &&
   1.165 -            WinRing0.RdmsrTx(P_STATE_0, out maxEax, out maxEdx,
   1.166              (UIntPtr)(1L << cpuid[i][0].Thread))) 
   1.167            {
   1.168              // 8:6 CpuDid: current core divisor ID
   1.169              // 5:0 CpuFid: current core frequency ID
   1.170 -            uint curCpuDid = (curEax >> 6) & 7;
   1.171 -            uint curCpuFid = curEax & 0x1F;
   1.172 -            double multiplier = MultiplierFromIDs(curCpuDid, curCpuFid);
   1.173 +            uint cpuDid = (curEax >> 6) & 7;
   1.174 +            uint cpuFid = curEax & 0x1F;
   1.175 +            double multiplier = MultiplierFromIDs(cpuDid, cpuFid);
   1.176  
   1.177 -            // we assume that the max multiplier (used for the TSC) 
   1.178 -            // can be found in the P_STATE_0 MSR
   1.179 -            uint maxCpuDid = (maxEax >> 6) & 7;
   1.180 -            uint maxCpuFid = maxEax & 0x1F;
   1.181 -            double maxMultiplier = MultiplierFromIDs(maxCpuDid, maxCpuFid);
   1.182 -
   1.183 -            coreClocks[i].Value = (float)(multiplier * MaxClock / maxMultiplier);
   1.184 -            newBusClock = (float)(MaxClock / maxMultiplier);
   1.185 +            coreClocks[i].Value = 
   1.186 +              (float)(multiplier * TimeStampCounterFrequency / 
   1.187 +              timeStampCounterMultiplier);
   1.188 +            newBusClock = 
   1.189 +              (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
   1.190            } else {
   1.191 -            coreClocks[i].Value = (float)MaxClock;
   1.192 +            coreClocks[i].Value = (float)TimeStampCounterFrequency;
   1.193            }
   1.194          }
   1.195  
   1.196 @@ -155,6 +236,17 @@
   1.197            ActivateSensor(this.busClock);
   1.198          }
   1.199        }
   1.200 -    } 
   1.201 +    }
   1.202 +
   1.203 +    private static class NativeMethods {
   1.204 +      private const string KERNEL = "kernel32.dll";
   1.205 +
   1.206 +      [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   1.207 +      public static extern UIntPtr
   1.208 +        SetThreadAffinityMask(IntPtr handle, UIntPtr mask);
   1.209 +
   1.210 +      [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   1.211 +      public static extern IntPtr GetCurrentThread();
   1.212 +    }
   1.213    }
   1.214  }