Hardware/CPU/AMD10CPU.cs
author moel.mich
Thu, 30 Sep 2010 16:51:09 +0000
changeset 201 958e9fe8afdf
parent 197 fb66f749b7ff
child 203 ca487ba88c24
permissions -rw-r--r--
Improved the implementation for the AMD 10h family CPU clock speeds.
     1 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     5   The contents of this file are subject to the Mozilla Public License Version
     6   1.1 (the "License"); you may not use this file except in compliance with
     7   the License. You may obtain a copy of the License at
     8  
     9   http://www.mozilla.org/MPL/
    10 
    11   Software distributed under the License is distributed on an "AS IS" basis,
    12   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    13   for the specific language governing rights and limitations under the License.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    17   The Initial Developer of the Original Code is 
    18   Michael Möller <m.moeller@gmx.ch>.
    19   Portions created by the Initial Developer are Copyright (C) 2009-2010
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s):
    23 
    24   Alternatively, the contents of this file may be used under the terms of
    25   either the GNU General Public License Version 2 or later (the "GPL"), or
    26   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    27   in which case the provisions of the GPL or the LGPL are applicable instead
    28   of those above. If you wish to allow use of your version of this file only
    29   under the terms of either the GPL or the LGPL, and not to allow others to
    30   use your version of this file under the terms of the MPL, indicate your
    31   decision by deleting the provisions above and replace them with the notice
    32   and other provisions required by the GPL or the LGPL. If you do not delete
    33   the provisions above, a recipient may use your version of this file under
    34   the terms of any one of the MPL, the GPL or the LGPL.
    35  
    36 */
    37 
    38 using System;
    39 using System.Collections.Generic;
    40 using System.Diagnostics;
    41 using System.Globalization;
    42 using System.Runtime.InteropServices;
    43 using System.Text;
    44 using System.Threading;
    45 
    46 namespace OpenHardwareMonitor.Hardware.CPU {
    47 
    48   internal sealed class AMD10CPU : AMDCPU {
    49 
    50     private readonly Sensor coreTemperature;
    51     private readonly Sensor[] coreClocks;
    52     private readonly Sensor busClock;
    53       
    54     private const uint PERF_CTL_0 = 0xC0010000;
    55     private const uint PERF_CTR_0 = 0xC0010004;
    56     private const uint P_STATE_0 = 0xC0010064;
    57     private const uint COFVID_STATUS = 0xC0010071;
    58 
    59     private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
    60     private const ushort MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
    61     private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
    62 
    63     private readonly uint miscellaneousControlAddress;
    64 
    65     private double timeStampCounterMultiplier;
    66 
    67     private StringBuilder debug = new StringBuilder();
    68 
    69     public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
    70       : base(processorIndex, cpuid, settings) 
    71     {            
    72       // AMD family 10h processors support only one temperature sensor
    73       coreTemperature = new Sensor(
    74         "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
    75         SensorType.Temperature, this, new [] {
    76             new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
    77           }, settings);
    78 
    79       // get the pci address for the Miscellaneous Control registers 
    80       miscellaneousControlAddress = GetPciAddress(
    81         MISCELLANEOUS_CONTROL_FUNCTION, MISCELLANEOUS_CONTROL_DEVICE_ID);
    82 
    83       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
    84       coreClocks = new Sensor[coreCount];
    85       for (int i = 0; i < coreClocks.Length; i++) {
    86         coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
    87           this, settings);
    88         if (HasTimeStampCounter)
    89           ActivateSensor(coreClocks[i]);
    90       }
    91 
    92       // set affinity to the first thread for all frequency estimations
    93       IntPtr thread = NativeMethods.GetCurrentThread();
    94       UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
    95         (UIntPtr)(1L << cpuid[0][0].Thread));
    96 
    97       uint ctlEax, ctlEdx;
    98       WinRing0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
    99       uint ctrEax, ctrEdx;
   100       WinRing0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
   101 
   102       timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
   103 
   104       // restore the performance counter registers
   105       WinRing0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
   106       WinRing0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
   107 
   108       // restore the thread affinity.
   109       NativeMethods.SetThreadAffinityMask(thread, mask);
   110 
   111       Update();                   
   112     }
   113 
   114     private double estimateTimeStampCounterMultiplier() {
   115       // preload the function
   116       estimateTimeStampCounterMultiplier(0);
   117       estimateTimeStampCounterMultiplier(0);
   118 
   119       // estimate the multiplier
   120       List<double> estimate = new List<double>(3);
   121       for (int i = 0; i < 3; i++)
   122         estimate.Add(estimateTimeStampCounterMultiplier(0.025));
   123       estimate.Sort();
   124       return estimate[1];
   125     }
   126 
   127     private double estimateTimeStampCounterMultiplier(double timeWindow) {
   128       uint eax, edx;
   129      
   130       // select event "076h CPU Clocks not Halted" and enable the counter
   131       WinRing0.Wrmsr(PERF_CTL_0,
   132         (1 << 22) | // enable performance counter
   133         (1 << 17) | // count events in user mode
   134         (1 << 16) | // count events in operating-system mode
   135         0x76, 0x00000000);
   136 
   137       // set the counter to 0
   138       WinRing0.Wrmsr(PERF_CTR_0, 0, 0);
   139 
   140       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   141       uint lsbBegin, msbBegin, lsbEnd, msbEnd;
   142 
   143       long timeBegin = Stopwatch.GetTimestamp() +
   144         (long)Math.Ceiling(0.001 * ticks);
   145       long timeEnd = timeBegin + ticks;
   146       while (Stopwatch.GetTimestamp() < timeBegin) { }
   147       WinRing0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
   148       while (Stopwatch.GetTimestamp() < timeEnd) { }
   149       WinRing0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
   150 
   151       WinRing0.Rdmsr(COFVID_STATUS, out eax, out edx);
   152       uint cpuDid = (eax >> 6) & 7;
   153       uint cpuFid = eax & 0x1F;
   154       double coreMultiplier = MultiplierFromIDs(cpuDid, cpuFid);
   155 
   156       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   157       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   158 
   159       double coreFrequency = 1e-6 * 
   160         (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
   161         (timeEnd - timeBegin);
   162 
   163       double busFrequency = coreFrequency / coreMultiplier;
   164       return 0.5 * Math.Round(2 * TimeStampCounterFrequency / busFrequency);
   165     }
   166 
   167     protected override uint[] GetMSRs() {
   168       return new uint[] { PERF_CTL_0, PERF_CTR_0, P_STATE_0, COFVID_STATUS };
   169     }
   170 
   171     public override string GetReport() {
   172       StringBuilder r = new StringBuilder();
   173       r.Append(base.GetReport());
   174 
   175       r.Append("Miscellaneous Control Address: 0x");
   176       r.AppendLine((miscellaneousControlAddress).ToString("X",
   177         CultureInfo.InvariantCulture));
   178       r.Append("Time Stamp Counter Multiplier: ");
   179       r.AppendLine(timeStampCounterMultiplier.ToString(
   180         CultureInfo.InvariantCulture));
   181       r.AppendLine();
   182 
   183       r.Append(debug);
   184       r.AppendLine();
   185 
   186       return r.ToString();
   187     }
   188 
   189     private static double MultiplierFromIDs(uint divisorID, uint frequencyID) {
   190       return 0.5 * (frequencyID + 0x10) / (1 << (int)divisorID);
   191     }
   192 
   193     public override void Update() {
   194       base.Update();
   195 
   196       if (miscellaneousControlAddress != WinRing0.InvalidPciAddress) {
   197         uint value;
   198         if (WinRing0.ReadPciConfigDwordEx(miscellaneousControlAddress,
   199           REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
   200           coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
   201             coreTemperature.Parameters[0].Value;
   202           ActivateSensor(coreTemperature);
   203         } else {
   204           DeactivateSensor(coreTemperature);
   205         }
   206       }
   207 
   208       if (HasTimeStampCounter) {
   209         double newBusClock = 0;
   210 
   211         for (int i = 0; i < coreClocks.Length; i++) {
   212           Thread.Sleep(1);
   213 
   214           uint curEax, curEdx;
   215           if (WinRing0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
   216             (UIntPtr)(1L << cpuid[i][0].Thread))) 
   217           {
   218             // 8:6 CpuDid: current core divisor ID
   219             // 5:0 CpuFid: current core frequency ID
   220             uint cpuDid = (curEax >> 6) & 7;
   221             uint cpuFid = curEax & 0x1F;
   222             double multiplier = MultiplierFromIDs(cpuDid, cpuFid);
   223 
   224             coreClocks[i].Value = 
   225               (float)(multiplier * TimeStampCounterFrequency / 
   226               timeStampCounterMultiplier);
   227             newBusClock = 
   228               (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
   229           } else {
   230             coreClocks[i].Value = (float)TimeStampCounterFrequency;
   231           }
   232         }
   233 
   234         if (newBusClock > 0) {
   235           this.busClock.Value = (float)newBusClock;
   236           ActivateSensor(this.busClock);
   237         }
   238       }
   239     }
   240 
   241     private static class NativeMethods {
   242       private const string KERNEL = "kernel32.dll";
   243 
   244       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   245       public static extern UIntPtr
   246         SetThreadAffinityMask(IntPtr handle, UIntPtr mask);
   247 
   248       [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
   249       public static extern IntPtr GetCurrentThread();
   250     }
   251   }
   252 }